diff --git a/binding.gyp b/binding.gyp index 568828d..10da871 100644 --- a/binding.gyp +++ b/binding.gyp @@ -5,6 +5,7 @@ "sources": [ "src/main.cc", "src/cryptonote_core/cryptonote_format_utils.cpp", + "src/offshore/pricing_record.cpp", "src/crypto/tree-hash.c", "src/crypto/crypto.cpp", "src/crypto/crypto-ops.c", diff --git a/src/contrib/epee/include/storages/crypted_storage.h b/src/contrib/epee/include/storages/crypted_storage.h new file mode 100644 index 0000000..163728c --- /dev/null +++ b/src/contrib/epee/include/storages/crypted_storage.h @@ -0,0 +1,62 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + +#ifndef _CRYPTED_STORAGE_H_ +#define _CRYPTED_STORAGE_H_ + +#include "cryptopp_helper.h" + +namespace epee +{ +template +class crypted_storage: public t_base_storage +{ +public: + size_t PackToSolidBuffer(std::string& targetObj) + { + size_t res = t_base_storage::PackToSolidBuffer(targetObj); + if(res <= 0) + return res; + + if(!crypt_provider::encrypt(targetObj, t_key_provider::get_storage_default_key())) + return 0; + + return targetObj.size(); + } + + size_t LoadFromSolidBuffer(const std::string& pTargetObj) + { + std::string buff_to_decrypt = pTargetObj; + if(crypt_provider::decrypt(buff_to_decrypt, t_key_provider::get_storage_default_key())) + return t_base_storage::LoadFromSolidBuffer(buff_to_decrypt); + + return 0; + } +}; +} + +#endif //_CRYPTED_STORAGE_H_ diff --git a/src/contrib/epee/include/storages/gzipped_inmemstorage.h b/src/contrib/epee/include/storages/gzipped_inmemstorage.h new file mode 100644 index 0000000..229a56d --- /dev/null +++ b/src/contrib/epee/include/storages/gzipped_inmemstorage.h @@ -0,0 +1,68 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + +#ifndef _GZIPPED_INMEMSTORAGE_H_ +#define _GZIPPED_INMEMSTORAGE_H_ + +#include "zlib_helper.h" +namespace epee +{ +namespace StorageNamed +{ + + template + class gziped_storage: public t_base_storage + { + public: + size_t PackToSolidBuffer(std::string& targetObj) + { + size_t res = t_base_storage::PackToSolidBuffer(targetObj); + if(res <= 0) + return res; + + if(!zlib_helper::pack(targetObj)) + return 0; + + return targetObj.size(); + } + + size_t LoadFromSolidBuffer(const std::string& pTargetObj) + { + std::string buff_to_ungzip = pTargetObj; + if(zlib_helper::unpack(buff_to_ungzip)) + return t_base_storage::LoadFromSolidBuffer(buff_to_ungzip); + + return 0; + } + + private: + }; + +} +} + +#endif diff --git a/src/contrib/epee/include/storages/http_abstract_invoke.h b/src/contrib/epee/include/storages/http_abstract_invoke.h new file mode 100644 index 0000000..a8bc945 --- /dev/null +++ b/src/contrib/epee/include/storages/http_abstract_invoke.h @@ -0,0 +1,141 @@ + +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once +#include +#include +#include +#include "portable_storage_template_helper.h" +#include "net/http_base.h" +#include "net/http_server_handlers_map2.h" + +namespace epee +{ + namespace net_utils + { + template + bool invoke_http_json(const boost::string_ref uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref method = "POST") + { + std::string req_param; + if(!serialization::store_t_to_json(out_struct, req_param)) + return false; + + http::fields_list additional_params; + additional_params.push_back(std::make_pair("Content-Type","application/json; charset=utf-8")); + + const http::http_response_info* pri = NULL; + if(!transport.invoke(uri, method, req_param, timeout, std::addressof(pri), std::move(additional_params))) + { + LOG_PRINT_L1("Failed to invoke http request to " << uri); + return false; + } + + if(!pri) + { + LOG_PRINT_L1("Failed to invoke http request to " << uri << ", internal error (null response ptr)"); + return false; + } + + if(pri->m_response_code != 200) + { + LOG_PRINT_L1("Failed to invoke http request to " << uri << ", wrong response code: " << pri->m_response_code); + return false; + } + + return serialization::load_t_from_json(result_struct, pri->m_body); + } + + + + template + bool invoke_http_bin(const boost::string_ref uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref method = "POST") + { + std::string req_param; + if(!serialization::store_t_to_binary(out_struct, req_param)) + return false; + + const http::http_response_info* pri = NULL; + if(!transport.invoke(uri, method, req_param, timeout, std::addressof(pri))) + { + LOG_PRINT_L1("Failed to invoke http request to " << uri); + return false; + } + + if(!pri) + { + LOG_PRINT_L1("Failed to invoke http request to " << uri << ", internal error (null response ptr)"); + return false; + } + + if(pri->m_response_code != 200) + { + LOG_PRINT_L1("Failed to invoke http request to " << uri << ", wrong response code: " << pri->m_response_code); + return false; + } + + return serialization::load_t_from_binary(result_struct, epee::strspan(pri->m_body)); + } + + template + bool invoke_http_json_rpc(const boost::string_ref uri, std::string method_name, const t_request& out_struct, t_response& result_struct, epee::json_rpc::error &error_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "POST", const std::string& req_id = "0") + { + epee::json_rpc::request req_t = AUTO_VAL_INIT(req_t); + req_t.jsonrpc = "2.0"; + req_t.id = req_id; + req_t.method = std::move(method_name); + req_t.params = out_struct; + epee::json_rpc::response resp_t = AUTO_VAL_INIT(resp_t); + if(!epee::net_utils::invoke_http_json(uri, req_t, resp_t, transport, timeout, http_method)) + { + error_struct = {}; + return false; + } + if(resp_t.error.code || resp_t.error.message.size()) + { + error_struct = resp_t.error; + LOG_ERROR("RPC call of \"" << req_t.method << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message); + return false; + } + result_struct = resp_t.result; + return true; + } + + template + bool invoke_http_json_rpc(const boost::string_ref uri, std::string method_name, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "POST", const std::string& req_id = "0") + { + epee::json_rpc::error error_struct; + return invoke_http_json_rpc(uri, method_name, out_struct, result_struct, error_struct, transport, timeout, http_method, req_id); + } + + template + bool invoke_http_json_rpc(const boost::string_ref uri, typename t_command::request& out_struct, typename t_command::response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "POST", const std::string& req_id = "0") + { + return invoke_http_json_rpc(uri, t_command::methodname(), out_struct, result_struct, transport, timeout, http_method, req_id); + } + + } +} diff --git a/src/contrib/epee/include/storages/levin_abstract_invoke2.h b/src/contrib/epee/include/storages/levin_abstract_invoke2.h new file mode 100644 index 0000000..4633fa5 --- /dev/null +++ b/src/contrib/epee/include/storages/levin_abstract_invoke2.h @@ -0,0 +1,342 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +#include "portable_storage_template_helper.h" +#include +#include +#include "span.h" +#include "net/levin_base.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "net" + +namespace +{ + template + void on_levin_traffic(const context_t &context, bool initiator, bool sent, bool error, size_t bytes, const char *category) + { + MCINFO("net.p2p.traffic", context << bytes << " bytes " << (sent ? "sent" : "received") << (error ? "/corrupt" : "") + << " for category " << category << " initiated by " << (initiator ? "us" : "peer")); + } + template + void on_levin_traffic(const context_t &context, bool initiator, bool sent, bool error, size_t bytes, int command) + { + char buf[32]; + snprintf(buf, sizeof(buf), "command-%u", command); + return on_levin_traffic(context, initiator, sent, error, bytes, buf); + } +} + +namespace epee +{ + namespace net_utils + { +#if 0 + template + bool invoke_remote_command2(int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport) + { + if(!transport.is_connected()) + return false; + + serialization::portable_storage stg; + out_struct.store(stg); + std::string buff_to_send, buff_to_recv; + stg.store_to_binary(buff_to_send); + + int res = transport.invoke(command, buff_to_send, buff_to_recv); + if( res <=0 ) + { + MERROR("Failed to invoke command " << command << " return code " << res); + return false; + } + serialization::portable_storage stg_ret; + if(!stg_ret.load_from_binary(buff_to_recv)) + { + LOG_ERROR("Failed to load_from_binary on command " << command); + return false; + } + return result_struct.load(stg_ret); + } + + template + bool notify_remote_command2(int command, const t_arg& out_struct, t_transport& transport) + { + if(!transport.is_connected()) + return false; + + serialization::portable_storage stg; + out_struct.store(&stg); + std::string buff_to_send; + stg.store_to_binary(buff_to_send); + + int res = transport.notify(command, buff_to_send); + if(res <=0 ) + { + LOG_ERROR("Failed to notify command " << command << " return code " << res); + return false; + } + return true; + } +#endif + + template + bool invoke_remote_command2(const epee::net_utils::connection_context_base context, int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport) + { + const boost::uuids::uuid &conn_id = context.m_connection_id; + typename serialization::portable_storage stg; + out_struct.store(stg); + std::string buff_to_send, buff_to_recv; + stg.store_to_binary(buff_to_send); + + on_levin_traffic(context, true, true, false, buff_to_send.size(), command); + int res = transport.invoke(command, buff_to_send, buff_to_recv, conn_id); + if( res <=0 ) + { + LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res); + return false; + } + typename serialization::portable_storage stg_ret; + if(!stg_ret.load_from_binary(buff_to_recv)) + { + on_levin_traffic(context, true, false, true, buff_to_recv.size(), command); + LOG_ERROR("Failed to load_from_binary on command " << command); + return false; + } + on_levin_traffic(context, true, false, false, buff_to_recv.size(), command); + return result_struct.load(stg_ret); + } + + template + bool async_invoke_remote_command2(const epee::net_utils::connection_context_base &context, int command, const t_arg& out_struct, t_transport& transport, const callback_t &cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + { + const boost::uuids::uuid &conn_id = context.m_connection_id; + typename serialization::portable_storage stg; + const_cast(out_struct).store(stg);//TODO: add true const support to searilzation + std::string buff_to_send; + stg.store_to_binary(buff_to_send); + on_levin_traffic(context, true, true, false, buff_to_send.size(), command); + int res = transport.invoke_async(command, epee::strspan(buff_to_send), conn_id, [cb, command](int code, const epee::span buff, typename t_transport::connection_context& context)->bool + { + t_result result_struct = AUTO_VAL_INIT(result_struct); + if( code <=0 ) + { + if (!buff.empty()) + on_levin_traffic(context, true, false, true, buff.size(), command); + LOG_PRINT_L1("Failed to invoke command " << command << " return code " << code); + cb(code, result_struct, context); + return false; + } + serialization::portable_storage stg_ret; + if(!stg_ret.load_from_binary(buff)) + { + on_levin_traffic(context, true, false, true, buff.size(), command); + LOG_ERROR("Failed to load_from_binary on command " << command); + cb(LEVIN_ERROR_FORMAT, result_struct, context); + return false; + } + if (!result_struct.load(stg_ret)) + { + on_levin_traffic(context, true, false, true, buff.size(), command); + LOG_ERROR("Failed to load result struct on command " << command); + cb(LEVIN_ERROR_FORMAT, result_struct, context); + return false; + } + on_levin_traffic(context, true, false, false, buff.size(), command); + cb(code, result_struct, context); + return true; + }, inv_timeout); + if( res <=0 ) + { + LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res); + return false; + } + return true; + } + + template + bool notify_remote_command2(const typename t_transport::connection_context &context, int command, const t_arg& out_struct, t_transport& transport) + { + const boost::uuids::uuid &conn_id = context.m_connection_id; + serialization::portable_storage stg; + out_struct.store(stg); + std::string buff_to_send; + stg.store_to_binary(buff_to_send); + + on_levin_traffic(context, true, true, false, buff_to_send.size(), command); + int res = transport.notify(command, epee::strspan(buff_to_send), conn_id); + if(res <=0 ) + { + MERROR("Failed to notify command " << command << " return code " << res); + return false; + } + return true; + } + //---------------------------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------------------------- + template + int buff_to_t_adapter(int command, const epee::span in_buff, std::string& buff_out, callback_t cb, t_context& context ) + { + serialization::portable_storage strg; + if(!strg.load_from_binary(in_buff)) + { + on_levin_traffic(context, false, false, true, in_buff.size(), command); + LOG_ERROR("Failed to load_from_binary in command " << command); + return -1; + } + boost::value_initialized in_struct; + boost::value_initialized out_struct; + + if (!static_cast(in_struct).load(strg)) + { + on_levin_traffic(context, false, false, true, in_buff.size(), command); + LOG_ERROR("Failed to load in_struct in command " << command); + return -1; + } + on_levin_traffic(context, false, false, false, in_buff.size(), command); + int res = cb(command, static_cast(in_struct), static_cast(out_struct), context); + serialization::portable_storage strg_out; + static_cast(out_struct).store(strg_out); + + if(!strg_out.store_to_binary(buff_out)) + { + LOG_ERROR("Failed to store_to_binary in command" << command); + return -1; + } + on_levin_traffic(context, false, true, false, buff_out.size(), command); + + return res; + } + + template + int buff_to_t_adapter(t_owner* powner, int command, const epee::span in_buff, callback_t cb, t_context& context) + { + serialization::portable_storage strg; + if(!strg.load_from_binary(in_buff)) + { + on_levin_traffic(context, false, false, true, in_buff.size(), command); + LOG_ERROR("Failed to load_from_binary in notify " << command); + return -1; + } + boost::value_initialized in_struct; + if (!static_cast(in_struct).load(strg)) + { + on_levin_traffic(context, false, false, true, in_buff.size(), command); + LOG_ERROR("Failed to load in_struct in notify " << command); + return -1; + } + on_levin_traffic(context, false, false, false, in_buff.size(), command); + return cb(command, in_struct, context); + } + +#define CHAIN_LEVIN_INVOKE_MAP2(context_type) \ + int invoke(int command, const epee::span in_buff, std::string& buff_out, context_type& context) \ + { \ + bool handled = false; \ + return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \ + } + +#define CHAIN_LEVIN_NOTIFY_MAP2(context_type) \ + int notify(int command, const epee::span in_buff, context_type& context) \ + { \ + bool handled = false; std::string fake_str;\ + return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \ + } + + +#define CHAIN_LEVIN_INVOKE_MAP() \ + int invoke(int command, const epee::span in_buff, std::string& buff_out, epee::net_utils::connection_context_base& context) \ + { \ + bool handled = false; \ + return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \ + } + +#define CHAIN_LEVIN_NOTIFY_MAP() \ + int notify(int command, const epee::span in_buff, epee::net_utils::connection_context_base& context) \ + { \ + bool handled = false; std::string fake_str;\ + return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \ + } + +#define CHAIN_LEVIN_NOTIFY_STUB() \ + int notify(int command, const epee::span in_buff, epee::net_utils::connection_context_base& context) \ + { \ + return -1; \ + } + +#define BEGIN_INVOKE_MAP2(owner_type) \ + template int handle_invoke_map(bool is_notify, int command, const epee::span in_buff, std::string& buff_out, t_context& context, bool& handled) \ + { \ + typedef owner_type internal_owner_type_name; + +#define HANDLE_INVOKE2(command_id, func, type_name_in, typename_out) \ + if(!is_notify && command_id == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter(this, command, in_buff, buff_out, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), context);} + +#define HANDLE_INVOKE_T2(COMMAND, func) \ + if(!is_notify && COMMAND::ID == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter(command, in_buff, buff_out, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), context);} + + +#define HANDLE_NOTIFY2(command_id, func, type_name_in) \ + if(is_notify && command_id == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter(this, command, in_buff, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), context);} + +#define HANDLE_NOTIFY_T2(NOTIFY, func) \ + if(is_notify && NOTIFY::ID == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter(this, command, in_buff, std::bind(func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), context);} + + +#define CHAIN_INVOKE_MAP2(func) \ + { \ + int res = func(is_notify, command, in_buff, buff_out, context, handled); \ + if(handled) \ + return res; \ + } + +#define CHAIN_INVOKE_MAP_TO_OBJ2(obj) \ + { \ + int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, context, handled); \ + if(handled) \ + return res; \ + } + +#define CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(obj, context_type) \ + { \ + int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, static_cast(context), handled); \ + if(handled) return res; \ + } + + +#define END_INVOKE_MAP2() \ + LOG_ERROR("Unknown command:" << command); \ + on_levin_traffic(context, false, false, true, in_buff.size(), "invalid-command"); \ + return LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; \ + } + } +} + diff --git a/src/contrib/epee/include/storages/parserse_base_utils.h b/src/contrib/epee/include/storages/parserse_base_utils.h new file mode 100644 index 0000000..5a6cc0b --- /dev/null +++ b/src/contrib/epee/include/storages/parserse_base_utils.h @@ -0,0 +1,378 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + + +#pragma once + +#include +#include + +#include "misc_log_ex.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "serialization" + +namespace epee +{ +namespace misc_utils +{ + namespace parse + { + // 1: digit + // 2: .eE (floating point) + // 4: alpha + // 8: whitespace + // 16: allowed in float but doesn't necessarily mean it's a float + // 32: \ and " (end of verbatim string) + static const constexpr uint8_t lut[256]={ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 0, 0, // 16 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 + 8, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 16, 18, 0, // 48 + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 0, 0, 0, 0, // 64 + 0, 4, 4, 4, 4, 22, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 80 + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 32, 0, 0, 0, // 96 + 0, 4, 4, 4, 4, 22, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 112 + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, // 128 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + static const constexpr unsigned char isx[256] = + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 10, 11, 12, 13, 14, 15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 10, 11, 12, 13, 14, 15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }; + + inline bool isspace(char c) + { + return lut[(uint8_t)c] & 8; + } + + inline bool isdigit(char c) + { + return lut[(uint8_t)c] & 1; + } + + inline std::string transform_to_escape_sequence(const std::string& src) + { + static const char escaped[] = "\b\f\n\r\t\v\"\\/"; + std::string::const_iterator it = std::find_first_of(src.begin(), src.end(), escaped, escaped + sizeof(escaped)); + if (it == src.end()) + return src; + + std::string res; + res.reserve(2 * src.size()); + res.assign(src.begin(), it); + for(; it!=src.end(); ++it) + { + switch(*it) + { + case '\b': //Backspace (ascii code 08) + res+="\\b"; break; + case '\f': //Form feed (ascii code 0C) + res+="\\f"; break; + case '\n': //New line + res+="\\n"; break; + case '\r': //Carriage return + res+="\\r"; break; + case '\t': //Tab + res+="\\t"; break; + case '\v': //Vertical tab + res+="\\v"; break; + //case '\'': //Apostrophe or single quote + // res+="\\'"; break; + case '"': //Double quote + res+="\\\""; break; + case '\\': //Backslash caracter + res+="\\\\"; break; + case '/': //Backslash caracter + res+="\\/"; break; + default: + res.push_back(*it); + } + } + return res; + } + /* + + \b Backspace (ascii code 08) + \f Form feed (ascii code 0C) + \n New line + \r Carriage return + \t Tab + \v Vertical tab + \' Apostrophe or single quote + \" Double quote + \\ Backslash character + + */ + inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + bool escape_mode = false; + std::string::const_iterator it = star_end_string; + ++it; + std::string::const_iterator fi = it; + while (fi != buf_end && ((lut[(uint8_t)*fi] & 32)) == 0) + ++fi; + val.assign(it, fi); + it = fi; + for(;it != buf_end;it++) + { + if(escape_mode/*prev_ch == '\\'*/) + { + switch(*it) + { + case 'b': //Backspace (ascii code 08) + val.push_back(0x08);break; + case 'f': //Form feed (ascii code 0C) + val.push_back(0x0C);break; + case 'n': //New line + val.push_back('\n');break; + case 'r': //Carriage return + val.push_back('\r');break; + case 't': //Tab + val.push_back('\t');break; + case 'v': //Vertical tab + val.push_back('\v');break; + case '\'': //Apostrophe or single quote + val.push_back('\'');break; + case '"': //Double quote + val.push_back('"');break; + case '\\': //Backslash character + val.push_back('\\');break; + case '/': //Slash character + val.push_back('/');break; + case 'u': //Unicode code point + if (buf_end - it < 4) + { + ASSERT_MES_AND_THROW("Invalid Unicode escape sequence"); + } + else + { + uint32_t dst = 0; + for (int i = 0; i < 4; ++i) + { + const unsigned char tmp = isx[(unsigned char)*++it]; + CHECK_AND_ASSERT_THROW_MES(tmp != 0xff, "Bad Unicode encoding"); + dst = dst << 4 | tmp; + } + // encode as UTF-8 + if (dst <= 0x7f) + { + val.push_back(dst); + } + else if (dst <= 0x7ff) + { + val.push_back(0xc0 | (dst >> 6)); + val.push_back(0x80 | (dst & 0x3f)); + } + else if (dst <= 0xffff) + { + val.push_back(0xe0 | (dst >> 12)); + val.push_back(0x80 | ((dst >> 6) & 0x3f)); + val.push_back(0x80 | (dst & 0x3f)); + } + else + { + ASSERT_MES_AND_THROW("Unicode code point is out or range"); + } + } + break; + default: + val.push_back(*it); + LOG_PRINT_L0("Unknown escape sequence :\"\\" << *it << "\""); + } + escape_mode = false; + }else if(*it == '"') + { + star_end_string = it; + return; + }else if(*it == '\\') + { + escape_mode = true; + } + else + { + val.push_back(*it); + } + } + ASSERT_MES_AND_THROW("Failed to match string in json entry: " << std::string(star_end_string, buf_end)); + } + inline bool match_string(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + try + { + + match_string2(star_end_string, buf_end, val); + return true; + } + catch(...) + { + return false; + } + } + inline void match_number2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, boost::string_ref& val, bool& is_float_val, bool& is_signed_val) + { + val.clear(); + uint8_t float_flag = 0; + is_signed_val = false; + size_t chars = 0; + std::string::const_iterator it = star_end_string; + if (it != buf_end && *it == '-') + { + is_signed_val = true; + ++chars; + ++it; + } + for(;it != buf_end;it++) + { + const uint8_t flags = lut[(uint8_t)*it]; + if (flags & 16) + { + float_flag |= flags; + ++chars; + } + else + { + val = boost::string_ref(&*star_end_string, chars); + if(val.size()) + { + star_end_string = --it; + is_float_val = !!(float_flag & 2); + return; + } + else + ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end)); + } + } + ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end)); + } + inline bool match_number(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, boost::string_ref& val) + { + try + { + bool is_v_float = false;bool is_signed_val = false; + match_number2(star_end_string, buf_end, val, is_v_float, is_signed_val); + return !is_v_float; + } + catch(...) + { + return false; + } + } + inline void match_word2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, boost::string_ref& val) + { + val.clear(); + + for(std::string::const_iterator it = star_end_string;it != buf_end;it++) + { + if (!(lut[(uint8_t)*it] & 4)) + { + val = boost::string_ref(&*star_end_string, std::distance(star_end_string, it)); + if(val.size()) + { + star_end_string = --it; + return; + }else + ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end)); + } + } + ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end)); + } + inline bool match_word(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, boost::string_ref& val) + { + try + { + match_word2(star_end_string, buf_end, val); + return true; + } + catch(...) + { + return false; + } + } + inline bool match_word_with_extrasymb(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + val.clear(); + + for(std::string::const_iterator it = star_end_string;it != buf_end;it++) + { + if(!isalnum(*it) && *it != '-' && *it != '_') + { + val.assign(star_end_string, it); + if(val.size()) + { + star_end_string = --it; + return true; + }else + return false; + } + } + return false; + } + inline bool match_word_til_equal_mark(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string::const_iterator& word_end) + { + word_end = star_end_string; + + for(std::string::const_iterator it = star_end_string;it != buf_end;it++) + { + if(isspace(*it)) + { + + continue; + }else if( *it == '=' ) + { + star_end_string = it; + word_end = it; + return true; + } + } + return false; + } + } +} +} diff --git a/src/contrib/epee/include/storages/portable_storage.h b/src/contrib/epee/include/storages/portable_storage.h new file mode 100644 index 0000000..4b759a2 --- /dev/null +++ b/src/contrib/epee/include/storages/portable_storage.h @@ -0,0 +1,490 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + + +#pragma once + +#include + +#include "misc_language.h" +#include "portable_storage_base.h" +#include "portable_storage_to_bin.h" +#include "portable_storage_from_bin.h" +#include "portable_storage_to_json.h" +#include "portable_storage_from_json.h" +#include "portable_storage_val_converters.h" +#include "span.h" +#include "int-util.h" + +namespace epee +{ + namespace serialization + { + /************************************************************************/ + /* */ + /************************************************************************/ + class portable_storage + { + public: + typedef epee::serialization::hsection hsection; + typedef epee::serialization::harray harray; + typedef storage_entry meta_entry; + + portable_storage(){} + virtual ~portable_storage(){} + hsection open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist = false); + template + bool get_value(const std::string& value_name, t_value& val, hsection hparent_section); + bool get_value(const std::string& value_name, storage_entry& val, hsection hparent_section); + template + bool set_value(const std::string& value_name, t_value&& target, hsection hparent_section); + + //serial access for arrays of values -------------------------------------- + //values + template + harray get_first_value(const std::string& value_name, t_value& target, hsection hparent_section); + template + bool get_next_value(harray hval_array, t_value& target); + template + harray insert_first_value(const std::string& value_name, t_value&& target, hsection hparent_section); + template + bool insert_next_value(harray hval_array, t_value&& target); + //sections + harray get_first_section(const std::string& pSectionName, hsection& h_child_section, hsection hparent_section); + bool get_next_section(harray hSecArray, hsection& h_child_section); + harray insert_first_section(const std::string& pSectionName, hsection& hinserted_childsection, hsection hparent_section); + bool insert_next_section(harray hSecArray, hsection& hinserted_childsection); + //------------------------------------------------------------------------ + //delete entry (section, value or array) + bool delete_entry(const std::string& pentry_name, hsection hparent_section = nullptr); + + //------------------------------------------------------------------------------- + bool store_to_binary(binarybuffer& target); + bool load_from_binary(const epee::span target); + bool load_from_binary(const std::string& target) { return load_from_binary(epee::strspan(target)); } + template + bool dump_as_xml(std::string& targetObj, const std::string& root_name = ""); + bool dump_as_json(std::string& targetObj, size_t indent = 0, bool insert_newlines = true); + bool load_from_json(const std::string& source); + + private: + section m_root; + hsection get_root_section() {return &m_root;} + storage_entry* find_storage_entry(const std::string& pentry_name, hsection psection); + template + storage_entry* insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, entry_type&& entry); + + hsection insert_new_section(const std::string& pentry_name, hsection psection); + +#pragma pack(push) +#pragma pack(1) + struct storage_block_header + { + uint32_t m_signature_a; + uint32_t m_signature_b; + uint8_t m_ver; + }; +#pragma pack(pop) + }; + inline + bool portable_storage::dump_as_json(std::string& buff, size_t indent, bool insert_newlines) + { + TRY_ENTRY(); + std::stringstream ss; + epee::serialization::dump_as_json(ss, m_root, indent, insert_newlines); + buff = ss.str(); + return true; + CATCH_ENTRY("portable_storage::dump_as_json", false) + } + inline + bool portable_storage::load_from_json(const std::string& source) + { + TRY_ENTRY(); + return json::load_from_json(source, *this); + CATCH_ENTRY("portable_storage::load_from_json", false) + } + + template + bool portable_storage::dump_as_xml(std::string& targetObj, const std::string& root_name) + { + return false;//TODO: don't think i ever again will use xml - ambiguous and "overtagged" format + } + + inline + bool portable_storage::store_to_binary(binarybuffer& target) + { + TRY_ENTRY(); + std::stringstream ss; + storage_block_header sbh = AUTO_VAL_INIT(sbh); + sbh.m_signature_a = SWAP32LE(PORTABLE_STORAGE_SIGNATUREA); + sbh.m_signature_b = SWAP32LE(PORTABLE_STORAGE_SIGNATUREB); + sbh.m_ver = PORTABLE_STORAGE_FORMAT_VER; + ss.write((const char*)&sbh, sizeof(storage_block_header)); + pack_entry_to_buff(ss, m_root); + target = ss.str(); + return true; + CATCH_ENTRY("portable_storage::store_to_binary", false) + } + inline + bool portable_storage::load_from_binary(const epee::span source) + { + m_root.m_entries.clear(); + if(source.size() < sizeof(storage_block_header)) + { + LOG_ERROR("portable_storage: wrong binary format, packet size = " << source.size() << " less than expected sizeof(storage_block_header)=" << sizeof(storage_block_header)); + return false; + } + storage_block_header* pbuff = (storage_block_header*)source.data(); + if(pbuff->m_signature_a != SWAP32LE(PORTABLE_STORAGE_SIGNATUREA) || + pbuff->m_signature_b != SWAP32LE(PORTABLE_STORAGE_SIGNATUREB) + ) + { + LOG_ERROR("portable_storage: wrong binary format - signature mismatch"); + return false; + } + if(pbuff->m_ver != PORTABLE_STORAGE_FORMAT_VER) + { + LOG_ERROR("portable_storage: wrong binary format - unknown format ver = " << pbuff->m_ver); + return false; + } + TRY_ENTRY(); + throwable_buffer_reader buf_reader(source.data()+sizeof(storage_block_header), source.size()-sizeof(storage_block_header)); + buf_reader.read(m_root); + return true;//TODO: + CATCH_ENTRY("portable_storage::load_from_binary", false); + } + //--------------------------------------------------------------------------------------------------------------- + inline + hsection portable_storage::open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist) + { + TRY_ENTRY(); + hparent_section = hparent_section ? hparent_section:&m_root; + storage_entry* pentry = find_storage_entry(section_name, hparent_section); + if(!pentry) + { + if(!create_if_notexist) + return nullptr; + return insert_new_section(section_name, hparent_section); + } + CHECK_AND_ASSERT(pentry , nullptr); + //check that section_entry we find is real "CSSection" + if(pentry->type() != typeid(section)) + { + if(create_if_notexist) + *pentry = storage_entry(section());//replace + else + return nullptr; + } + return &boost::get
(*pentry); + CATCH_ENTRY("portable_storage::open_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template + struct get_value_visitor: boost::static_visitor + { + to_type& m_target; + get_value_visitor(to_type& target):m_target(target){} + template + void operator()(const from_type& v){convert_t(v, m_target);} + }; + + template + bool portable_storage::get_value(const std::string& value_name, t_value& val, hsection hparent_section) + { + BOOST_MPL_ASSERT(( boost::mpl::contains )); + //TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + return false; + + get_value_visitor gvv(val); + boost::apply_visitor(gvv, *pentry); + return true; + //CATCH_ENTRY("portable_storage::template<>get_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + inline + bool portable_storage::get_value(const std::string& value_name, storage_entry& val, hsection hparent_section) + { + //TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + return false; + + val = *pentry; + return true; + //CATCH_ENTRY("portable_storage::template<>get_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + template + bool portable_storage::set_value(const std::string& value_name, t_value&& v, hsection hparent_section) + { + using t_real_value = typename std::decay::type; + BOOST_MPL_ASSERT(( boost::mpl::contains::type, t_real_value> )); + TRY_ENTRY(); + if(!hparent_section) + hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + { + pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, std::forward(v)); + if(!pentry) + return false; + return true; + } + *pentry = std::forward(v); + return true; + CATCH_ENTRY("portable_storage::template<>set_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + inline + storage_entry* portable_storage::find_storage_entry(const std::string& pentry_name, hsection psection) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(psection, nullptr); + auto it = psection->m_entries.find(pentry_name); + if(it == psection->m_entries.end()) + return nullptr; + + return &it->second; + CATCH_ENTRY("portable_storage::find_storage_entry", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template + storage_entry* portable_storage::insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, entry_type&& entry) + { + static_assert(std::is_rvalue_reference(), "unexpected copy of value"); + TRY_ENTRY(); + CHECK_AND_ASSERT(psection, nullptr); + auto ins_res = psection->m_entries.emplace(pentry_name, std::forward(entry)); + return &ins_res.first->second; + CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + inline + hsection portable_storage::insert_new_section(const std::string& pentry_name, hsection psection) + { + TRY_ENTRY(); + storage_entry* pse = insert_new_entry_get_storage_entry(pentry_name, psection, section()); + if(!pse) return nullptr; + return &boost::get
(*pse); + CATCH_ENTRY("portable_storage::insert_new_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template + struct get_first_value_visitor: boost::static_visitor + { + to_type& m_target; + get_first_value_visitor(to_type& target):m_target(target){} + template + bool operator()(const array_entry_t& a) + { + const from_type* pv = a.get_first_val(); + if(!pv) + return false; + convert_t(*pv, m_target); + return true; + } + }; + //--------------------------------------------------------------------------------------------------------------- + template + harray portable_storage::get_first_value(const std::string& value_name, t_value& target, hsection hparent_section) + { + BOOST_MPL_ASSERT(( boost::mpl::contains )); + //TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + return nullptr; + if(pentry->type() != typeid(array_entry)) + return nullptr; + array_entry& ar_entry = boost::get(*pentry); + + get_first_value_visitor gfv(target); + if(!boost::apply_visitor(gfv, ar_entry)) + return nullptr; + return &ar_entry; + //CATCH_ENTRY("portable_storage::get_first_value", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template + struct get_next_value_visitor: boost::static_visitor + { + to_type& m_target; + get_next_value_visitor(to_type& target):m_target(target){} + template + bool operator()(const array_entry_t& a) + { + //TODO: optimize code here: work without get_next_val function + const from_type* pv = a.get_next_val(); + if(!pv) + return false; + convert_t(*pv, m_target); + return true; + } + }; + + + template + bool portable_storage::get_next_value(harray hval_array, t_value& target) + { + BOOST_MPL_ASSERT(( boost::mpl::contains )); + //TRY_ENTRY(); + CHECK_AND_ASSERT(hval_array, false); + array_entry& ar_entry = *hval_array; + get_next_value_visitor gnv(target); + if(!boost::apply_visitor(gnv, ar_entry)) + return false; + return true; + //CATCH_ENTRY("portable_storage::get_next_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + template + harray portable_storage::insert_first_value(const std::string& value_name, t_value&& target, hsection hparent_section) + { + using t_real_value = typename std::decay::type; + static_assert(std::is_rvalue_reference(), "unexpected copy of value"); + TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + { + pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, array_entry(array_entry_t())); + if(!pentry) + return nullptr; + } + if(pentry->type() != typeid(array_entry)) + *pentry = storage_entry(array_entry(array_entry_t())); + + array_entry& arr = boost::get(*pentry); + if(arr.type() != typeid(array_entry_t)) + arr = array_entry(array_entry_t()); + + array_entry_t& arr_typed = boost::get >(arr); + arr_typed.insert_first_val(std::forward(target)); + return &arr; + CATCH_ENTRY("portable_storage::insert_first_value", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template + bool portable_storage::insert_next_value(harray hval_array, t_value&& target) + { + using t_real_value = typename std::decay::type; + static_assert(std::is_rvalue_reference(), "unexpected copy of value"); + TRY_ENTRY(); + CHECK_AND_ASSERT(hval_array, false); + + CHECK_AND_ASSERT_MES(hval_array->type() == typeid(array_entry_t), + false, "unexpected type in insert_next_value: " << typeid(array_entry_t).name()); + + array_entry_t& arr_typed = boost::get >(*hval_array); + arr_typed.insert_next_value(std::forward(target)); + return true; + CATCH_ENTRY("portable_storage::insert_next_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + //sections + inline + harray portable_storage::get_first_section(const std::string& sec_name, hsection& h_child_section, hsection hparent_section) + { + TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(sec_name, hparent_section); + if(!pentry) + return nullptr; + if(pentry->type() != typeid(array_entry)) + return nullptr; + array_entry& ar_entry = boost::get(*pentry); + if(ar_entry.type() != typeid(array_entry_t
)) + return nullptr; + array_entry_t
& sec_array = boost::get>(ar_entry); + section* psec = sec_array.get_first_val(); + if(!psec) + return nullptr; + h_child_section = psec; + return &ar_entry; + CATCH_ENTRY("portable_storage::get_first_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + inline + bool portable_storage::get_next_section(harray hsec_array, hsection& h_child_section) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(hsec_array, false); + if(hsec_array->type() != typeid(array_entry_t
)) + return false; + array_entry_t
& sec_array = boost::get>(*hsec_array); + h_child_section = sec_array.get_next_val(); + if(!h_child_section) + return false; + return true; + CATCH_ENTRY("portable_storage::get_next_section", false); + } + //--------------------------------------------------------------------------------------------------------------- + inline + harray portable_storage::insert_first_section(const std::string& sec_name, hsection& hinserted_childsection, hsection hparent_section) + { + TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(sec_name, hparent_section); + if(!pentry) + { + pentry = insert_new_entry_get_storage_entry(sec_name, hparent_section, array_entry(array_entry_t
())); + if(!pentry) + return nullptr; + } + if(pentry->type() != typeid(array_entry)) + *pentry = storage_entry(array_entry(array_entry_t
())); + + array_entry& ar_entry = boost::get(*pentry); + if(ar_entry.type() != typeid(array_entry_t
)) + ar_entry = array_entry(array_entry_t
()); + + array_entry_t
& sec_array = boost::get>(ar_entry); + hinserted_childsection = &sec_array.insert_first_val(section()); + return &ar_entry; + CATCH_ENTRY("portable_storage::insert_first_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + inline + bool portable_storage::insert_next_section(harray hsec_array, hsection& hinserted_childsection) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(hsec_array, false); + CHECK_AND_ASSERT_MES(hsec_array->type() == typeid(array_entry_t
), + false, "unexpected type(not 'section') in insert_next_section, type: " << hsec_array->type().name()); + + array_entry_t
& sec_array = boost::get>(*hsec_array); + hinserted_childsection = &sec_array.insert_next_value(section()); + return true; + CATCH_ENTRY("portable_storage::insert_next_section", false); + } + //--------------------------------------------------------------------------------------------------------------- + } +} diff --git a/src/contrib/epee/include/storages/portable_storage_base.h b/src/contrib/epee/include/storages/portable_storage_base.h new file mode 100644 index 0000000..1676f41 --- /dev/null +++ b/src/contrib/epee/include/storages/portable_storage_base.h @@ -0,0 +1,177 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + + +#pragma once + +#include +#include +#include +#include +#include + +#define PORTABLE_STORAGE_SIGNATUREA 0x01011101 +#define PORTABLE_STORAGE_SIGNATUREB 0x01020101 // bender's nightmare +#define PORTABLE_STORAGE_FORMAT_VER 1 + +#define PORTABLE_RAW_SIZE_MARK_MASK 0x03 +#define PORTABLE_RAW_SIZE_MARK_BYTE 0 +#define PORTABLE_RAW_SIZE_MARK_WORD 1 +#define PORTABLE_RAW_SIZE_MARK_DWORD 2 +#define PORTABLE_RAW_SIZE_MARK_INT64 3 + +#ifndef MAX_STRING_LEN_POSSIBLE +#define MAX_STRING_LEN_POSSIBLE 2000000000 //do not let string be so big +#endif + +//data types +#define SERIALIZE_TYPE_INT64 1 +#define SERIALIZE_TYPE_INT32 2 +#define SERIALIZE_TYPE_INT16 3 +#define SERIALIZE_TYPE_INT8 4 +#define SERIALIZE_TYPE_UINT64 5 +#define SERIALIZE_TYPE_UINT32 6 +#define SERIALIZE_TYPE_UINT16 7 +#define SERIALIZE_TYPE_UINT8 8 +#define SERIALIZE_TYPE_DUOBLE 9 +#define SERIALIZE_TYPE_STRING 10 +#define SERIALIZE_TYPE_BOOL 11 +#define SERIALIZE_TYPE_OBJECT 12 +#define SERIALIZE_TYPE_ARRAY 13 + +#define SERIALIZE_FLAG_ARRAY 0x80 + + +namespace epee +{ + namespace serialization + { + struct section; + + template struct entry_container { typedef std::vector type; static void reserve(type &t, size_t n) { t.reserve(n); } }; + template<> struct entry_container { typedef std::deque type; static void reserve(type &t, size_t n) {} }; + + /************************************************************************/ + /* */ + /************************************************************************/ + template + struct array_entry_t + { + array_entry_t():m_it(m_array.end()){} + array_entry_t(const array_entry_t& other):m_array(other.m_array), m_it(m_array.end()){} + + array_entry_t& operator=(const array_entry_t& other) + { + m_array = other.m_array; + m_it = m_array.end(); + return *this; + } + + const t_entry_type* get_first_val() const + { + m_it = m_array.begin(); + return get_next_val(); + } + + t_entry_type* get_first_val() + { + m_it = m_array.begin(); + return get_next_val(); + } + + + const t_entry_type* get_next_val() const + { + if(m_it == m_array.end()) + return nullptr; + return &(*(m_it++)); + } + + t_entry_type* get_next_val() + { + if(m_it == m_array.end()) + return nullptr; + return (t_entry_type*)&(*(m_it++));//fuckoff + } + + t_entry_type& insert_first_val(t_entry_type&& v) + { + m_array.clear(); + m_it = m_array.end(); + return insert_next_value(std::move(v)); + } + + t_entry_type& insert_next_value(t_entry_type&& v) + { + m_array.push_back(std::move(v)); + return m_array.back(); + } + + void reserve(size_t n) + { + entry_container::reserve(m_array, n); + } + + typename entry_container::type m_array; + mutable typename entry_container::type::const_iterator m_it; + }; + + + typedef boost::make_recursive_variant< + array_entry_t
, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t
, + array_entry_t + >::type array_entry; + + typedef boost::variant storage_entry; + + typedef std::string binarybuffer;//it's ok + + /************************************************************************/ + /* */ + /************************************************************************/ + struct section + { + std::map m_entries; + }; + + //handle-like aliases + typedef section* hsection; + typedef array_entry* harray; + } +} diff --git a/src/contrib/epee/include/storages/portable_storage_bin_utils.h b/src/contrib/epee/include/storages/portable_storage_bin_utils.h new file mode 100644 index 0000000..bcde644 --- /dev/null +++ b/src/contrib/epee/include/storages/portable_storage_bin_utils.h @@ -0,0 +1,46 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "int-util.h" + +template T convert_swapper(T t) { return t; } +template<> inline uint16_t convert_swapper(uint16_t t) { return SWAP16LE(t); } +template<> inline int16_t convert_swapper(int16_t t) { return SWAP16LE((uint16_t&)t); } +template<> inline uint32_t convert_swapper(uint32_t t) { return SWAP32LE(t); } +template<> inline int32_t convert_swapper(int32_t t) { return SWAP32LE((uint32_t&)t); } +template<> inline uint64_t convert_swapper(uint64_t t) { return SWAP64LE(t); } +template<> inline int64_t convert_swapper(int64_t t) { return SWAP64LE((uint64_t&)t); } +template<> inline double convert_swapper(double t) { union { uint64_t u; double d; } u; u.d = t; u.u = SWAP64LE(u.u); return u.d; } + +#if BYTE_ORDER == BIG_ENDIAN +#define CONVERT_POD(x) convert_swapper(x) +#else +#define CONVERT_POD(x) (x) +#endif diff --git a/src/contrib/epee/include/storages/portable_storage_from_bin.h b/src/contrib/epee/include/storages/portable_storage_from_bin.h new file mode 100644 index 0000000..b39dc7c --- /dev/null +++ b/src/contrib/epee/include/storages/portable_storage_from_bin.h @@ -0,0 +1,293 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + + +#pragma once + +#include "misc_language.h" +#include "portable_storage_base.h" +#include "portable_storage_bin_utils.h" + +#ifdef EPEE_PORTABLE_STORAGE_RECURSION_LIMIT +#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL EPEE_PORTABLE_STORAGE_RECURSION_LIMIT +#else +#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL 100 +#endif + +namespace epee +{ + namespace serialization + { + struct throwable_buffer_reader + { + throwable_buffer_reader(const void* ptr, size_t sz); + void read(void* target, size_t count); + void read_sec_name(std::string& sce_name); + template + void read(t_pod_type& pod_val); + template + t_type read(); + template + storage_entry read_ae(); + storage_entry load_storage_array_entry(uint8_t type); + size_t read_varint(); + template + storage_entry read_se(); + storage_entry load_storage_entry(); + void read(section& sec); + void read(std::string& str); + void read(array_entry &ae); + private: + struct recursuion_limitation_guard + { + size_t& m_counter_ref; + recursuion_limitation_guard(size_t& counter):m_counter_ref(counter) + { + ++m_counter_ref; + CHECK_AND_ASSERT_THROW_MES(m_counter_ref < EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL, "Wrong blob data in portable storage: recursion limitation (" << EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL << ") exceeded"); + } + ~recursuion_limitation_guard() noexcept(false) + { + CHECK_AND_ASSERT_THROW_MES(m_counter_ref != 0, "Internal error: m_counter_ref == 0 while ~recursuion_limitation_guard()"); + --m_counter_ref; + } + }; +#define RECURSION_LIMITATION() recursuion_limitation_guard rl(m_recursion_count) + + const uint8_t* m_ptr; + size_t m_count; + size_t m_recursion_count; + }; + + inline throwable_buffer_reader::throwable_buffer_reader(const void* ptr, size_t sz) + { + if(!ptr) + throw std::runtime_error("throwable_buffer_reader: ptr==nullptr"); + if(!sz) + throw std::runtime_error("throwable_buffer_reader: sz==0"); + m_ptr = (uint8_t*)ptr; + m_count = sz; + m_recursion_count = 0; + } + inline + void throwable_buffer_reader::read(void* target, size_t count) + { + RECURSION_LIMITATION(); + CHECK_AND_ASSERT_THROW_MES(m_count >= count, " attempt to read " << count << " bytes from buffer with " << m_count << " bytes remained"); + memcpy(target, m_ptr, count); + m_ptr += count; + m_count -= count; + } + inline + void throwable_buffer_reader::read_sec_name(std::string& sce_name) + { + RECURSION_LIMITATION(); + uint8_t name_len = 0; + read(name_len); + sce_name.resize(name_len); + read((void*)sce_name.data(), name_len); + } + + template + void throwable_buffer_reader::read(t_pod_type& pod_val) + { + RECURSION_LIMITATION(); + static_assert(std::is_pod::value, "POD type expected"); + read(&pod_val, sizeof(pod_val)); + pod_val = CONVERT_POD(pod_val); + } + + template + t_type throwable_buffer_reader::read() + { + RECURSION_LIMITATION(); + t_type v; + read(v); + return v; + } + + + template + storage_entry throwable_buffer_reader::read_ae() + { + RECURSION_LIMITATION(); + //for pod types + array_entry_t sa; + size_t size = read_varint(); + CHECK_AND_ASSERT_THROW_MES(size <= m_count, "Size sanity check failed"); + sa.reserve(size); + //TODO: add some optimization here later + while(size--) + sa.m_array.push_back(read()); + return storage_entry(array_entry(std::move(sa))); + } + + inline + storage_entry throwable_buffer_reader::load_storage_array_entry(uint8_t type) + { + RECURSION_LIMITATION(); + type &= ~SERIALIZE_FLAG_ARRAY; + switch(type) + { + case SERIALIZE_TYPE_INT64: return read_ae(); + case SERIALIZE_TYPE_INT32: return read_ae(); + case SERIALIZE_TYPE_INT16: return read_ae(); + case SERIALIZE_TYPE_INT8: return read_ae(); + case SERIALIZE_TYPE_UINT64: return read_ae(); + case SERIALIZE_TYPE_UINT32: return read_ae(); + case SERIALIZE_TYPE_UINT16: return read_ae(); + case SERIALIZE_TYPE_UINT8: return read_ae(); + case SERIALIZE_TYPE_DUOBLE: return read_ae(); + case SERIALIZE_TYPE_BOOL: return read_ae(); + case SERIALIZE_TYPE_STRING: return read_ae(); + case SERIALIZE_TYPE_OBJECT: return read_ae
(); + case SERIALIZE_TYPE_ARRAY: return read_ae(); + default: + CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << type); + } + } + + inline + size_t throwable_buffer_reader::read_varint() + { + RECURSION_LIMITATION(); + CHECK_AND_ASSERT_THROW_MES(m_count >= 1, "empty buff, expected place for varint"); + size_t v = 0; + uint8_t size_mask = (*(uint8_t*)m_ptr) &PORTABLE_RAW_SIZE_MARK_MASK; + switch (size_mask) + { + case PORTABLE_RAW_SIZE_MARK_BYTE: v = read();break; + case PORTABLE_RAW_SIZE_MARK_WORD: v = read();break; + case PORTABLE_RAW_SIZE_MARK_DWORD: v = read();break; + case PORTABLE_RAW_SIZE_MARK_INT64: v = read();break; + default: + CHECK_AND_ASSERT_THROW_MES(false, "unknown varint size_mask = " << size_mask); + } + v >>= 2; + return v; + } + + template + storage_entry throwable_buffer_reader::read_se() + { + RECURSION_LIMITATION(); + t_type v; + read(v); + return storage_entry(v); + } + + template<> + inline storage_entry throwable_buffer_reader::read_se() + { + RECURSION_LIMITATION(); + return storage_entry(read()); + } + + + template<> + inline storage_entry throwable_buffer_reader::read_se
() + { + RECURSION_LIMITATION(); + section s;//use extra variable due to vs bug, line "storage_entry se(section()); " can't be compiled in visual studio + storage_entry se(std::move(s)); + section& section_entry = boost::get
(se); + read(section_entry); + return se; + } + + template<> + inline storage_entry throwable_buffer_reader::read_se() + { + RECURSION_LIMITATION(); + uint8_t ent_type = 0; + read(ent_type); + CHECK_AND_ASSERT_THROW_MES(ent_type&SERIALIZE_FLAG_ARRAY, "wrong type sequenses"); + return load_storage_array_entry(ent_type); + } + + inline + storage_entry throwable_buffer_reader::load_storage_entry() + { + RECURSION_LIMITATION(); + uint8_t ent_type = 0; + read(ent_type); + if(ent_type&SERIALIZE_FLAG_ARRAY) + return load_storage_array_entry(ent_type); + + switch(ent_type) + { + case SERIALIZE_TYPE_INT64: return read_se(); + case SERIALIZE_TYPE_INT32: return read_se(); + case SERIALIZE_TYPE_INT16: return read_se(); + case SERIALIZE_TYPE_INT8: return read_se(); + case SERIALIZE_TYPE_UINT64: return read_se(); + case SERIALIZE_TYPE_UINT32: return read_se(); + case SERIALIZE_TYPE_UINT16: return read_se(); + case SERIALIZE_TYPE_UINT8: return read_se(); + case SERIALIZE_TYPE_DUOBLE: return read_se(); + case SERIALIZE_TYPE_BOOL: return read_se(); + case SERIALIZE_TYPE_STRING: return read_se(); + case SERIALIZE_TYPE_OBJECT: return read_se
(); + case SERIALIZE_TYPE_ARRAY: return read_se(); + default: + CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << ent_type); + } + } + inline + void throwable_buffer_reader::read(section& sec) + { + RECURSION_LIMITATION(); + sec.m_entries.clear(); + size_t count = read_varint(); + while(count--) + { + //read section name string + std::string sec_name; + read_sec_name(sec_name); + sec.m_entries.emplace(std::move(sec_name), load_storage_entry()); + } + } + inline + void throwable_buffer_reader::read(std::string& str) + { + RECURSION_LIMITATION(); + size_t len = read_varint(); + CHECK_AND_ASSERT_THROW_MES(len < MAX_STRING_LEN_POSSIBLE, "to big string len value in storage: " << len); + CHECK_AND_ASSERT_THROW_MES(m_count >= len, "string len count value " << len << " goes out of remain storage len " << m_count); + //do this manually to avoid double memory write in huge strings (first time at resize, second at read) + str.assign((const char*)m_ptr, len); + m_ptr+=len; + m_count -= len; + } + inline + void throwable_buffer_reader::read(array_entry &ae) + { + RECURSION_LIMITATION(); + CHECK_AND_ASSERT_THROW_MES(false, "Reading array entry is not supported"); + } + } +} diff --git a/src/contrib/epee/include/storages/portable_storage_from_json.h b/src/contrib/epee/include/storages/portable_storage_from_json.h new file mode 100644 index 0000000..2b2dc7f --- /dev/null +++ b/src/contrib/epee/include/storages/portable_storage_from_json.h @@ -0,0 +1,414 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once +#include +#include +#include "parserse_base_utils.h" +#include "file_io_utils.h" + +#define EPEE_JSON_RECURSION_LIMIT_INTERNAL 100 + +namespace epee +{ + using namespace misc_utils::parse; + namespace serialization + { + namespace json + { +#define CHECK_ISSPACE() if(!epee::misc_utils::parse::isspace(*it)){ ASSERT_MES_AND_THROW("Wrong JSON character at: " << std::string(it, buf_end));} + + /*inline void parse_error() + { + ASSERT_MES_AND_THROW("json parse error"); + }*/ + template + inline void run_handler(typename t_storage::hsection current_section, std::string::const_iterator& sec_buf_begin, std::string::const_iterator buf_end, t_storage& stg, unsigned int recursion) + { + CHECK_AND_ASSERT_THROW_MES(recursion < EPEE_JSON_RECURSION_LIMIT_INTERNAL, "Wrong JSON data: recursion limitation (" << EPEE_JSON_RECURSION_LIMIT_INTERNAL << ") exceeded"); + + std::string::const_iterator sub_element_start; + std::string name; + typename t_storage::harray h_array = nullptr; + enum match_state + { + match_state_lookup_for_section_start, + match_state_lookup_for_name, + match_state_waiting_separator, + match_state_wonder_after_separator, + match_state_wonder_after_value, + match_state_wonder_array, + match_state_array_after_value, + match_state_array_waiting_value, + match_state_error + }; + + enum array_mode + { + array_mode_undifined = 0, + array_mode_sections, + array_mode_string, + array_mode_numbers, + array_mode_booleans + }; + + match_state state = match_state_lookup_for_section_start; + array_mode array_md = array_mode_undifined; + std::string::const_iterator it = sec_buf_begin; + for(;it != buf_end;it++) + { + switch (state) + { + case match_state_lookup_for_section_start: + if(*it == '{') + state = match_state_lookup_for_name; + else CHECK_ISSPACE(); + break; + case match_state_lookup_for_name: + switch(*it) + { + case '"': + match_string2(it, buf_end, name); + state = match_state_waiting_separator; + break; + case '}': + //this is it! section ends here. + //seems that it is empty section + sec_buf_begin = it; + return; + default: + CHECK_ISSPACE(); + } + break; + case match_state_waiting_separator: + if(*it == ':') + state = match_state_wonder_after_separator; + else CHECK_ISSPACE(); + break; + case match_state_wonder_after_separator: + if(*it == '"') + {//just a named string value started + std::string val; + match_string2(it, buf_end, val); + //insert text value + stg.set_value(name, std::move(val), current_section); + state = match_state_wonder_after_value; + }else if (epee::misc_utils::parse::isdigit(*it) || *it == '-') + {//just a named number value started + boost::string_ref val; + bool is_v_float = false;bool is_signed = false; + match_number2(it, buf_end, val, is_v_float, is_signed); + if(!is_v_float) + { + if(is_signed) + { + errno = 0; + int64_t nval = strtoll(val.data(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); + stg.set_value(name, int64_t(nval), current_section); + }else + { + errno = 0; + uint64_t nval = strtoull(val.data(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); + stg.set_value(name, uint64_t(nval), current_section); + } + }else + { + errno = 0; + double nval = strtod(val.data(), NULL); + if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); + stg.set_value(name, double(nval), current_section); + } + state = match_state_wonder_after_value; + }else if(isalpha(*it) ) + {// could be null, true or false + boost::string_ref word; + match_word2(it, buf_end, word); + if(boost::iequals(word, "null")) + { + state = match_state_wonder_after_value; + //just skip this, + }else if(boost::iequals(word, "true")) + { + stg.set_value(name, true, current_section); + state = match_state_wonder_after_value; + }else if(boost::iequals(word, "false")) + { + stg.set_value(name, false, current_section); + state = match_state_wonder_after_value; + }else ASSERT_MES_AND_THROW("Unknown value keyword " << word); + }else if(*it == '{') + { + //sub section here + typename t_storage::hsection new_sec = stg.open_section(name, current_section, true); + CHECK_AND_ASSERT_THROW_MES(new_sec, "Failed to insert new section in json: " << std::string(it, buf_end)); + run_handler(new_sec, it, buf_end, stg, recursion + 1); + state = match_state_wonder_after_value; + }else if(*it == '[') + {//array of something + state = match_state_wonder_array; + }else CHECK_ISSPACE(); + break; + case match_state_wonder_after_value: + if(*it == ',') + state = match_state_lookup_for_name; + else if(*it == '}') + { + //this is it! section ends here. + sec_buf_begin = it; + return; + }else CHECK_ISSPACE(); + break; + case match_state_wonder_array: + if(*it == '[') + { + ASSERT_MES_AND_THROW("array of array not suppoerted yet :( sorry"); + //mean array of array + } + if(*it == '{') + { + //mean array of sections + typename t_storage::hsection new_sec = nullptr; + h_array = stg.insert_first_section(name, new_sec, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array&&new_sec, "failed to create new section"); + run_handler(new_sec, it, buf_end, stg, recursion + 1); + state = match_state_array_after_value; + array_md = array_mode_sections; + }else if(*it == '"') + { + //mean array of strings + std::string val; + match_string2(it, buf_end, val); + h_array = stg.insert_first_value(name, std::move(val), current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values entry"); + state = match_state_array_after_value; + array_md = array_mode_string; + }else if (epee::misc_utils::parse::isdigit(*it) || *it == '-') + {//array of numbers value started + boost::string_ref val; + bool is_v_float = false;bool is_signed_val = false; + match_number2(it, buf_end, val, is_v_float, is_signed_val); + if(!is_v_float) + { + if (is_signed_val) + { + errno = 0; + int64_t nval = strtoll(val.data(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); + h_array = stg.insert_first_value(name, int64_t(nval), current_section); + }else + { + errno = 0; + uint64_t nval = strtoull(val.data(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); + h_array = stg.insert_first_value(name, uint64_t(nval), current_section); + } + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + }else + { + errno = 0; + double nval = strtod(val.data(), NULL); + if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); + h_array = stg.insert_first_value(name, double(nval), current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + } + + state = match_state_array_after_value; + array_md = array_mode_numbers; + }else if(*it == ']')//empty array + { + array_md = array_mode_undifined; + state = match_state_wonder_after_value; + }else if(isalpha(*it) ) + {// array of booleans + boost::string_ref word; + match_word2(it, buf_end, word); + if(boost::iequals(word, "true")) + { + h_array = stg.insert_first_value(name, true, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + state = match_state_array_after_value; + array_md = array_mode_booleans; + }else if(boost::iequals(word, "false")) + { + h_array = stg.insert_first_value(name, false, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + state = match_state_array_after_value; + array_md = array_mode_booleans; + + }else ASSERT_MES_AND_THROW("Unknown value keyword " << word) + }else CHECK_ISSPACE(); + break; + case match_state_array_after_value: + if(*it == ',') + state = match_state_array_waiting_value; + else if(*it == ']') + { + h_array = nullptr; + array_md = array_mode_undifined; + state = match_state_wonder_after_value; + }else CHECK_ISSPACE(); + break; + case match_state_array_waiting_value: + switch(array_md) + { + case array_mode_sections: + if(*it == '{') + { + typename t_storage::hsection new_sec = NULL; + bool res = stg.insert_next_section(h_array, new_sec); + CHECK_AND_ASSERT_THROW_MES(res&&new_sec, "failed to insert next section"); + run_handler(new_sec, it, buf_end, stg, recursion + 1); + state = match_state_array_after_value; + }else CHECK_ISSPACE(); + break; + case array_mode_string: + if(*it == '"') + { + std::string val; + match_string2(it, buf_end, val); + bool res = stg.insert_next_value(h_array, std::move(val)); + CHECK_AND_ASSERT_THROW_MES(res, "failed to insert values"); + state = match_state_array_after_value; + }else CHECK_ISSPACE(); + break; + case array_mode_numbers: + if (epee::misc_utils::parse::isdigit(*it) || *it == '-') + {//array of numbers value started + boost::string_ref val; + bool is_v_float = false;bool is_signed_val = false; + match_number2(it, buf_end, val, is_v_float, is_signed_val); + bool insert_res = false; + if(!is_v_float) + { + if (is_signed_val) + { + errno = 0; + int64_t nval = strtoll(val.data(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); + insert_res = stg.insert_next_value(h_array, int64_t(nval)); + }else + { + errno = 0; + uint64_t nval = strtoull(val.data(), NULL, 10); + if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); + insert_res = stg.insert_next_value(h_array, uint64_t(nval)); + } + }else + { + errno = 0; + double nval = strtod(val.data(), NULL); + if (errno) throw std::runtime_error("Invalid number: " + std::string(val)); + insert_res = stg.insert_next_value(h_array, double(nval)); + } + CHECK_AND_ASSERT_THROW_MES(insert_res, "Failed to insert next value"); + state = match_state_array_after_value; + array_md = array_mode_numbers; + }else CHECK_ISSPACE(); + break; + case array_mode_booleans: + if(isalpha(*it) ) + {// array of booleans + boost::string_ref word; + match_word2(it, buf_end, word); + if(boost::iequals(word, "true")) + { + bool r = stg.insert_next_value(h_array, true); + CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry"); + state = match_state_array_after_value; + }else if(boost::iequals(word, "false")) + { + bool r = stg.insert_next_value(h_array, false); + CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry"); + state = match_state_array_after_value; + } + else ASSERT_MES_AND_THROW("Unknown value keyword " << word); + }else CHECK_ISSPACE(); + break; + case array_mode_undifined: + default: + ASSERT_MES_AND_THROW("Bad array state"); + } + break; + case match_state_error: + default: + ASSERT_MES_AND_THROW("WRONG JSON STATE"); + } + } + } +/* +{ + "firstName": "John", + "lastName": "Smith", + "age": 25, + "address": { + "streetAddress": "21 2nd Street", + "city": "New York", + "state": "NY", + "postalCode": -10021, + "have_boobs": true, + "have_balls": false + }, + "phoneNumber": [ + { + "type": "home", + "number": "212 555-1234" + }, + { + "type": "fax", + "number": "646 555-4567" + } + ], + "phoneNumbers": [ + "812 123-1234", + "916 123-4567" + ] +} +*/ + template + inline bool load_from_json(const std::string& buff_json, t_storage& stg) + { + std::string::const_iterator sec_buf_begin = buff_json.begin(); + try + { + run_handler(nullptr, sec_buf_begin, buff_json.end(), stg, 0); + return true; + } + catch(const std::exception& ex) + { + MERROR("Failed to parse json, what: " << ex.what()); + return false; + } + catch(...) + { + MERROR("Failed to parse json"); + return false; + } + } + } + } +} diff --git a/src/contrib/epee/include/storages/portable_storage_template_helper.h b/src/contrib/epee/include/storages/portable_storage_template_helper.h new file mode 100644 index 0000000..13c870d --- /dev/null +++ b/src/contrib/epee/include/storages/portable_storage_template_helper.h @@ -0,0 +1,129 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +#include + +#include "parserse_base_utils.h" +#include "portable_storage.h" +#include "file_io_utils.h" + +namespace epee +{ + namespace serialization + { + //----------------------------------------------------------------------------------------------------------- + template + bool load_t_from_json(t_struct& out, const std::string& json_buff) + { + portable_storage ps; + bool rs = ps.load_from_json(json_buff); + if(!rs) + return false; + + return out.load(ps); + } + //----------------------------------------------------------------------------------------------------------- + template + bool load_t_from_json_file(t_struct& out, const std::string& json_file) + { + std::string f_buff; + if(!file_io_utils::load_file_to_string(json_file, f_buff)) + return false; + + return load_t_from_json(out, f_buff); + } + //----------------------------------------------------------------------------------------------------------- + template + bool store_t_to_json(t_struct& str_in, std::string& json_buff, size_t indent = 0, bool insert_newlines = true) + { + portable_storage ps; + str_in.store(ps); + ps.dump_as_json(json_buff, indent, insert_newlines); + return true; + } + //----------------------------------------------------------------------------------------------------------- + template + std::string store_t_to_json(t_struct& str_in, size_t indent = 0, bool insert_newlines = true) + { + std::string json_buff; + store_t_to_json(str_in, json_buff, indent, insert_newlines); + return json_buff; + } + //----------------------------------------------------------------------------------------------------------- + template + bool store_t_to_json_file(t_struct& str_in, const std::string& fpath) + { + std::string json_buff; + store_t_to_json(str_in, json_buff); + return file_io_utils::save_string_to_file(fpath, json_buff); + } + //----------------------------------------------------------------------------------------------------------- + template + bool load_t_from_binary(t_struct& out, const epee::span binary_buff) + { + portable_storage ps; + bool rs = ps.load_from_binary(binary_buff); + if(!rs) + return false; + + return out.load(ps); + } + //----------------------------------------------------------------------------------------------------------- + template + bool load_t_from_binary(t_struct& out, const std::string& binary_buff) + { + return load_t_from_binary(out, epee::strspan(binary_buff)); + } + //----------------------------------------------------------------------------------------------------------- + template + bool load_t_from_binary_file(t_struct& out, const std::string& binary_file) + { + std::string f_buff; + if(!file_io_utils::load_file_to_string(binary_file, f_buff)) + return false; + + return load_t_from_binary(out, f_buff); + } + //----------------------------------------------------------------------------------------------------------- + template + bool store_t_to_binary(t_struct& str_in, std::string& binary_buff, size_t indent = 0) + { + portable_storage ps; + str_in.store(ps); + return ps.store_to_binary(binary_buff); + } + //----------------------------------------------------------------------------------------------------------- + template + std::string store_t_to_binary(t_struct& str_in, size_t indent = 0) + { + std::string binary_buff; + store_t_to_binary(str_in, binary_buff, indent); + return binary_buff; + } + } +} diff --git a/src/contrib/epee/include/storages/portable_storage_to_bin.h b/src/contrib/epee/include/storages/portable_storage_to_bin.h new file mode 100644 index 0000000..137497e --- /dev/null +++ b/src/contrib/epee/include/storages/portable_storage_to_bin.h @@ -0,0 +1,222 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + + +#pragma once + +#include "pragma_comp_defs.h" +#include "misc_language.h" +#include "portable_storage_base.h" +#include "portable_storage_bin_utils.h" + +namespace epee +{ + namespace serialization + { + + template + size_t pack_varint_t(t_stream& strm, uint8_t type_or, size_t& pv) + { + pack_value v = pv << 2; + v |= type_or; + v = CONVERT_POD(v); + strm.write((const char*)&v, sizeof(pack_value)); + return sizeof(pack_value); + } + + PRAGMA_WARNING_PUSH + PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-aliasing\"") +#ifdef __clang__ + PRAGMA_GCC("GCC diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"") +#endif + template + size_t pack_varint(t_stream& strm, size_t val) + { //the first two bits always reserved for size information + + if(val <= 63) + {//mean enough one byte + return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_BYTE, val); + } + else if(val <= 16383) + {//mean need word + return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_WORD, val); + }else if(val <= 1073741823) + {//mean need dword + return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_DWORD, val); + }else + { + CHECK_AND_ASSERT_THROW_MES(val <= 4611686018427387903, "failed to pack varint - too big amount = " << val); + return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_INT64, val); + } + } + PRAGMA_WARNING_POP + + template + bool put_string(t_stream& strm, const std::string& v) + { + pack_varint(strm, v.size()); + if(v.size()) + strm.write((const char*)v.data(), v.size()); + return true; + } + + template + struct array_entry_store_visitor: public boost::static_visitor + { + t_stream& m_strm; + + template + bool pack_pod_array_type(uint8_t contained_type, const array_entry_t& arr_pod) + { + uint8_t type = contained_type|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arr_pod.m_array.size()); + for(t_pod_type x: arr_pod.m_array) + { + x = CONVERT_POD(x); + m_strm.write((const char*)&x, sizeof(t_pod_type)); + } + return true; + } + + array_entry_store_visitor(t_stream& strm):m_strm(strm){} + bool operator()(const array_entry_t& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT64, v);} + bool operator()(const array_entry_t& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT32, v);} + bool operator()(const array_entry_t& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT16, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_UINT8, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT64, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT32, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT16, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT8, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_DUOBLE, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_BOOL, v);} + bool operator()(const array_entry_t& arr_str) + { + uint8_t type = SERIALIZE_TYPE_STRING|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arr_str.m_array.size()); + for(const std::string& s: arr_str.m_array) + put_string(m_strm, s); + return true; + } + bool operator()(const array_entry_t
& arr_sec) + { + uint8_t type = SERIALIZE_TYPE_OBJECT|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arr_sec.m_array.size()); + for(const section& s: arr_sec.m_array) + pack_entry_to_buff(m_strm, s); + return true; + } + bool operator()(const array_entry_t& arra_ar) + { + uint8_t type = SERIALIZE_TYPE_ARRAY|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arra_ar.m_array.size()); + for(const array_entry& s: arra_ar.m_array) + pack_entry_to_buff(m_strm, s); + return true; + } + }; + + template + struct storage_entry_store_visitor: public boost::static_visitor + { + t_stream& m_strm; + storage_entry_store_visitor(t_stream& strm):m_strm(strm){} + template + bool pack_pod_type(uint8_t type, const pod_type& v) + { + m_strm.write((const char*)&type, 1); + pod_type v0 = CONVERT_POD(v); + m_strm.write((const char*)&v0, sizeof(pod_type)); + return true; + } + //section, array_entry + bool operator()(const uint64_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT64, v);} + bool operator()(const uint32_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT32, v);} + bool operator()(const uint16_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT16, v);} + bool operator()(const uint8_t& v) { return pack_pod_type(SERIALIZE_TYPE_UINT8, v);} + bool operator()(const int64_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT64, v);} + bool operator()(const int32_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT32, v);} + bool operator()(const int16_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT16, v);} + bool operator()(const int8_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT8, v);} + bool operator()(const double& v) { return pack_pod_type(SERIALIZE_TYPE_DUOBLE, v);} + bool operator()(const bool& v) { return pack_pod_type(SERIALIZE_TYPE_BOOL, v);} + bool operator()(const std::string& v) + { + uint8_t type = SERIALIZE_TYPE_STRING; + m_strm.write((const char*)&type, 1); + put_string(m_strm, v); + return true; + } + bool operator()(const section& v) + { + uint8_t type = SERIALIZE_TYPE_OBJECT; + m_strm.write((const char*)&type, 1); + return pack_entry_to_buff(m_strm, v); + } + + bool operator()(const array_entry& v) + { + //uint8_t type = SERIALIZE_TYPE_ARRAY; + //m_strm.write((const char*)&type, 1); + return pack_entry_to_buff(m_strm, v); + } + }; + + template + bool pack_entry_to_buff(t_stream& strm, const array_entry& ae) + { + array_entry_store_visitor aesv(strm); + return boost::apply_visitor(aesv, ae); + } + + template + bool pack_entry_to_buff(t_stream& strm, const storage_entry& se) + { + storage_entry_store_visitor sv(strm); + return boost::apply_visitor(sv, se); + } + + template + bool pack_entry_to_buff(t_stream& strm, const section& sec) + { + typedef std::map::value_type section_pair; + pack_varint(strm, sec.m_entries.size()); + for(const section_pair& se: sec.m_entries) + { + CHECK_AND_ASSERT_THROW_MES(se.first.size() < std::numeric_limits::max(), "storage_entry_name is too long: " << se.first.size() << ", val: " << se.first); + uint8_t len = static_cast(se.first.size()); + strm.write((const char*)&len, sizeof(len)); + strm.write(se.first.data(), size_t(len)); + pack_entry_to_buff(strm, se.second); + } + return true; + } + } +} diff --git a/src/contrib/epee/include/storages/portable_storage_to_json.h b/src/contrib/epee/include/storages/portable_storage_to_json.h new file mode 100644 index 0000000..43e3412 --- /dev/null +++ b/src/contrib/epee/include/storages/portable_storage_to_json.h @@ -0,0 +1,181 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + + +#pragma once + +#include "misc_language.h" +#include "portable_storage_base.h" +#include "parserse_base_utils.h" + +namespace epee +{ + namespace serialization + { + + template + void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent, bool insert_newlines); + template + void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent, bool insert_newlines); + template + void dump_as_json(t_stream& strm, const std::string& v, size_t indent, bool insert_newlines); + template + void dump_as_json(t_stream& strm, const int8_t& v, size_t indent, bool insert_newlines); + template + void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent, bool insert_newlines); + template + void dump_as_json(t_stream& strm, const bool& v, size_t indent, bool insert_newlines); + template + void dump_as_json(t_stream& strm, const t_type& v, size_t indent, bool insert_newlines); + template + void dump_as_json(t_stream& strm, const section& sec, size_t indent, bool insert_newlines); + + + inline std::string make_indent(size_t indent) + { + return std::string(indent*2, ' '); + } + + template + struct array_entry_store_to_json_visitor: public boost::static_visitor + { + t_stream& m_strm; + size_t m_indent; + bool m_insert_newlines; + array_entry_store_to_json_visitor(t_stream& strm, size_t indent, + bool insert_newlines = true) + : m_strm(strm), m_indent(indent), m_insert_newlines(insert_newlines) + {} + + template + void operator()(const array_entry_t& a) + { + m_strm << "["; + if(a.m_array.size()) + { + auto last_it = --a.m_array.end(); + for(auto it = a.m_array.begin(); it != a.m_array.end(); it++) + { + dump_as_json(m_strm, *it, m_indent, m_insert_newlines); + if(it != last_it) + m_strm << ","; + } + } + m_strm << "]"; + } + }; + + template + struct storage_entry_store_to_json_visitor: public boost::static_visitor + { + t_stream& m_strm; + size_t m_indent; + bool m_insert_newlines; + storage_entry_store_to_json_visitor(t_stream& strm, size_t indent, + bool insert_newlines = true) + : m_strm(strm), m_indent(indent), m_insert_newlines(insert_newlines) + {} + //section, array_entry + template + void operator()(const visited_type& v) + { + dump_as_json(m_strm, v, m_indent, m_insert_newlines); + } + }; + + template + void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent, bool insert_newlines) + { + array_entry_store_to_json_visitor aesv(strm, indent, insert_newlines); + boost::apply_visitor(aesv, ae); + } + + template + void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent, bool insert_newlines) + { + storage_entry_store_to_json_visitor sv(strm, indent, insert_newlines); + boost::apply_visitor(sv, se); + } + + template + void dump_as_json(t_stream& strm, const std::string& v, size_t indent, bool insert_newlines) + { + strm << "\"" << misc_utils::parse::transform_to_escape_sequence(v) << "\""; + } + + template + void dump_as_json(t_stream& strm, const int8_t& v, size_t indent, bool insert_newlines) + { + strm << static_cast(v); + } + + template + void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent, bool insert_newlines) + { + strm << static_cast(v); + } + + template + void dump_as_json(t_stream& strm, const bool& v, size_t indent, bool insert_newlines) + { + if(v) + strm << "true"; + else + strm << "false"; + } + + + + template + void dump_as_json(t_stream& strm, const t_type& v, size_t indent, bool insert_newlines) + { + strm << v; + } + + template + void dump_as_json(t_stream& strm, const section& sec, size_t indent, bool insert_newlines) + { + size_t local_indent = indent + 1; + std::string newline = insert_newlines ? "\r\n" : ""; + strm << "{" << newline; + std::string indent_str = make_indent(local_indent); + if(sec.m_entries.size()) + { + auto it_last = --sec.m_entries.end(); + for(auto it = sec.m_entries.begin(); it!= sec.m_entries.end();it++) + { + strm << indent_str << "\"" << misc_utils::parse::transform_to_escape_sequence(it->first) << "\"" << ": "; + dump_as_json(strm, it->second, local_indent, insert_newlines); + if(it_last != it) + strm << ","; + strm << newline; + } + } + strm << make_indent(indent) << "}"; + } + } +} diff --git a/src/contrib/epee/include/storages/portable_storage_val_converters.h b/src/contrib/epee/include/storages/portable_storage_val_converters.h new file mode 100644 index 0000000..e54cda8 --- /dev/null +++ b/src/contrib/epee/include/storages/portable_storage_val_converters.h @@ -0,0 +1,202 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + + +#pragma once + +#include +#include + +#include "misc_language.h" +#include "portable_storage_base.h" +#include "warnings.h" + +namespace epee +{ + namespace serialization + { +#define ASSERT_AND_THROW_WRONG_CONVERSION() ASSERT_MES_AND_THROW("WRONG DATA CONVERSION: from type=" << typeid(from).name() << " to type " << typeid(to).name()) + + template + void convert_int_to_uint(const from_type& from, to_type& to) + { +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4018) + CHECK_AND_ASSERT_THROW_MES(from >=0, "unexpected int value with signed storage value less than 0, and unsigned receiver value"); +DISABLE_GCC_AND_CLANG_WARNING(sign-compare) + CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits::max(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits::max()); + to = static_cast(from); +POP_WARNINGS + } + template + void convert_int_to_int(const from_type& from, to_type& to) + { + CHECK_AND_ASSERT_THROW_MES(from >= boost::numeric::bounds::lowest(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with lowest possible value = " << boost::numeric::bounds::lowest()); +PUSH_WARNINGS +DISABLE_CLANG_WARNING(tautological-constant-out-of-range-compare) + CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits::max(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits::max()); +POP_WARNINGS + to = static_cast(from); + } + template + void convert_uint_to_any_int(const from_type& from, to_type& to) + { +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4018) +DISABLE_CLANG_WARNING(tautological-constant-out-of-range-compare) + CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits::max(), "uint value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits::max()); + to = static_cast(from); +POP_WARNINGS + } + + template //is from signed, is from to signed + struct convert_to_signed_unsigned; + + template + struct convert_to_signed_unsigned + { + static void convert(const from_type& from, to_type& to) + {//from signed to signed + convert_int_to_int(from, to); + } + }; + + template + struct convert_to_signed_unsigned + { + static void convert(const from_type& from, to_type& to) + {//from signed to unsigned + convert_int_to_uint(from, to); + } + }; + + template + struct convert_to_signed_unsigned + { + static void convert(const from_type& from, to_type& to) + {//from unsigned to signed + convert_uint_to_any_int(from, to); + } + }; + + template + struct convert_to_signed_unsigned + { + static void convert(const from_type& from, to_type& to) + { + //from unsigned to unsigned + convert_uint_to_any_int(from, to); + } + }; + + template + struct convert_to_integral; + + template + struct convert_to_integral + { + static void convert(const from_type& from, to_type& to) + { + convert_to_signed_unsigned::value, std::is_signed::value>::convert(from, to); + } + }; + + template + struct convert_to_integral + { + static void convert(const from_type& from, to_type& to) + { + ASSERT_AND_THROW_WRONG_CONVERSION(); + } + }; + + // For MyMonero/OpenMonero backend compatibility + // MyMonero backend sends amount, fees and timestamp values as strings. + // Until MM backend is updated, this is needed for compatibility between OpenMonero and MyMonero. + template<> + struct convert_to_integral + { + static void convert(const std::string& from, uint64_t& to) + { + MTRACE("Converting std::string to uint64_t. Source: " << from); + // String only contains digits + if(std::all_of(from.begin(), from.end(), epee::misc_utils::parse::isdigit)) + to = boost::lexical_cast(from); + // MyMonero ISO 8061 timestamp (2017-05-06T16:27:06Z) + else if (boost::regex_match (from, boost::regex("\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\dZ"))) + { + // Convert to unix timestamp +#ifdef HAVE_STRPTIME + struct tm tm; + if (strptime(from.c_str(), "%Y-%m-%dT%H:%M:%S", &tm)) +#else + std::tm tm = {}; + std::istringstream ss(from); + if (ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S")) +#endif + to = std::mktime(&tm); + } else + ASSERT_AND_THROW_WRONG_CONVERSION(); + } + }; + + template + struct is_convertable: std::integral_constant::value && + std::is_integral::value && + !std::is_same::value && + !std::is_same::value > {}; + + template + struct convert_to_same; + + template + struct convert_to_same + { + static void convert(const from_type& from, to_type& to) + { + to = from; + } + }; + + template + struct convert_to_same + { + static void convert(const from_type& from, to_type& to) + { + convert_to_integral::value>::convert(from, to);// ASSERT_AND_THROW_WRONG_CONVERSION(); + } + }; + + + template + void convert_t(const from_type& from, to_type& to) + { + convert_to_same::value>::convert(from, to); + } + } +} diff --git a/src/offshore/pricing_record.cpp b/src/offshore/pricing_record.cpp new file mode 100644 index 0000000..fda0b08 --- /dev/null +++ b/src/offshore/pricing_record.cpp @@ -0,0 +1,339 @@ +// Copyright (c) 2019, Haven Protocol +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Portions of this code based upon code Copyright (c) 2019, The Monero Project + +#include "pricing_record.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serialization/keyvalue_serialization.h" +#include "storages/portable_storage.h" + +namespace offshore +{ + + namespace + { + struct pr_serialized + { + uint64_t xAG; + uint64_t xAU; + uint64_t xAUD; + uint64_t xBTC; + uint64_t xCAD; + uint64_t xCHF; + uint64_t xCNY; + uint64_t xEUR; + uint64_t xGBP; + uint64_t xJPY; + uint64_t xNOK; + uint64_t xNZD; + uint64_t xUSD; + uint64_t unused1; + uint64_t unused2; + uint64_t unused3; + std::string signature; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(xAG) + KV_SERIALIZE(xAU) + KV_SERIALIZE(xAUD) + KV_SERIALIZE(xBTC) + KV_SERIALIZE(xCAD) + KV_SERIALIZE(xCHF) + KV_SERIALIZE(xCNY) + KV_SERIALIZE(xEUR) + KV_SERIALIZE(xGBP) + KV_SERIALIZE(xJPY) + KV_SERIALIZE(xNOK) + KV_SERIALIZE(xNZD) + KV_SERIALIZE(xUSD) + KV_SERIALIZE(unused1) + KV_SERIALIZE(unused2) + KV_SERIALIZE(unused3) + KV_SERIALIZE(signature) + END_KV_SERIALIZE_MAP() + }; + } + + pricing_record::pricing_record() noexcept + : xAG(0) + , xAU(0) + , xAUD(0) + , xBTC(0) + , xCAD(0) + , xCHF(0) + , xCNY(0) + , xEUR(0) + , xGBP(0) + , xJPY(0) + , xNOK(0) + , xNZD(0) + , xUSD(0) + , unused1(0) + , unused2(0) + , unused3(0) + { + std::memset(signature, 0, sizeof(signature)); + } + + bool pricing_record::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent) + { + pr_serialized in{}; + if (in._load(src, hparent)) + { + // Copy everything into the local instance + xAG = in.xAG; + xAU = in.xAU; + xAUD = in.xAUD; + xBTC = in.xBTC; + xCAD = in.xCAD; + xCHF = in.xCHF; + xCNY = in.xCNY; + xEUR = in.xEUR; + xGBP = in.xGBP; + xJPY = in.xJPY; + xNOK = in.xNOK; + xNZD = in.xNZD; + xUSD = in.xUSD; + unused1 = in.unused1; + unused2 = in.unused2; + unused3 = in.unused3; + for (unsigned int i = 0; i < in.signature.length(); i += 2) { + std::string byteString = in.signature.substr(i, 2); + signature[i>>1] = (char) strtol(byteString.c_str(), NULL, 16); + } + return true; + } + + // Report error here? + return false; + } + + bool pricing_record::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const + { + std::string sig_hex; + for (unsigned int i=0; i<64; i++) { + std::stringstream ss; + ss << std::hex << std::setw(2) << std::setfill('0') << (0xff & signature[i]); + sig_hex += ss.str(); + } + const pr_serialized out{xAG,xAU,xAUD,xBTC,xCAD,xCHF,xCNY,xEUR,xGBP,xJPY,xNOK,xNZD,xUSD,unused1,unused2,unused3,sig_hex}; + return out.store(dest, hparent); + } + + pricing_record::pricing_record(const pricing_record& orig) noexcept + : xAG(orig.xAG) + , xAU(orig.xAU) + , xAUD(orig.xAUD) + , xBTC(orig.xBTC) + , xCAD(orig.xCAD) + , xCHF(orig.xCHF) + , xCNY(orig.xCNY) + , xEUR(orig.xEUR) + , xGBP(orig.xGBP) + , xJPY(orig.xJPY) + , xNOK(orig.xNOK) + , xNZD(orig.xNZD) + , xUSD(orig.xUSD) + , unused1(orig.unused1) + , unused2(orig.unused2) + , unused3(orig.unused3) + { + std::memcpy(signature, orig.signature, sizeof(signature)); + } + + pricing_record& pricing_record::operator=(const pricing_record& orig) noexcept + { + xAG = orig.xAG; + xAU = orig.xAU; + xAUD = orig.xAUD; + xBTC = orig.xBTC; + xCAD = orig.xCAD; + xCHF = orig.xCHF; + xCNY = orig.xCNY; + xEUR = orig.xEUR; + xGBP = orig.xGBP; + xJPY = orig.xJPY; + xNOK = orig.xNOK; + xNZD = orig.xNZD; + xUSD = orig.xUSD; + unused1 = orig.unused1; + unused2 = orig.unused2; + unused3 = orig.unused3; + ::memcpy(signature, orig.signature, sizeof(signature)); + return *this; + } + + bool pricing_record::equal(const pricing_record& other) const noexcept + { + return ((xAG == other.xAG) && + (xAU == other.xAU) && + (xAUD == other.xAUD) && + (xBTC == other.xBTC) && + (xCAD == other.xCAD) && + (xCHF == other.xCHF) && + (xCNY == other.xCNY) && + (xEUR == other.xEUR) && + (xGBP == other.xGBP) && + (xJPY == other.xJPY) && + (xNOK == other.xNOK) && + (xNZD == other.xNZD) && + (xUSD == other.xUSD) && + (unused1 == other.unused1) && + (unused2 == other.unused2) && + (unused3 == other.unused3) && + !::memcmp(signature, other.signature, sizeof(signature))); + } + + + bool pricing_record::verifySignature() const noexcept + { + // Sanity check - accept empty pricing records + unsigned char test_sig[64]; + std::memset(test_sig, 0, sizeof(test_sig)); + if (std::memcmp(test_sig, signature, sizeof(signature)) == 0) { + return true; + } + + // Convert our internal 64-byte binary representation into 128-byte hex string + std::string sig_hex; + for (unsigned int i=0; i<64; i++) { + std::stringstream ss; + ss << std::hex << std::setw(2) << std::setfill('0') << (0xff & signature[i]); + sig_hex += ss.str(); + } + + // Rebuild the OpenSSL format of the signature from the r+s values + std::string sig_rebuilt = "30"; + std::string r_rebuilt = (signature[0] == 0) ? sig_hex.substr(2, 62) : sig_hex.substr(0,64); + if (signature[(signature[0] == 0) ? 1 : 0] & 0x80) + r_rebuilt = "00" + r_rebuilt; + std::string s_rebuilt = (signature[(signature[32] == 0) ? 33 : 32] == 0) ? sig_hex.substr(66, 62) : sig_hex.substr(64,64); + if (signature[(signature[32] == 0) ? 33 : 32] & 0x80) + s_rebuilt = "00" + s_rebuilt; + size_t sig_length = (r_rebuilt.length() + s_rebuilt.length() + 8) >> 1; + std::stringstream ss; + ss << std::hex << sig_length; + sig_rebuilt += std::string(2-ss.str().length(), '0') + ss.str(); + ss.clear(); + sig_rebuilt += "02"; + size_t r_length = r_rebuilt.length() >> 1; + std::stringstream ss2; + ss2 << std::hex << r_length; + sig_rebuilt += std::string(2-ss2.str().length(), '0') + ss2.str(); + ss2.clear(); + sig_rebuilt += r_rebuilt; + sig_rebuilt += "02"; + size_t s_length = s_rebuilt.length() >> 1; + std::stringstream ss3; + ss3 << std::hex << s_length; + sig_rebuilt += std::string(2-ss3.str().length(), '0') + ss3.str(); + ss3.clear(); + sig_rebuilt += s_rebuilt; + + // Build the JSON string, so that we can verify the signature + std::ostringstream oss; + oss << "{\"xAG\":" << xAG; + oss << ",\"xAU\":" << xAU; + oss << ",\"xAUD\":" << xAUD; + oss << ",\"xBTC\":" << xBTC; + oss << ",\"xCAD\":" << xCAD; + oss << ",\"xCHF\":" << xCHF; + oss << ",\"xCNY\":" << xCNY; + oss << ",\"xEUR\":" << xEUR; + oss << ",\"xGBP\":" << xGBP; + oss << ",\"xJPY\":" << xJPY; + oss << ",\"xNOK\":" << xNOK; + oss << ",\"xNZD\":" << xNZD; + oss << ",\"xUSD\":" << xUSD; + oss << ",\"unused1\":" << unused1; + oss << ",\"unused2\":" << unused2; + oss << ",\"unused3\":" << unused3; + oss << "}"; + std::string message = oss.str(); + + // Convert signature from hex-encoded to binary + std::string compact; + for (unsigned int i = 0; i < sig_rebuilt.length(); i += 2) { + std::string byteString = sig_rebuilt.substr(i, 2); + char byte = (char) strtol(byteString.c_str(), NULL, 16); + compact += (byte); + } + + // HERE BE DRAGONS!!! + // NEAC: the public key should be in a file + static const char public_key[] = "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5YBxWx1AZCA9jTUk8Pr2uZ9jpfRt\n" + "KWv3Vo1/Gny+1vfaxsXhBQiG1KlHkafNGarzoL0WHW4ocqaaqF5iv8i35A==\n" + "-----END PUBLIC KEY-----\n"; + // LAND AHOY!!! + + // Grab the public key and make it usable + BIO* bio = BIO_new_mem_buf(public_key, (int)sizeof(public_key)); + assert(bio != NULL); + EVP_PKEY* pubkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); + BIO_free(bio); + assert(pubkey != NULL); + + // Create a verify digest from the message + EVP_MD_CTX *ctx = EVP_MD_CTX_create(); + int ret = 0; + if (ctx) { + ret=EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, pubkey); + if (ret == 1) { + ret=EVP_DigestVerifyUpdate(ctx, message.data(), message.length()); + if (ret == 1) { + ret=EVP_DigestVerifyFinal(ctx, (const unsigned char *)compact.data(), compact.length()); + } + } + } + // Cleanup the context we created + EVP_MD_CTX_destroy(ctx); + + // Cleanup the openssl stuff + EVP_PKEY_free(pubkey); + + if (ret == 1) + return true; + + // Get the errors from OpenSSL + //ERR_print_errors_fp (stderr); + + return false; + } +} \ No newline at end of file