<commit_before>// MIT License

// Copyright (c) 2018 Zhuhao Wang

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include "nekit/data_flow/tls_data_flow.h"

namespace nekit {
namespace data_flow {
TlsDataFlow::TlsDataFlow(std::shared_ptr<utils::Session> session,
                         std::shared_ptr<SSL_CTX> ctx,
                         std::unique_ptr<RemoteDataFlowInterface>&& data_flow)
    : session_{session},
      tunnel_{ctx, crypto::TlsTunnel::Mode::Client},
      data_flow_{std::move(data_flow)} {};

TlsDataFlow::~TlsDataFlow() {
  read_cancelable_.Cancel();
  write_cancelable_.Cancel();
  connect_cancelable_.Cancel();
  next_read_cancelable_.Cancel();
  next_write_cancelable_.Cancel();
}

utils::Cancelable TlsDataFlow::Read(DataEventHandler handler) {
  BOOST_ASSERT(!error_reported_);

  read_cancelable_ = utils::Cancelable();
  read_handler_ = handler;

  state_machine_.ReadBegin();
  Process();

  return read_cancelable_;
}

utils::Cancelable TlsDataFlow::Write(utils::Buffer&& buffer,
                                     EventHandler handler) {
  BOOST_ASSERT(!error_reported_);

  write_cancelable_ = utils::Cancelable();
  write_handler_ = handler;

  state_machine_.WriteBegin();

  tunnel_.WritePlainTextData(std::move(buffer));

  Process();

  return write_cancelable_;
}

utils::Cancelable TlsDataFlow::CloseWrite(EventHandler handler) {
  (void)handler;
  return write_cancelable_;
}

const data_flow::FlowStateMachine& TlsDataFlow::StateMachine() const {
  return state_machine_;
}

data_flow::DataFlowInterface* TlsDataFlow::NextHop() const {
  return data_flow_.get();
}

std::shared_ptr<utils::Endpoint> TlsDataFlow::ConnectingTo() {
  return connect_to_;
}

data_flow::DataType TlsDataFlow::FlowDataType() const {
  return data_flow::DataType::Stream;
}

std::shared_ptr<utils::Session> TlsDataFlow::Session() const {
  return session_;
}

utils::Runloop* TlsDataFlow::GetRunloop() { return data_flow_->GetRunloop(); }

utils::Cancelable TlsDataFlow::Connect(
    std::shared_ptr<utils::Endpoint> endpoint, EventHandler handler) {
  connect_cancelable_ = utils::Cancelable();
  connect_to_ = endpoint;
  tunnel_.SetDomain(endpoint->host());

  connect_handler_ = handler;
  state_machine_.ConnectBegin();
  (void)data_flow_->Connect(endpoint, [this, cancelable{connect_cancelable_}](
                                          utils::Result<void>&& result) {
    if (cancelable.canceled()) {
      return;
    }

    if (!result) {
      connect_handler_(std::move(result));
      return;
    }

    HandShake();
  });

  return connect_cancelable_;
}

RemoteDataFlowInterface* TlsDataFlow::NextRemoteHop() const {
  return static_cast<RemoteDataFlowInterface*>(data_flow_.get());
}

void TlsDataFlow::HandShake() {
  using namespace crypto;
  auto action = tunnel_.HandShake();
  if (action) {
    switch (*action) {
      case TlsTunnel::HandShakeAction::Success: {
        if (utils::Buffer b = tunnel_.ReadCipherTextData()) {
          write_cancelable_ = data_flow_->Write(
              std::move(b), [this, cancelable{connect_cancelable_}](
                                utils::Result<void>&& result) {
                if (cancelable.canceled()) {
                  return;
                }

                if (!result) {
                  state_machine_.Errored();
                  connect_handler_(std::move(result));
                  return;
                }
                HandShake();
                return;
              });
          return;
        }
        state_machine_.Connected();
        connect_handler_({});
        connect_handler_ = nullptr;
        return;
      }
      case TlsTunnel::HandShakeAction::WantIo: {
        if (utils::Buffer b = tunnel_.ReadCipherTextData()) {
          write_cancelable_ = data_flow_->Write(
              std::move(b), [this, cancelable{connect_cancelable_}](
                                utils::Result<void>&& result) {
                if (cancelable.canceled()) {
                  return;
                }

                if (!result) {
                  state_machine_.Errored();
                  connect_handler_(std::move(result));
                  return;
                }
                HandShake();
                return;
              });
        } else {
          read_cancelable_ =
              data_flow_->Read([this, cancelable{connect_cancelable_}](
                                   utils::Result<utils::Buffer>&& buffer) {
                if (cancelable.canceled()) {
                  return;
                }

                if (!buffer) {
                  state_machine_.Errored();
                  connect_handler_(
                      utils::MakeErrorResult(std::move(buffer).error()));
                  return;
                }
                tunnel_.WriteCipherTextData(*std::move(buffer));
                HandShake();
                return;
              });
        }
        return;
      }
    }
  } else {
    state_machine_.Errored();
    connect_handler_(utils::MakeErrorResult(std::move(action).error()));
    return;
  }
}

void TlsDataFlow::Process() {
  if (error_reported_) {
    return;
  }

  if (pending_error_) {
    if (ReportError(std::move(pending_error_), true)) {
      error_reported_ = true;
    }
    return;
  }

  TryRead();
  TryWrite();
}

void TlsDataFlow::TryRead() {
  if (read_handler_) {
    if (tunnel_.HasPlainTextDataToRead()) {
      GetRunloop()->Post([this, buffer{tunnel_.ReadPlainTextData()},
                          handler{read_handler_},
                          cancelable{read_cancelable_}]() mutable {
        if (cancelable.canceled()) {
          return;
        }

        state_machine_.ReadEnd();

        if (!buffer) {
          state_machine_.Errored();
        }

        handler(std::move(buffer));
      });

      read_handler_ = nullptr;

      if (tunnel_.NeedCipherInput()) {
        TryReadNextHop();
      }

      return;
    } else {
      TryReadNextHop();
      return;
    }
  } else {
    if (tunnel_.NeedCipherInput()) {
      TryReadNextHop();
      return;
    }
  }
}

void TlsDataFlow::TryWrite() {
  if (tunnel_.FinishWritingCipherData() && write_handler_) {
    GetRunloop()->Post(
        [this, handler{write_handler_}, cancelable{write_cancelable_}]() {
          if (cancelable.canceled()) {
            return;
          }

          state_machine_.WriteEnd();

          handler({});
        });
    write_handler_ = nullptr;
    return;
  };

  if (!tunnel_.FinishWritingCipherData()) {
    TryWriteNextHop();
  }
}

void TlsDataFlow::TryReadNextHop() {
  if (data_flow_->StateMachine().IsReading()) {
    return;
  }

  next_read_cancelable_ =
      data_flow_->Read([this, cancelable{read_cancelable_}](
                           utils::Result<utils::Buffer>&& buffer) {
        if (cancelable.canceled()) {
          return;
        }

        if (!buffer) {
          if (!ReportError(std::move(buffer).error(), true)) {
            pending_error_ = std::move(buffer).error();
          } else {
            error_reported_ = true;
          }
          return;
        }

        tunnel_.WriteCipherTextData(*std::move(buffer));
        Process();
      });
}

void TlsDataFlow::TryWriteNextHop() {
  if (data_flow_->StateMachine().IsWriting()) {
    return;
  }

  if (tunnel_.FinishWritingCipherData()) {
    return;
  }

  next_write_cancelable_ = data_flow_->Write(
      tunnel_.ReadCipherTextData(),
      [this, cancelable{write_cancelable_}](utils::Result<void>&& result) {
        if (cancelable.canceled()) {
          return;
        }

        if (!result) {
          if (!ReportError(std::move(result).error(), false)) {
            pending_error_ = std::move(result).error();
          } else {
            error_reported_ = true;
          }
          return;
        }

        Process();
      });
}

bool TlsDataFlow::ReportError(utils::Error&& error, bool try_read_first) {
  if (try_read_first ? ReadReportError(std::move(error))
                     : WriteReportError(std::move(error))) {
    return true;
  }

  return try_read_first ? WriteReportError(std::move(error))
                        : ReadReportError(std::move(error));
}

bool TlsDataFlow::ReadReportError(utils::Error&& error) {
  if (read_handler_) {
    // should we update error_reported here?
    auto handler = read_handler_;
    handler(utils::MakeErrorResult(std::move(error)));
    read_handler_ = nullptr;
    return true;
  }
  return false;
}

bool TlsDataFlow::WriteReportError(utils::Error&& error) {
  if (write_handler_) {
    auto handler = write_handler_;
    handler(utils::MakeErrorResult(std::move(error)));
    write_handler_ = nullptr;
    return true;
  }

  return false;
}
}  // namespace data_flow
}  // namespace nekit
<commit_msg>STYLE: Fix syntax issue<commit_after>// MIT License

// Copyright (c) 2018 Zhuhao Wang

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include "nekit/data_flow/tls_data_flow.h"

namespace nekit {
namespace data_flow {
TlsDataFlow::TlsDataFlow(std::shared_ptr<utils::Session> session,
                         std::shared_ptr<SSL_CTX> ctx,
                         std::unique_ptr<RemoteDataFlowInterface>&& data_flow)
    : session_{session},
      tunnel_{ctx, crypto::TlsTunnel::Mode::Client},
      data_flow_{std::move(data_flow)} {}

TlsDataFlow::~TlsDataFlow() {
  read_cancelable_.Cancel();
  write_cancelable_.Cancel();
  connect_cancelable_.Cancel();
  next_read_cancelable_.Cancel();
  next_write_cancelable_.Cancel();
}

utils::Cancelable TlsDataFlow::Read(DataEventHandler handler) {
  BOOST_ASSERT(!error_reported_);

  read_cancelable_ = utils::Cancelable();
  read_handler_ = handler;

  state_machine_.ReadBegin();
  Process();

  return read_cancelable_;
}

utils::Cancelable TlsDataFlow::Write(utils::Buffer&& buffer,
                                     EventHandler handler) {
  BOOST_ASSERT(!error_reported_);

  write_cancelable_ = utils::Cancelable();
  write_handler_ = handler;

  state_machine_.WriteBegin();

  tunnel_.WritePlainTextData(std::move(buffer));

  Process();

  return write_cancelable_;
}

utils::Cancelable TlsDataFlow::CloseWrite(EventHandler handler) {
  (void)handler;
  return write_cancelable_;
}

const data_flow::FlowStateMachine& TlsDataFlow::StateMachine() const {
  return state_machine_;
}

data_flow::DataFlowInterface* TlsDataFlow::NextHop() const {
  return data_flow_.get();
}

std::shared_ptr<utils::Endpoint> TlsDataFlow::ConnectingTo() {
  return connect_to_;
}

data_flow::DataType TlsDataFlow::FlowDataType() const {
  return data_flow::DataType::Stream;
}

std::shared_ptr<utils::Session> TlsDataFlow::Session() const {
  return session_;
}

utils::Runloop* TlsDataFlow::GetRunloop() { return data_flow_->GetRunloop(); }

utils::Cancelable TlsDataFlow::Connect(
    std::shared_ptr<utils::Endpoint> endpoint, EventHandler handler) {
  connect_cancelable_ = utils::Cancelable();
  connect_to_ = endpoint;
  tunnel_.SetDomain(endpoint->host());

  connect_handler_ = handler;
  state_machine_.ConnectBegin();
  (void)data_flow_->Connect(endpoint, [this, cancelable{connect_cancelable_}](
                                          utils::Result<void>&& result) {
    if (cancelable.canceled()) {
      return;
    }

    if (!result) {
      connect_handler_(std::move(result));
      return;
    }

    HandShake();
  });

  return connect_cancelable_;
}

RemoteDataFlowInterface* TlsDataFlow::NextRemoteHop() const {
  return static_cast<RemoteDataFlowInterface*>(data_flow_.get());
}

void TlsDataFlow::HandShake() {
  using namespace crypto;
  auto action = tunnel_.HandShake();
  if (action) {
    switch (*action) {
      case TlsTunnel::HandShakeAction::Success: {
        if (utils::Buffer b = tunnel_.ReadCipherTextData()) {
          write_cancelable_ = data_flow_->Write(
              std::move(b), [this, cancelable{connect_cancelable_}](
                                utils::Result<void>&& result) {
                if (cancelable.canceled()) {
                  return;
                }

                if (!result) {
                  state_machine_.Errored();
                  connect_handler_(std::move(result));
                  return;
                }
                HandShake();
                return;
              });
          return;
        }
        state_machine_.Connected();
        connect_handler_({});
        connect_handler_ = nullptr;
        return;
      }
      case TlsTunnel::HandShakeAction::WantIo: {
        if (utils::Buffer b = tunnel_.ReadCipherTextData()) {
          write_cancelable_ = data_flow_->Write(
              std::move(b), [this, cancelable{connect_cancelable_}](
                                utils::Result<void>&& result) {
                if (cancelable.canceled()) {
                  return;
                }

                if (!result) {
                  state_machine_.Errored();
                  connect_handler_(std::move(result));
                  return;
                }
                HandShake();
                return;
              });
        } else {
          read_cancelable_ =
              data_flow_->Read([this, cancelable{connect_cancelable_}](
                                   utils::Result<utils::Buffer>&& buffer) {
                if (cancelable.canceled()) {
                  return;
                }

                if (!buffer) {
                  state_machine_.Errored();
                  connect_handler_(
                      utils::MakeErrorResult(std::move(buffer).error()));
                  return;
                }
                tunnel_.WriteCipherTextData(*std::move(buffer));
                HandShake();
                return;
              });
        }
        return;
      }
    }
  } else {
    state_machine_.Errored();
    connect_handler_(utils::MakeErrorResult(std::move(action).error()));
    return;
  }
}

void TlsDataFlow::Process() {
  if (error_reported_) {
    return;
  }

  if (pending_error_) {
    if (ReportError(std::move(pending_error_), true)) {
      error_reported_ = true;
    }
    return;
  }

  TryRead();
  TryWrite();
}

void TlsDataFlow::TryRead() {
  if (read_handler_) {
    if (tunnel_.HasPlainTextDataToRead()) {
      GetRunloop()->Post([this, buffer{tunnel_.ReadPlainTextData()},
                          handler{read_handler_},
                          cancelable{read_cancelable_}]() mutable {
        if (cancelable.canceled()) {
          return;
        }

        state_machine_.ReadEnd();

        if (!buffer) {
          state_machine_.Errored();
        }

        handler(std::move(buffer));
      });

      read_handler_ = nullptr;

      if (tunnel_.NeedCipherInput()) {
        TryReadNextHop();
      }

      return;
    } else {
      TryReadNextHop();
      return;
    }
  } else {
    if (tunnel_.NeedCipherInput()) {
      TryReadNextHop();
      return;
    }
  }
}

void TlsDataFlow::TryWrite() {
  if (tunnel_.FinishWritingCipherData() && write_handler_) {
    GetRunloop()->Post(
        [this, handler{write_handler_}, cancelable{write_cancelable_}]() {
          if (cancelable.canceled()) {
            return;
          }

          state_machine_.WriteEnd();

          handler({});
        });
    write_handler_ = nullptr;
    return;
  };

  if (!tunnel_.FinishWritingCipherData()) {
    TryWriteNextHop();
  }
}

void TlsDataFlow::TryReadNextHop() {
  if (data_flow_->StateMachine().IsReading()) {
    return;
  }

  next_read_cancelable_ =
      data_flow_->Read([this, cancelable{read_cancelable_}](
                           utils::Result<utils::Buffer>&& buffer) {
        if (cancelable.canceled()) {
          return;
        }

        if (!buffer) {
          if (!ReportError(std::move(buffer).error(), true)) {
            pending_error_ = std::move(buffer).error();
          } else {
            error_reported_ = true;
          }
          return;
        }

        tunnel_.WriteCipherTextData(*std::move(buffer));
        Process();
      });
}

void TlsDataFlow::TryWriteNextHop() {
  if (data_flow_->StateMachine().IsWriting()) {
    return;
  }

  if (tunnel_.FinishWritingCipherData()) {
    return;
  }

  next_write_cancelable_ = data_flow_->Write(
      tunnel_.ReadCipherTextData(),
      [this, cancelable{write_cancelable_}](utils::Result<void>&& result) {
        if (cancelable.canceled()) {
          return;
        }

        if (!result) {
          if (!ReportError(std::move(result).error(), false)) {
            pending_error_ = std::move(result).error();
          } else {
            error_reported_ = true;
          }
          return;
        }

        Process();
      });
}

bool TlsDataFlow::ReportError(utils::Error&& error, bool try_read_first) {
  if (try_read_first ? ReadReportError(std::move(error))
                     : WriteReportError(std::move(error))) {
    return true;
  }

  return try_read_first ? WriteReportError(std::move(error))
                        : ReadReportError(std::move(error));
}

bool TlsDataFlow::ReadReportError(utils::Error&& error) {
  if (read_handler_) {
    // should we update error_reported here?
    auto handler = read_handler_;
    handler(utils::MakeErrorResult(std::move(error)));
    read_handler_ = nullptr;
    return true;
  }
  return false;
}

bool TlsDataFlow::WriteReportError(utils::Error&& error) {
  if (write_handler_) {
    auto handler = write_handler_;
    handler(utils::MakeErrorResult(std::move(error)));
    write_handler_ = nullptr;
    return true;
  }

  return false;
}
}  // namespace data_flow
}  // namespace nekit
<|endoftext|>


<commit_before>#include "DatabaseBackend.h"
#include "DBBackendFactory.h"
#include "DatabaseServer.h"

#include "core/global.h"
#include "util/DatagramIterator.h"
#include "util/Datagram.h"

#include <client/dbclient.h>
#include <bson/bson.h>

using namespace std;
using namespace mongo;

static ConfigVariable<string> server("server", "localhost", db_backend_config);
static ConfigVariable<string> database("database", "test", db_backend_config);
static ConfigVariable<string> username("username", "", db_backend_config);
static ConfigVariable<string> password("password", "", db_backend_config);

// These are helper functions to convert between BSONElement and packed Bamboo
// field values.

// TODO: Right now they just store the packed Bamboo field data in a BSON blob
//       verbatim. They need to do an in-depth conversion so that the database
//       is accessible by other tools and tolerant of changes to the .dc file.
static void unpack_bson(const dclass::Field *field,
                        const std::vector<uint8_t> data,
                        BSONObjBuilder &ob)
{
	ob.appendBinData(field->get_name(), data.size(), BinDataGeneral, data.data());
}

static void pack_bson(const dclass::Field *field,
                      const BSONElement &element,
                      std::vector<uint8_t> data)
{
	int len;
	const char *rawdata = element.binData(len);
	data.resize(len);
	memcpy(data.data(), rawdata, len);
}

class MongoDatabase : public DatabaseBackend
{
	public:
		MongoDatabase(ConfigNode dbeconfig, doid_t min_id, doid_t max_id) :
			DatabaseBackend(dbeconfig, min_id, max_id), m_monotonic_exhausted(false)
		{
			stringstream log_name;
			log_name << "Database-MongoDB" << "(Range: [" << min_id << ", " << max_id << "])";
			m_log = new LogCategory("mongodb", log_name.str());

			// Init connection.
			string error;

			// TODO: This only creates a single connection. When this class is
			// made multithreaded, we will need a connection pool instead.
			if(!m_conn.connect(server.get_rval(m_config), error))
			{
				m_log->fatal() << "Connection failure: " << error << endl;
				exit(1);
			}

			m_db = database.get_rval(m_config);

			string u = username.get_rval(m_config);
			string p = password.get_rval(m_config);
			if(!u.empty() && !p.empty())
			{
				if(!m_conn.auth(m_db, u, p, error))
				{
					m_log->fatal() << "Authentication failure: " << error << endl;
					exit(1);
				}
			}

			// Init the collection names:
			stringstream obj_collection;
			obj_collection << m_db << ".astron.objects";
			m_obj_collection = obj_collection.str();
			stringstream global_collection;
			global_collection << m_db << ".astron.globals";
			m_global_collection = global_collection.str();

			// Init the globals collection/document:
			BSONObj globals = BSON("_id" << "GLOBALS" <<
			                       "doid" << BSON(
				                       "monotonic" << min_id <<
				                       "free" << BSONArray()
			                       ));
			m_conn.insert(m_global_collection, globals);
		}

		virtual void submit(DBOperation *operation)
		{
			// TODO: This should run in a separate thread.
			handle_operation(operation);
		}

	private:
		LogCategory *m_log;

		DBClientConnection m_conn;
		string m_db;
		string m_obj_collection;
		string m_global_collection;

		// N.B. this variable is NOT guarded by a lock. While there can conceivably
		// be races on accessing it, this is not a problem, because:
		// 1) It is initialized to false by the main thread, and only set to true
		//    by sub-threads. There is no way for this variable to go back from
		//    true to false.
		// 2) It is only used to tell the DOID allocator to stop trying to use the
		//    monotonic counter. If a thread misses the update from false->true,
		//    it will only waste time fruitlessly trying to allocate an ID from
		//    the (exhausted) monotonic counter, before falling back on the free
		//    DOIDs list.
		bool m_monotonic_exhausted;

		void handle_operation(DBOperation *operation)
		{
			// First, figure out what kind of operation it is, and dispatch:
			if(operation->m_type == DBOperation::OperationType::CREATE_OBJECT)
			{
				handle_create(operation);
			}
			else if(operation->m_type == DBOperation::OperationType::DELETE_OBJECT)
			{
				handle_delete(operation);
			}
			else if(operation->m_type == DBOperation::OperationType::GET_OBJECT ||
			        operation->m_type == DBOperation::OperationType::GET_FIELDS)
			{
				handle_get(operation);
			}
			else if(operation->m_type == DBOperation::OperationType::MODIFY_FIELDS)
			{
				handle_modify(operation);
			}
		}

		void handle_create(DBOperation *operation)
		{
			// First, let's convert the requested object into BSON; this way, if
			// a failure happens, it happens before we waste a doid.
			BSONObjBuilder fields;

			try
			{
				for(auto it = operation->m_set_fields.begin();
				    it != operation->m_set_fields.end();
				    ++it)
				{
					unpack_bson(it->first, it->second, fields);
				}
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "While formatting "
				               << operation->m_dclass->get_name()
				               << " for insertion: " << e.what() << endl;
				operation->on_failure();
				return;
			}

			doid_t doid = assign_doid();
			if(doid == INVALID_DO_ID)
			{
				// The error will already have been emitted at this point, so
				// all that's left for us to do is fail silently:
				operation->on_failure();
				return;
			}

			BSONObj b = BSON("_id" << doid <<
			                 "dclass" << operation->m_dclass->get_name() <<
			                 "fields" << fields.obj());

			m_log->trace() << "Inserting new " << operation->m_dclass->get_name()
				           << "(" << doid << "): " << b << endl;

			try
			{
				m_conn.insert(m_obj_collection, b);
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "Cannot insert new "
				               << operation->m_dclass->get_name()
				               << "(" << doid << "): " << e.what() << endl;
				operation->on_failure();
				return;
			}

			operation->on_complete(doid);
		}

		void handle_delete(DBOperation *operation)
		{
			BSONObj result;

			bool success;
			try
			{
				success = m_conn.runCommand(
					m_db,
					BSON("findandmodify" << "astron.objects" <<
					     "query" << BSON(
						     "_id" << operation->m_doid) <<
					     "remove" << true),
					result);
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "Unexpected error while deleting "
				               << operation->m_doid << ": " << e.what() << endl;
				operation->on_failure();
				return;
			}

			m_log->trace() << "handle_delete: got response: "
			               << result << endl;

			// If the findandmodify command failed, there wasn't anything there
			// to delete in the first place.
			if(!success || result["value"].isNull())
			{
				m_log->error() << "Tried to delete non-existent doid "
				               << operation->m_doid << endl;
				operation->on_failure();
				return;
			}

			free_doid(operation->m_doid);
			operation->on_complete();
		}

		void handle_get(DBOperation *operation)
		{
			// TODO: GET
		}

		void handle_modify(DBOperation *operation)
		{
			// TODO: MODIFY
		}

		// This function is used by handle_create to get a fresh DOID assignment.
		doid_t assign_doid()
		{
			try
			{
				if(!m_monotonic_exhausted)
				{
					doid_t doid = assign_doid_monotonic();
					if(doid == INVALID_DO_ID)
					{
						m_monotonic_exhausted = true;
					}
					else
					{
						return doid;
					}
				}

				// We've exhausted our supply of doids from the monotonic counter.
				// We must now resort to pulling things out of the free list:
				return assign_doid_reuse();
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "Unexpected error occurred while trying to"
				                  " allocate a new DOID: " << e.what() << endl;
				return INVALID_DO_ID;
			}
		}

		doid_t assign_doid_monotonic()
		{
			BSONObj result;

			bool success = m_conn.runCommand(
				m_db,
				BSON("findandmodify" << "astron.globals" <<
				     "query" << BSON(
					     "_id" << "GLOBALS" <<
					     "doid.monotonic" << GTE << m_min_id <<
					     "doid.monotonic" << LTE << m_max_id
				     ) <<
				     "update" << BSON(
					     "$inc" << BSON("doid.monotonic" << 1)
				     )), result);

			// If the findandmodify command failed, the document either doesn't
			// exist, or we ran out of monotonic doids.
			if(!success || result["value"].isNull())
			{
				return INVALID_DO_ID;
			}

			m_log->trace() << "assign_doid_monotonic: got globals element: "
			               << result << endl;

			doid_t doid;
			const BSONElement &element = result["value"]["doid"]["monotonic"];
			if(sizeof(doid) == sizeof(long long))
			{
				doid = element.Long();
			}
			else if(sizeof(doid) == sizeof(int))
			{
				doid = element.Int();
			}
			return doid;
		}

		// This is used when the monotonic counter is exhausted:
		doid_t assign_doid_reuse()
		{
			BSONObj result;

			bool success = m_conn.runCommand(
				m_db,
				BSON("findandmodify" << "astron.globals" <<
				     "query" << BSON(
					     "_id" << "GLOBALS" <<
					     "doid.free.0" << BSON("$exists" << true)
				     ) <<
				     "update" << BSON(
					     "$pop" << BSON("doid.free" << -1)
				     )), result);

			// If the findandmodify command failed, the document either doesn't
			// exist, or we ran out of reusable doids.
			if(!success || result["value"].isNull())
			{
				m_log->error() << "Could not allocate a reused DOID!" << endl;
				return INVALID_DO_ID;
			}

			m_log->trace() << "assign_doid_reuse: got globals element: "
			               << result << endl;

			// Otherwise, use the first one:
			doid_t doid;
			const BSONElement &element = result["value"]["doid"]["free"];
			if(sizeof(doid) == sizeof(long long))
			{
				doid = element.Array()[0].Long();
			}
			else if(sizeof(doid) == sizeof(int))
			{
				doid = element.Array()[0].Int();
			}
			return doid;
		}

		// This returns a DOID to the free list:
		void free_doid(doid_t doid)
		{
			m_log->trace() << "Returning doid " << doid << " to the free pool..." << endl;

			try
			{
				m_conn.update(
					m_global_collection,
					BSON("_id" << "GLOBALS"),
					BSON("$push" << BSON("doid.free" << doid)));
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "Could not return doid " << doid
				               << " to free pool: " << e.what() << endl;
			}
		}
};

DBBackendFactoryItem<MongoDatabase> mongodb_factory("mongodb");
<commit_msg>MongoDB: Implement object/field retrieval. 5/10 tests pass.<commit_after>#include "DatabaseBackend.h"
#include "DBBackendFactory.h"
#include "DatabaseServer.h"

#include "core/global.h"
#include "util/DatagramIterator.h"
#include "util/Datagram.h"

#include <client/dbclient.h>
#include <bson/bson.h>

using namespace std;
using namespace mongo;

static ConfigVariable<string> server("server", "localhost", db_backend_config);
static ConfigVariable<string> database("database", "test", db_backend_config);
static ConfigVariable<string> username("username", "", db_backend_config);
static ConfigVariable<string> password("password", "", db_backend_config);

// These are helper functions to convert between BSONElement and packed Bamboo
// field values.

// TODO: Right now they just store the packed Bamboo field data in a BSON blob
//       verbatim. They need to do an in-depth conversion so that the database
//       is accessible by other tools and tolerant of changes to the .dc file.
static void unpack_bson(const dclass::Field *field,
                        const std::vector<uint8_t> data,
                        BSONObjBuilder &ob)
{
	ob.appendBinData(field->get_name(), data.size(), BinDataGeneral, data.data());
}

static void pack_bson(const dclass::Field *field,
                      const BSONElement &element,
                      std::vector<uint8_t> &data)
{
	int len;
	const char *rawdata = element.binData(len);
	data.resize(len);
	memcpy(data.data(), rawdata, len);
}

class MongoDatabase : public DatabaseBackend
{
	public:
		MongoDatabase(ConfigNode dbeconfig, doid_t min_id, doid_t max_id) :
			DatabaseBackend(dbeconfig, min_id, max_id), m_monotonic_exhausted(false)
		{
			stringstream log_name;
			log_name << "Database-MongoDB" << "(Range: [" << min_id << ", " << max_id << "])";
			m_log = new LogCategory("mongodb", log_name.str());

			// Init connection.
			string error;

			// TODO: This only creates a single connection. When this class is
			// made multithreaded, we will need a connection pool instead.
			if(!m_conn.connect(server.get_rval(m_config), error))
			{
				m_log->fatal() << "Connection failure: " << error << endl;
				exit(1);
			}

			m_db = database.get_rval(m_config);

			string u = username.get_rval(m_config);
			string p = password.get_rval(m_config);
			if(!u.empty() && !p.empty())
			{
				if(!m_conn.auth(m_db, u, p, error))
				{
					m_log->fatal() << "Authentication failure: " << error << endl;
					exit(1);
				}
			}

			// Init the collection names:
			stringstream obj_collection;
			obj_collection << m_db << ".astron.objects";
			m_obj_collection = obj_collection.str();
			stringstream global_collection;
			global_collection << m_db << ".astron.globals";
			m_global_collection = global_collection.str();

			// Init the globals collection/document:
			BSONObj globals = BSON("_id" << "GLOBALS" <<
			                       "doid" << BSON(
				                       "monotonic" << min_id <<
				                       "free" << BSONArray()
			                       ));
			m_conn.insert(m_global_collection, globals);
		}

		virtual void submit(DBOperation *operation)
		{
			// TODO: This should run in a separate thread.
			handle_operation(operation);
		}

	private:
		LogCategory *m_log;

		DBClientConnection m_conn;
		string m_db;
		string m_obj_collection;
		string m_global_collection;

		// N.B. this variable is NOT guarded by a lock. While there can conceivably
		// be races on accessing it, this is not a problem, because:
		// 1) It is initialized to false by the main thread, and only set to true
		//    by sub-threads. There is no way for this variable to go back from
		//    true to false.
		// 2) It is only used to tell the DOID allocator to stop trying to use the
		//    monotonic counter. If a thread misses the update from false->true,
		//    it will only waste time fruitlessly trying to allocate an ID from
		//    the (exhausted) monotonic counter, before falling back on the free
		//    DOIDs list.
		bool m_monotonic_exhausted;

		void handle_operation(DBOperation *operation)
		{
			// First, figure out what kind of operation it is, and dispatch:
			if(operation->m_type == DBOperation::OperationType::CREATE_OBJECT)
			{
				handle_create(operation);
			}
			else if(operation->m_type == DBOperation::OperationType::DELETE_OBJECT)
			{
				handle_delete(operation);
			}
			else if(operation->m_type == DBOperation::OperationType::GET_OBJECT ||
			        operation->m_type == DBOperation::OperationType::GET_FIELDS)
			{
				handle_get(operation);
			}
			else if(operation->m_type == DBOperation::OperationType::MODIFY_FIELDS)
			{
				handle_modify(operation);
			}
		}

		void handle_create(DBOperation *operation)
		{
			// First, let's convert the requested object into BSON; this way, if
			// a failure happens, it happens before we waste a doid.
			BSONObjBuilder fields;

			try
			{
				for(auto it = operation->m_set_fields.begin();
				    it != operation->m_set_fields.end();
				    ++it)
				{
					unpack_bson(it->first, it->second, fields);
				}
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "While formatting "
				               << operation->m_dclass->get_name()
				               << " for insertion: " << e.what() << endl;
				operation->on_failure();
				return;
			}

			doid_t doid = assign_doid();
			if(doid == INVALID_DO_ID)
			{
				// The error will already have been emitted at this point, so
				// all that's left for us to do is fail silently:
				operation->on_failure();
				return;
			}

			BSONObj b = BSON("_id" << doid <<
			                 "dclass" << operation->m_dclass->get_name() <<
			                 "fields" << fields.obj());

			m_log->trace() << "Inserting new " << operation->m_dclass->get_name()
				           << "(" << doid << "): " << b << endl;

			try
			{
				m_conn.insert(m_obj_collection, b);
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "Cannot insert new "
				               << operation->m_dclass->get_name()
				               << "(" << doid << "): " << e.what() << endl;
				operation->on_failure();
				return;
			}

			operation->on_complete(doid);
		}

		void handle_delete(DBOperation *operation)
		{
			BSONObj result;

			bool success;
			try
			{
				success = m_conn.runCommand(
					m_db,
					BSON("findandmodify" << "astron.objects" <<
					     "query" << BSON(
						     "_id" << operation->m_doid) <<
					     "remove" << true),
					result);
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "Unexpected error while deleting "
				               << operation->m_doid << ": " << e.what() << endl;
				operation->on_failure();
				return;
			}

			m_log->trace() << "handle_delete: got response: "
			               << result << endl;

			// If the findandmodify command failed, there wasn't anything there
			// to delete in the first place.
			if(!success || result["value"].isNull())
			{
				m_log->error() << "Tried to delete non-existent doid "
				               << operation->m_doid << endl;
				operation->on_failure();
				return;
			}

			free_doid(operation->m_doid);
			operation->on_complete();
		}

		void handle_get(DBOperation *operation)
		{
			BSONObj obj;
			try
			{
				obj = m_conn.findOne(m_obj_collection,
				                     BSON("_id" << operation->m_doid));
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "Unexpected error occurred while trying to"
				                  " retrieve object with DOID "
				               << operation->m_doid << ": " << e.what() << endl;
				operation->on_failure();
				return;
			}

			if(obj.isEmpty())
			{
				m_log->warning() << "Got queried for non-existent object with DOID "
				                 << operation->m_doid << endl;
				operation->on_failure();
				return;
			}

			DBObjectSnapshot *snap = format_snapshot(operation->m_doid, obj);
			if(!snap || !operation->verify_class(snap->m_dclass))
			{
				operation->on_failure();
			}
			else
			{
				operation->on_complete(snap);
			}
		}

		void handle_modify(DBOperation *operation)
		{
			// TODO: MODIFY
		}

		// Get a DBObjectSnapshot from a MongoDB BSON object; returns NULL if failure.
		DBObjectSnapshot *format_snapshot(doid_t doid, const BSONObj &obj)
		{
			try
			{
				string dclass_name = obj["dclass"].String();
				const dclass::Class *dclass = g_dcf->get_class_by_name(dclass_name);
				if(!dclass)
				{
					m_log->error() << "Encountered unknown database object: "
					               << dclass_name << "(" << doid << ")" << endl;
					return NULL;
				}

				BSONObj fields = obj["fields"].Obj();

				DBObjectSnapshot *snap = new DBObjectSnapshot();
				snap->m_dclass = dclass;
				for(auto it = fields.begin(); it.more(); ++it)
				{
					const char *name = (*it).fieldName();
					const dclass::Field *field = dclass->get_field_by_name(name);
					if(!field)
					{
						m_log->warning() << "Encountered unexpected field " << name
						                 << " while formatting " << dclass_name
						                 << "(" << doid << "); ignored." << endl;
						continue;
					}
					pack_bson(field, *it, snap->m_fields[field]);
				}

				return snap;
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "Unexpected error while trying to format"
				                  " database snapshot for " << doid << ": "
				               << e.what() << endl;
				return NULL;
			}
		}

		// This function is used by handle_create to get a fresh DOID assignment.
		doid_t assign_doid()
		{
			try
			{
				if(!m_monotonic_exhausted)
				{
					doid_t doid = assign_doid_monotonic();
					if(doid == INVALID_DO_ID)
					{
						m_monotonic_exhausted = true;
					}
					else
					{
						return doid;
					}
				}

				// We've exhausted our supply of doids from the monotonic counter.
				// We must now resort to pulling things out of the free list:
				return assign_doid_reuse();
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "Unexpected error occurred while trying to"
				                  " allocate a new DOID: " << e.what() << endl;
				return INVALID_DO_ID;
			}
		}

		doid_t assign_doid_monotonic()
		{
			BSONObj result;

			bool success = m_conn.runCommand(
				m_db,
				BSON("findandmodify" << "astron.globals" <<
				     "query" << BSON(
					     "_id" << "GLOBALS" <<
					     "doid.monotonic" << GTE << m_min_id <<
					     "doid.monotonic" << LTE << m_max_id
				     ) <<
				     "update" << BSON(
					     "$inc" << BSON("doid.monotonic" << 1)
				     )), result);

			// If the findandmodify command failed, the document either doesn't
			// exist, or we ran out of monotonic doids.
			if(!success || result["value"].isNull())
			{
				return INVALID_DO_ID;
			}

			m_log->trace() << "assign_doid_monotonic: got globals element: "
			               << result << endl;

			doid_t doid;
			const BSONElement &element = result["value"]["doid"]["monotonic"];
			if(sizeof(doid) == sizeof(long long))
			{
				doid = element.Long();
			}
			else if(sizeof(doid) == sizeof(int))
			{
				doid = element.Int();
			}
			return doid;
		}

		// This is used when the monotonic counter is exhausted:
		doid_t assign_doid_reuse()
		{
			BSONObj result;

			bool success = m_conn.runCommand(
				m_db,
				BSON("findandmodify" << "astron.globals" <<
				     "query" << BSON(
					     "_id" << "GLOBALS" <<
					     "doid.free.0" << BSON("$exists" << true)
				     ) <<
				     "update" << BSON(
					     "$pop" << BSON("doid.free" << -1)
				     )), result);

			// If the findandmodify command failed, the document either doesn't
			// exist, or we ran out of reusable doids.
			if(!success || result["value"].isNull())
			{
				m_log->error() << "Could not allocate a reused DOID!" << endl;
				return INVALID_DO_ID;
			}

			m_log->trace() << "assign_doid_reuse: got globals element: "
			               << result << endl;

			// Otherwise, use the first one:
			doid_t doid;
			const BSONElement &element = result["value"]["doid"]["free"];
			if(sizeof(doid) == sizeof(long long))
			{
				doid = element.Array()[0].Long();
			}
			else if(sizeof(doid) == sizeof(int))
			{
				doid = element.Array()[0].Int();
			}
			return doid;
		}

		// This returns a DOID to the free list:
		void free_doid(doid_t doid)
		{
			m_log->trace() << "Returning doid " << doid << " to the free pool..." << endl;

			try
			{
				m_conn.update(
					m_global_collection,
					BSON("_id" << "GLOBALS"),
					BSON("$push" << BSON("doid.free" << doid)));
			}
			catch(bson::assertion &e)
			{
				m_log->error() << "Could not return doid " << doid
				               << " to free pool: " << e.what() << endl;
			}
		}
};

DBBackendFactoryItem<MongoDatabase> mongodb_factory("mongodb");
<|endoftext|>


<commit_before>// Dak Washbrook - CS M10B
/* ****************************************************
 ASSIGNMENT -------------------------------------------
 Design a class that will determine the monthly payment
 on a home mortgage. The class should have member
 functions for setting the loan amount, interest rate,
 and number of years of the loan. It should also have
 member functions for returning the monthly payment
 amount and the total amount paid to the bank at the
 end of the loan period, Implement the class in
 complete program.
 Input validation: Do not accept negative numbers for
 any of the loan values.
 *************************************************** */

#include <iostream>
#include <math.h>
#include <string>

using namespace std;

class Mortgage {

private:

    const float NUM_MONTHS = 12.0;  // Number of months in a year.
    const float ONE = 1.0;          // Const for one.
    const float HUNDRED = 100.0;    // Const for one hundred.

    /* **************************************************
     String Error constants for each possible error.
     ************************************************* */
    const string ERR_AMOUNT = "Invalid Amount!", ERR_RATE = "Invalid Interest Rate!", ERR_YEARS = "Invalid Years!";

    float monthlyPayment = 0.0;     // Monthly Payment.
    float loanAmount = 0.0;         // The dollar amount of the loan.
    float interestRate = 0.0;       // The annual interest rate.
    float numYears = 0.0;           // The number of years of the loan.

    void MonthlyPayment() {
        /* **************************************************
         This function will calculate the monthly payment on
         a home loan.
         ************************************************* */
        this->monthlyPayment = (this->loanAmount * this->PeriodInterestRate() * this->term()) / (this->term() - Mortgage::ONE);
    }

    float PeriodInterestRate() {
        return this->interestRate / Mortgage::NUM_MONTHS;
    }

    float term() {
        /* **************************************************
         Calculate the term.
         ************************************************* */
        return pow(Mortgage::ONE + this->PeriodInterestRate(), (Mortgage::NUM_MONTHS * this->numYears));
    }

    void ThrowError(const string ERROR) {
        /* **************************************************
         This function will print the error passed into it.
         ************************************************* */
        cout << endl << ERROR << endl << endl;
    }

    float GetMonthInterest() {
        return this->loanAmount * this->PeriodInterestRate();
    }

    float GetMonthPrincipal() {
        return this->monthlyPayment - this->GetMonthInterest();
    }

public:

    bool setLoanAmount(float amount) {
        /* **************************************************
         This function sets the loanAmount member variable.
         Throws an error if the value is less than 1.
         ************************************************* */
        if (amount < 1) {
            this->ThrowError(ERR_AMOUNT);
            return false;
        }
        else {
            this->loanAmount = amount;
            return true;
        }
    }

    bool setInterestRate(float rate) {
        /* **************************************************
         This function sets the interestRate member variable.
         Throws an error if the value is less than 1.
         ************************************************* */
        if (rate < 1) {
            this->ThrowError(ERR_RATE);
            return false;
        }
        else {
            this->interestRate = rate / Mortgage::HUNDRED;
            return true;
        }
    }

    bool setYears(float years) {
        /* **************************************************
         This function sets the numYears member variable.
         Throws an error if the value is less than 1.
         ************************************************* */
        if (years < 1) {
            this->ThrowError(ERR_YEARS);
            return false;
        }
        else {
            this->numYears = years;
            return true;
        }
    }

    float getMonthlyPayment() {
        /* **************************************************
         This function returns the monthlyPayment member
         variable value after calculating it.
         ************************************************* */
        this->MonthlyPayment();
        return this->monthlyPayment;
    }

};

int main() {

    /* **************************************************
     First, initialize the floating point values for
     years, rate, and amount. These will store the values
     that the user inputs.
     ************************************************* */
    float years = 0.0, rate = 0.0, amount = 0.0;

    /* **************************************************
     This boolean value is purely for error checking.
     good will be false if the user ever inputs an
     unacceptable value. This will cause the do loops to
     loop until good == true.
     ************************************************* */
    bool good = true;

    /* **************************************************
     Initialize the class under the homeLoan variable.
     ************************************************* */
    Mortgage homeLoan;

    /* **************************************************
     Get and set the Loan Amount. Error checking for if
     the user inputs any value under 1.
     ************************************************* */
    do {
        cout << "Loan Amount: $";
        cin >> amount;
        good = homeLoan.setLoanAmount(amount);
    } while (!good);

    /* **************************************************
     Get and set the Interest Rate. Error checking for if
     the user inputs any value under 1.
     ************************************************* */
    do {
        cout << "Interest Rate (Percent): %";
        cin >> rate;
        good = homeLoan.setInterestRate(rate);
    } while (!good);

    /* **************************************************
     Get and set the Number of Years. Error checking for
     if the user inputs any value under 1.
     ************************************************* */
    do {
        cout << "Number of Years for Loan: ";
        cin >> years;
        good = homeLoan.setYears(years);
    } while (!good);

    cout.precision(2);      // Set the precision to 2 so that a valid dollar ($) amount is shown to the user.

    // Print the Monthly Payment.
    cout << endl << "Monthly Payment = $" << fixed << homeLoan.getMonthlyPayment() << endl << endl;

    return 0;
}
<commit_msg>Update Homework-3.cpp<commit_after>// Dak Washbrook - CS M10B
/* ****************************************************
ASSIGNMENT -------------------------------------------
Design a class that will determine the monthly payment
on a home mortgage. The class should have member
functions for setting the loan amount, interest rate,
and number of years of the loan. It should also have
member functions for returning the monthly payment
amount and the total amount paid to the bank at the
end of the loan period, Implement the class in
complete program.
Input validation: Do not accept negative numbers for
any of the loan values.

CONTINUED ASSIGNMENT --------------------------------
Modify your mortgage program (HW#2) to display the
monthly payment amount and then, list the loan balance
and interest paid for each payment over the term of
the loan (for example 360 payments for 30 years). So
the balance of the loan for the last payment should be
0. Use loops to prevent lists from scrolling off the
screen (about 20 lines on a page). You can use the
Sleep(3000) function to make the program to wait for 3
seconds. Insert comments in the program to document
the program.
*************************************************** */

#include <iostream>
#include <math.h>
#include <string>

using namespace std;

class Mortgage {

private:

	/* **************************************************
	Class Constants
	************************************************** */
	const float NUM_MONTHS = 12.0;  // Number of months in a year.
	const float ONE = 1.0;          // Const for one.
	const float HUNDRED = 100.0;    // Const for one hundred.

	/* **************************************************
	String Error constants for each possible error.
	************************************************** */
	const string ERR_AMOUNT = "Invalid Amount!", ERR_RATE = "Invalid Interest Rate!", ERR_YEARS = "Invalid Years!";

	/* **************************************************
	Class member variables
	************************************************** */
	float monthlyPayment = 0.0;     // Monthly Payment.
	float loanAmount = 0.0;         // The dollar amount of the loan.
	float interestRate = 0.0;       // The annual interest rate.
	float numYears = 0.0;           // The number of years of the loan.

	void MonthlyPayment() {
		/* **************************************************
		This function will calculate the monthly payment on
		a home loan.
		************************************************** */
		this->monthlyPayment = (this->loanAmount * this->PeriodInterestRate() * this->Term()) / (this->Term() - Mortgage::ONE);
	}

	float PeriodInterestRate() {
		/* **************************************************
		The interest rate for a year.
		************************************************** */
		return this->interestRate / Mortgage::NUM_MONTHS;
	}

	float Term() {
		/* **************************************************
		Calculate the term.
		************************************************** */
		return pow(Mortgage::ONE + this->PeriodInterestRate(), (Mortgage::NUM_MONTHS * this->numYears));
	}

	void ThrowError(const string ERROR) {
		/* **************************************************
		This function will print the error passed into it.
		************************************************** */
		cout << endl << ERROR << endl << endl;
	}

	float GetMonthInterest(float balance) {
		/* **************************************************
		Calculate one month interest.
		************************************************** */
		return balance * this->PeriodInterestRate();
	}

	float GetMonthPrincipal(float balance) {
		/* **************************************************
		Calculate one month principal.
		************************************************** */
		return this->monthlyPayment - this->GetMonthInterest(balance);
	}

public:

	bool SetLoanAmount(float amount) {
		/* **************************************************
		This function sets the loanAmount member variable.
		Throws an error if the value is less than 1.
		************************************************** */
		if (amount < 1) {
			this->ThrowError(ERR_AMOUNT);
			return false;
		}
		else {
			this->loanAmount = amount;
			return true;
		}
	}

	bool SetInterestRate(float rate) {
		/* **************************************************
		This function sets the interestRate member variable.
		Throws an error if the value is less than 1.
		************************************************** */
		if (rate < 1) {
			this->ThrowError(ERR_RATE);
			return false;
		}
		else {
			this->interestRate = rate / Mortgage::HUNDRED;
			return true;
		}
	}

	bool SetYears(float years) {
		/* **************************************************
		This function sets the numYears member variable.
		Throws an error if the value is less than 1.
		************************************************** */
		if (years < 1) {
			this->ThrowError(ERR_YEARS);
			return false;
		}
		else {
			this->numYears = years;
			return true;
		}
	}

	float GetMonthlyPayment() {
		/* **************************************************
		This function returns the monthlyPayment member
		variable value after calculating it.
		************************************************** */
		this->MonthlyPayment();
		return this->monthlyPayment;
	}

	void PrintMonthlyPayment() {
		/* **************************************************
		This function prints the monthly payment to the user.
		************************************************** */
		cout.precision(2);      // Set the precision to 2 so that a valid dollar ($) amount is shown to the user.

		// Print the Monthly Payment.
		cout << endl << "Monthly Payment = $" << fixed << this->GetMonthlyPayment() << endl << endl;
	}

	void PrintStatement() {
		/* **************************************************
		This function prints the monthly payment to the user.
		************************************************** */
		float balance = this->loanAmount;
		int month = 1;
		string cont;

		while (balance > 0.0) {
			cout << "Month\tMonthly Payment\tPrincipal Paid\tInterest Paid\tBalance" << endl;
			cout << "-------------------------------------------------------------------------------" << endl;
			for (int i = 0;  i < 20; i++) {
				cout << month << "\t$" << this->GetMonthlyPayment() << "\t$" << this->GetMonthPrincipal(balance) << "\t\t$" << this->GetMonthInterest(balance) << "\t\t$" << balance << endl;
				balance = balance - this->GetMonthPrincipal(balance);
				month++;
			}
			cout << "Show 20 more..." << endl;
			system("pause");
		}
	}

	void AskUser() {
		/* **************************************************
		First, initialize the floating point values for
		years, rate, and amount. These will store the values
		that the user inputs.
		************************************************** */
		float years = 0.0, rate = 0.0, amount = 0.0;

		/* **************************************************
		This boolean value is purely for error checking.
		good will be false if the user ever inputs an
		unacceptable value. This will cause the do loops to
		loop until good == true.
		************************************************** */
		bool good = true;

		/* **************************************************
		Get and set the Loan Amount. Error checking for if
		the user inputs any value under 1.
		************************************************** */
		do {
			cout << "Loan Amount: $";
			cin >> amount;
			good = this->SetLoanAmount(amount);
		} while (!good);

		/* **************************************************
		Get and set the Interest Rate. Error checking for if
		the user inputs any value under 1.
		************************************************** */
		do {
			cout << "Interest Rate (Percent): %";
			cin >> rate;
			good = this->SetInterestRate(rate);
		} while (!good);

		/* **************************************************
		Get and set the Number of Years. Error checking for
		if the user inputs any value under 1.
		************************************************** */
		do {
			cout << "Number of Years for Loan: ";
			cin >> years;
			good = this->SetYears(years);
		} while (!good);

		/* **************************************************
		Calculate and store the MonthlyPayment
		************************************************** */
		this->MonthlyPayment();
	}

};

int main() {

	/* **************************************************
	Initialize the class under the homeLoan variable.
	************************************************* */
	Mortgage homeLoan;

	homeLoan.AskUser();					// Ask the user for the information.

	homeLoan.PrintMonthlyPayment();		// Print the monthly payment they will pay.

	homeLoan.PrintStatement();			// Print the table of payments.

	return 0;
}
<|endoftext|>


<commit_before>#include "mainwindow.h"

#include <QPainter>

namespace {
const float MINNOTE = 100;
const float MAXNOTE = 400;
}

ViewerWidget::ViewerWidget(QWidget* parent)
    : QGLWidget(parent),
      m_parent(dynamic_cast<MainWindow*>(parent)),
      m_update_pending(false)
{
}

void ViewerWidget::renderLater() {
    if (!m_update_pending) {
        m_update_pending = true;
        QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
    }
}
void ViewerWidget::renderNow() {
  QPainter painter(this);

  painter.fillRect(rect(), Qt::black);

  int m_width = 1024;
  int m_height = 800;

  std::lock_guard<std::mutex> lockGuard(m_parent->mutex());

  auto noteToPos = [this, m_height](double note) {
    return m_height - (note - MINNOTE) * (m_height / (MAXNOTE - MINNOTE));
  };

  const double noteWidth = 2;

  QPen pen(QColor(255, 255, 255));
  painter.setPen(pen);

  QBrush brush(QColor(255, 255, 255));
  painter.setBrush(brush);

  double position = 0;
  for (auto note : m_parent->cb()) {
    if (note.f0 != 0) {  // TODO float compare

      double ypos = noteToPos(note.f0);

//      m_driver->draw2DRectangle(position, ypos - 1, position + noteWidth,
//                                ypos + 1, video::Color(255, 255, 255, 255));

      painter.drawRect(position, ypos - 1, noteWidth, noteWidth);
    }

#if 0
    {
      double ypos = note.rms * (m_height / 3 / 24000.0) + (2.0 * m_height / 3);

      m_driver->draw2DRectangle(position, ypos - 1, position + noteWidth,
                                ypos + 1, video::Color(0, 100, 100, 255));
    }
#endif

    position += noteWidth;
  }

  // G3
  {
    double ypos = noteToPos(196);
    QPen pen(QColor(100, 0, 0));
    painter.setPen(pen);
    painter.drawLine(0, ypos, m_width, ypos);
  }

#if 0
  // A3
  {
    double ypos = noteToPos(220);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(255, 0, 0, 255));
  }

  // A#3
  {
    double ypos = noteToPos(233);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(50, 0, 0, 255));
  }

  // B3
  {
    double ypos = noteToPos(247);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // C4 - wow!
  {
    double ypos = noteToPos(262);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // D4
  {
    double ypos = noteToPos(294);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // E4
  {
    double ypos = noteToPos(330);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // C3
  {
    double ypos = noteToPos(131);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }
#endif

  renderLater();
}

bool ViewerWidget::event(QEvent* event)
{
  switch (event->type()) {
  case QEvent::UpdateRequest:
    m_update_pending = false;
    renderNow();
    return true;
  default:
    return QGLWidget::event(event);
  }
}

void ViewerWidget::paintEvent(QPaintEvent* event) {
  renderNow();
}

MainWindow::MainWindow(std::size_t bufferCapacity) : m_cb(bufferCapacity)
{
  m_ui.setupUi(this);

  ViewerWidget* vw = new ViewerWidget(this);

  setCentralWidget(vw);
}
<commit_msg>now also drawing the RMS line<commit_after>#include "mainwindow.h"

#include <QPainter>

namespace {
const float MINNOTE = 100;
const float MAXNOTE = 400;
}

ViewerWidget::ViewerWidget(QWidget* parent)
    : QGLWidget(parent),
      m_parent(dynamic_cast<MainWindow*>(parent)),
      m_update_pending(false)
{
}

void ViewerWidget::renderLater() {
    if (!m_update_pending) {
        m_update_pending = true;
        QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
    }
}

void ViewerWidget::renderNow() {
  QPainter painter(this);

  painter.fillRect(rect(), Qt::black);

  const QPen penWhite(QColor(255, 255, 255));
  const QPen penRMS(QColor(0, 100, 100));
  const QBrush brushWhite(QColor(255, 255, 255));
  const QBrush brushRMS(QColor(0, 100, 100));

  int m_width = 1024;
  int m_height = 800;

  std::lock_guard<std::mutex> lockGuard(m_parent->mutex());

  auto noteToPos = [this, m_height](double note) {
    return m_height - (note - MINNOTE) * (m_height / (MAXNOTE - MINNOTE));
  };

  const double noteWidth = 2;

  double position = 0;
  for (auto note : m_parent->cb()) {
    if (note.f0 != 0) {  // TODO float compare

      double ypos = noteToPos(note.f0);

      painter.setPen(penWhite);
      painter.setBrush(brushWhite);

      painter.drawRect(position, ypos - 1, noteWidth, noteWidth);
    }

    {
      double ypos = m_height - (note.rms * (m_height / 3 / 24000.0) + (2.0 * m_height / 3));

      painter.setPen(penRMS);
      painter.setBrush(brushRMS);

      painter.drawRect(position, ypos - 1, noteWidth, noteWidth);
    }

    position += noteWidth;
  }

  // G3
  {
    double ypos = noteToPos(196);
    QPen pen(QColor(100, 0, 0));
    painter.setPen(pen);
    painter.drawLine(0, ypos, m_width, ypos);
  }

#if 0
  // A3
  {
    double ypos = noteToPos(220);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(255, 0, 0, 255));
  }

  // A#3
  {
    double ypos = noteToPos(233);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(50, 0, 0, 255));
  }

  // B3
  {
    double ypos = noteToPos(247);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // C4 - wow!
  {
    double ypos = noteToPos(262);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // D4
  {
    double ypos = noteToPos(294);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // E4
  {
    double ypos = noteToPos(330);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }

  // C3
  {
    double ypos = noteToPos(131);
    m_driver->draw2DLine(0, ypos, m_width, ypos, video::Color(100, 0, 0, 255));
  }
#endif

  renderLater();
}

bool ViewerWidget::event(QEvent* event)
{
  switch (event->type()) {
  case QEvent::UpdateRequest:
    m_update_pending = false;
    renderNow();
    return true;
  default:
    return QGLWidget::event(event);
  }
}

void ViewerWidget::paintEvent(QPaintEvent* event) {
  renderNow();
}

MainWindow::MainWindow(std::size_t bufferCapacity) : m_cb(bufferCapacity)
{
  m_ui.setupUi(this);

  ViewerWidget* vw = new ViewerWidget(this);

  setCentralWidget(vw);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "jni_internal.h"
#include "object.h"

#include "JniConstants.h" // Last to avoid problems with LOG redefinition.

/*
 * We make guarantees about the atomicity of accesses to primitive
 * variables.  These guarantees also apply to elements of arrays.
 * In particular, 8-bit, 16-bit, and 32-bit accesses must be atomic and
 * must not cause "word tearing".  Accesses to 64-bit array elements must
 * either be atomic or treated as two 32-bit operations.  References are
 * always read and written atomically, regardless of the number of bits
 * used to represent them.
 *
 * We can't rely on standard libc functions like memcpy(3) and memmove(3)
 * in our implementation of System.arraycopy, because they may copy
 * byte-by-byte (either for the full run or for "unaligned" parts at the
 * start or end).  We need to use functions that guarantee 16-bit or 32-bit
 * atomicity as appropriate.
 *
 * System.arraycopy() is heavily used, so having an efficient implementation
 * is important.  The bionic libc provides a platform-optimized memory move
 * function that should be used when possible.  If it's not available,
 * the trivial "reference implementation" versions below can be used until
 * a proper version can be written.
 *
 * For these functions, The caller must guarantee that dst/src are aligned
 * appropriately for the element type, and that n is a multiple of the
 * element size.
 */
#ifdef __BIONIC__
#define HAVE_MEMMOVE_WORDS
#endif

#ifdef HAVE_MEMMOVE_WORDS
extern "C" void _memmove_words(void* dst, const void* src, size_t n);
#define move16 _memmove_words
#define move32 _memmove_words
#else
static void move16(void* dst, const void* src, size_t n) {
  DCHECK_EQ((((uintptr_t) dst | (uintptr_t) src | n) & 0x01), 0U);

  uint16_t* d = reinterpret_cast<uint16_t*>(dst);
  const uint16_t* s = reinterpret_cast<const uint16_t*>(src);

  n /= sizeof(uint16_t);

  if (d < s) {
    // Copy forwards.
    while (n--) {
      *d++ = *s++;
    }
  } else {
    // Copy backwards.
    d += n;
    s += n;
    while (n--) {
      *--d = *--s;
    }
  }
}

static void move32(void* dst, const void* src, size_t n) {
  DCHECK_EQ((((uintptr_t) dst | (uintptr_t) src | n) & 0x03), 0U);

  uint32_t* d = reinterpret_cast<uint32_t*>(dst);
  const uint32_t* s = reinterpret_cast<const uint32_t*>(src);

  n /= sizeof(uint32_t);

  if (d < s) {
    // Copy forwards.
    while (n--) {
      *d++ = *s++;
    }
  } else {
    // Copy backwards.
    d += n;
    s += n;
    while (n--) {
      *--d = *--s;
    }
  }
}
#endif // HAVE_MEMMOVE_WORDS

namespace art {

namespace {

void ThrowArrayStoreException_NotAnArray(const char* identifier, Object* array) {
  std::string actualType(PrettyTypeOf(array));
  Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
      "%s is not an array: %s", identifier, actualType.c_str());
}

void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
  ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
  Thread* self = Thread::Current();

  // Null pointer checks.
  if (javaSrc == NULL) {
    self->ThrowNewException("Ljava/lang/NullPointerException;", "src == null");
    return;
  }
  if (javaDst == NULL) {
    self->ThrowNewException("Ljava/lang/NullPointerException;", "dst == null");
    return;
  }

  // Make sure source and destination are both arrays.
  Object* srcObject = Decode<Object*>(env, javaSrc);
  Object* dstObject = Decode<Object*>(env, javaDst);
  if (!srcObject->IsArrayInstance()) {
    ThrowArrayStoreException_NotAnArray("src", srcObject);
    return;
  }
  if (!dstObject->IsArrayInstance()) {
    ThrowArrayStoreException_NotAnArray("dst", dstObject);
    return;
  }
  Array* srcArray = srcObject->AsArray();
  Array* dstArray = dstObject->AsArray();
  Class* srcComponentType = srcArray->GetClass()->GetComponentType();
  Class* dstComponentType = dstArray->GetClass()->GetComponentType();

  // Bounds checking.
  if (srcPos < 0 || dstPos < 0 || length < 0 || srcPos > srcArray->GetLength() - length || dstPos > dstArray->GetLength() - length) {
    self->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
        "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
        srcArray->GetLength(), srcPos, dstArray->GetLength(), dstPos, length);
    return;
  }

  uint8_t* dstBytes = reinterpret_cast<uint8_t*>(dstArray->GetRawData());
  const uint8_t* srcBytes = reinterpret_cast<const uint8_t*>(srcArray->GetRawData());

  // Handle primitive arrays.
  if (srcComponentType->IsPrimitive() || dstComponentType->IsPrimitive()) {
    // If one of the arrays holds a primitive type the other array must hold the exact same type.
    if (srcComponentType->IsPrimitive() != dstComponentType->IsPrimitive() || srcComponentType != dstComponentType) {
      std::string srcType(PrettyTypeOf(srcArray));
      std::string dstType(PrettyTypeOf(dstArray));
      self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
          "Incompatible types: src=%s, dst=%s", srcType.c_str(), dstType.c_str());
      return;
    }

    switch (srcArray->GetClass()->GetComponentSize()) {
    case 1:
      memmove(dstBytes + dstPos, srcBytes + srcPos, length);
      break;
    case 2:
      move16(dstBytes + dstPos * 2, srcBytes + srcPos * 2, length * 2);
      break;
    case 4:
      move32(dstBytes + dstPos * 4, srcBytes + srcPos * 4, length * 4);
      break;
    case 8:
      // We don't need to guarantee atomicity of the entire 64-bit word.
      move32(dstBytes + dstPos * 8, srcBytes + srcPos * 8, length * 8);
      break;
    default:
      LOG(FATAL) << "Unknown primitive array type: " << PrettyTypeOf(srcArray);
    }

    return;
  }

  // Neither class is primitive. Are the types trivially compatible?
  const size_t width = sizeof(Object*);
  if (dstArray == srcArray || dstComponentType->IsAssignableFrom(srcComponentType)) {
    // Yes. Bulk copy.
    COMPILE_ASSERT(sizeof(width) == sizeof(uint32_t), move32_assumes_Object_references_are_32_bit);
    move32(dstBytes + dstPos * width, srcBytes + srcPos * width, length * width);
    Heap::WriteBarrierArray(dstArray, dstPos, length);
    return;
  }

  // The arrays are not trivially compatible. However, we may still be able to copy some or all of
  // the elements if the source objects are compatible (for example, copying an Object[] to
  // String[], the Objects being copied might actually be Strings).
  // We can't do a bulk move because that would introduce a check-use race condition, so we copy
  // elements one by one.

  // We already dealt with overlapping copies, so we don't need to cope with that case below.
  CHECK_NE(dstArray, srcArray);

  Object* const * srcObjects = reinterpret_cast<Object* const *>(srcBytes + srcPos * width);
  Object** dstObjects = reinterpret_cast<Object**>(dstBytes + dstPos * width);
  Class* dstClass = dstArray->GetClass()->GetComponentType();

  // We want to avoid redundant IsAssignableFrom checks where possible, so we cache a class that
  // we know is assignable to the destination array's component type.
  Class* lastAssignableElementClass = dstClass;

  Object* o = NULL;
  int i = 0;
  for (; i < length; ++i) {
    o = srcObjects[i];
    if (o != NULL) {
      Class* oClass = o->GetClass();
      if (lastAssignableElementClass == oClass) {
        dstObjects[i] = o;
      } else if (dstClass->IsAssignableFrom(oClass)) {
        lastAssignableElementClass = oClass;
        dstObjects[i] = o;
      } else {
        // Can't put this element into the array.
        break;
      }
    } else {
      dstObjects[i] = NULL;
    }
  }

  Heap::WriteBarrierArray(dstArray, dstPos, length);
  if (i != length) {
    std::string actualSrcType(PrettyTypeOf(o));
    std::string dstType(PrettyTypeOf(dstArray));
    self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
        "source[%d] of type %s cannot be stored in destination array of type %s",
        srcPos + i, actualSrcType.c_str(), dstType.c_str());
    return;
  }
}

jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) {
  Object* o = Decode<Object*>(env, javaObject);
  return static_cast<jint>(reinterpret_cast<uintptr_t>(o));
}

JNINativeMethod gMethods[] = {
  NATIVE_METHOD(System, arraycopy, "(Ljava/lang/Object;ILjava/lang/Object;II)V"),
  NATIVE_METHOD(System, identityHashCode, "(Ljava/lang/Object;)I"),
};

}  // namespace

void register_java_lang_System(JNIEnv* env) {
    jniRegisterNativeMethods(env, "java/lang/System", gMethods, NELEM(gMethods));
}

}  // namespace art
<commit_msg>Emit the same detail messages as dalvik for ArrayStoreExceptions from System.arraycopy.<commit_after>/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "jni_internal.h"
#include "object.h"

#include "JniConstants.h" // Last to avoid problems with LOG redefinition.

/*
 * We make guarantees about the atomicity of accesses to primitive
 * variables.  These guarantees also apply to elements of arrays.
 * In particular, 8-bit, 16-bit, and 32-bit accesses must be atomic and
 * must not cause "word tearing".  Accesses to 64-bit array elements must
 * either be atomic or treated as two 32-bit operations.  References are
 * always read and written atomically, regardless of the number of bits
 * used to represent them.
 *
 * We can't rely on standard libc functions like memcpy(3) and memmove(3)
 * in our implementation of System.arraycopy, because they may copy
 * byte-by-byte (either for the full run or for "unaligned" parts at the
 * start or end).  We need to use functions that guarantee 16-bit or 32-bit
 * atomicity as appropriate.
 *
 * System.arraycopy() is heavily used, so having an efficient implementation
 * is important.  The bionic libc provides a platform-optimized memory move
 * function that should be used when possible.  If it's not available,
 * the trivial "reference implementation" versions below can be used until
 * a proper version can be written.
 *
 * For these functions, The caller must guarantee that dst/src are aligned
 * appropriately for the element type, and that n is a multiple of the
 * element size.
 */
#ifdef __BIONIC__
#define HAVE_MEMMOVE_WORDS
#endif

#ifdef HAVE_MEMMOVE_WORDS
extern "C" void _memmove_words(void* dst, const void* src, size_t n);
#define move16 _memmove_words
#define move32 _memmove_words
#else
static void move16(void* dst, const void* src, size_t n) {
  DCHECK_EQ((((uintptr_t) dst | (uintptr_t) src | n) & 0x01), 0U);

  uint16_t* d = reinterpret_cast<uint16_t*>(dst);
  const uint16_t* s = reinterpret_cast<const uint16_t*>(src);

  n /= sizeof(uint16_t);

  if (d < s) {
    // Copy forwards.
    while (n--) {
      *d++ = *s++;
    }
  } else {
    // Copy backwards.
    d += n;
    s += n;
    while (n--) {
      *--d = *--s;
    }
  }
}

static void move32(void* dst, const void* src, size_t n) {
  DCHECK_EQ((((uintptr_t) dst | (uintptr_t) src | n) & 0x03), 0U);

  uint32_t* d = reinterpret_cast<uint32_t*>(dst);
  const uint32_t* s = reinterpret_cast<const uint32_t*>(src);

  n /= sizeof(uint32_t);

  if (d < s) {
    // Copy forwards.
    while (n--) {
      *d++ = *s++;
    }
  } else {
    // Copy backwards.
    d += n;
    s += n;
    while (n--) {
      *--d = *--s;
    }
  }
}
#endif // HAVE_MEMMOVE_WORDS

namespace art {

namespace {

void ThrowArrayStoreException_NotAnArray(const char* identifier, Object* array) {
  std::string actualType(PrettyTypeOf(array));
  Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
      "%s of type %s is not an array", identifier, actualType.c_str());
}

void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
  ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
  Thread* self = Thread::Current();

  // Null pointer checks.
  if (javaSrc == NULL) {
    self->ThrowNewException("Ljava/lang/NullPointerException;", "src == null");
    return;
  }
  if (javaDst == NULL) {
    self->ThrowNewException("Ljava/lang/NullPointerException;", "dst == null");
    return;
  }

  // Make sure source and destination are both arrays.
  Object* srcObject = Decode<Object*>(env, javaSrc);
  Object* dstObject = Decode<Object*>(env, javaDst);
  if (!srcObject->IsArrayInstance()) {
    ThrowArrayStoreException_NotAnArray("source", srcObject);
    return;
  }
  if (!dstObject->IsArrayInstance()) {
    ThrowArrayStoreException_NotAnArray("destination", dstObject);
    return;
  }
  Array* srcArray = srcObject->AsArray();
  Array* dstArray = dstObject->AsArray();
  Class* srcComponentType = srcArray->GetClass()->GetComponentType();
  Class* dstComponentType = dstArray->GetClass()->GetComponentType();

  // Bounds checking.
  if (srcPos < 0 || dstPos < 0 || length < 0 || srcPos > srcArray->GetLength() - length || dstPos > dstArray->GetLength() - length) {
    self->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
        "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
        srcArray->GetLength(), srcPos, dstArray->GetLength(), dstPos, length);
    return;
  }

  uint8_t* dstBytes = reinterpret_cast<uint8_t*>(dstArray->GetRawData());
  const uint8_t* srcBytes = reinterpret_cast<const uint8_t*>(srcArray->GetRawData());

  // Handle primitive arrays.
  if (srcComponentType->IsPrimitive() || dstComponentType->IsPrimitive()) {
    // If one of the arrays holds a primitive type the other array must hold the exact same type.
    if (srcComponentType->IsPrimitive() != dstComponentType->IsPrimitive() || srcComponentType != dstComponentType) {
      std::string srcType(PrettyTypeOf(srcArray));
      std::string dstType(PrettyTypeOf(dstArray));
      self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
          "Incompatible types: src=%s, dst=%s", srcType.c_str(), dstType.c_str());
      return;
    }

    switch (srcArray->GetClass()->GetComponentSize()) {
    case 1:
      memmove(dstBytes + dstPos, srcBytes + srcPos, length);
      break;
    case 2:
      move16(dstBytes + dstPos * 2, srcBytes + srcPos * 2, length * 2);
      break;
    case 4:
      move32(dstBytes + dstPos * 4, srcBytes + srcPos * 4, length * 4);
      break;
    case 8:
      // We don't need to guarantee atomicity of the entire 64-bit word.
      move32(dstBytes + dstPos * 8, srcBytes + srcPos * 8, length * 8);
      break;
    default:
      LOG(FATAL) << "Unknown primitive array type: " << PrettyTypeOf(srcArray);
    }

    return;
  }

  // Neither class is primitive. Are the types trivially compatible?
  const size_t width = sizeof(Object*);
  if (dstArray == srcArray || dstComponentType->IsAssignableFrom(srcComponentType)) {
    // Yes. Bulk copy.
    COMPILE_ASSERT(sizeof(width) == sizeof(uint32_t), move32_assumes_Object_references_are_32_bit);
    move32(dstBytes + dstPos * width, srcBytes + srcPos * width, length * width);
    Heap::WriteBarrierArray(dstArray, dstPos, length);
    return;
  }

  // The arrays are not trivially compatible. However, we may still be able to copy some or all of
  // the elements if the source objects are compatible (for example, copying an Object[] to
  // String[], the Objects being copied might actually be Strings).
  // We can't do a bulk move because that would introduce a check-use race condition, so we copy
  // elements one by one.

  // We already dealt with overlapping copies, so we don't need to cope with that case below.
  CHECK_NE(dstArray, srcArray);

  Object* const * srcObjects = reinterpret_cast<Object* const *>(srcBytes + srcPos * width);
  Object** dstObjects = reinterpret_cast<Object**>(dstBytes + dstPos * width);
  Class* dstClass = dstArray->GetClass()->GetComponentType();

  // We want to avoid redundant IsAssignableFrom checks where possible, so we cache a class that
  // we know is assignable to the destination array's component type.
  Class* lastAssignableElementClass = dstClass;

  Object* o = NULL;
  int i = 0;
  for (; i < length; ++i) {
    o = srcObjects[i];
    if (o != NULL) {
      Class* oClass = o->GetClass();
      if (lastAssignableElementClass == oClass) {
        dstObjects[i] = o;
      } else if (dstClass->IsAssignableFrom(oClass)) {
        lastAssignableElementClass = oClass;
        dstObjects[i] = o;
      } else {
        // Can't put this element into the array.
        break;
      }
    } else {
      dstObjects[i] = NULL;
    }
  }

  Heap::WriteBarrierArray(dstArray, dstPos, length);
  if (i != length) {
    std::string actualSrcType(PrettyTypeOf(o));
    std::string dstType(PrettyTypeOf(dstArray));
    self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
        "source[%d] of type %s cannot be stored in destination array of type %s",
        srcPos + i, actualSrcType.c_str(), dstType.c_str());
    return;
  }
}

jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) {
  Object* o = Decode<Object*>(env, javaObject);
  return static_cast<jint>(reinterpret_cast<uintptr_t>(o));
}

JNINativeMethod gMethods[] = {
  NATIVE_METHOD(System, arraycopy, "(Ljava/lang/Object;ILjava/lang/Object;II)V"),
  NATIVE_METHOD(System, identityHashCode, "(Ljava/lang/Object;)I"),
};

}  // namespace

void register_java_lang_System(JNIEnv* env) {
    jniRegisterNativeMethods(env, "java/lang/System", gMethods, NELEM(gMethods));
}

}  // namespace art
<|endoftext|>