From 30b53a664613ba8e9aad63532ae5cbddd3aa4980 Mon Sep 17 00:00:00 2001 From: Erdem Ergen Date: Thu, 28 Nov 2019 11:35:57 +0300 Subject: [PATCH] Added client-id and role fetching functionality for permission manager Now direct-access-api uses permission manager to get in-vehicle application roles from keycloak server. Signed-off-by: Erdem Ergen --- direct-access-api/client-app.py | 52 + direct-access-api/include/client_ws.hpp | 729 +++ direct-access-api/include/client_wss.hpp | 92 + direct-access-api/include/crypto.hpp | 227 + .../include/directAccessProcessor.hpp | 7 +- .../include/heap_only_string.hpp | 155 + direct-access-api/include/json.hpp | 5054 +++++++++++++++++ .../include/json_content_handler.hpp | 385 ++ .../include/json_convert_traits.hpp | 317 ++ direct-access-api/include/json_decoder.hpp | 310 + .../include/json_deserializer.hpp | 12 + .../include/json_error_category.hpp | 148 + direct-access-api/include/json_exception.hpp | 107 + direct-access-api/include/json_filter.hpp | 204 + direct-access-api/include/json_fwd.hpp | 32 + direct-access-api/include/json_parser.hpp | 2981 ++++++++++ direct-access-api/include/json_query.hpp | 1119 ++++ direct-access-api/include/json_reader.hpp | 456 ++ direct-access-api/include/json_serializer.hpp | 674 +++ .../include/json_serializing_options.hpp | 339 ++ direct-access-api/include/json_structures.hpp | 1864 ++++++ .../include/json_type_traits.hpp | 958 ++++ direct-access-api/include/jsoncons_config.hpp | 106 + .../include/jsoncons_utilities.hpp | 954 ++++ .../include/jsonpath_error_category.hpp | 97 + direct-access-api/include/jsonpath_filter.hpp | 1874 ++++++ direct-access-api/include/number_parsers.hpp | 266 + direct-access-api/include/number_printers.hpp | 374 ++ direct-access-api/include/obeclient.hpp | 9 +- direct-access-api/include/obufferedstream.hpp | 264 + .../include/parse_error_handler.hpp | 147 + .../include/serialization_traits.hpp | 315 + .../include/serializing_context.hpp | 44 + direct-access-api/include/server_ws.hpp | 802 +++ direct-access-api/include/server_wss.hpp | 81 + direct-access-api/include/status_code.hpp | 167 + .../include/type_traits_helper.hpp | 226 + direct-access-api/include/unicode_traits.hpp | 1463 +++++ direct-access-api/include/utility.hpp | 357 ++ direct-access-api/include/vcanhandler.hpp | 16 +- direct-access-api/include/version.hpp | 50 + direct-access-api/include/writer.hpp | 155 + direct-access-api/src/dbmanager.cpp | 72 +- .../src/directAccessProcessor.cpp | 224 +- direct-access-api/src/obeclient.cpp | 152 +- direct-access-api/src/vcanhandler.cpp | 84 +- direct-access-api/src/wsserver.cpp | 9 +- permision-mngt/keycloakconnector.py | 46 + permision-mngt/requestHandler.py | 93 + permision-mngt/tokenValidator.py | 19 + 50 files changed, 24493 insertions(+), 195 deletions(-) create mode 100644 direct-access-api/client-app.py create mode 100644 direct-access-api/include/client_ws.hpp create mode 100644 direct-access-api/include/client_wss.hpp create mode 100644 direct-access-api/include/crypto.hpp create mode 100644 direct-access-api/include/heap_only_string.hpp create mode 100644 direct-access-api/include/json.hpp create mode 100644 direct-access-api/include/json_content_handler.hpp create mode 100644 direct-access-api/include/json_convert_traits.hpp create mode 100644 direct-access-api/include/json_decoder.hpp create mode 100644 direct-access-api/include/json_deserializer.hpp create mode 100644 direct-access-api/include/json_error_category.hpp create mode 100644 direct-access-api/include/json_exception.hpp create mode 100644 direct-access-api/include/json_filter.hpp create mode 100644 direct-access-api/include/json_fwd.hpp create mode 100644 direct-access-api/include/json_parser.hpp create mode 100644 direct-access-api/include/json_query.hpp create mode 100644 direct-access-api/include/json_reader.hpp create mode 100644 direct-access-api/include/json_serializer.hpp create mode 100644 direct-access-api/include/json_serializing_options.hpp create mode 100644 direct-access-api/include/json_structures.hpp create mode 100644 direct-access-api/include/json_type_traits.hpp create mode 100644 direct-access-api/include/jsoncons_config.hpp create mode 100644 direct-access-api/include/jsoncons_utilities.hpp create mode 100644 direct-access-api/include/jsonpath_error_category.hpp create mode 100644 direct-access-api/include/jsonpath_filter.hpp create mode 100644 direct-access-api/include/number_parsers.hpp create mode 100644 direct-access-api/include/number_printers.hpp create mode 100644 direct-access-api/include/obufferedstream.hpp create mode 100644 direct-access-api/include/parse_error_handler.hpp create mode 100644 direct-access-api/include/serialization_traits.hpp create mode 100644 direct-access-api/include/serializing_context.hpp create mode 100644 direct-access-api/include/server_ws.hpp create mode 100644 direct-access-api/include/server_wss.hpp create mode 100644 direct-access-api/include/status_code.hpp create mode 100644 direct-access-api/include/type_traits_helper.hpp create mode 100644 direct-access-api/include/unicode_traits.hpp create mode 100644 direct-access-api/include/utility.hpp create mode 100644 direct-access-api/include/version.hpp create mode 100644 direct-access-api/include/writer.hpp diff --git a/direct-access-api/client-app.py b/direct-access-api/client-app.py new file mode 100644 index 0000000..67649a8 --- /dev/null +++ b/direct-access-api/client-app.py @@ -0,0 +1,52 @@ +from autobahn.asyncio.websocket import WebSocketClientProtocol, \ + WebSocketClientFactory + +import json +import random + +try: + import asyncio +except ImportError: + import trollius as asyncio + + +class MyClientProtocol(WebSocketClientProtocol): + + def onConnect(self, response): + print("Server connected: {0}".format(response.peer)) + + async def onOpen(self): + print("WebSocket connection open.") + + resp = json.loads('{"appid" : "", "secret" : ""}') + resp["action"] = "create" + # resp["vchannel"] = "vcan83" + resp["channel"] = "CAN0" + resp["appid"] = "ks-app1" + resp["secret"] = "d6d6846a-646a-4d70-ae86-91d3a4eeea79" + + dictionaryToJson = json.dumps(resp) + self.sendMessage(dictionaryToJson.encode('utf8')) + await asyncio.sleep(1) + + def onMessage(self, payload, isBinary): + if isBinary: + print("Binary message received: {0} bytes".format(len(payload))) + else: + # print("Text message received: {0}".format(payload.decode('utf8'))) + resp = json.loads(payload.decode('utf8')) + print(resp) + + def onClose(self, wasClean, code, reason): + print("WebSocket connection closed: {0}".format(reason)) + + +if __name__ == '__main__': + factory = WebSocketClientFactory(u"ws://127.0.0.1:9000/vss") + factory.protocol = MyClientProtocol + + loop = asyncio.get_event_loop() + coro = loop.create_connection(factory, '127.0.0.1', 9000) + loop.run_until_complete(coro) + loop.run_forever() +loop.close() \ No newline at end of file diff --git a/direct-access-api/include/client_ws.hpp b/direct-access-api/include/client_ws.hpp new file mode 100644 index 0000000..c5a75e3 --- /dev/null +++ b/direct-access-api/include/client_ws.hpp @@ -0,0 +1,729 @@ +#ifndef CLIENT_WS_HPP +#define CLIENT_WS_HPP + +#include "crypto.hpp" +#include "utility.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_STANDALONE_ASIO +#include +#include +namespace SimpleWeb { + using error_code = std::error_code; + using errc = std::errc; + namespace make_error_code = std; +} // namespace SimpleWeb +#else +#include +#include +namespace SimpleWeb { + namespace asio = boost::asio; + using error_code = boost::system::error_code; + namespace errc = boost::system::errc; + namespace make_error_code = boost::system::errc; +} // namespace SimpleWeb +#endif + +namespace SimpleWeb { + template + class SocketClient; + + template + class SocketClientBase { + public: + class Message : public std::istream { + friend class SocketClientBase; + friend class Connection; + + public: + unsigned char fin_rsv_opcode; + std::size_t size() noexcept { + return length; + } + + /// Convenience function to return std::string. The stream buffer is consumed. + std::string string() noexcept { + try { + std::string str; + auto size = streambuf.size(); + str.resize(size); + read(&str[0], static_cast(size)); + return str; + } + catch(...) { + return std::string(); + } + } + + private: + Message() noexcept : std::istream(&streambuf), length(0) {} + Message(unsigned char fin_rsv_opcode, std::size_t length) noexcept : std::istream(&streambuf), fin_rsv_opcode(fin_rsv_opcode), length(length) {} + std::size_t length; + asio::streambuf streambuf; + }; + + /// The buffer is consumed during send operations. + class SendStream : public std::iostream { + friend class SocketClientBase; + + asio::streambuf streambuf; + + public: + SendStream() noexcept : std::iostream(&streambuf) {} + + /// Returns the size of the buffer + std::size_t size() const noexcept { + return streambuf.size(); + } + }; + + class Connection : public std::enable_shared_from_this { + friend class SocketClientBase; + friend class SocketClient; + + public: + std::string http_version, status_code; + CaseInsensitiveMultimap header; + + asio::ip::tcp::endpoint remote_endpoint; + + std::string remote_endpoint_address() noexcept { + try { + return remote_endpoint.address().to_string(); + } + catch(...) { + return std::string(); + } + } + + unsigned short remote_endpoint_port() noexcept { + return remote_endpoint.port(); + } + + private: + template + Connection(std::shared_ptr handler_runner, long timeout_idle, Args &&... args) noexcept + : handler_runner(std::move(handler_runner)), socket(new socket_type(std::forward(args)...)), timeout_idle(timeout_idle), strand(socket->get_io_service()), closed(false) {} + + std::shared_ptr handler_runner; + + std::unique_ptr socket; // Socket must be unique_ptr since asio::ssl::stream is not movable + std::mutex socket_close_mutex; + + std::shared_ptr message; + std::shared_ptr fragmented_message; + + long timeout_idle; + std::unique_ptr timer; + std::mutex timer_mutex; + + void close() noexcept { + error_code ec; + std::unique_lock lock(socket_close_mutex); // The following operations seems to be needed to run sequentially + socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec); + socket->lowest_layer().close(ec); + } + + void set_timeout(long seconds = -1) noexcept { + bool use_timeout_idle = false; + if(seconds == -1) { + use_timeout_idle = true; + seconds = timeout_idle; + } + + std::unique_lock lock(timer_mutex); + + if(seconds == 0) { + timer = nullptr; + return; + } + + timer = std::unique_ptr(new asio::steady_timer(socket->get_io_service())); + timer->expires_from_now(std::chrono::seconds(seconds)); + std::weak_ptr connection_weak(this->shared_from_this()); // To avoid keeping Connection instance alive longer than needed + timer->async_wait([connection_weak, use_timeout_idle](const error_code &ec) { + if(!ec) { + if(auto connection = connection_weak.lock()) { + if(use_timeout_idle) + connection->send_close(1000, "idle timeout"); // 1000=normal closure + else + connection->close(); + } + } + }); + } + + void cancel_timeout() noexcept { + std::unique_lock lock(timer_mutex); + if(timer) { + error_code ec; + timer->cancel(ec); + } + } + + asio::io_service::strand strand; + + class SendData { + public: + SendData(std::shared_ptr send_stream, std::function &&callback) noexcept + : send_stream(std::move(send_stream)), callback(std::move(callback)) {} + std::shared_ptr send_stream; + std::function callback; + }; + + std::list send_queue; + + void send_from_queue() { + auto self = this->shared_from_this(); + strand.post([self]() { + asio::async_write(*self->socket, self->send_queue.begin()->send_stream->streambuf, self->strand.wrap([self](const error_code &ec, std::size_t /*bytes_transferred*/) { + auto lock = self->handler_runner->continue_lock(); + if(!lock) + return; + auto send_queued = self->send_queue.begin(); + if(send_queued->callback) + send_queued->callback(ec); + if(!ec) { + self->send_queue.erase(send_queued); + if(self->send_queue.size() > 0) + self->send_from_queue(); + } + else + self->send_queue.clear(); + })); + }); + } + + std::atomic closed; + + void read_remote_endpoint() noexcept { + try { + remote_endpoint = socket->lowest_layer().remote_endpoint(); + } + catch(const std::exception &e) { + std::cerr << e.what() << std::endl; + } + } + + public: + /// fin_rsv_opcode: 129=one fragment, text, 130=one fragment, binary, 136=close connection. + /// See http://tools.ietf.org/html/rfc6455#section-5.2 for more information + void send(const std::shared_ptr &send_stream, const std::function &callback = nullptr, + unsigned char fin_rsv_opcode = 129) { + cancel_timeout(); + set_timeout(); + + // Create mask + std::array mask; + std::uniform_int_distribution dist(0, 255); + std::random_device rd; + for(std::size_t c = 0; c < 4; c++) + mask[c] = static_cast(dist(rd)); + + auto message_stream = std::make_shared(); + + std::size_t length = send_stream->size(); + + message_stream->put(static_cast(fin_rsv_opcode)); + // Masked (first length byte>=128) + if(length >= 126) { + std::size_t num_bytes; + if(length > 0xffff) { + num_bytes = 8; + message_stream->put(static_cast(127 + 128)); + } + else { + num_bytes = 2; + message_stream->put(static_cast(126 + 128)); + } + + for(std::size_t c = num_bytes - 1; c != static_cast(-1); c--) + message_stream->put((static_cast(length) >> (8 * c)) % 256); + } + else + message_stream->put(static_cast(length + 128)); + + for(std::size_t c = 0; c < 4; c++) + message_stream->put(static_cast(mask[c])); + + for(std::size_t c = 0; c < length; c++) + message_stream->put(send_stream->get() ^ mask[c % 4]); + + auto self = this->shared_from_this(); + strand.post([self, message_stream, callback]() { + self->send_queue.emplace_back(message_stream, callback); + if(self->send_queue.size() == 1) + self->send_from_queue(); + }); + } + + void send_close(int status, const std::string &reason = "", const std::function &callback = nullptr) { + // Send close only once (in case close is initiated by client) + if(closed) + return; + closed = true; + + auto send_stream = std::make_shared(); + + send_stream->put(status >> 8); + send_stream->put(status % 256); + + *send_stream << reason; + + // fin_rsv_opcode=136: message close + send(send_stream, callback, 136); + } + }; + + class Config { + friend class SocketClientBase; + + private: + Config() noexcept {} + + public: + /// Timeout on request handling. Defaults to no timeout. + long timeout_request = 0; + /// Idle timeout. Defaults to no timeout. + long timeout_idle = 0; + /// Maximum size of incoming messages. Defaults to architecture maximum. + /// Exceeding this limit will result in a message_size error code and the connection will be closed. + std::size_t max_message_size = std::numeric_limits::max(); + /// Additional header fields to send when performing WebSocket handshake. + /// Use this variable to for instance set Sec-WebSocket-Protocol. + CaseInsensitiveMultimap header; + }; + /// Set before calling start(). + Config config; + + std::function)> on_open; + std::function, std::shared_ptr)> on_message; + std::function, int, const std::string &)> on_close; + std::function, const error_code &)> on_error; + std::function)> on_ping; + std::function)> on_pong; + + void start() { + if(!io_service) { + io_service = std::make_shared(); + internal_io_service = true; + } + + if(io_service->stopped()) + io_service->reset(); + + connect(); + + if(internal_io_service) + io_service->run(); + } + + void stop() noexcept { + { + std::unique_lock lock(connection_mutex); + if(connection) + connection->close(); + } + + if(internal_io_service) + io_service->stop(); + } + + virtual ~SocketClientBase() noexcept { + handler_runner->stop(); + stop(); + } + + /// If you have your own asio::io_service, store its pointer here before running start(). + std::shared_ptr io_service; + + protected: + bool internal_io_service = false; + + std::string host; + unsigned short port; + std::string path; + + std::shared_ptr connection; + std::mutex connection_mutex; + + std::shared_ptr handler_runner; + + SocketClientBase(const std::string &host_port_path, unsigned short default_port) noexcept : handler_runner(new ScopeRunner()) { + std::size_t host_end = host_port_path.find(':'); + std::size_t host_port_end = host_port_path.find('/'); + if(host_end == std::string::npos) { + host_end = host_port_end; + port = default_port; + } + else { + if(host_port_end == std::string::npos) + port = static_cast(stoul(host_port_path.substr(host_end + 1))); + else + port = static_cast(stoul(host_port_path.substr(host_end + 1, host_port_end - (host_end + 1)))); + } + if(host_port_end == std::string::npos) + path = "/"; + else + path = host_port_path.substr(host_port_end); + if(host_end == std::string::npos) + host = host_port_path; + else + host = host_port_path.substr(0, host_end); + } + + virtual void connect() = 0; + + void handshake(const std::shared_ptr &connection) { + connection->read_remote_endpoint(); + + auto write_buffer = std::make_shared(); + + std::ostream request(write_buffer.get()); + + request << "GET " << path << " HTTP/1.1" + << "\r\n"; + request << "Host: " << host << "\r\n"; + request << "Upgrade: websocket\r\n"; + request << "Connection: Upgrade\r\n"; + + // Make random 16-byte nonce + std::string nonce; + nonce.reserve(16); + std::uniform_int_distribution dist(0, 255); + std::random_device rd; + for(std::size_t c = 0; c < 16; c++) + nonce += static_cast(dist(rd)); + + auto nonce_base64 = std::make_shared(Crypto::Base64::encode(nonce)); + request << "Sec-WebSocket-Key: " << *nonce_base64 << "\r\n"; + request << "Sec-WebSocket-Version: 13\r\n"; + for(auto &header_field : config.header) + request << header_field.first << ": " << header_field.second << "\r\n"; + request << "\r\n"; + + connection->message = std::shared_ptr(new Message()); + + connection->set_timeout(config.timeout_request); + asio::async_write(*connection->socket, *write_buffer, [this, connection, write_buffer, nonce_base64](const error_code &ec, std::size_t /*bytes_transferred*/) { + connection->cancel_timeout(); + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + connection->set_timeout(this->config.timeout_request); + asio::async_read_until(*connection->socket, connection->message->streambuf, "\r\n\r\n", [this, connection, nonce_base64](const error_code &ec, std::size_t bytes_transferred) { + connection->cancel_timeout(); + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + // connection->message->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs: + // "After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter" + // The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the + // streambuf (maybe some bytes of a message) is appended to in the next async_read-function + std::size_t num_additional_bytes = connection->message->streambuf.size() - bytes_transferred; + + if(!ResponseMessage::parse(*connection->message, connection->http_version, connection->status_code, connection->header) || + connection->status_code.empty() || connection->status_code.compare(0, 4, "101 ") != 0) { + this->connection_error(connection, make_error_code::make_error_code(errc::protocol_error)); + return; + } + auto header_it = connection->header.find("Sec-WebSocket-Accept"); + static auto ws_magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + if(header_it != connection->header.end() && + Crypto::Base64::decode(header_it->second) == Crypto::sha1(*nonce_base64 + ws_magic_string)) { + this->connection_open(connection); + read_message(connection, num_additional_bytes); + } + else + this->connection_error(connection, make_error_code::make_error_code(errc::protocol_error)); + } + else + this->connection_error(connection, ec); + }); + } + else + this->connection_error(connection, ec); + }); + } + + void read_message(const std::shared_ptr &connection, std::size_t num_additional_bytes) { + asio::async_read(*connection->socket, connection->message->streambuf, asio::transfer_exactly(num_additional_bytes > 2 ? 0 : 2 - num_additional_bytes), [this, connection](const error_code &ec, std::size_t bytes_transferred) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + if(bytes_transferred == 0 && connection->message->streambuf.size() == 0) { // TODO: This might happen on server at least, might also happen here + this->read_message(connection, 0); + return; + } + std::size_t num_additional_bytes = connection->message->streambuf.size() - bytes_transferred; + + std::array first_bytes; + connection->message->read(reinterpret_cast(&first_bytes[0]), 2); + + connection->message->fin_rsv_opcode = first_bytes[0]; + + // Close connection if masked message from server (protocol error) + if(first_bytes[1] >= 128) { + const std::string reason("message from server masked"); + connection->send_close(1002, reason); + this->connection_close(connection, 1002, reason); + return; + } + + std::size_t length = (first_bytes[1] & 127); + + if(length == 126) { + // 2 next bytes is the size of content + asio::async_read(*connection->socket, connection->message->streambuf, asio::transfer_exactly(num_additional_bytes > 2 ? 0 : 2 - num_additional_bytes), [this, connection](const error_code &ec, std::size_t bytes_transferred) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + std::size_t num_additional_bytes = connection->message->streambuf.size() - bytes_transferred; + + std::array length_bytes; + connection->message->read(reinterpret_cast(&length_bytes[0]), 2); + + std::size_t length = 0; + std::size_t num_bytes = 2; + for(std::size_t c = 0; c < num_bytes; c++) + length += static_cast(length_bytes[c]) << (8 * (num_bytes - 1 - c)); + + connection->message->length = length; + this->read_message_content(connection, num_additional_bytes); + } + else + this->connection_error(connection, ec); + }); + } + else if(length == 127) { + // 8 next bytes is the size of content + asio::async_read(*connection->socket, connection->message->streambuf, asio::transfer_exactly(num_additional_bytes > 8 ? 0 : 8 - num_additional_bytes), [this, connection](const error_code &ec, std::size_t bytes_transferred) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + std::size_t num_additional_bytes = connection->message->streambuf.size() - bytes_transferred; + + std::array length_bytes; + connection->message->read(reinterpret_cast(&length_bytes[0]), 8); + + std::size_t length = 0; + std::size_t num_bytes = 8; + for(std::size_t c = 0; c < num_bytes; c++) + length += static_cast(length_bytes[c]) << (8 * (num_bytes - 1 - c)); + + connection->message->length = length; + this->read_message_content(connection, num_additional_bytes); + } + else + this->connection_error(connection, ec); + }); + } + else { + connection->message->length = length; + this->read_message_content(connection, num_additional_bytes); + } + } + else + this->connection_error(connection, ec); + }); + } + + void read_message_content(const std::shared_ptr &connection, std::size_t num_additional_bytes) { + if(connection->message->length + (connection->fragmented_message ? connection->fragmented_message->length : 0) > config.max_message_size) { + connection_error(connection, make_error_code::make_error_code(errc::message_size)); + const int status = 1009; + const std::string reason = "message too big"; + connection->send_close(status, reason); + connection_close(connection, status, reason); + return; + } + asio::async_read(*connection->socket, connection->message->streambuf, asio::transfer_exactly(num_additional_bytes > connection->message->length ? 0 : connection->message->length - num_additional_bytes), [this, connection](const error_code &ec, std::size_t bytes_transferred) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + std::size_t num_additional_bytes = connection->message->streambuf.size() - bytes_transferred; + std::shared_ptr next_message; + if(num_additional_bytes > 0) { // Extract bytes that are not extra bytes in buffer (only happen when several messages are sent in handshake response) + next_message = connection->message; + connection->message = std::shared_ptr(new Message(next_message->fin_rsv_opcode, next_message->length)); + std::ostream ostream(&connection->message->streambuf); + for(std::size_t c = 0; c < next_message->length; ++c) + ostream.put(next_message->get()); + } + else + next_message = std::shared_ptr(new Message()); + + // If connection close + if((connection->message->fin_rsv_opcode & 0x0f) == 8) { + connection->cancel_timeout(); + connection->set_timeout(); + + int status = 0; + if(connection->message->length >= 2) { + unsigned char byte1 = connection->message->get(); + unsigned char byte2 = connection->message->get(); + status = (static_cast(byte1) << 8) + byte2; + } + + auto reason = connection->message->string(); + connection->send_close(status, reason); + this->connection_close(connection, status, reason); + } + // If ping + else if((connection->message->fin_rsv_opcode & 0x0f) == 9) { + connection->cancel_timeout(); + connection->set_timeout(); + + // Send pong + auto empty_send_stream = std::make_shared(); + connection->send(empty_send_stream, nullptr, connection->message->fin_rsv_opcode + 1); + + if(this->on_ping) + this->on_ping(connection); + + // Next message + connection->message = next_message; + this->read_message(connection, num_additional_bytes); + } + // If pong + else if((connection->message->fin_rsv_opcode & 0x0f) == 10) { + connection->cancel_timeout(); + connection->set_timeout(); + + if(this->on_pong) + this->on_pong(connection); + + // Next message + connection->message = next_message; + this->read_message(connection, num_additional_bytes); + } + // If fragmented message and not final fragment + else if((connection->message->fin_rsv_opcode & 0x80) == 0) { + if(!connection->fragmented_message) { + connection->fragmented_message = connection->message; + connection->fragmented_message->fin_rsv_opcode |= 0x80; + } + else { + connection->fragmented_message->length += connection->message->length; + std::ostream ostream(&connection->fragmented_message->streambuf); + ostream << connection->message->rdbuf(); + } + + // Next message + connection->message = next_message; + this->read_message(connection, num_additional_bytes); + } + else { + connection->cancel_timeout(); + connection->set_timeout(); + + if(this->on_message) { + if(connection->fragmented_message) { + connection->fragmented_message->length += connection->message->length; + std::ostream ostream(&connection->fragmented_message->streambuf); + ostream << connection->message->rdbuf(); + + this->on_message(connection, connection->fragmented_message); + } + else + this->on_message(connection, connection->message); + } + + // Next message + connection->message = next_message; + // Only reset fragmented_message for non-control frames (control frames can be in between a fragmented message) + connection->fragmented_message = nullptr; + this->read_message(connection, num_additional_bytes); + } + } + else + this->connection_error(connection, ec); + }); + } + + void connection_open(const std::shared_ptr &connection) const { + connection->cancel_timeout(); + connection->set_timeout(); + + if(on_open) + on_open(connection); + } + + void connection_close(const std::shared_ptr &connection, int status, const std::string &reason) const { + connection->cancel_timeout(); + connection->set_timeout(); + + if(on_close) + on_close(connection, status, reason); + } + + void connection_error(const std::shared_ptr &connection, const error_code &ec) const { + connection->cancel_timeout(); + connection->set_timeout(); + + if(on_error) + on_error(connection, ec); + } + }; + + template + class SocketClient : public SocketClientBase {}; + + using WS = asio::ip::tcp::socket; + + template <> + class SocketClient : public SocketClientBase { + public: + SocketClient(const std::string &server_port_path) noexcept : SocketClientBase::SocketClientBase(server_port_path, 80){}; + + protected: + void connect() override { + std::unique_lock lock(connection_mutex); + auto connection = this->connection = std::shared_ptr(new Connection(handler_runner, config.timeout_idle, *io_service)); + lock.unlock(); + asio::ip::tcp::resolver::query query(host, std::to_string(port)); + auto resolver = std::make_shared(*io_service); + connection->set_timeout(config.timeout_request); + resolver->async_resolve(query, [this, connection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) { + connection->cancel_timeout(); + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + connection->set_timeout(this->config.timeout_request); + asio::async_connect(*connection->socket, it, [this, connection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) { + connection->cancel_timeout(); + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + asio::ip::tcp::no_delay option(true); + connection->socket->set_option(option); + + this->handshake(connection); + } + else + this->connection_error(connection, ec); + }); + } + else + this->connection_error(connection, ec); + }); + } + }; +} // namespace SimpleWeb + +#endif /* CLIENT_WS_HPP */ diff --git a/direct-access-api/include/client_wss.hpp b/direct-access-api/include/client_wss.hpp new file mode 100644 index 0000000..bf27841 --- /dev/null +++ b/direct-access-api/include/client_wss.hpp @@ -0,0 +1,92 @@ +#ifndef CLIENT_WSS_HPP +#define CLIENT_WSS_HPP + +#include "client_ws.hpp" + +#ifdef USE_STANDALONE_ASIO +#include +#else +#include +#endif + +namespace SimpleWeb { + using WSS = asio::ssl::stream; + + template <> + class SocketClient : public SocketClientBase { + public: + SocketClient(const std::string &server_port_path, bool verify_certificate = true, + const std::string &cert_file = std::string(), const std::string &private_key_file = std::string(), + const std::string &verify_file = std::string()) + : SocketClientBase::SocketClientBase(server_port_path, 443), context(asio::ssl::context::tlsv12) { + if(cert_file.size() > 0 && private_key_file.size() > 0) { + context.use_certificate_chain_file(cert_file); + context.use_private_key_file(private_key_file, asio::ssl::context::pem); + } + + if(verify_certificate) + context.set_verify_callback(asio::ssl::rfc2818_verification(host)); + + if(verify_file.size() > 0) + context.load_verify_file(verify_file); + else + context.set_default_verify_paths(); + + if(verify_file.size() > 0 || verify_certificate) + context.set_verify_mode(asio::ssl::verify_peer); + else + context.set_verify_mode(asio::ssl::verify_none); + }; + + protected: + asio::ssl::context context; + + void connect() override { + std::unique_lock connection_lock(connection_mutex); + auto connection = this->connection = std::shared_ptr(new Connection(handler_runner, config.timeout_idle, *io_service, context)); + connection_lock.unlock(); + asio::ip::tcp::resolver::query query(host, std::to_string(port)); + auto resolver = std::make_shared(*io_service); + connection->set_timeout(config.timeout_request); + resolver->async_resolve(query, [this, connection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) { + connection->cancel_timeout(); + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + connection->set_timeout(this->config.timeout_request); + asio::async_connect(connection->socket->lowest_layer(), it, [this, connection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) { + connection->cancel_timeout(); + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + asio::ip::tcp::no_delay option(true); + connection->socket->lowest_layer().set_option(option); + + SSL_set_tlsext_host_name(connection->socket->native_handle(), this->host.c_str()); + + connection->set_timeout(this->config.timeout_request); + connection->socket->async_handshake(asio::ssl::stream_base::client, [this, connection](const error_code &ec) { + connection->cancel_timeout(); + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) + handshake(connection); + else + this->connection_error(connection, ec); + }); + } + else + this->connection_error(connection, ec); + }); + } + else + this->connection_error(connection, ec); + }); + } + }; +} // namespace SimpleWeb + +#endif /* CLIENT_WSS_HPP */ diff --git a/direct-access-api/include/crypto.hpp b/direct-access-api/include/crypto.hpp new file mode 100644 index 0000000..c8e8cee --- /dev/null +++ b/direct-access-api/include/crypto.hpp @@ -0,0 +1,227 @@ +#ifndef SIMPLE_WEB_CRYPTO_HPP +#define SIMPLE_WEB_CRYPTO_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace SimpleWeb { +// TODO 2017: remove workaround for MSVS 2012 +#if _MSC_VER == 1700 // MSVS 2012 has no definition for round() + inline double round(double x) noexcept { // Custom definition of round() for positive numbers + return floor(x + 0.5); + } +#endif + + class Crypto { + const static std::size_t buffer_size = 131072; + + public: + class Base64 { + public: + static std::string encode(const std::string &ascii) noexcept { + std::string base64; + + BIO *bio, *b64; + BUF_MEM *bptr = BUF_MEM_new(); + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + bio = BIO_new(BIO_s_mem()); + BIO_push(b64, bio); + BIO_set_mem_buf(b64, bptr, BIO_CLOSE); + + // Write directly to base64-buffer to avoid copy + auto base64_length = static_cast(round(4 * ceil(static_cast(ascii.size()) / 3.0))); + base64.resize(base64_length); + bptr->length = 0; + bptr->max = base64_length + 1; + bptr->data = &base64[0]; + + if(BIO_write(b64, &ascii[0], static_cast(ascii.size())) <= 0 || BIO_flush(b64) <= 0) + base64.clear(); + + // To keep &base64[0] through BIO_free_all(b64) + bptr->length = 0; + bptr->max = 0; + bptr->data = nullptr; + + BIO_free_all(b64); + + return base64; + } + + static std::string decode(const std::string &base64) noexcept { + std::string ascii; + + // Resize ascii, however, the size is a up to two bytes too large. + ascii.resize((6 * base64.size()) / 8); + BIO *b64, *bio; + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); +// TODO: Remove in 2020 +#if OPENSSL_VERSION_NUMBER <= 0x1000115fL + bio = BIO_new_mem_buf((char *)&base64[0], static_cast(base64.size())); +#else + bio = BIO_new_mem_buf(&base64[0], static_cast(base64.size())); +#endif + bio = BIO_push(b64, bio); + + auto decoded_length = BIO_read(bio, &ascii[0], static_cast(ascii.size())); + if(decoded_length > 0) + ascii.resize(static_cast(decoded_length)); + else + ascii.clear(); + + BIO_free_all(b64); + + return ascii; + } + }; + + /// Return hex string from bytes in input string. + static std::string to_hex_string(const std::string &input) noexcept { + std::stringstream hex_stream; + hex_stream << std::hex << std::internal << std::setfill('0'); + for(auto &byte : input) + hex_stream << std::setw(2) << static_cast(static_cast(byte)); + return hex_stream.str(); + } + + static std::string md5(const std::string &input, std::size_t iterations = 1) noexcept { + std::string hash; + + hash.resize(128 / 8); + MD5(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); + + for(std::size_t c = 1; c < iterations; ++c) + MD5(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } + + static std::string md5(std::istream &stream, std::size_t iterations = 1) noexcept { + MD5_CTX context; + MD5_Init(&context); + std::streamsize read_length; + std::vector buffer(buffer_size); + while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0) + MD5_Update(&context, buffer.data(), static_cast(read_length)); + std::string hash; + hash.resize(128 / 8); + MD5_Final(reinterpret_cast(&hash[0]), &context); + + for(std::size_t c = 1; c < iterations; ++c) + MD5(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } + + static std::string sha1(const std::string &input, std::size_t iterations = 1) noexcept { + std::string hash; + + hash.resize(160 / 8); + SHA1(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); + + for(std::size_t c = 1; c < iterations; ++c) + SHA1(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } + + static std::string sha1(std::istream &stream, std::size_t iterations = 1) noexcept { + SHA_CTX context; + SHA1_Init(&context); + std::streamsize read_length; + std::vector buffer(buffer_size); + while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0) + SHA1_Update(&context, buffer.data(), static_cast(read_length)); + std::string hash; + hash.resize(160 / 8); + SHA1_Final(reinterpret_cast(&hash[0]), &context); + + for(std::size_t c = 1; c < iterations; ++c) + SHA1(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } + + static std::string sha256(const std::string &input, std::size_t iterations = 1) noexcept { + std::string hash; + + hash.resize(256 / 8); + SHA256(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); + + for(std::size_t c = 1; c < iterations; ++c) + SHA256(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } + + static std::string sha256(std::istream &stream, std::size_t iterations = 1) noexcept { + SHA256_CTX context; + SHA256_Init(&context); + std::streamsize read_length; + std::vector buffer(buffer_size); + while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0) + SHA256_Update(&context, buffer.data(), static_cast(read_length)); + std::string hash; + hash.resize(256 / 8); + SHA256_Final(reinterpret_cast(&hash[0]), &context); + + for(std::size_t c = 1; c < iterations; ++c) + SHA256(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } + + static std::string sha512(const std::string &input, std::size_t iterations = 1) noexcept { + std::string hash; + + hash.resize(512 / 8); + SHA512(reinterpret_cast(&input[0]), input.size(), reinterpret_cast(&hash[0])); + + for(std::size_t c = 1; c < iterations; ++c) + SHA512(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } + + static std::string sha512(std::istream &stream, std::size_t iterations = 1) noexcept { + SHA512_CTX context; + SHA512_Init(&context); + std::streamsize read_length; + std::vector buffer(buffer_size); + while((read_length = stream.read(&buffer[0], buffer_size).gcount()) > 0) + SHA512_Update(&context, buffer.data(), static_cast(read_length)); + std::string hash; + hash.resize(512 / 8); + SHA512_Final(reinterpret_cast(&hash[0]), &context); + + for(std::size_t c = 1; c < iterations; ++c) + SHA512(reinterpret_cast(&hash[0]), hash.size(), reinterpret_cast(&hash[0])); + + return hash; + } + + /// key_size is number of bytes of the returned key. + static std::string pbkdf2(const std::string &password, const std::string &salt, int iterations, int key_size) noexcept { + std::string key; + key.resize(static_cast(key_size)); + PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(), + reinterpret_cast(salt.c_str()), salt.size(), iterations, + key_size, reinterpret_cast(&key[0])); + return key; + } + }; +} +#endif /* SIMPLE_WEB_CRYPTO_HPP */ diff --git a/direct-access-api/include/directAccessProcessor.hpp b/direct-access-api/include/directAccessProcessor.hpp index 1d6927e..d1c034c 100644 --- a/direct-access-api/include/directAccessProcessor.hpp +++ b/direct-access-api/include/directAccessProcessor.hpp @@ -21,20 +21,23 @@ #include "vcanhandler.hpp" #include "dbmanager.hpp" +#define PERM_MNGT_BUFFER_SIZE 1000 + using namespace std; using namespace jsoncons; using jsoncons::json; + class directAccessProcessor { private: string ks_token; + string perm_mngt_srv_address = "/home/a02482333/repo/permission-mngt/socket"; class authenticator* tokenValidator = NULL; class accesschecker* accessValidator = NULL; class vcanhandler* vcanHandler = NULL; - string processCreate(class wschannel& channel, string rchannel); + string processCreate(json perm_json, string rchannel); string processDelete(string vchannel); - string processAuthorize(string token, class wschannel& channel); public: directAccessProcessor(class authenticator* vdator); diff --git a/direct-access-api/include/heap_only_string.hpp b/direct-access-api/include/heap_only_string.hpp new file mode 100644 index 0000000..5348b43 --- /dev/null +++ b/direct-access-api/include/heap_only_string.hpp @@ -0,0 +1,155 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_HEAPONLYSTRING_HPP +#define JSONCONS_DETAIL_HEAPONLYSTRING_HPP + +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace detail { + +template +class heap_only_string_base +{ + Allocator allocator_; +public: + Allocator& get_allocator() + { + return allocator_; + } + + const Allocator& get_allocator() const + { + return allocator_; + } +protected: + heap_only_string_base(const Allocator& allocator) + : allocator_(allocator) + { + } + + ~heap_only_string_base() {} +}; + +template +class heap_only_string_factory; + +template +class heap_only_string : public heap_only_string_base +{ + typedef typename std::allocator_traits::template rebind_alloc allocator_type; + typedef std::allocator_traits allocator_traits_type; + typedef typename allocator_traits_type::pointer pointer; + + friend class heap_only_string_factory; +public: + typedef CharT char_type; + typedef heap_only_string value_type; + + ~heap_only_string() {} + + const char_type* c_str() const { return to_plain_pointer(p_); } + const char_type* data() const { return to_plain_pointer(p_); } + size_t length() const { return length_; } + + using heap_only_string_base::get_allocator; + + + friend std::basic_ostream& operator<<(std::basic_ostream& os, const heap_only_string& s) + { + os.write(s.data(),s.length()); + return os; + } +private: + heap_only_string() + : heap_only_string_base(Allocator()) + { + + } + heap_only_string(const Allocator& allocator) + : heap_only_string_base(allocator) + { + + } + + pointer p_; + size_t length_; + + heap_only_string(const heap_only_string&) = delete; + heap_only_string& operator=(const heap_only_string&) = delete; + +}; + +template +class heap_only_string_factory +{ + typedef CharT char_type; + typedef typename std::allocator_traits::template rebind_alloc byte_allocator_type; + typedef std::allocator_traits byte_allocator_traits_type; + typedef typename byte_allocator_traits_type::pointer byte_pointer; + typedef typename heap_only_string::pointer pointer; +public: + + typedef typename std::allocator_traits::template rebind_alloc> string_allocator_type; + typedef std::allocator_traits string_allocator_traits_type; + typedef typename string_allocator_traits_type::pointer string_pointer; + + typedef heap_only_string* raw_string_pointer_type; + + struct string_storage + { + heap_only_string data; + char_type c[1]; + }; + typedef typename std::aligned_storage::type storage_type; + + static size_t aligned_size(size_t n) + { + return sizeof(storage_type) + n; + } +public: + static string_pointer create(const char_type* s, size_t length) + { + return create(s, length, Allocator()); + } + static string_pointer create(const char_type* s, size_t length, const Allocator& allocator) + { + size_t mem_size = aligned_size(length*sizeof(char_type)); + + byte_allocator_type alloc(allocator); + byte_pointer ptr = alloc.allocate(mem_size); + + char* storage = to_plain_pointer(ptr); + raw_string_pointer_type ps = new(storage)heap_only_string(alloc); + auto psa = reinterpret_cast(storage); + + CharT* p = new(&psa->c)char_type[length + 1]; + memcpy(p, s, length*sizeof(char_type)); + p[length] = 0; + ps->p_ = std::pointer_traits::pointer_to(*p); + ps->length_ = length; + return std::pointer_traits::pointer_to(*ps); + } + + static void destroy(string_pointer ptr) + { + raw_string_pointer_type rawp = to_plain_pointer(ptr); + char* p = reinterpret_cast(rawp); + size_t mem_size = aligned_size(ptr->length_*sizeof(char_type)); + byte_allocator_type alloc(ptr->get_allocator()); + alloc.deallocate(p,mem_size); + } +}; + + +}} + +#endif diff --git a/direct-access-api/include/json.hpp b/direct-access-api/include/json.hpp new file mode 100644 index 0000000..9dade69 --- /dev/null +++ b/direct-access-api/include/json.hpp @@ -0,0 +1,5054 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_HPP +#define JSONCONS_JSON_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" +#endif + +namespace jsoncons { + +struct sorted_policy +{ + static const bool preserve_order = false; + + template + using object_storage = std::vector; + + template + using array_storage = std::vector; + + template + using key_storage = std::basic_string; + + template + using string_storage = std::basic_string; + + typedef default_parse_error_handler parse_error_handler_type; +}; + +struct preserve_order_policy : public sorted_policy +{ + static const bool preserve_order = true; +}; + +template +class range +{ + IteratorT first_; + IteratorT last_; +public: + range(const IteratorT& first, const IteratorT& last) + : first_(first), last_(last) + { + } + +public: + IteratorT begin() + { + return first_; + } + IteratorT end() + { + return last_; + } +}; + +enum class json_type_tag : uint8_t +{ + null_t = 0, + empty_object_t, + bool_t, + integer_t, + uinteger_t, + double_t, + small_string_t, + string_t, + byte_string_t, + array_t, + object_t +}; + +template +class basic_json +{ +public: + + typedef Allocator allocator_type; + + typedef ImplementationPolicy implementation_policy; + + typedef typename ImplementationPolicy::parse_error_handler_type parse_error_handler_type; + + typedef CharT char_type; + typedef typename std::char_traits char_traits_type; + + typedef basic_string_view_ext string_view_type; + + typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; + using string_storage_type = typename implementation_policy::template string_storage; + using key_storage_type = typename implementation_policy::template key_storage; + + // string_type is for interface only, not storage + typedef std::basic_string string_type; + + typedef basic_json value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef key_value_pair key_value_pair_type; + +#if !defined(JSONCONS_NO_DEPRECATED) + typedef value_type json_type; + typedef key_value_pair_type kvp_type; + typedef key_value_pair_type member_type; + typedef jsoncons::null_type null_type; +#endif + + typedef typename std::allocator_traits:: template rebind_alloc val_allocator_type; + using array_storage_type = typename implementation_policy::template array_storage; + + typedef typename std::allocator_traits:: template rebind_alloc byte_allocator_type; + using byte_string_storage_type = typename implementation_policy::template array_storage; + + typedef json_array array; + + typedef typename std::allocator_traits:: template rebind_alloc kvp_allocator_type; + + using object_storage_type = typename implementation_policy::template object_storage; + typedef json_object object; + + typedef typename std::allocator_traits:: template rebind_alloc array_allocator; + typedef typename std::allocator_traits:: template rebind_alloc object_allocator; + + typedef typename object::iterator object_iterator; + typedef typename object::const_iterator const_object_iterator; + typedef typename array::iterator array_iterator; + typedef typename array::const_iterator const_array_iterator; + + struct variant + { + struct data_base + { + json_type_tag type_id_; + + data_base(json_type_tag id) + : type_id_(id) + {} + }; + + class null_data final : public data_base + { + public: + null_data() + : data_base(json_type_tag::null_t) + { + } + }; + + class empty_object_data final : public data_base + { + public: + empty_object_data() + : data_base(json_type_tag::empty_object_t) + { + } + }; + + class bool_data final : public data_base + { + bool val_; + public: + bool_data(bool val) + : data_base(json_type_tag::bool_t),val_(val) + { + } + + bool_data(const bool_data& val) + : data_base(json_type_tag::bool_t),val_(val.val_) + { + } + + bool value() const + { + return val_; + } + + }; + + class integer_data final : public data_base + { + int64_t val_; + public: + integer_data(int64_t val) + : data_base(json_type_tag::integer_t),val_(val) + { + } + + integer_data(const integer_data& val) + : data_base(json_type_tag::integer_t),val_(val.val_) + { + } + + int64_t value() const + { + return val_; + } + }; + + class uinteger_data final : public data_base + { + uint64_t val_; + public: + uinteger_data(uint64_t val) + : data_base(json_type_tag::uinteger_t),val_(val) + { + } + + uinteger_data(const uinteger_data& val) + : data_base(json_type_tag::uinteger_t),val_(val.val_) + { + } + + uint64_t value() const + { + return val_; + } + }; + + class double_data final : public data_base + { + chars_format format_; + uint8_t precision_; + uint8_t decimal_places_; + double val_; + public: + double_data(double val) + : data_base(json_type_tag::double_t), + precision_(0), + decimal_places_(0), + val_(val) + { + } + double_data(double val, const number_format& fmt) + : data_base(json_type_tag::double_t), + format_(fmt.floating_point_format()), + precision_(fmt.precision()), + decimal_places_(fmt.decimal_places()), + val_(val) + { + } + + double_data(const double_data& val) + : data_base(json_type_tag::double_t), + precision_(val.precision_), + decimal_places_(val.decimal_places_), + val_(val.val_) + { + } + + double value() const + { + return val_; + } + + uint8_t precision() const + { + return precision_; + } + + uint8_t decimal_places() const + { + return precision_; + } + }; + + class small_string_data final : public data_base + { + static const size_t capacity = 14/sizeof(char_type); + uint8_t length_; + char_type data_[capacity]; + public: + static const size_t max_length = (14 / sizeof(char_type)) - 1; + + small_string_data(const char_type* p, uint8_t length) + : data_base(json_type_tag::small_string_t), length_(length) + { + JSONCONS_ASSERT(length <= max_length); + std::memcpy(data_,p,length*sizeof(char_type)); + data_[length] = 0; + } + + small_string_data(const small_string_data& val) + : data_base(json_type_tag::small_string_t), length_(val.length_) + { + std::memcpy(data_,val.data_,val.length_*sizeof(char_type)); + data_[length_] = 0; + } + + uint8_t length() const + { + return length_; + } + + const char_type* data() const + { + return data_; + } + + const char_type* c_str() const + { + return data_; + } + }; + + // string_data + class string_data final : public data_base + { + typedef typename detail::heap_only_string_factory::string_pointer pointer; + + pointer ptr_; + public: + string_data(const string_data& val) + : data_base(json_type_tag::string_t) + { + ptr_ = detail::heap_only_string_factory::create(val.data(),val.length(),val.get_allocator()); + } + + string_data(string_data&& val) + : data_base(json_type_tag::string_t), ptr_(nullptr) + { + std::swap(val.ptr_,ptr_); + } + + string_data(const string_data& val, const Allocator& a) + : data_base(json_type_tag::string_t) + { + ptr_ = detail::heap_only_string_factory::create(val.data(),val.length(),a); + } + + string_data(const char_type* data, size_t length, const Allocator& a) + : data_base(json_type_tag::string_t) + { + ptr_ = detail::heap_only_string_factory::create(data,length,a); + } + + ~string_data() + { + if (ptr_ != nullptr) + { + detail::heap_only_string_factory::destroy(ptr_); + } + } + + void swap(string_data& val) + { + std::swap(val.ptr_,ptr_); + } + + const char_type* data() const + { + return ptr_->data(); + } + + const char_type* c_str() const + { + return ptr_->c_str(); + } + + size_t length() const + { + return ptr_->length(); + } + + allocator_type get_allocator() const + { + return ptr_->get_allocator(); + } + }; + + // byte_string_data + class byte_string_data final : public data_base + { + typedef typename std::allocator_traits:: template rebind_alloc string_holder_allocator_type; + typedef typename std::allocator_traits::pointer pointer; + + pointer ptr_; + + template + void create(string_holder_allocator_type allocator, Args&& ... args) + { + typename std::allocator_traits:: template rebind_alloc alloc(allocator); + ptr_ = alloc.allocate(1); + try + { + std::allocator_traits:: template rebind_traits::construct(alloc, detail::to_plain_pointer(ptr_), std::forward(args)...); + } + catch (...) + { + alloc.deallocate(ptr_,1); + throw; + } + } + public: + byte_string_data(const byte_string_data& val) + : data_base(json_type_tag::byte_string_t) + { + create(val.ptr_->get_allocator(), *(val.ptr_)); + } + + byte_string_data(byte_string_data&& val) + : data_base(json_type_tag::byte_string_t), ptr_(nullptr) + { + std::swap(val.ptr_,ptr_); + } + + byte_string_data(const byte_string_data& val, const Allocator& a) + : data_base(json_type_tag::byte_string_t) + { + create(string_holder_allocator_type(a), *(val.ptr_), a); + } + + byte_string_data(const uint8_t* data, size_t length, const Allocator& a) + : data_base(json_type_tag::byte_string_t) + { + create(string_holder_allocator_type(a), data, data+length, a); + } + + ~byte_string_data() + { + if (ptr_ != nullptr) + { + typename std::allocator_traits:: template rebind_alloc alloc(ptr_->get_allocator()); + std::allocator_traits:: template rebind_traits::destroy(alloc, detail::to_plain_pointer(ptr_)); + alloc.deallocate(ptr_,1); + } + } + + void swap(byte_string_data& val) + { + std::swap(val.ptr_,ptr_); + } + + const uint8_t* data() const + { + return ptr_->data(); + } + + size_t length() const + { + return ptr_->size(); + } + + allocator_type get_allocator() const + { + return ptr_->get_allocator(); + } + }; + + // array_data + class array_data final : public data_base + { + typedef typename std::allocator_traits::pointer pointer; + pointer ptr_; + + template + void create(array_allocator allocator, Args&& ... args) + { + typename std::allocator_traits:: template rebind_alloc alloc(allocator); + ptr_ = alloc.allocate(1); + try + { + std::allocator_traits:: template rebind_traits::construct(alloc, detail::to_plain_pointer(ptr_), std::forward(args)...); + } + catch (...) + { + alloc.deallocate(ptr_,1); + throw; + } + } + public: + array_data(const array& val) + : data_base(json_type_tag::array_t) + { + create(val.get_allocator(), val); + } + + array_data(const array& val, const Allocator& a) + : data_base(json_type_tag::array_t) + { + create(array_allocator(a), val, a); + } + + array_data(const array_data& val) + : data_base(json_type_tag::array_t) + { + create(val.ptr_->get_allocator(), *(val.ptr_)); + } + + array_data(array_data&& val) + : data_base(json_type_tag::array_t), ptr_(nullptr) + { + std::swap(val.ptr_, ptr_); + } + + array_data(const array_data& val, const Allocator& a) + : data_base(json_type_tag::array_t) + { + create(array_allocator(a), *(val.ptr_), a); + } + ~array_data() + { + if (ptr_ != nullptr) + { + typename std::allocator_traits:: template rebind_alloc alloc(ptr_->get_allocator()); + std::allocator_traits:: template rebind_traits::destroy(alloc, detail::to_plain_pointer(ptr_)); + alloc.deallocate(ptr_,1); + } + } + + allocator_type get_allocator() const + { + return ptr_->get_allocator(); + } + + void swap(array_data& val) + { + std::swap(val.ptr_,ptr_); + } + + array& value() + { + return *ptr_; + } + + const array& value() const + { + return *ptr_; + } + }; + + // object_data + class object_data final : public data_base + { + typedef typename std::allocator_traits::pointer pointer; + pointer ptr_; + + template + void create(Allocator allocator, Args&& ... args) + { + typename std::allocator_traits:: template rebind_alloc alloc(allocator); + ptr_ = alloc.allocate(1); + try + { + std::allocator_traits:: template rebind_traits::construct(alloc, detail::to_plain_pointer(ptr_), std::forward(args)...); + } + catch (...) + { + alloc.deallocate(ptr_,1); + throw; + } + } + public: + explicit object_data(const Allocator& a) + : data_base(json_type_tag::object_t) + { + create(a,a); + } + + explicit object_data(const object& val) + : data_base(json_type_tag::object_t) + { + create(val.get_allocator(), val); + } + + explicit object_data(const object& val, const Allocator& a) + : data_base(json_type_tag::object_t) + { + create(object_allocator(a), val, a); + } + + explicit object_data(const object_data& val) + : data_base(json_type_tag::object_t) + { + create(val.ptr_->get_allocator(), *(val.ptr_)); + } + + explicit object_data(object_data&& val) + : data_base(json_type_tag::object_t), ptr_(nullptr) + { + std::swap(val.ptr_,ptr_); + } + + explicit object_data(const object_data& val, const Allocator& a) + : data_base(json_type_tag::object_t) + { + create(object_allocator(a), *(val.ptr_), a); + } + + ~object_data() + { + if (ptr_ != nullptr) + { + typename std::allocator_traits:: template rebind_alloc alloc(ptr_->get_allocator()); + std::allocator_traits:: template rebind_traits::destroy(alloc, detail::to_plain_pointer(ptr_)); + alloc.deallocate(ptr_,1); + } + } + + void swap(object_data& val) + { + std::swap(val.ptr_,ptr_); + } + + object& value() + { + return *ptr_; + } + + const object& value() const + { + return *ptr_; + } + + allocator_type get_allocator() const + { + return ptr_->get_allocator(); + } + }; + + private: + static const size_t data_size = static_max::value; + static const size_t data_align = static_max::value; + + typedef typename std::aligned_storage::type data_t; + + data_t data_; + public: + variant() + { + new(reinterpret_cast(&data_))empty_object_data(); + } + + variant(const Allocator& a) + { + new(reinterpret_cast(&data_))object_data(a); + } + + variant(const variant& val) + { + Init_(val); + } + + variant(const variant& val, const Allocator& allocator) + { + Init_(val,allocator); + } + + variant(variant&& val) JSONCONS_NOEXCEPT + { + Init_rv_(std::forward(val)); + } + + variant(variant&& val, const Allocator& allocator) JSONCONS_NOEXCEPT + { + Init_rv_(std::forward(val), allocator, + typename std::allocator_traits::propagate_on_container_move_assignment()); + } + + explicit variant(null_type) + { + new(reinterpret_cast(&data_))null_data(); + } + explicit variant(bool val) + { + new(reinterpret_cast(&data_))bool_data(val); + } + explicit variant(int64_t val) + { + new(reinterpret_cast(&data_))integer_data(val); + } + explicit variant(uint64_t val, const Allocator&) + { + new(reinterpret_cast(&data_))uinteger_data(val); + } + explicit variant(uint64_t val) + { + new(reinterpret_cast(&data_))uinteger_data(val); + } + variant(double val) + { + new(reinterpret_cast(&data_))double_data(val); + } + variant(double val, const number_format& fmt) + { + new(reinterpret_cast(&data_))double_data(val, fmt); + } + variant(const char_type* s, size_t length) + { + if (length <= small_string_data::max_length) + { + new(reinterpret_cast(&data_))small_string_data(s, static_cast(length)); + } + else + { + new(reinterpret_cast(&data_))string_data(s, length, char_allocator_type()); + } + } + variant(const uint8_t* s, size_t length) + { + new(reinterpret_cast(&data_))byte_string_data(s, length, byte_allocator_type()); + } + + variant(const uint8_t* s, size_t length, const Allocator& alloc) + { + new(reinterpret_cast(&data_))byte_string_data(s, length, alloc); + } + + variant(const char_type* s) + { + size_t length = char_traits_type::length(s); + if (length <= small_string_data::max_length) + { + new(reinterpret_cast(&data_))small_string_data(s, static_cast(length)); + } + else + { + new(reinterpret_cast(&data_))string_data(s, length, char_allocator_type()); + } + } + + variant(const char_type* s, const Allocator& alloc) + { + size_t length = char_traits_type::length(s); + if (length <= small_string_data::max_length) + { + new(reinterpret_cast(&data_))small_string_data(s, static_cast(length)); + } + else + { + new(reinterpret_cast(&data_))string_data(s, length, alloc); + } + } + + variant(const char_type* s, size_t length, const Allocator& alloc) + { + if (length <= small_string_data::max_length) + { + new(reinterpret_cast(&data_))small_string_data(s, static_cast(length)); + } + else + { + new(reinterpret_cast(&data_))string_data(s, length, alloc); + } + } + variant(const object& val) + { + new(reinterpret_cast(&data_))object_data(val); + } + variant(const object& val, const Allocator& alloc) + { + new(reinterpret_cast(&data_))object_data(val, alloc); + } + variant(const array& val) + { + new(reinterpret_cast(&data_))array_data(val); + } + variant(const array& val, const Allocator& alloc) + { + new(reinterpret_cast(&data_))array_data(val,alloc); + } + + ~variant() + { + Destroy_(); + } + + void Destroy_() + { + switch (type_id()) + { + case json_type_tag::string_t: + reinterpret_cast(&data_)->~string_data(); + break; + case json_type_tag::byte_string_t: + reinterpret_cast(&data_)->~byte_string_data(); + break; + case json_type_tag::object_t: + reinterpret_cast(&data_)->~object_data(); + break; + case json_type_tag::array_t: + reinterpret_cast(&data_)->~array_data(); + break; + default: + break; + } + } + + variant& operator=(const variant& val) + { + if (this !=&val) + { + Destroy_(); + switch (val.type_id()) + { + case json_type_tag::null_t: + new(reinterpret_cast(&data_))null_data(); + break; + case json_type_tag::empty_object_t: + new(reinterpret_cast(&data_))empty_object_data(); + break; + case json_type_tag::bool_t: + new(reinterpret_cast(&data_))bool_data(*(val.bool_data_cast())); + break; + case json_type_tag::integer_t: + new(reinterpret_cast(&data_))integer_data(*(val.integer_data_cast())); + break; + case json_type_tag::uinteger_t: + new(reinterpret_cast(&data_))uinteger_data(*(val.uinteger_data_cast())); + break; + case json_type_tag::double_t: + new(reinterpret_cast(&data_))double_data(*(val.double_data_cast())); + break; + case json_type_tag::small_string_t: + new(reinterpret_cast(&data_))small_string_data(*(val.small_string_data_cast())); + break; + case json_type_tag::string_t: + new(reinterpret_cast(&data_))string_data(*(val.string_data_cast())); + break; + case json_type_tag::byte_string_t: + new(reinterpret_cast(&data_))byte_string_data(*(val.byte_string_data_cast())); + break; + case json_type_tag::array_t: + new(reinterpret_cast(&data_))array_data(*(val.array_data_cast())); + break; + case json_type_tag::object_t: + new(reinterpret_cast(&data_))object_data(*(val.object_data_cast())); + break; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + return *this; + } + + variant& operator=(variant&& val) JSONCONS_NOEXCEPT + { + if (this !=&val) + { + swap(val); + } + return *this; + } + + json_type_tag type_id() const + { + return reinterpret_cast(&data_)->type_id_; + } + + const null_data* null_data_cast() const + { + return reinterpret_cast(&data_); + } + + const empty_object_data* empty_object_data_cast() const + { + return reinterpret_cast(&data_); + } + + const bool_data* bool_data_cast() const + { + return reinterpret_cast(&data_); + } + + const integer_data* integer_data_cast() const + { + return reinterpret_cast(&data_); + } + + const uinteger_data* uinteger_data_cast() const + { + return reinterpret_cast(&data_); + } + + const double_data* double_data_cast() const + { + return reinterpret_cast(&data_); + } + + const small_string_data* small_string_data_cast() const + { + return reinterpret_cast(&data_); + } + + string_data* string_data_cast() + { + return reinterpret_cast(&data_); + } + + const string_data* string_data_cast() const + { + return reinterpret_cast(&data_); + } + + byte_string_data* byte_string_data_cast() + { + return reinterpret_cast(&data_); + } + + const byte_string_data* byte_string_data_cast() const + { + return reinterpret_cast(&data_); + } + + object_data* object_data_cast() + { + return reinterpret_cast(&data_); + } + + const object_data* object_data_cast() const + { + return reinterpret_cast(&data_); + } + + array_data* array_data_cast() + { + return reinterpret_cast(&data_); + } + + const array_data* array_data_cast() const + { + return reinterpret_cast(&data_); + } + + string_view_type as_string_view() const + { + switch (type_id()) + { + case json_type_tag::small_string_t: + return string_view_type(small_string_data_cast()->data(),small_string_data_cast()->length()); + case json_type_tag::string_t: + return string_view_type(string_data_cast()->data(),string_data_cast()->length()); + default: + JSONCONS_THROW(json_exception_impl("Not a string")); + } + } + + byte_string_view as_byte_string_view() const + { + switch (type_id()) + { + case json_type_tag::byte_string_t: + return byte_string_view(byte_string_data_cast()->data(),byte_string_data_cast()->length()); + default: + JSONCONS_THROW(json_exception_impl("Not a byte string")); + } + } + + bool operator==(const variant& rhs) const + { + if (this ==&rhs) + { + return true; + } + switch (type_id()) + { + case json_type_tag::null_t: + switch (rhs.type_id()) + { + case json_type_tag::null_t: + return true; + default: + return false; + } + break; + case json_type_tag::empty_object_t: + switch (rhs.type_id()) + { + case json_type_tag::empty_object_t: + return true; + case json_type_tag::object_t: + return rhs.object_data_cast()->value().size() == 0; + default: + return false; + } + break; + case json_type_tag::bool_t: + switch (rhs.type_id()) + { + case json_type_tag::bool_t: + return bool_data_cast()->value() == rhs.bool_data_cast()->value(); + default: + return false; + } + break; + case json_type_tag::integer_t: + switch (rhs.type_id()) + { + case json_type_tag::integer_t: + return integer_data_cast()->value() == rhs.integer_data_cast()->value(); + case json_type_tag::uinteger_t: + return integer_data_cast()->value() >= 0 ? static_cast(integer_data_cast()->value()) == rhs.uinteger_data_cast()->value() : false; + case json_type_tag::double_t: + return static_cast(integer_data_cast()->value()) == rhs.double_data_cast()->value(); + default: + return false; + } + break; + case json_type_tag::uinteger_t: + switch (rhs.type_id()) + { + case json_type_tag::integer_t: + return rhs.integer_data_cast()->value() >= 0 ? uinteger_data_cast()->value() == static_cast(rhs.integer_data_cast()->value()) : false; + case json_type_tag::uinteger_t: + return uinteger_data_cast()->value() == rhs.uinteger_data_cast()->value(); + case json_type_tag::double_t: + return static_cast(uinteger_data_cast()->value()) == rhs.double_data_cast()->value(); + default: + return false; + } + break; + case json_type_tag::double_t: + switch (rhs.type_id()) + { + case json_type_tag::integer_t: + return double_data_cast()->value() == static_cast(rhs.integer_data_cast()->value()); + case json_type_tag::uinteger_t: + return double_data_cast()->value() == static_cast(rhs.uinteger_data_cast()->value()); + case json_type_tag::double_t: + return double_data_cast()->value() == rhs.double_data_cast()->value(); + default: + return false; + } + break; + case json_type_tag::small_string_t: + switch (rhs.type_id()) + { + case json_type_tag::small_string_t: + return as_string_view() == rhs.as_string_view(); + case json_type_tag::string_t: + return as_string_view() == rhs.as_string_view(); + default: + return false; + } + break; + case json_type_tag::byte_string_t: + switch (rhs.type_id()) + { + case json_type_tag::byte_string_t: + { + return as_byte_string_view() == rhs.as_byte_string_view(); + } + default: + return false; + } + break; + case json_type_tag::string_t: + switch (rhs.type_id()) + { + case json_type_tag::small_string_t: + return as_string_view() == rhs.as_string_view(); + case json_type_tag::string_t: + return as_string_view() == rhs.as_string_view(); + default: + return false; + } + break; + case json_type_tag::array_t: + switch (rhs.type_id()) + { + case json_type_tag::array_t: + return array_data_cast()->value() == rhs.array_data_cast()->value(); + default: + return false; + } + break; + case json_type_tag::object_t: + switch (rhs.type_id()) + { + case json_type_tag::empty_object_t: + return object_data_cast()->value().size() == 0; + case json_type_tag::object_t: + return object_data_cast()->value() == rhs.object_data_cast()->value(); + default: + return false; + } + break; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + + bool operator!=(const variant& rhs) const + { + return !(*this == rhs); + } + + template + typename std::enable_if::pointer>::value,void>::type + swap(variant& other) JSONCONS_NOEXCEPT + { + if (this ==&other) + { + return; + } + + std::swap(data_,other.data_); + } + + template + typename std::enable_if::pointer>::value, void>::type + swap(variant& other) JSONCONS_NOEXCEPT + { + if (this ==&other) + { + return; + } + switch (type_id()) + { + case json_type_tag::null_t: + { + switch (other.type_id()) + { + case json_type_tag::string_t: + { + string_data temp(std::move(*other.string_data_cast())); + new(reinterpret_cast(&(other.data_)))null_data(); + new(reinterpret_cast(&data_))string_data(std::move(temp)); + } + break; + case json_type_tag::byte_string_t: + { + byte_string_data temp(std::move(*other.byte_string_data_cast())); + new(reinterpret_cast(&(other.data_)))null_data(); + new(reinterpret_cast(&data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::array_t: + { + array_data temp(std::move(*other.array_data_cast())); + new(reinterpret_cast(&(other.data_)))null_data(); + new(reinterpret_cast(&data_))array_data(std::move(temp)); + } + break; + case json_type_tag::object_t: + { + object_data temp(std::move(*other.object_data_cast())); + new(reinterpret_cast(&(other.data_)))null_data(); + new(reinterpret_cast(&data_))object_data(std::move(temp)); + } + break; + default: + std::swap(data_,other.data_); + break; + } + } + break; + case json_type_tag::empty_object_t: + { + switch (other.type_id()) + { + case json_type_tag::string_t: + { + string_data temp(std::move(*other.string_data_cast())); + new(reinterpret_cast(&(other.data_)))empty_object_data(); + new(reinterpret_cast(&data_))string_data(std::move(temp)); + } + break; + case json_type_tag::byte_string_t: + { + byte_string_data temp(std::move(*other.byte_string_data_cast())); + new(reinterpret_cast(&(other.data_)))empty_object_data(); + new(reinterpret_cast(&data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::array_t: + { + array_data temp(std::move(*other.array_data_cast())); + new(reinterpret_cast(&(other.data_)))empty_object_data(); + new(reinterpret_cast(&data_))array_data(std::move(temp)); + } + break; + case json_type_tag::object_t: + { + object_data temp(std::move(*other.object_data_cast())); + new(reinterpret_cast(&(other.data_)))empty_object_data(); + new(reinterpret_cast(&data_))object_data(std::move(temp)); + } + break; + default: + std::swap(data_,other.data_); + break; + } + } + break; + case json_type_tag::bool_t: + { + switch (other.type_id()) + { + case json_type_tag::string_t: + { + string_data temp(std::move(*other.string_data_cast())); + new(reinterpret_cast(&(other.data_)))bool_data(*bool_data_cast()); + new(reinterpret_cast(&data_))string_data(std::move(temp)); + } + break; + case json_type_tag::byte_string_t: + { + byte_string_data temp(std::move(*other.byte_string_data_cast())); + new(reinterpret_cast(&(other.data_)))bool_data(*bool_data_cast()); + new(reinterpret_cast(&data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::array_t: + { + array_data temp(std::move(*other.array_data_cast())); + new(reinterpret_cast(&(other.data_)))bool_data(*bool_data_cast()); + new(reinterpret_cast(&data_))array_data(std::move(temp)); + } + break; + case json_type_tag::object_t: + { + object_data temp(std::move(*other.object_data_cast())); + new(reinterpret_cast(&(other.data_)))bool_data(*bool_data_cast()); + new(reinterpret_cast(&data_))object_data(std::move(temp)); + } + break; + default: + std::swap(data_,other.data_); + break; + } + } + break; + case json_type_tag::integer_t: + { + switch (other.type_id()) + { + case json_type_tag::string_t: + { + string_data temp(std::move(*other.string_data_cast())); + new(reinterpret_cast(&(other.data_)))integer_data(*integer_data_cast()); + new(reinterpret_cast(&data_))string_data(std::move(temp)); + } + break; + case json_type_tag::byte_string_t: + { + byte_string_data temp(std::move(*other.byte_string_data_cast())); + new(reinterpret_cast(&(other.data_)))integer_data(*integer_data_cast()); + new(reinterpret_cast(&data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::array_t: + { + array_data temp(std::move(*other.array_data_cast())); + new(reinterpret_cast(&(other.data_)))integer_data(*integer_data_cast()); + new(reinterpret_cast(&data_))array_data(std::move(temp)); + } + break; + case json_type_tag::object_t: + { + object_data temp(std::move(*other.object_data_cast())); + new(reinterpret_cast(&(other.data_)))integer_data(*integer_data_cast()); + new(reinterpret_cast(&data_))object_data(std::move(temp)); + } + break; + default: + std::swap(data_,other.data_); + break; + } + } + break; + case json_type_tag::uinteger_t: + { + switch (other.type_id()) + { + case json_type_tag::string_t: + { + string_data temp(std::move(*other.string_data_cast())); + new(reinterpret_cast(&(other.data_)))uinteger_data(*uinteger_data_cast()); + new(reinterpret_cast(&data_))string_data(std::move(temp)); + } + break; + case json_type_tag::byte_string_t: + { + byte_string_data temp(std::move(*other.byte_string_data_cast())); + new(reinterpret_cast(&(other.data_)))uinteger_data(*uinteger_data_cast()); + new(reinterpret_cast(&data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::array_t: + { + array_data temp(std::move(*other.array_data_cast())); + new(reinterpret_cast(&(other.data_)))uinteger_data(*uinteger_data_cast()); + new(reinterpret_cast(&data_))array_data(std::move(temp)); + } + break; + case json_type_tag::object_t: + { + object_data temp(std::move(*other.object_data_cast())); + new(reinterpret_cast(&(other.data_)))uinteger_data(*uinteger_data_cast()); + new(reinterpret_cast(&data_))object_data(std::move(temp)); + } + break; + default: + std::swap(data_,other.data_); + break; + } + } + break; + case json_type_tag::double_t: + { + switch (other.type_id()) + { + case json_type_tag::string_t: + { + string_data temp(std::move(*other.string_data_cast())); + new(reinterpret_cast(&(other.data_)))double_data(*double_data_cast()); + new(reinterpret_cast(&data_))string_data(std::move(temp)); + } + break; + case json_type_tag::byte_string_t: + { + byte_string_data temp(std::move(*other.byte_string_data_cast())); + new(reinterpret_cast(&(other.data_)))double_data(*double_data_cast()); + new(reinterpret_cast(&data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::array_t: + { + array_data temp(std::move(*other.array_data_cast())); + new(reinterpret_cast(&(other.data_)))double_data(*double_data_cast()); + new(reinterpret_cast(&data_))array_data(std::move(temp)); + } + break; + case json_type_tag::object_t: + { + object_data temp(std::move(*other.object_data_cast())); + new(reinterpret_cast(&(other.data_)))double_data(*double_data_cast()); + new(reinterpret_cast(&data_))object_data(std::move(temp)); + } + break; + default: + std::swap(data_,other.data_); + break; + } + } + break; + case json_type_tag::small_string_t: + { + switch (other.type_id()) + { + case json_type_tag::string_t: + { + string_data temp(std::move(*other.string_data_cast())); + new(reinterpret_cast(&(other.data_)))small_string_data(*small_string_data_cast()); + new(reinterpret_cast(&data_))string_data(std::move(temp)); + } + break; + case json_type_tag::byte_string_t: + { + byte_string_data temp(std::move(*other.byte_string_data_cast())); + new(reinterpret_cast(&(other.data_)))small_string_data(*small_string_data_cast()); + new(reinterpret_cast(&data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::array_t: + { + array_data temp(std::move(*other.array_data_cast())); + new(reinterpret_cast(&(other.data_)))small_string_data(*small_string_data_cast()); + new(reinterpret_cast(&data_))array_data(std::move(temp)); + } + break; + case json_type_tag::object_t: + { + object_data temp(std::move(*other.object_data_cast())); + new(reinterpret_cast(&(other.data_)))small_string_data(*small_string_data_cast()); + new(reinterpret_cast(&data_))object_data(std::move(temp)); + } + break; + default: + std::swap(data_,other.data_); + break; + } + } + break; + case json_type_tag::string_t: + { + switch (other.type_id()) + { + case json_type_tag::null_t: + { + string_data temp(std::move(*string_data_cast())); + new(reinterpret_cast(&data_))null_data(); + new(reinterpret_cast(&other.data_))string_data(std::move(temp)); + } + break; + case json_type_tag::empty_object_t: + { + string_data temp(std::move(*string_data_cast())); + new(reinterpret_cast(&data_))empty_object_data(); + new(reinterpret_cast(&other.data_))string_data(std::move(temp)); + } + break; + case json_type_tag::bool_t: + { + string_data temp(std::move(*string_data_cast())); + new(reinterpret_cast(&data_))bool_data(*(other.bool_data_cast())); + new(reinterpret_cast(&other.data_))string_data(std::move(temp)); + } + break; + case json_type_tag::integer_t: + { + string_data temp(std::move(*string_data_cast())); + new(reinterpret_cast(&data_))integer_data(*(other.integer_data_cast())); + new(reinterpret_cast(&other.data_))string_data(std::move(temp)); + } + break; + case json_type_tag::uinteger_t: + { + string_data temp(std::move(*string_data_cast())); + new(reinterpret_cast(&data_))uinteger_data(*(other.uinteger_data_cast())); + new(reinterpret_cast(&other.data_))string_data(std::move(temp)); + } + break; + case json_type_tag::double_t: + { + string_data temp(std::move(*string_data_cast())); + new(reinterpret_cast(&data_))double_data(*(other.double_data_cast())); + new(reinterpret_cast(&other.data_))string_data(std::move(temp)); + } + break; + case json_type_tag::small_string_t: + { + string_data temp(std::move(*string_data_cast())); + new(reinterpret_cast(&data_))small_string_data(*(other.small_string_data_cast())); + new(reinterpret_cast(&other.data_))string_data(std::move(temp)); + } + break; + case json_type_tag::string_t: + { + string_data_cast()->swap(*other.string_data_cast()); + } + break; + case json_type_tag::byte_string_t: + { + string_data temp(std::move(*string_data_cast())); + new(reinterpret_cast(&data_))byte_string_data(std::move(*other.byte_string_data_cast())); + new(reinterpret_cast(&other.data_))string_data(std::move(temp)); + } + break; + case json_type_tag::array_t: + { + string_data temp(std::move(*string_data_cast())); + new(reinterpret_cast(&data_))array_data(std::move(*other.array_data_cast())); + new(reinterpret_cast(&other.data_))string_data(std::move(temp)); + } + break; + case json_type_tag::object_t: + { + string_data temp(std::move(*string_data_cast())); + new(reinterpret_cast(&data_))object_data(std::move(*other.object_data_cast())); + new(reinterpret_cast(&other.data_))string_data(std::move(temp)); + } + break; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + break; + case json_type_tag::byte_string_t: + { + switch (other.type_id()) + { + case json_type_tag::null_t: + { + byte_string_data temp(std::move(*byte_string_data_cast())); + new(reinterpret_cast(&data_))null_data(); + new(reinterpret_cast(&other.data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::empty_object_t: + { + byte_string_data temp(std::move(*byte_string_data_cast())); + new(reinterpret_cast(&data_))empty_object_data(); + new(reinterpret_cast(&other.data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::bool_t: + { + byte_string_data temp(std::move(*byte_string_data_cast())); + new(reinterpret_cast(&data_))bool_data(*(other.bool_data_cast())); + new(reinterpret_cast(&other.data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::integer_t: + { + byte_string_data temp(std::move(*byte_string_data_cast())); + new(reinterpret_cast(&data_))integer_data(*(other.integer_data_cast())); + new(reinterpret_cast(&other.data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::uinteger_t: + { + byte_string_data temp(std::move(*byte_string_data_cast())); + new(reinterpret_cast(&data_))uinteger_data(*(other.uinteger_data_cast())); + new(reinterpret_cast(&other.data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::double_t: + { + byte_string_data temp(std::move(*byte_string_data_cast())); + new(reinterpret_cast(&data_))double_data(*(other.double_data_cast())); + new(reinterpret_cast(&other.data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::small_string_t: + { + byte_string_data temp(std::move(*byte_string_data_cast())); + new(reinterpret_cast(&data_))small_string_data(*(other.small_string_data_cast())); + new(reinterpret_cast(&other.data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::string_t: + { + byte_string_data temp(std::move(*byte_string_data_cast())); + new(reinterpret_cast(&data_))string_data(*(other.string_data_cast())); + new(reinterpret_cast(&other.data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::byte_string_t: + { + byte_string_data_cast()->swap(*other.byte_string_data_cast()); + } + break; + case json_type_tag::array_t: + { + byte_string_data temp(std::move(*byte_string_data_cast())); + new(reinterpret_cast(&data_))array_data(std::move(*other.array_data_cast())); + new(reinterpret_cast(&other.data_))byte_string_data(std::move(temp)); + } + break; + case json_type_tag::object_t: + { + byte_string_data temp(std::move(*byte_string_data_cast())); + new(reinterpret_cast(&data_))object_data(std::move(*other.object_data_cast())); + new(reinterpret_cast(&other.data_))byte_string_data(std::move(temp)); + } + break; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + break; + case json_type_tag::array_t: + { + switch (other.type_id()) + { + case json_type_tag::null_t: + { + array_data temp(std::move(*array_data_cast())); + new(reinterpret_cast(&data_))null_data(); + new(reinterpret_cast(&(other.data_)))array_data(std::move(temp)); + } + break; + case json_type_tag::empty_object_t: + { + array_data temp(std::move(*array_data_cast())); + new(reinterpret_cast(&data_))empty_object_data(); + new(reinterpret_cast(&(other.data_)))array_data(std::move(temp)); + } + break; + case json_type_tag::bool_t: + { + array_data temp(std::move(*array_data_cast())); + new(reinterpret_cast(&data_))bool_data(*(other.bool_data_cast())); + new(reinterpret_cast(&(other.data_)))array_data(std::move(temp)); + } + break; + case json_type_tag::integer_t: + { + array_data temp(std::move(*array_data_cast())); + new(reinterpret_cast(&data_))integer_data(*(other.integer_data_cast())); + new(reinterpret_cast(&(other.data_)))array_data(std::move(temp)); + } + break; + case json_type_tag::uinteger_t: + { + array_data temp(std::move(*array_data_cast())); + new(reinterpret_cast(&data_))uinteger_data(*(other.uinteger_data_cast())); + new(reinterpret_cast(&(other.data_)))array_data(std::move(temp)); + } + break; + case json_type_tag::double_t: + { + array_data temp(std::move(*array_data_cast())); + new(reinterpret_cast(&data_))double_data(*(other.double_data_cast())); + new(reinterpret_cast(&(other.data_)))array_data(std::move(temp)); + } + break; + case json_type_tag::small_string_t: + { + array_data temp(std::move(*array_data_cast())); + new(reinterpret_cast(&data_))small_string_data(*(other.small_string_data_cast())); + new(reinterpret_cast(&(other.data_)))array_data(std::move(temp)); + } + break; + case json_type_tag::string_t: + { + array_data temp(std::move(*array_data_cast())); + new(reinterpret_cast(&data_))string_data(std::move(*other.string_data_cast())); + new(reinterpret_cast(&(other.data_)))array_data(std::move(temp)); + } + break; + case json_type_tag::byte_string_t: + { + array_data temp(std::move(*array_data_cast())); + new(reinterpret_cast(&data_))byte_string_data(std::move(*other.byte_string_data_cast())); + new(reinterpret_cast(&(other.data_)))array_data(std::move(temp)); + } + break; + case json_type_tag::array_t: + { + array_data_cast()->swap(*other.array_data_cast()); + } + break; + case json_type_tag::object_t: + { + array_data temp(std::move(*array_data_cast())); + new(reinterpret_cast(&data_))object_data(std::move(*other.object_data_cast())); + new(reinterpret_cast(&(other.data_)))array_data(std::move(temp)); + } + break; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + break; + case json_type_tag::object_t: + { + switch (other.type_id()) + { + case json_type_tag::null_t: + { + object_data temp(std::move(*object_data_cast())); + new(reinterpret_cast(&data_))null_data(); + new(reinterpret_cast(&(other.data_)))object_data(std::move(temp)); + } + break; + case json_type_tag::empty_object_t: + { + object_data temp(std::move(*object_data_cast())); + new(reinterpret_cast(&data_))empty_object_data(); + new(reinterpret_cast(&(other.data_)))object_data(std::move(temp)); + } + break; + case json_type_tag::bool_t: + { + object_data temp(std::move(*object_data_cast())); + new(reinterpret_cast(&data_))bool_data(*(other.bool_data_cast())); + new(reinterpret_cast(&(other.data_)))object_data(std::move(temp)); + } + break; + case json_type_tag::integer_t: + { + object_data temp(std::move(*object_data_cast())); + new(reinterpret_cast(&data_))integer_data(*(other.integer_data_cast())); + new(reinterpret_cast(&(other.data_)))object_data(std::move(temp)); + } + break; + case json_type_tag::uinteger_t: + { + object_data temp(std::move(*object_data_cast())); + new(reinterpret_cast(&data_))uinteger_data(*(other.uinteger_data_cast())); + new(reinterpret_cast(&(other.data_)))object_data(std::move(temp)); + } + break; + case json_type_tag::double_t: + { + object_data temp(std::move(*object_data_cast())); + new(reinterpret_cast(&data_))double_data(*(other.double_data_cast())); + new(reinterpret_cast(&(other.data_)))object_data(std::move(temp)); + } + break; + case json_type_tag::small_string_t: + { + object_data temp(std::move(*object_data_cast())); + new(reinterpret_cast(&data_))small_string_data(*(other.small_string_data_cast())); + new(reinterpret_cast(&(other.data_)))object_data(std::move(temp)); + } + break; + case json_type_tag::string_t: + { + object_data temp(std::move(*object_data_cast())); + new(reinterpret_cast(&data_))string_data(std::move(*other.string_data_cast())); + new(reinterpret_cast(&(other.data_)))object_data(std::move(temp)); + } + break; + case json_type_tag::byte_string_t: + { + object_data temp(std::move(*object_data_cast())); + new(reinterpret_cast(&data_))byte_string_data(std::move(*other.byte_string_data_cast())); + new(reinterpret_cast(&(other.data_)))object_data(std::move(temp)); + } + break; + case json_type_tag::array_t: + { + object_data temp(std::move(*object_data_cast())); + new(reinterpret_cast(&data_))array_data(std::move(*other.array_data_cast())); + new(reinterpret_cast(&(other.data_)))object_data(std::move(temp)); + } + break; + case json_type_tag::object_t: + { + object_data_cast()->swap(*other.object_data_cast()); + } + break; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + break; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + private: + + void Init_(const variant& val) + { + switch (val.type_id()) + { + case json_type_tag::null_t: + new(reinterpret_cast(&data_))null_data(); + break; + case json_type_tag::empty_object_t: + new(reinterpret_cast(&data_))empty_object_data(); + break; + case json_type_tag::bool_t: + new(reinterpret_cast(&data_))bool_data(*(val.bool_data_cast())); + break; + case json_type_tag::integer_t: + new(reinterpret_cast(&data_))integer_data(*(val.integer_data_cast())); + break; + case json_type_tag::uinteger_t: + new(reinterpret_cast(&data_))uinteger_data(*(val.uinteger_data_cast())); + break; + case json_type_tag::double_t: + new(reinterpret_cast(&data_))double_data(*(val.double_data_cast())); + break; + case json_type_tag::small_string_t: + new(reinterpret_cast(&data_))small_string_data(*(val.small_string_data_cast())); + break; + case json_type_tag::string_t: + new(reinterpret_cast(&data_))string_data(*(val.string_data_cast())); + break; + case json_type_tag::byte_string_t: + new(reinterpret_cast(&data_))byte_string_data(*(val.byte_string_data_cast())); + break; + case json_type_tag::object_t: + new(reinterpret_cast(&data_))object_data(*(val.object_data_cast())); + break; + case json_type_tag::array_t: + new(reinterpret_cast(&data_))array_data(*(val.array_data_cast())); + break; + default: + break; + } + } + + void Init_(const variant& val, const Allocator& a) + { + switch (val.type_id()) + { + case json_type_tag::null_t: + case json_type_tag::empty_object_t: + case json_type_tag::bool_t: + case json_type_tag::integer_t: + case json_type_tag::uinteger_t: + case json_type_tag::double_t: + case json_type_tag::small_string_t: + Init_(val); + break; + case json_type_tag::string_t: + new(reinterpret_cast(&data_))string_data(*(val.string_data_cast()),a); + break; + case json_type_tag::byte_string_t: + new(reinterpret_cast(&data_))byte_string_data(*(val.byte_string_data_cast()),a); + break; + case json_type_tag::array_t: + new(reinterpret_cast(&data_))array_data(*(val.array_data_cast()),a); + break; + case json_type_tag::object_t: + new(reinterpret_cast(&data_))object_data(*(val.object_data_cast()),a); + break; + default: + break; + } + } + + void Init_rv_(variant&& val) JSONCONS_NOEXCEPT + { + switch (val.type_id()) + { + case json_type_tag::null_t: + case json_type_tag::empty_object_t: + case json_type_tag::double_t: + case json_type_tag::integer_t: + case json_type_tag::uinteger_t: + case json_type_tag::bool_t: + case json_type_tag::small_string_t: + Init_(val); + break; + case json_type_tag::string_t: + { + new(reinterpret_cast(&data_))string_data(std::move(*val.string_data_cast())); + new(reinterpret_cast(&val.data_))null_data(); + } + break; + case json_type_tag::byte_string_t: + { + new(reinterpret_cast(&data_))byte_string_data(std::move(*val.byte_string_data_cast())); + new(reinterpret_cast(&val.data_))null_data(); + } + break; + case json_type_tag::array_t: + { + new(reinterpret_cast(&data_))array_data(std::move(*val.array_data_cast())); + new(reinterpret_cast(&val.data_))null_data(); + } + break; + case json_type_tag::object_t: + { + new(reinterpret_cast(&data_))object_data(std::move(*val.object_data_cast())); + new(reinterpret_cast(&val.data_))null_data(); + } + break; + default: + JSONCONS_UNREACHABLE(); + break; + } + } + + void Init_rv_(variant&& val, const Allocator& a, std::true_type) JSONCONS_NOEXCEPT + { + Init_rv_(std::forward(val)); + } + + void Init_rv_(variant&& val, const Allocator& a, std::false_type) JSONCONS_NOEXCEPT + { + switch (val.type_id()) + { + case json_type_tag::null_t: + case json_type_tag::empty_object_t: + case json_type_tag::double_t: + case json_type_tag::integer_t: + case json_type_tag::uinteger_t: + case json_type_tag::bool_t: + case json_type_tag::small_string_t: + Init_(std::forward(val)); + break; + case json_type_tag::string_t: + { + if (a == val.string_data_cast()->get_allocator()) + { + Init_rv_(std::forward(val), a, std::true_type()); + } + else + { + Init_(val,a); + } + } + break; + case json_type_tag::byte_string_t: + { + if (a == val.byte_string_data_cast()->get_allocator()) + { + Init_rv_(std::forward(val), a, std::true_type()); + } + else + { + Init_(val,a); + } + } + break; + case json_type_tag::object_t: + { + if (a == val.object_data_cast()->get_allocator()) + { + Init_rv_(std::forward(val), a, std::true_type()); + } + else + { + Init_(val,a); + } + } + break; + case json_type_tag::array_t: + { + if (a == val.array_data_cast()->get_allocator()) + { + Init_rv_(std::forward(val), a, std::true_type()); + } + else + { + Init_(val,a); + } + } + break; + default: + break; + } + } + }; + + template + class json_proxy + { + private: + typedef json_proxy proxy_type; + + ParentT& parent_; + key_storage_type key_; + + json_proxy() = delete; + json_proxy& operator = (const json_proxy& other) = delete; + + json_proxy(ParentT& parent, key_storage_type&& name) + : parent_(parent), key_(std::forward(name)) + { + } + + basic_json& evaluate() + { + return parent_.evaluate(string_view_type(key_.data(),key_.size())); + } + + const basic_json& evaluate() const + { + return parent_.evaluate(string_view_type(key_.data(),key_.size())); + } + + basic_json& evaluate_with_default() + { + basic_json& val = parent_.evaluate_with_default(); + auto it = val.find(string_view_type(key_.data(),key_.size())); + if (it == val.object_range().end()) + { + it = val.set_(val.object_range().begin(),std::move(key_),object(val.object_value().get_allocator())); + } + return it->value(); + } + + basic_json& evaluate(size_t index) + { + return evaluate().at(index); + } + + const basic_json& evaluate(size_t index) const + { + return evaluate().at(index); + } + + basic_json& evaluate(const string_view_type& index) + { + return evaluate().at(index); + } + + const basic_json& evaluate(const string_view_type& index) const + { + return evaluate().at(index); + } + public: + + friend class basic_json; + + range object_range() + { + return evaluate().object_range(); + } + + range object_range() const + { + return evaluate().object_range(); + } + + range array_range() + { + return evaluate().array_range(); + } + + range array_range() const + { + return evaluate().array_range(); + } + + size_t size() const JSONCONS_NOEXCEPT + { + return evaluate().size(); + } + + json_type_tag type_id() const + { + return evaluate().type_id(); + } + + size_t count(const string_view_type& name) const + { + return evaluate().count(name); + } + + bool has_key(const string_view_type& name) const + { + return evaluate().has_key(name); + } + + bool is_null() const JSONCONS_NOEXCEPT + { + return evaluate().is_null(); + } + + bool empty() const + { + return evaluate().empty(); + } + + size_t capacity() const + { + return evaluate().capacity(); + } + + void reserve(size_t n) + { + evaluate().reserve(n); + } + + void resize(size_t n) + { + evaluate().resize(n); + } + + template + void resize(size_t n, T val) + { + evaluate().resize(n,val); + } + + template + bool is(Args&&... args) const + { + return evaluate().template is(std::forward(args)...); + } + + bool is_string() const JSONCONS_NOEXCEPT + { + return evaluate().is_string(); + } + + bool is_byte_string() const JSONCONS_NOEXCEPT + { + return evaluate().is_byte_string(); + } + + bool is_number() const JSONCONS_NOEXCEPT + { + return evaluate().is_number(); + } + bool is_bool() const JSONCONS_NOEXCEPT + { + return evaluate().is_bool(); + } + + bool is_object() const JSONCONS_NOEXCEPT + { + return evaluate().is_object(); + } + + bool is_array() const JSONCONS_NOEXCEPT + { + return evaluate().is_array(); + } + bool is_integer() const JSONCONS_NOEXCEPT + { + return evaluate().is_integer(); + } + + bool is_uinteger() const JSONCONS_NOEXCEPT + { + return evaluate().is_uinteger(); + } + + bool is_double() const JSONCONS_NOEXCEPT + { + return evaluate().is_double(); + } + + string_view_type as_string_view() const + { + return evaluate().as_string_view(); + } + + byte_string_view as_byte_string_view() const + { + return evaluate().as_byte_string_view(); + } + + string_type as_string() const + { + return evaluate().as_string(); + } + + template + string_type as_string(const SAllocator& allocator) const + { + return evaluate().as_string(allocator); + } + + string_type as_string(const basic_json_serializing_options& options) const + { + return evaluate().as_string(options); + } + + template + string_type as_string(const basic_json_serializing_options& options, + const SAllocator& allocator) const + { + return evaluate().as_string(options,allocator); + } + + template + T as(Args&&... args) const + { + return evaluate().template as(std::forward(args)...); + } + + template + typename std::enable_if::value,T>::type + as(const char_allocator_type& allocator) const + { + return evaluate().template as(allocator); + } + bool as_bool() const + { + return evaluate().as_bool(); + } + + double as_double() const + { + return evaluate().as_double(); + } + + int64_t as_integer() const + { + return evaluate().as_integer(); + } + + unsigned long long as_ulonglong() const + { + return evaluate().as_ulonglong(); + } + + uint64_t as_uinteger() const + { + return evaluate().as_uinteger(); + } + + template + json_proxy& operator=(T&& val) + { + parent_.evaluate_with_default().set_(std::move(key_), std::forward(val)); + return *this; + } + + bool operator==(const basic_json& val) const + { + return evaluate() == val; + } + + bool operator!=(const basic_json& val) const + { + return evaluate() != val; + } + + basic_json& operator[](size_t i) + { + return evaluate_with_default().at(i); + } + + const basic_json& operator[](size_t i) const + { + return evaluate().at(i); + } + + json_proxy operator[](const string_view_type& name) + { + return json_proxy(*this,key_storage_type(name.begin(),name.end(),key_.get_allocator())); + } + + const basic_json& operator[](const string_view_type& name) const + { + return at(name); + } + + basic_json& at(const string_view_type& name) + { + return evaluate().at(name); + } + + const basic_json& at(const string_view_type& name) const + { + return evaluate().at(name); + } + + const basic_json& at(size_t index) + { + return evaluate().at(index); + } + + const basic_json& at(size_t index) const + { + return evaluate().at(index); + } + + object_iterator find(const string_view_type& name) + { + return evaluate().find(name); + } + + const_object_iterator find(const string_view_type& name) const + { + return evaluate().find(name); + } + + template + basic_json get(const string_view_type& name, T&& default_val) const + { + return evaluate().get(name,std::forward(default_val)); + } + + template + T get_with_default(const string_view_type& name, const T& default_val) const + { + return evaluate().get_with_default(name,default_val); + } + + const CharT* get_with_default(const string_view_type& name, const CharT* default_val) const + { + return evaluate().get_with_default(name,default_val); + } + + void shrink_to_fit() + { + evaluate_with_default().shrink_to_fit(); + } + + void clear() + { + evaluate().clear(); + } + // Remove all elements from an array or object + + void erase(const_object_iterator pos) + { + evaluate().erase(pos); + } + // Remove a range of elements from an object + + void erase(const_object_iterator first, const_object_iterator last) + { + evaluate().erase(first, last); + } + // Remove a range of elements from an object + + void erase(const string_view_type& name) + { + evaluate().erase(name); + } + + void erase(const_array_iterator pos) + { + evaluate().erase(pos); + } + // Removes the element at pos + + void erase(const_array_iterator first, const_array_iterator last) + { + evaluate().erase(first, last); + } + // Remove a range of elements from an array + + // merge + + void merge(const basic_json& source) + { + return evaluate().merge(source); + } + + void merge(basic_json&& source) + { + return evaluate().merge(std::forward(source)); + } + + void merge(object_iterator hint, const basic_json& source) + { + return evaluate().merge(hint, source); + } + + void merge(object_iterator hint, basic_json&& source) + { + return evaluate().merge(hint, std::forward(source)); + } + + // merge_or_update + + void merge_or_update(const basic_json& source) + { + return evaluate().merge_or_update(source); + } + + void merge_or_update(basic_json&& source) + { + return evaluate().merge_or_update(std::forward(source)); + } + + void merge_or_update(object_iterator hint, const basic_json& source) + { + return evaluate().merge_or_update(hint, source); + } + + void merge_or_update(object_iterator hint, basic_json&& source) + { + return evaluate().merge_or_update(hint, std::forward(source)); + } + + // set + + template + std::pair set(const string_view_type& name, T&& val) + { + return evaluate().set(name,std::forward(val)); + } + + template + std::pair insert_or_assign(const string_view_type& name, T&& val) + { + return evaluate().insert_or_assign(name,std::forward(val)); + } + + template + void set_(key_storage_type&& name, T&& val) + { + evaluate().set_(std::forward(name),std::forward(val)); + } + + // emplace + + template + std::pair try_emplace(const string_view_type& name, Args&&... args) + { + return evaluate().try_emplace(name,std::forward(args)...); + } + + template + object_iterator set(object_iterator hint, const string_view_type& name, T&& val) + { + return evaluate().set(hint, name, std::forward(val)); + } + + template + object_iterator insert_or_assign(object_iterator hint, const string_view_type& name, T&& val) + { + return evaluate().insert_or_assign(hint, name, std::forward(val)); + } + + template + object_iterator try_emplace(object_iterator hint, const string_view_type& name, Args&&... args) + { + return evaluate().try_emplace(hint, name, std::forward(args)...); + } + + template + object_iterator set_(object_iterator hint, key_storage_type&& name, T&& val) + { + return evaluate().set_(hint, std::forward(name), std::forward(val)); + } + + template + array_iterator emplace(const_array_iterator pos, Args&&... args) + { + evaluate_with_default().emplace(pos, std::forward(args)...); + } + + template + basic_json& emplace_back(Args&&... args) + { + return evaluate_with_default().emplace_back(std::forward(args)...); + } + + template + void add(T&& val) + { + evaluate_with_default().add(std::forward(val)); + } + + template + void push_back(T&& val) + { + evaluate_with_default().push_back(std::forward(val)); + } + + template + array_iterator add(const_array_iterator pos, T&& val) + { + return evaluate_with_default().add(pos, std::forward(val)); + } + + template + array_iterator insert(const_array_iterator pos, T&& val) + { + return evaluate_with_default().insert(pos, std::forward(val)); + } + + template + array_iterator insert(const_array_iterator pos, InputIt first, InputIt last) + { + return evaluate_with_default().insert(pos, first, last); + } + + template + void dump(std::basic_string& s) const + { + evaluate().dump(s); + } + + template + void dump(std::basic_string& s, + indenting line_indent) const + { + evaluate().dump(s, line_indent); + } + + template + void dump(std::basic_string& s, + const basic_json_serializing_options& options) const + { + evaluate().dump(s,options); + } + + template + void dump(std::basic_string& s, + const basic_json_serializing_options& options, + indenting line_indent) const + { + evaluate().dump(s,options,line_indent); + } + + void dump(basic_json_content_handler& handler) const + { + evaluate().dump(handler); + } + + void dump(std::basic_ostream& os) const + { + evaluate().dump(os); + } + + void dump(std::basic_ostream& os, indenting line_indent) const + { + evaluate().dump(os, line_indent); + } + + void dump(std::basic_ostream& os, const basic_json_serializing_options& options) const + { + evaluate().dump(os,options); + } + + void dump(std::basic_ostream& os, const basic_json_serializing_options& options, indenting line_indent) const + { + evaluate().dump(os,options,line_indent); + } +#if !defined(JSONCONS_NO_DEPRECATED) + + void dump(std::basic_ostream& os, const basic_json_serializing_options& options, bool pprint) const + { + evaluate().dump(os,options,pprint); + } + + void dump(std::basic_ostream& os, bool pprint) const + { + evaluate().dump(os, pprint); + } + + string_type to_string(const char_allocator_type& allocator = char_allocator_type()) const JSONCONS_NOEXCEPT + { + return evaluate().to_string(allocator); + } + void write(basic_json_content_handler& handler) const + { + evaluate().write(handler); + } + + void write(std::basic_ostream& os) const + { + evaluate().write(os); + } + + void write(std::basic_ostream& os, const basic_json_serializing_options& options) const + { + evaluate().write(os,options); + } + + void write(std::basic_ostream& os, const basic_json_serializing_options& options, bool pprint) const + { + evaluate().write(os,options,pprint); + } + + string_type to_string(const basic_json_serializing_options& options, char_allocator_type& allocator = char_allocator_type()) const + { + return evaluate().to_string(options,allocator); + } + + range members() + { + return evaluate().members(); + } + + range members() const + { + return evaluate().members(); + } + + range elements() + { + return evaluate().elements(); + } + + range elements() const + { + return evaluate().elements(); + } + void to_stream(basic_json_content_handler& handler) const + { + evaluate().to_stream(handler); + } + + void to_stream(std::basic_ostream& os) const + { + evaluate().to_stream(os); + } + + void to_stream(std::basic_ostream& os, const basic_json_serializing_options& options) const + { + evaluate().to_stream(os,options); + } + + void to_stream(std::basic_ostream& os, const basic_json_serializing_options& options, bool pprint) const + { + evaluate().to_stream(os,options,pprint); + } +#endif + void swap(basic_json& val) + { + evaluate_with_default().swap(val); + } + + friend std::basic_ostream& operator<<(std::basic_ostream& os, const json_proxy& o) + { + o.dump(os); + return os; + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + void resize_array(size_t n) + { + evaluate().resize_array(n); + } + + template + void resize_array(size_t n, T val) + { + evaluate().resize_array(n,val); + } + + object_iterator begin_members() + { + return evaluate().begin_members(); + } + + const_object_iterator begin_members() const + { + return evaluate().begin_members(); + } + + object_iterator end_members() + { + return evaluate().end_members(); + } + + const_object_iterator end_members() const + { + return evaluate().end_members(); + } + + array_iterator begin_elements() + { + return evaluate().begin_elements(); + } + + const_array_iterator begin_elements() const + { + return evaluate().begin_elements(); + } + + array_iterator end_elements() + { + return evaluate().end_elements(); + } + + const_array_iterator end_elements() const + { + return evaluate().end_elements(); + } + + const basic_json& get(const string_view_type& name) const + { + return evaluate().get(name); + } + + bool is_ulonglong() const JSONCONS_NOEXCEPT + { + return evaluate().is_ulonglong(); + } + + bool is_longlong() const JSONCONS_NOEXCEPT + { + return evaluate().is_longlong(); + } + + int as_int() const + { + return evaluate().as_int(); + } + + unsigned int as_uint() const + { + return evaluate().as_uint(); + } + + long as_long() const + { + return evaluate().as_long(); + } + + unsigned long as_ulong() const + { + return evaluate().as_ulong(); + } + + long long as_longlong() const + { + return evaluate().as_longlong(); + } + + void add(size_t index, const basic_json& value) + { + evaluate_with_default().add(index, value); + } + + void add(size_t index, basic_json&& value) + { + evaluate_with_default().add(index, std::forward(value)); + } + + bool has_member(const key_storage_type& name) const + { + return evaluate().has_member(name); + } + + // Remove a range of elements from an array + void remove_range(size_t from_index, size_t to_index) + { + evaluate().remove_range(from_index, to_index); + } + // Remove a range of elements from an array + void remove(const string_view_type& name) + { + evaluate().remove(name); + } + void remove_member(const string_view_type& name) + { + evaluate().remove(name); + } + bool is_empty() const JSONCONS_NOEXCEPT + { + return empty(); + } + bool is_numeric() const JSONCONS_NOEXCEPT + { + return is_number(); + } +#endif + }; + + static basic_json parse(std::basic_istream& is) + { + parse_error_handler_type err_handler; + return parse(is,err_handler); + } + + static basic_json parse(std::basic_istream& is, parse_error_handler& err_handler) + { + json_decoder> handler; + basic_json_reader reader(is, handler, err_handler); + reader.read_next(); + reader.check_done(); + if (!handler.is_valid()) + { + JSONCONS_THROW(json_exception_impl("Failed to parse json stream")); + } + return handler.get_result(); + } + + static basic_json parse(const string_view_type& s) + { + parse_error_handler_type err_handler; + return parse(s,err_handler); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + static basic_json parse(const char_type* s, size_t length) + { + parse_error_handler_type err_handler; + return parse(s,length,err_handler); + } + + static basic_json parse(const char_type* s, size_t length, parse_error_handler& err_handler) + { + return parse(string_view_type(s,length),err_handler); + } +#endif + + static basic_json parse(const string_view_type& s, parse_error_handler& err_handler) + { + json_decoder decoder; + basic_json_parser parser(decoder,err_handler); + + auto result = unicons::skip_bom(s.begin(), s.end()); + if (result.ec != unicons::encoding_errc()) + { + throw parse_error(result.ec,1,1); + } + size_t offset = result.it - s.begin(); + parser.set_source(s.data()+offset,s.size()-offset); + parser.parse_some(); + parser.end_parse(); + parser.check_done(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(json_exception_impl("Failed to parse json string")); + } + return decoder.get_result(); + } + + static basic_json parse(std::basic_istream& is, const basic_json_serializing_options& options) + { + parse_error_handler_type err_handler; + return parse(is,options,err_handler); + } + + static basic_json parse(std::basic_istream& is, const basic_json_serializing_options& options, parse_error_handler& err_handler) + { + json_decoder> handler; + basic_json_reader reader(is, handler, options, err_handler); + reader.read_next(); + reader.check_done(); + if (!handler.is_valid()) + { + JSONCONS_THROW(json_exception_impl("Failed to parse json stream")); + } + return handler.get_result(); + } + + static basic_json parse(const string_view_type& s, const basic_json_serializing_options& options) + { + parse_error_handler_type err_handler; + return parse(s,options,err_handler); + } + + static basic_json parse(const string_view_type& s, const basic_json_serializing_options& options, parse_error_handler& err_handler) + { + json_decoder decoder; + basic_json_parser parser(decoder,options,err_handler); + + auto result = unicons::skip_bom(s.begin(), s.end()); + if (result.ec != unicons::encoding_errc()) + { + throw parse_error(result.ec,1,1); + } + size_t offset = result.it - s.begin(); + parser.set_source(s.data()+offset,s.size()-offset); + parser.parse_some(); + parser.end_parse(); + parser.check_done(); + if (!decoder.is_valid()) + { + JSONCONS_THROW(json_exception_impl("Failed to parse json string")); + } + return decoder.get_result(); + } + + static basic_json make_array() + { + return basic_json(variant(array())); + } + + static basic_json make_array(const array& a) + { + return basic_json(variant(a)); + } + + static basic_json make_array(const array& a, allocator_type allocator) + { + return basic_json(variant(a,allocator)); + } + + static basic_json make_array(std::initializer_list init, const Allocator& allocator = Allocator()) + { + return array(std::move(init),allocator); + } + + static basic_json make_array(size_t n, const Allocator& allocator = Allocator()) + { + return array(n,allocator); + } + + template + static basic_json make_array(size_t n, const T& val, const Allocator& allocator = Allocator()) + { + return basic_json::array(n, val,allocator); + } + + template + static typename std::enable_if::type make_array(size_t n) + { + return array(n); + } + + template + static typename std::enable_if::type make_array(size_t n, const T& val, const Allocator& allocator = Allocator()) + { + return array(n,val,allocator); + } + + template + static typename std::enable_if<(dim>1),basic_json>::type make_array(size_t n, Args... args) + { + const size_t dim1 = dim - 1; + + basic_json val = make_array(args...); + val.resize(n); + for (size_t i = 0; i < n; ++i) + { + val[i] = make_array(args...); + } + return val; + } + + static const basic_json& null() + { + static basic_json a_null = basic_json(variant(null_type())); + return a_null; + } + + variant var_; + + basic_json() + : var_() + { + } + + explicit basic_json(const Allocator& allocator) + : var_(allocator) + { + } + + basic_json(const basic_json& val) + : var_(val.var_) + { + } + + basic_json(const basic_json& val, const Allocator& allocator) + : var_(val.var_,allocator) + { + } + + basic_json(basic_json&& other) JSONCONS_NOEXCEPT + : var_(std::move(other.var_)) + { + } + + basic_json(basic_json&& other, const Allocator& allocator) JSONCONS_NOEXCEPT + : var_(std::move(other.var_) /*,allocator*/ ) + { + } + + basic_json(const variant& val) + : var_(val) + { + } + + basic_json(variant&& other) + : var_(std::forward(other)) + { + } + + basic_json(const array& val) + : var_(val) + { + } + + basic_json(array&& other) + : var_(std::forward(other)) + { + } + + basic_json(const object& other) + : var_(other) + { + } + + basic_json(object&& other) + : var_(std::forward(other)) + { + } + + template + basic_json(const json_proxy& proxy) + : var_(proxy.evaluate().var_) + { + } + + template + basic_json(const json_proxy& proxy, const Allocator& allocator) + : var_(proxy.evaluate().var_,allocator) + { + } + + template + basic_json(const T& val) + : var_(json_type_traits::to_json(val).var_) + { + } + + template + basic_json(const T& val, const Allocator& allocator) + : var_(json_type_traits::to_json(val,allocator).var_) + { + } + + basic_json(const char_type* s) + : var_(s) + { + } + + basic_json(const char_type* s, const Allocator& allocator) + : var_(s,allocator) + { + } + + basic_json(double val, uint8_t precision) + : var_(val, number_format(precision, 0)) + { + } + + basic_json(double val, const number_format& fmt) + : var_(val, fmt) + { + } + + basic_json(const char_type *s, size_t length) + : var_(s, length) + { + } + + basic_json(const char_type *s, size_t length, const Allocator& allocator) + : var_(s, length, allocator) + { + } + + basic_json(const uint8_t* s, size_t length) + : var_(s, length) + { + } + + explicit basic_json(const byte_string_view& s) + : var_(s.data(), s.length()) + { + } + + basic_json(const uint8_t* s, size_t length, const Allocator& allocator) + : var_(s, length, allocator) + { + } +#if !defined(JSONCONS_NO_DEPRECATED) + template + basic_json(InputIterator first, InputIterator last, const Allocator& allocator = Allocator()) + : var_(first,last,allocator) + { + } +#endif + + ~basic_json() + { + } + + basic_json& operator=(const basic_json& rhs) + { + if (this != &rhs) + { + var_ = rhs.var_; + } + return *this; + } + + basic_json& operator=(basic_json&& rhs) JSONCONS_NOEXCEPT + { + if (this !=&rhs) + { + var_ = std::move(rhs.var_); + } + return *this; + } + + template + basic_json& operator=(const T& val) + { + var_ = json_type_traits::to_json(val).var_; + return *this; + } + + basic_json& operator=(const char_type* s) + { + var_ = variant(s); + return *this; + } + + bool operator!=(const basic_json& rhs) const + { + return !(*this == rhs); + } + + bool operator==(const basic_json& rhs) const + { + return var_ == rhs.var_; + } + + size_t size() const JSONCONS_NOEXCEPT + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + return 0; + case json_type_tag::object_t: + return object_value().size(); + case json_type_tag::array_t: + return array_value().size(); + default: + return 0; + } + } + + basic_json& operator[](size_t i) + { + return at(i); + } + + const basic_json& operator[](size_t i) const + { + return at(i); + } + + json_proxy operator[](const string_view_type& name) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return json_proxy(*this, key_storage_type(name.begin(),name.end(),char_allocator_type(object_value().get_allocator()))); + break; + default: + JSONCONS_THROW(not_an_object(name.data(),name.length())); + break; + } + } + + const basic_json& operator[](const string_view_type& name) const + { + return at(name); + } + + template + void dump(std::basic_string& s) const + { + basic_json_serializer> serializer(s); + dump(serializer); + } + + template + void dump(std::basic_string& s, indenting line_indent) const + { + basic_json_serializer> serializer(s, line_indent); + dump(serializer); + } + + template + void dump(std::basic_string& s, + const basic_json_serializing_options& options) const + { + basic_json_serializer> serializer(s, options); + dump(serializer); + } + + template + void dump(std::basic_string& s, + const basic_json_serializing_options& options, + indenting line_indent) const + { + basic_json_serializer> serializer(s, options, line_indent); + dump(serializer); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + void dump_body(basic_json_content_handler& handler) const + { + dump_fragment(handler); + } +#endif + void dump_fragment(basic_json_content_handler& handler) const + { + switch (var_.type_id()) + { + case json_type_tag::small_string_t: + case json_type_tag::string_t: + handler.string_value(as_string_view()); + break; + case json_type_tag::byte_string_t: + handler.byte_string_value(var_.byte_string_data_cast()->data(), var_.byte_string_data_cast()->length()); + break; + case json_type_tag::double_t: + handler.double_value(var_.double_data_cast()->value(), number_format(var_.double_data_cast()->precision(), var_.double_data_cast()->decimal_places())); + break; + case json_type_tag::integer_t: + handler.integer_value(var_.integer_data_cast()->value()); + break; + case json_type_tag::uinteger_t: + handler.uinteger_value(var_.uinteger_data_cast()->value()); + break; + case json_type_tag::bool_t: + handler.bool_value(var_.bool_data_cast()->value()); + break; + case json_type_tag::null_t: + handler.null_value(); + break; + case json_type_tag::empty_object_t: + handler.begin_object(0); + handler.end_object(); + break; + case json_type_tag::object_t: + { + handler.begin_object(size()); + const object& o = object_value(); + for (const_object_iterator it = o.begin(); it != o.end(); ++it) + { + handler.name(string_view_type((it->key()).data(),it->key().length())); + it->value().dump_fragment(handler); + } + handler.end_object(); + } + break; + case json_type_tag::array_t: + { + handler.begin_array(size()); + const array& o = array_value(); + for (const_array_iterator it = o.begin(); it != o.end(); ++it) + { + it->dump_fragment(handler); + } + handler.end_array(); + } + break; + default: + break; + } + } + void dump(basic_json_content_handler& handler) const + { + handler.begin_json(); + dump_fragment(handler); + handler.end_json(); + } + + void dump(std::basic_ostream& os) const + { + basic_json_serializer serializer(os); + dump(serializer); + } + + void dump(std::basic_ostream& os, indenting line_indent) const + { + basic_json_serializer serializer(os, line_indent); + dump(serializer); + } + + void dump(std::basic_ostream& os, const basic_json_serializing_options& options) const + { + basic_json_serializer serializer(os, options); + dump(serializer); + } + + void dump(std::basic_ostream& os, const basic_json_serializing_options& options, indenting line_indent) const + { + basic_json_serializer serializer(os, options, line_indent); + dump(serializer); + } + + string_type to_string(const char_allocator_type& allocator=char_allocator_type()) const JSONCONS_NOEXCEPT + { + string_type s(allocator); + basic_json_serializer> serializer(s); + dump_fragment(serializer); + return s; + } + + string_type to_string(const basic_json_serializing_options& options, + const char_allocator_type& allocator=char_allocator_type()) const + { + string_type s(allocator); + basic_json_serializer> serializer(s,options); + dump_fragment(serializer); + return s; + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + void dump(std::basic_ostream& os, bool pprint) const + { + basic_json_serializer serializer(os, pprint); + dump(serializer); + } + + void dump(std::basic_ostream& os, const basic_json_serializing_options& options, bool pprint) const + { + basic_json_serializer serializer(os, options, pprint); + dump(serializer); + } + + void write_body(basic_json_content_handler& handler) const + { + dump(handler); + } + void write(basic_json_content_handler& handler) const + { + dump(handler); + } + + void write(std::basic_ostream& os) const + { + dump(os); + } + + void write(std::basic_ostream& os, const basic_json_serializing_options& options) const + { + dump(os,options); + } + + void write(std::basic_ostream& os, const basic_json_serializing_options& options, bool pprint) const + { + dump(os,options,pprint); + } + + void to_stream(basic_json_content_handler& handler) const + { + handler.begin_json(); + dump_fragment(handler); + handler.end_json(); + } + + void to_stream(std::basic_ostream& os) const + { + basic_json_serializer serializer(os); + to_stream(serializer); + } + + void to_stream(std::basic_ostream& os, const basic_json_serializing_options& options) const + { + basic_json_serializer serializer(os, options); + to_stream(serializer); + } + + void to_stream(std::basic_ostream& os, const basic_json_serializing_options& options, bool pprint) const + { + basic_json_serializer serializer(os, options, pprint); + to_stream(serializer); + } +#endif + bool is_null() const JSONCONS_NOEXCEPT + { + return var_.type_id() == json_type_tag::null_t; + } + + bool has_key(const string_view_type& name) const + { + switch (var_.type_id()) + { + case json_type_tag::object_t: + { + const_object_iterator it = object_value().find(name); + return it != object_range().end(); + } + break; + default: + return false; + } + } + + size_t count(const string_view_type& name) const + { + switch (var_.type_id()) + { + case json_type_tag::object_t: + { + auto it = object_value().find(name); + if (it == object_range().end()) + { + return 0; + } + size_t count = 0; + while (it != object_range().end()&& it->key() == name) + { + ++count; + ++it; + } + return count; + } + break; + default: + return 0; + } + } + + template + bool is(Args&&... args) const + { + return json_type_traits::is(*this,std::forward(args)...); + } + + bool is_string() const JSONCONS_NOEXCEPT + { + return (var_.type_id() == json_type_tag::string_t) || (var_.type_id() == json_type_tag::small_string_t); + } + + bool is_byte_string() const JSONCONS_NOEXCEPT + { + return (var_.type_id() == json_type_tag::byte_string_t); + } + + bool is_bool() const JSONCONS_NOEXCEPT + { + return var_.type_id() == json_type_tag::bool_t; + } + + bool is_object() const JSONCONS_NOEXCEPT + { + return var_.type_id() == json_type_tag::object_t || var_.type_id() == json_type_tag::empty_object_t; + } + + bool is_array() const JSONCONS_NOEXCEPT + { + return var_.type_id() == json_type_tag::array_t; + } + + bool is_integer() const JSONCONS_NOEXCEPT + { + return var_.type_id() == json_type_tag::integer_t || (var_.type_id() == json_type_tag::uinteger_t&& (as_uinteger() <= static_cast((std::numeric_limits::max)()))); + } + + bool is_uinteger() const JSONCONS_NOEXCEPT + { + return var_.type_id() == json_type_tag::uinteger_t || (var_.type_id() == json_type_tag::integer_t&& as_integer() >= 0); + } + + bool is_double() const JSONCONS_NOEXCEPT + { + return var_.type_id() == json_type_tag::double_t; + } + + bool is_number() const JSONCONS_NOEXCEPT + { + return var_.type_id() == json_type_tag::integer_t || var_.type_id() == json_type_tag::uinteger_t || var_.type_id() == json_type_tag::double_t; + } + + bool empty() const JSONCONS_NOEXCEPT + { + switch (var_.type_id()) + { + case json_type_tag::small_string_t: + return var_.small_string_data_cast()->length() == 0; + case json_type_tag::string_t: + return var_.string_data_cast()->length() == 0; + case json_type_tag::array_t: + return array_value().size() == 0; + case json_type_tag::empty_object_t: + return true; + case json_type_tag::object_t: + return object_value().size() == 0; + default: + return false; + } + } + + size_t capacity() const + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + return array_value().capacity(); + case json_type_tag::object_t: + return object_value().capacity(); + default: + return 0; + } + } + + template + typename std::enable_if::value,void>::type + create_object_implicitly() + { + var_ = variant(Allocator()); + } + + template + typename std::enable_if::value,void>::type + create_object_implicitly() const + { + JSONCONS_THROW(json_exception_impl("Cannot create object implicitly - allocator is not default constructible.")); + } + + void reserve(size_t n) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + array_value().reserve(n); + break; + case json_type_tag::empty_object_t: + { + create_object_implicitly(); + object_value().reserve(n); + } + break; + case json_type_tag::object_t: + { + object_value().reserve(n); + } + break; + default: + break; + } + } + + void resize(size_t n) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + array_value().resize(n); + break; + default: + break; + } + } + + template + void resize(size_t n, T val) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + array_value().resize(n, val); + break; + default: + break; + } + } + + template + T as(Args&&... args) const + { + return json_type_traits::as(*this,std::forward(args)...); + } + + template + typename std::enable_if::value,T>::type + as(const char_allocator_type& allocator) const + { + return json_type_traits::as(*this,allocator); + } + + bool as_bool() const + { + switch (var_.type_id()) + { + case json_type_tag::small_string_t: + case json_type_tag::string_t: + try + { + basic_json j = basic_json::parse(as_string_view()); + return j.as_bool(); + } + catch (...) + { + JSONCONS_THROW(json_exception_impl("Not a bool")); + } + break; + case json_type_tag::bool_t: + return var_.bool_data_cast()->value(); + case json_type_tag::double_t: + return var_.double_data_cast()->value() != 0.0; + case json_type_tag::integer_t: + return var_.integer_data_cast()->value() != 0; + case json_type_tag::uinteger_t: + return var_.uinteger_data_cast()->value() != 0; + default: + JSONCONS_THROW(json_exception_impl("Not a bool")); + } + } + + int64_t as_integer() const + { + switch (var_.type_id()) + { + case json_type_tag::small_string_t: + case json_type_tag::string_t: + try + { + basic_json j = basic_json::parse(as_string_view()); + return j.as(); + } + catch (...) + { + JSONCONS_THROW(json_exception_impl("Not an integer")); + } + break; + case json_type_tag::double_t: + return static_cast(var_.double_data_cast()->value()); + case json_type_tag::integer_t: + return static_cast(var_.integer_data_cast()->value()); + case json_type_tag::uinteger_t: + return static_cast(var_.uinteger_data_cast()->value()); + case json_type_tag::bool_t: + return var_.bool_data_cast()->value() ? 1 : 0; + default: + JSONCONS_THROW(json_exception_impl("Not an integer")); + } + } + + uint64_t as_uinteger() const + { + switch (var_.type_id()) + { + case json_type_tag::small_string_t: + case json_type_tag::string_t: + try + { + basic_json j = basic_json::parse(as_string_view()); + return j.as(); + } + catch (...) + { + JSONCONS_THROW(json_exception_impl("Not an unsigned integer")); + } + break; + case json_type_tag::double_t: + return static_cast(var_.double_data_cast()->value()); + case json_type_tag::integer_t: + return static_cast(var_.integer_data_cast()->value()); + case json_type_tag::uinteger_t: + return static_cast(var_.uinteger_data_cast()->value()); + case json_type_tag::bool_t: + return var_.bool_data_cast()->value() ? 1 : 0; + default: + JSONCONS_THROW(json_exception_impl("Not an unsigned integer")); + } + } + + size_t precision() const + { + switch (var_.type_id()) + { + case json_type_tag::double_t: + return var_.double_data_cast()->precision(); + default: + JSONCONS_THROW(json_exception_impl("Not a double")); + } + } + + size_t decimal_places() const + { + switch (var_.type_id()) + { + case json_type_tag::double_t: + return var_.double_data_cast()->decimal_places(); + default: + JSONCONS_THROW(json_exception_impl("Not a double")); + } + } + + double as_double() const + { + switch (var_.type_id()) + { + case json_type_tag::small_string_t: + case json_type_tag::string_t: + try + { + basic_json j = basic_json::parse(as_string_view()); + return j.as(); + } + catch (...) + { + JSONCONS_THROW(json_exception_impl("Not a double")); + } + break; + case json_type_tag::double_t: + return var_.double_data_cast()->value(); + case json_type_tag::integer_t: + return static_cast(var_.integer_data_cast()->value()); + case json_type_tag::uinteger_t: + return static_cast(var_.uinteger_data_cast()->value()); + //case json_type_tag::null_t: + // return std::numeric_limits::quiet_NaN(); + default: + JSONCONS_THROW(json_exception_impl("Not a double")); + } + } + + string_view_type as_string_view() const + { + return var_.as_string_view(); + } + + byte_string_view as_byte_string_view() const + { + return var_.as_byte_string_view(); + } + + string_type as_string() const + { + switch (var_.type_id()) + { + case json_type_tag::small_string_t: + case json_type_tag::string_t: + return string_type(as_string_view().data(),as_string_view().length()); + default: + return to_string(); + } + } + + template + string_type as_string(const SAllocator& allocator) const + { + switch (var_.type_id()) + { + case json_type_tag::small_string_t: + case json_type_tag::string_t: + return string_type(as_string_view().data(),as_string_view().length(),allocator); + default: + return to_string(allocator); + } + } + + string_type as_string(const basic_json_serializing_options& options) const + { + switch (var_.type_id()) + { + case json_type_tag::small_string_t: + case json_type_tag::string_t: + return string_type(as_string_view().data(),as_string_view().length()); + default: + return to_string(options); + } + } + + template + string_type as_string(const basic_json_serializing_options& options, + const SAllocator& allocator) const + { + switch (var_.type_id()) + { + case json_type_tag::small_string_t: + case json_type_tag::string_t: + return string_type(as_string_view().data(),as_string_view().length(),allocator); + default: + return to_string(options,allocator); + } + } + + const char_type* as_cstring() const + { + switch (var_.type_id()) + { + case json_type_tag::small_string_t: + return var_.small_string_data_cast()->c_str(); + case json_type_tag::string_t: + return var_.string_data_cast()->c_str(); + default: + JSONCONS_THROW(json_exception_impl("Not a cstring")); + } + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + size_t double_precision() const + { + switch (var_.type_id()) + { + case json_type_tag::double_t: + return var_.double_data_cast()->precision(); + default: + JSONCONS_THROW(json_exception_impl("Not a double")); + } + } +#endif + + basic_json& at(const string_view_type& name) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + JSONCONS_THROW(key_not_found(name.data(),name.length())); + case json_type_tag::object_t: + { + auto it = object_value().find(name); + if (it == object_range().end()) + { + JSONCONS_THROW(key_not_found(name.data(),name.length())); + } + return it->value(); + } + break; + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + basic_json& evaluate() + { + return *this; + } + + basic_json& evaluate_with_default() + { + return *this; + } + + const basic_json& evaluate() const + { + return *this; + } + basic_json& evaluate(const string_view_type& name) + { + return at(name); + } + + const basic_json& evaluate(const string_view_type& name) const + { + return at(name); + } + + const basic_json& at(const string_view_type& name) const + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + JSONCONS_THROW(key_not_found(name.data(),name.length())); + case json_type_tag::object_t: + { + auto it = object_value().find(name); + if (it == object_range().end()) + { + JSONCONS_THROW(key_not_found(name.data(),name.length())); + } + return it->value(); + } + break; + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + basic_json& at(size_t i) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + if (i >= array_value().size()) + { + JSONCONS_THROW(json_exception_impl("Invalid array subscript")); + } + return array_value().operator[](i); + case json_type_tag::object_t: + return object_value().at(i); + default: + JSONCONS_THROW(json_exception_impl("Index on non-array value not supported")); + } + } + + const basic_json& at(size_t i) const + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + if (i >= array_value().size()) + { + JSONCONS_THROW(json_exception_impl("Invalid array subscript")); + } + return array_value().operator[](i); + case json_type_tag::object_t: + return object_value().at(i); + default: + JSONCONS_THROW(json_exception_impl("Index on non-array value not supported")); + } + } + + object_iterator find(const string_view_type& name) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + return object_range().end(); + case json_type_tag::object_t: + return object_value().find(name); + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + const_object_iterator find(const string_view_type& name) const + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + return object_range().end(); + case json_type_tag::object_t: + return object_value().find(name); + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + template + basic_json get(const string_view_type& name, T&& default_val) const + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + { + return basic_json(std::forward(default_val)); + } + case json_type_tag::object_t: + { + const_object_iterator it = object_value().find(name); + if (it != object_range().end()) + { + return it->value(); + } + else + { + return basic_json(std::forward(default_val)); + } + } + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + template + T get_with_default(const string_view_type& name, const T& default_val) const + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + { + return default_val; + } + case json_type_tag::object_t: + { + const_object_iterator it = object_value().find(name); + if (it != object_range().end()) + { + return it->value().template as(); + } + else + { + return default_val; + } + } + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + const CharT* get_with_default(const string_view_type& name, const CharT* default_val) const + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + { + return default_val; + } + case json_type_tag::object_t: + { + const_object_iterator it = object_value().find(name); + if (it != object_range().end()) + { + return it->value().as_cstring(); + } + else + { + return default_val; + } + } + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + // Modifiers + + void shrink_to_fit() + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + array_value().shrink_to_fit(); + break; + case json_type_tag::object_t: + object_value().shrink_to_fit(); + break; + default: + break; + } + } + + void clear() + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + array_value().clear(); + break; + case json_type_tag::object_t: + object_value().clear(); + break; + default: + break; + } + } + + void erase(const_object_iterator pos) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + break; + case json_type_tag::object_t: + object_value().erase(pos); + break; + default: + JSONCONS_THROW(json_exception_impl("Not an object")); + break; + } + } + + void erase(const_object_iterator first, const_object_iterator last) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + break; + case json_type_tag::object_t: + object_value().erase(first, last); + break; + default: + JSONCONS_THROW(json_exception_impl("Not an object")); + break; + } + } + + void erase(const_array_iterator pos) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + array_value().erase(pos); + break; + default: + JSONCONS_THROW(json_exception_impl("Not an array")); + break; + } + } + + void erase(const_array_iterator first, const_array_iterator last) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + array_value().erase(first, last); + break; + default: + JSONCONS_THROW(json_exception_impl("Not an array")); + break; + } + } + + // Removes all elements from an array value whose index is between from_index, inclusive, and to_index, exclusive. + + void erase(const string_view_type& name) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + break; + case json_type_tag::object_t: + object_value().erase(name); + break; + default: + JSONCONS_THROW(not_an_object(name.data(),name.length())); + break; + } + } + + template + std::pair set(const string_view_type& name, T&& val) + { + return insert_or_assign(name, std::forward(val)); + } + + template + std::pair insert_or_assign(const string_view_type& name, T&& val) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().insert_or_assign(name, std::forward(val)); + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + template + std::pair try_emplace(const string_view_type& name, Args&&... args) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().try_emplace(name, std::forward(args)...); + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + template + void set_(key_storage_type&& name, T&& val) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + object_value().set_(std::forward(name), std::forward(val)); + break; + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + // merge + + void merge(const basic_json& source) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().merge(source.object_value()); + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to merge a value that is not an object")); + } + } + } + + void merge(basic_json&& source) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().merge(std::move(source.object_value())); + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to merge a value that is not an object")); + } + } + } + + void merge(object_iterator hint, const basic_json& source) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().merge(hint, source.object_value()); + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to merge a value that is not an object")); + } + } + } + + void merge(object_iterator hint, basic_json&& source) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().merge(hint, std::move(source.object_value())); + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to merge a value that is not an object")); + } + } + } + + // merge_or_update + + void merge_or_update(const basic_json& source) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().merge_or_update(source.object_value()); + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to merge or update a value that is not an object")); + } + } + } + + void merge_or_update(basic_json&& source) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().merge_or_update(std::move(source.object_value())); + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to merge or update a value that is not an object")); + } + } + } + + void merge_or_update(object_iterator hint, const basic_json& source) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().merge_or_update(hint, source.object_value()); + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to merge or update a value that is not an object")); + } + } + } + + void merge_or_update(object_iterator hint, basic_json&& source) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().merge_or_update(hint, std::move(source.object_value())); + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to merge or update a value that is not an object")); + } + } + } + + // set + + template + object_iterator set(object_iterator hint, const string_view_type& name, T&& val) + { + return insert_or_assign(hint, name, std::forward(val)); + } + + template + object_iterator insert_or_assign(object_iterator hint, const string_view_type& name, T&& val) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().insert_or_assign(hint, name, std::forward(val)); + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + template + object_iterator try_emplace(object_iterator hint, const string_view_type& name, Args&&... args) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().try_emplace(hint, name, std::forward(args)...); + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + template + object_iterator set_(object_iterator hint, key_storage_type&& name, T&& val) + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return object_value().set_(hint, std::forward(name), std::forward(val)); + break; + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + template + void add(T&& val) + { + push_back(std::forward(val)); + } + + template + void push_back(T&& val) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + array_value().push_back(std::forward(val)); + break; + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to insert into a value that is not an array")); + } + } + } + + template + array_iterator add(const_array_iterator pos, T&& val) + { + return insert(pos, std::forward(val)); + } + + template + array_iterator insert(const_array_iterator pos, T&& val) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + return array_value().insert(pos, std::forward(val)); + break; + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to insert into a value that is not an array")); + } + } + } + + template + array_iterator insert(const_array_iterator pos, InputIt first, InputIt last) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + return array_value().insert(pos, first, last); + break; + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to insert into a value that is not an array")); + } + } + } + + template + array_iterator emplace(const_array_iterator pos, Args&&... args) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + return array_value().emplace(pos, std::forward(args)...); + break; + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to insert into a value that is not an array")); + } + } + } + + template + basic_json& emplace_back(Args&&... args) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + return array_value().emplace_back(std::forward(args)...); + default: + { + JSONCONS_THROW(json_exception_impl("Attempting to insert into a value that is not an array")); + } + } + } + + json_type_tag type_id() const + { + return var_.type_id(); + } + + void swap(basic_json& b) + { + var_.swap(b.var_); + } + + friend void swap(basic_json& a, basic_json& b) + { + a.swap(b); + } + + static basic_json make_string(const string_view_type& s) + { + return basic_json(variant(s.data(),s.length())); + } + + static basic_json make_string(const char_type* rhs, size_t length) + { + return basic_json(variant(rhs,length)); + } + + static basic_json make_string(const string_view_type& s, allocator_type allocator) + { + return basic_json(variant(s.data(),s.length(),allocator)); + } + + static basic_json from_integer(int64_t val) + { + return basic_json(variant(val)); + } + + static basic_json from_integer(int64_t val, allocator_type) + { + return basic_json(variant(val)); + } + + static basic_json from_uinteger(uint64_t val) + { + return basic_json(variant(val)); + } + + static basic_json from_uinteger(uint64_t val, allocator_type) + { + return basic_json(variant(val)); + } + + static basic_json from_floating_point(double val) + { + return basic_json(variant(val)); + } + + static basic_json from_floating_point(double val, allocator_type) + { + return basic_json(variant(val)); + } + + static basic_json from_bool(bool val) + { + return basic_json(variant(val)); + } + + static basic_json make_object(const object& o) + { + return basic_json(variant(o)); + } + + static basic_json make_object(const object& o, allocator_type allocator) + { + return basic_json(variant(o,allocator)); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + static basic_json parse_file(const std::basic_string& filename) + { + parse_error_handler_type err_handler; + return parse_file(filename,err_handler); + } + + static basic_json parse_file(const std::basic_string& filename, + parse_error_handler& err_handler) + { + std::basic_ifstream is(filename); + return parse(is,err_handler); + } + + static basic_json parse_stream(std::basic_istream& is) + { + return parse(is); + } + static basic_json parse_stream(std::basic_istream& is, parse_error_handler& err_handler) + { + return parse(is,err_handler); + } + + static basic_json parse_string(const string_type& s) + { + return parse(s); + } + + static basic_json parse_string(const string_type& s, parse_error_handler& err_handler) + { + return parse(s,err_handler); + } + + void resize_array(size_t n) + { + resize(n); + } + + template + void resize_array(size_t n, T val) + { + resize(n,val); + } + + object_iterator begin_members() + { + return object_range().begin(); + } + + const_object_iterator begin_members() const + { + return object_range().begin(); + } + + object_iterator end_members() + { + return object_range().end(); + } + + const_object_iterator end_members() const + { + return object_range().end(); + } + + array_iterator begin_elements() + { + return array_range().begin(); + } + + const_array_iterator begin_elements() const + { + return array_range().begin(); + } + + array_iterator end_elements() + { + return array_range().end(); + } + + const_array_iterator end_elements() const + { + return array_range().end(); + } + + const basic_json& get(const string_view_type& name) const + { + static const basic_json a_null = null_type(); + + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + return a_null; + case json_type_tag::object_t: + { + const_object_iterator it = object_value().find(name); + return it != object_range().end() ? it->value() : a_null; + } + default: + { + JSONCONS_THROW(not_an_object(name.data(),name.length())); + } + } + } + + bool is_longlong() const JSONCONS_NOEXCEPT + { + return var_.type_id() == json_type_tag::integer_t; + } + + bool is_ulonglong() const JSONCONS_NOEXCEPT + { + return var_.type_id() == json_type_tag::uinteger_t; + } + + long long as_longlong() const + { + return as_integer(); + } + + unsigned long long as_ulonglong() const + { + return as_uinteger(); + } + + int as_int() const + { + switch (var_.type_id()) + { + case json_type_tag::double_t: + return static_cast(var_.double_data_cast()->value()); + case json_type_tag::integer_t: + return static_cast(var_.integer_data_cast()->value()); + case json_type_tag::uinteger_t: + return static_cast(var_.uinteger_data_cast()->value()); + case json_type_tag::bool_t: + return var_.bool_data_cast()->value() ? 1 : 0; + default: + JSONCONS_THROW(json_exception_impl("Not an int")); + } + } + + unsigned int as_uint() const + { + switch (var_.type_id()) + { + case json_type_tag::double_t: + return static_cast(var_.double_data_cast()->value()); + case json_type_tag::integer_t: + return static_cast(var_.integer_data_cast()->value()); + case json_type_tag::uinteger_t: + return static_cast(var_.uinteger_data_cast()->value()); + case json_type_tag::bool_t: + return var_.bool_data_cast()->value() ? 1 : 0; + default: + JSONCONS_THROW(json_exception_impl("Not an unsigned int")); + } + } + + long as_long() const + { + switch (var_.type_id()) + { + case json_type_tag::double_t: + return static_cast(var_.double_data_cast()->value()); + case json_type_tag::integer_t: + return static_cast(var_.integer_data_cast()->value()); + case json_type_tag::uinteger_t: + return static_cast(var_.uinteger_data_cast()->value()); + case json_type_tag::bool_t: + return var_.bool_data_cast()->value() ? 1 : 0; + default: + JSONCONS_THROW(json_exception_impl("Not a long")); + } + } + + unsigned long as_ulong() const + { + switch (var_.type_id()) + { + case json_type_tag::double_t: + return static_cast(var_.double_data_cast()->value()); + case json_type_tag::integer_t: + return static_cast(var_.integer_data_cast()->value()); + case json_type_tag::uinteger_t: + return static_cast(var_.uinteger_data_cast()->value()); + case json_type_tag::bool_t: + return var_.bool_data_cast()->value() ? 1 : 0; + default: + JSONCONS_THROW(json_exception_impl("Not an unsigned long")); + } + } + + bool has_member(const key_storage_type& name) const + { + switch (var_.type_id()) + { + case json_type_tag::object_t: + { + const_object_iterator it = object_value().find(name); + return it != object_range().end(); + } + break; + default: + return false; + } + } + + void remove_range(size_t from_index, size_t to_index) + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + array_value().remove_range(from_index, to_index); + break; + default: + break; + } + } + // Removes all elements from an array value whose index is between from_index, inclusive, and to_index, exclusive. + + void remove(const string_view_type& name) + { + erase(name); + } + void remove_member(const string_view_type& name) + { + erase(name); + } + // Removes a member from an object value + + bool is_empty() const JSONCONS_NOEXCEPT + { + return empty(); + } + bool is_numeric() const JSONCONS_NOEXCEPT + { + return is_number(); + } + + template + static typename std::enable_if::type make_multi_array() + { + return make_array(); + } + template + static typename std::enable_if::type make_multi_array(size_t n) + { + return make_array(n); + } + template + static typename std::enable_if::type make_multi_array(size_t n, T val) + { + return make_array(n,val); + } + template + static typename std::enable_if::type make_multi_array(size_t m, size_t n) + { + return make_array<2>(m, n); + } + template + static typename std::enable_if::type make_multi_array(size_t m, size_t n, T val) + { + return make_array<2>(m, n, val); + } + template + static typename std::enable_if::type make_multi_array(size_t m, size_t n, size_t k) + { + return make_array<3>(m, n, k); + } + template + static typename std::enable_if::type make_multi_array(size_t m, size_t n, size_t k, T val) + { + return make_array<3>(m, n, k, val); + } + range members() + { + return object_range(); + } + + range members() const + { + return object_range(); + } + + range elements() + { + return array_range(); + } + + range elements() const + { + return array_range(); + } +#endif + + range object_range() + { + static basic_json empty_object = object(); + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + return range(empty_object.object_range().begin(), empty_object.object_range().end()); + case json_type_tag::object_t: + return range(object_value().begin(),object_value().end()); + default: + JSONCONS_THROW(json_exception_impl("Not an object")); + } + } + + range object_range() const + { + static const basic_json empty_object = object(); + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + return range(empty_object.object_range().begin(), empty_object.object_range().end()); + case json_type_tag::object_t: + return range(object_value().begin(),object_value().end()); + default: + JSONCONS_THROW(json_exception_impl("Not an object")); + } + } + + range array_range() + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + return range(array_value().begin(),array_value().end()); + default: + JSONCONS_THROW(json_exception_impl("Not an array")); + } + } + + range array_range() const + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + return range(array_value().begin(),array_value().end()); + default: + JSONCONS_THROW(json_exception_impl("Not an array")); + } + } + + array& array_value() + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + return var_.array_data_cast()->value(); + default: + JSONCONS_THROW(json_exception_impl("Bad array cast")); + break; + } + } + + const array& array_value() const + { + switch (var_.type_id()) + { + case json_type_tag::array_t: + return var_.array_data_cast()->value(); + default: + JSONCONS_THROW(json_exception_impl("Bad array cast")); + break; + } + } + + object& object_value() + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + create_object_implicitly(); + // FALLTHRU + case json_type_tag::object_t: + return var_.object_data_cast()->value(); + default: + JSONCONS_THROW(json_exception_impl("Bad object cast")); + break; + } + } + + const object& object_value() const + { + switch (var_.type_id()) + { + case json_type_tag::empty_object_t: + const_cast(this)->create_object_implicitly(); // HERE + // FALLTHRU + case json_type_tag::object_t: + return var_.object_data_cast()->value(); + default: + JSONCONS_THROW(json_exception_impl("Bad object cast")); + break; + } + } + +private: + + friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_json& o) + { + o.dump(os); + return os; + } + + friend std::basic_istream& operator<<(std::basic_istream& is, basic_json& o) + { + json_decoder handler; + basic_json_reader reader(is, handler); + reader.read_next(); + reader.check_done(); + if (!handler.is_valid()) + { + JSONCONS_THROW(json_exception_impl("Failed to parse json stream")); + } + o = handler.get_result(); + return is; + } +}; + +template +void swap(typename Json::key_value_pair_type& a, typename Json::key_value_pair_type& b) +{ + a.swap(b); +} + +template +std::basic_istream& operator>>(std::basic_istream& is, Json& o) +{ + json_decoder handler; + basic_json_reader reader(is, handler); + reader.read_next(); + reader.check_done(); + if (!handler.is_valid()) + { + JSONCONS_THROW(json_exception_impl("Failed to parse json stream")); + } + o = handler.get_result(); + return is; +} + +template +class json_printable +{ +public: + typedef typename Json::char_type char_type; + + json_printable(const Json& o, indenting line_indent) + : o_(&o), indenting_(line_indent) + { + } + + json_printable(const Json& o, + const basic_json_serializing_options& options, + indenting line_indent) + : o_(&o), options_(options), indenting_(line_indent) + { + } + + void dump(std::basic_ostream& os) const + { + o_->dump(os, options_, indenting_); + } + + friend std::basic_ostream& operator<<(std::basic_ostream& os, const json_printable& o) + { + o.dump(os); + return os; + } + + const Json *o_; + basic_json_serializing_options options_; + indenting indenting_; +private: + json_printable(); +}; + +template +json_printable print(const Json& val) +{ + return json_printable(val, indenting::no_indent); +} + +template +json_printable print(const Json& val, + const basic_json_serializing_options& options) +{ + return json_printable(val, options, indenting::no_indent); +} + +template +json_printable pretty_print(const Json& val) +{ + return json_printable(val, indenting::indent); +} + +template +json_printable pretty_print(const Json& val, + const basic_json_serializing_options& options) +{ + return json_printable(val, options, indenting::indent); +} + +typedef basic_json> json; +typedef basic_json> wjson; +typedef basic_json> ojson; +typedef basic_json> wojson; + +#if !defined(JSONCONS_NO_DEPRECATED) +typedef basic_json> owjson; +typedef json_decoder json_deserializer; +typedef json_decoder wjson_deserializer; +typedef json_decoder ojson_deserializer; +typedef json_decoder wojson_deserializer; +#endif + +#if defined(JSONCONS_HAS_USER_DEFINED_LITERALS) +namespace literals { + +inline +jsoncons::json operator "" _json(const char* s, std::size_t n) +{ + return jsoncons::json::parse(jsoncons::json::string_view_type(s, n)); +} + +inline +jsoncons::wjson operator "" _json(const wchar_t* s, std::size_t n) +{ + return jsoncons::wjson::parse(jsoncons::wjson::string_view_type(s, n)); +} + +inline +jsoncons::ojson operator "" _ojson(const char* s, std::size_t n) +{ + return jsoncons::ojson::parse(jsoncons::ojson::string_view_type(s, n)); +} + +inline +jsoncons::wojson operator "" _ojson(const wchar_t* s, std::size_t n) +{ + return jsoncons::wojson::parse(jsoncons::wojson::string_view_type(s, n)); +} + +} +#endif + +} + +#include + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#endif diff --git a/direct-access-api/include/json_content_handler.hpp b/direct-access-api/include/json_content_handler.hpp new file mode 100644 index 0000000..98bcfa0 --- /dev/null +++ b/direct-access-api/include/json_content_handler.hpp @@ -0,0 +1,385 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_CONTENT_HANDLER_HPP +#define JSONCONS_JSON_CONTENT_HANDLER_HPP + +#include +#include +#include +#include +#if !defined(JSONCONS_NO_DEPRECATED) +#include // for null_type +#endif + +namespace jsoncons { + +template +class basic_json_content_handler +{ +public: + typedef CharT char_type; + typedef std::char_traits char_traits_type; + + typedef basic_string_view_ext string_view_type; + + virtual ~basic_json_content_handler() {} + + void begin_json() + { + do_begin_json(); + } + + void end_json() + { + do_end_json(); + } + + void begin_object() + { + do_begin_object(null_serializing_context()); + } + + void begin_object(const serializing_context& context) + { + do_begin_object(context); + } + + void begin_object(size_t length) + { + do_begin_object(length, null_serializing_context()); + } + + void begin_object(size_t length, const serializing_context& context) + { + do_begin_object(length, context); + } + + void end_object() + { + do_end_object(null_serializing_context()); + } + + void end_object(const serializing_context& context) + { + do_end_object(context); + } + + void begin_array() + { + do_begin_array(null_serializing_context()); + } + + void begin_array(size_t length) + { + do_begin_array(length, null_serializing_context()); + } + + void begin_array(const serializing_context& context) + { + do_begin_array(context); + } + + void begin_array(size_t length, const serializing_context& context) + { + do_begin_array(length, context); + } + + void end_array() + { + do_end_array(null_serializing_context()); + } + + void end_array(const serializing_context& context) + { + do_end_array(context); + } + + void name(const string_view_type& name) + { + do_name(name, null_serializing_context()); + } + + void name(const string_view_type& name, const serializing_context& context) + { + do_name(name, context); + } + + void string_value(const string_view_type& value) + { + do_string_value(value, null_serializing_context()); + } + + void string_value(const string_view_type& value, const serializing_context& context) + { + do_string_value(value, context); + } + + void byte_string_value(const uint8_t* data, size_t length) + { + do_byte_string_value(data, length, null_serializing_context()); + } + + void byte_string_value(const uint8_t* data, size_t length, const serializing_context& context) + { + do_byte_string_value(data, length, context); + } + + void integer_value(int64_t value) + { + do_integer_value(value,null_serializing_context()); + } + + void integer_value(int64_t value, const serializing_context& context) + { + do_integer_value(value,context); + } + + void uinteger_value(uint64_t value) + { + do_uinteger_value(value,null_serializing_context()); + } + + void uinteger_value(uint64_t value, const serializing_context& context) + { + do_uinteger_value(value,context); + } + + void double_value(double value) + { + do_double_value(value, number_format(), null_serializing_context()); + } + + void double_value(double value, uint8_t precision) + { + do_double_value(value, number_format(precision, 0), null_serializing_context()); + } + + void double_value(double value, const number_format& fmt) + { + do_double_value(value, fmt, null_serializing_context()); + } + + void double_value(double value, const serializing_context& context) + { + do_double_value(value, number_format(), context); + } + + void double_value(double value, uint8_t precision, const serializing_context& context) + { + do_double_value(value, number_format(precision, 0), context); + } + + void double_value(double value, const number_format& fmt, const serializing_context& context) + { + do_double_value(value, fmt, context); + } + + void bool_value(bool value) + { + do_bool_value(value,null_serializing_context()); + } + + void bool_value(bool value, const serializing_context& context) + { + do_bool_value(value,context); + } + + void null_value() + { + do_null_value(null_serializing_context()); + } + + void null_value(const serializing_context& context) + { + do_null_value(context); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + void name(const CharT* p, size_t length, const serializing_context& context) + { + do_name(string_view_type(p, length), context); + } + + void value(const std::basic_string& value, const serializing_context& context) + { + do_string_value(value, context); + } + + void value(const CharT* p, size_t length, const serializing_context& context) + { + do_string_value(string_view_type(p, length), context); + } + + void value(const CharT* p, const serializing_context& context) + { + do_string_value(string_view_type(p), context); + } + + void value(int value, const serializing_context& context) + { + do_integer_value(value,context); + } + + void value(long value, const serializing_context& context) + { + do_integer_value(value,context); + } + + void value(long long value, const serializing_context& context) + { + do_integer_value(value,context); + } + + void value(unsigned int value, const serializing_context& context) + { + do_uinteger_value(value,context); + } + + void value(unsigned long value, const serializing_context& context) + { + do_uinteger_value(value,context); + } + + void value(unsigned long long value, const serializing_context& context) + { + do_uinteger_value(value,context); + } + + void value(float value, uint8_t precision, const serializing_context& context) + { + do_double_value(value, number_format(precision, 0), context); + } + + void value(double value, uint8_t precision, const serializing_context& context) + { + do_double_value(value, number_format(precision, 0), context); + } + + void value(bool value, const serializing_context& context) + { + do_bool_value(value,context); + } + + void value(null_type, const serializing_context& context) + { + do_null_value(context); + } +#endif + +private: + virtual void do_begin_json() = 0; + + virtual void do_end_json() = 0; + + virtual void do_begin_object(const serializing_context& context) = 0; + + virtual void do_begin_object(size_t length, const serializing_context& context) + { + do_begin_object(context); + } + + virtual void do_end_object(const serializing_context& context) = 0; + + virtual void do_begin_array(const serializing_context& context) = 0; + + virtual void do_begin_array(size_t length, const serializing_context& context) + { + do_begin_array(context); + } + + virtual void do_end_array(const serializing_context& context) = 0; + + virtual void do_name(const string_view_type& name, const serializing_context& context) = 0; + + virtual void do_null_value(const serializing_context& context) = 0; + + virtual void do_string_value(const string_view_type& value, const serializing_context& context) = 0; + + virtual void do_byte_string_value(const uint8_t* data, size_t length, const serializing_context& context) = 0; + + virtual void do_double_value(double value, const number_format& fmt, const serializing_context& context) = 0; + + virtual void do_integer_value(int64_t value, const serializing_context& context) = 0; + + virtual void do_uinteger_value(uint64_t value, const serializing_context& context) = 0; + + virtual void do_bool_value(bool value, const serializing_context& context) = 0; +}; + +template +class basic_null_json_content_handler final : public basic_json_content_handler +{ +public: + using typename basic_json_content_handler::string_view_type ; +private: + void do_begin_json() override + { + } + + void do_end_json() override + { + } + + void do_begin_object(const serializing_context&) override + { + } + + void do_end_object(const serializing_context&) override + { + } + + void do_begin_array(const serializing_context&) override + { + } + + void do_end_array(const serializing_context&) override + { + } + + void do_name(const string_view_type&, const serializing_context&) override + { + } + + void do_null_value(const serializing_context&) override + { + } + + void do_string_value(const string_view_type&, const serializing_context&) override + { + } + + void do_byte_string_value(const uint8_t* data, size_t length, const serializing_context&) override + { + } + + void do_double_value(double, const number_format& fmt, const serializing_context&) override + { + } + + void do_integer_value(int64_t, const serializing_context&) override + { + } + + void do_uinteger_value(uint64_t, const serializing_context&) override + { + } + + void do_bool_value(bool, const serializing_context&) override + { + } +}; + +typedef basic_json_content_handler json_content_handler; +typedef basic_json_content_handler wjson_content_handler; + +typedef basic_null_json_content_handler null_json_content_handler; +typedef basic_null_json_content_handler wnull_json_content_handler; + +} + +#endif diff --git a/direct-access-api/include/json_convert_traits.hpp b/direct-access-api/include/json_convert_traits.hpp new file mode 100644 index 0000000..b9cec88 --- /dev/null +++ b/direct-access-api/include/json_convert_traits.hpp @@ -0,0 +1,317 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONCONVERTTRAITS_HPP +#define JSONCONS_JSONCONVERTTRAITS_HPP + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +// json_convert_traits + +template +struct json_convert_traits +{ + template + static T decode(std::basic_istringstream& is, + const basic_json_serializing_options& options) + { + basic_json j = basic_json::parse(is, options); + return j. template as(); + } + + template + static void encode(const T& val, basic_json_content_handler& serializer) + { + auto j = json_type_traits, T>::to_json(val); + j.dump(serializer); + } +}; + +// json_convert_traits specializations + +// vector like + +template +struct json_convert_traits::value +>::type> +{ + typedef typename std::iterator_traits::value_type value_type; + + template + static T decode(std::basic_istringstream& is, + const basic_json_serializing_options& options) + { + basic_json j = basic_json::parse(is, options); + return j. template as(); + } + + template + static void encode(const T& val, basic_json_content_handler& serializer) + { + serializer.begin_json(); + serializer.begin_array(); + for (auto it = std::begin(val); it != std::end(val); ++it) + { + json_convert_traits::encode(*it,serializer); + } + serializer.end_array(); + serializer.end_json(); + } +}; + +// std::array + +template +struct json_convert_traits> +{ + typedef typename std::array::value_type value_type; + + template + static std::array decode(std::basic_istringstream& is, + const basic_json_serializing_options& options) + { + basic_json j = basic_json::parse(is,options); + return j. template as>(); + } + + template + static void encode(const std::array& val, std::basic_string& s) + { + basic_json_serializer> serializer(s); + encode(val,serializer); + } + + template + static void encode(const std::array& val, basic_json_content_handler& serializer) + { + serializer.begin_json(); + serializer.begin_array(); + for (auto it = std::begin(val); it != std::end(val); ++it) + { + json_convert_traits::encode(*it,serializer); + } + serializer.end_array(); + serializer.end_json(); + } +}; + +// map like + +template +struct json_convert_traits::value +>::type> +{ + typedef typename T::mapped_type mapped_type; + typedef typename T::value_type value_type; + + template + static T decode(std::basic_istringstream& is, + const basic_json_serializing_options& options) + { + basic_json j = basic_json::parse(is, options); + return j. template as(); + } + + template + static void encode(const T& val, basic_json_content_handler& serializer) + { + serializer.begin_json(); + serializer.begin_object(); + for (auto it = std::begin(val); it != std::end(val); ++it) + { + serializer.name(it->first); + json_convert_traits::encode(it->second,serializer); + } + serializer.end_object(); + serializer.end_json(); + } +}; + +// std::tuple + +namespace detail { namespace streaming { + +template +struct tuple_helper +{ + using element_type = typename std::tuple_element::value - Pos, Tuple>::type; + using next = tuple_helper; + + template + static void encode(const Tuple& tuple, basic_json_content_handler& handler) + { + json_convert_traits::encode(std::get::value - Pos>(tuple),handler); + next::encode(tuple, handler); + } +}; + +template +struct tuple_helper<0, Tuple> +{ + template + static void encode(const Tuple&, basic_json_content_handler&) + { + } +}; + +}} + +template +struct json_convert_traits> +{ +private: + using helper = detail::streaming::tuple_helper>; +public: + + template + static std::tuple decode(std::basic_istringstream& is, + const basic_json_serializing_options& options) + { + basic_json j = basic_json::parse(is, options); + return j. template as>(); + } + + template + static void encode(const std::tuple& val, basic_json_content_handler& serializer) + { + serializer.begin_array(); + helper::encode(val, serializer); + serializer.end_array(); + } +}; + +// decode_json + +template +T decode_json(const std::basic_string& s) +{ + std::basic_istringstream is(s); + return json_convert_traits::decode(is, basic_json_serializing_options()); +} + +template +T decode_json(const std::basic_string& s, + const basic_json_serializing_options& options) +{ + std::basic_istringstream is(s); + return json_convert_traits::decode(is, options); +} + +template +T decode_json(std::basic_istringstream& is) +{ + return json_convert_traits::decode(is, basic_json_serializing_options()); +} + +template +T decode_json(std::basic_istringstream& is, + const basic_json_serializing_options& options) +{ + return json_convert_traits::decode(is, options); +} + +// encode_json + +template +void encode_json(const T& val, basic_json_content_handler& handler) +{ + handler.begin_json(); + json_convert_traits::encode(val,handler); + handler.end_json(); +} + +template +void encode_fragment(const T& val, basic_json_content_handler& handler) +{ + json_convert_traits::encode(val,handler); +} + +template +void encode_json(const T& val, std::basic_ostream& os) +{ + basic_json_serializer serializer(os); + encode_json(val, serializer); +} + +template +void encode_json(const T& val, const basic_json_serializing_options& options, + std::basic_ostream& os) +{ + basic_json_serializer serializer(os, options); + encode_json(val, serializer); +} + +template +void encode_json(const T& val, std::basic_ostream& os, indenting line_indent) +{ + basic_json_serializer serializer(os, line_indent); + encode_json(val, serializer); +} + +template +void encode_json(const T& val, const basic_json_serializing_options& options, + std::basic_ostream& os, indenting line_indent) +{ + basic_json_serializer serializer(os, options, line_indent); + encode_json(val, serializer); +} + +template +void encode_json(const T& val, std::basic_string& s) +{ + basic_json_serializer> serializer(s); + encode_json(val, serializer); +} + +template +void encode_json(const T& val, const basic_json_serializing_options& options, + std::basic_string& s) +{ + basic_json_serializer> serializer(s, options); + encode_json(val, serializer); +} + +template +void encode_json(const T& val, std::basic_string& s, indenting line_indent) +{ + basic_json_serializer> serializer(s, line_indent); + encode_json(val, serializer); +} + +template +void encode_json(const T& val, const basic_json_serializing_options& options, + std::basic_string>& s, indenting line_indent) +{ + basic_json_serializer serializer(s, options, line_indent); + encode_json(val, serializer); +} + +} +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#endif + diff --git a/direct-access-api/include/json_decoder.hpp b/direct-access-api/include/json_decoder.hpp new file mode 100644 index 0000000..562d611 --- /dev/null +++ b/direct-access-api/include/json_decoder.hpp @@ -0,0 +1,310 @@ +// Copyright 2013-2016 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_DECODER_HPP +#define JSONCONS_JSON_DECODER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +template > +class json_decoder final : public basic_json_content_handler +{ +public: + typedef typename Json::char_type char_type; + using typename basic_json_content_handler::string_view_type; + + typedef typename Json::key_value_pair_type key_value_pair_type; + typedef typename Json::key_storage_type key_storage_type; + typedef typename Json::string_type string_type; + typedef typename Json::array array; + typedef typename Json::object object; + typedef typename Json::allocator_type json_allocator_type; + typedef typename string_type::allocator_type json_string_allocator; + typedef typename array::allocator_type json_array_allocator; + typedef typename object::allocator_type json_object_allocator; + + json_string_allocator string_allocator_; + json_object_allocator object_allocator_; + json_array_allocator array_allocator_; + + Json result_; + + struct stack_item + { + stack_item(key_storage_type&& name) + : name_(std::forward(name)) + { + } + stack_item(Json&& value) + : value_(std::forward(value)) + { + } + + stack_item() = default; + stack_item(const stack_item&) = default; + stack_item(stack_item&&) = default; + stack_item& operator=(const stack_item&) = default; + stack_item& operator=(stack_item&&) = default; + + key_storage_type name_; + Json value_; + }; + + struct structure_offset + { + size_t offset_; + bool is_object_; + }; + + typedef Allocator allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc stack_item_allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc size_t_allocator_type; + + + std::vector stack_; + std::vector stack_offsets_; + bool is_valid_; + +public: + json_decoder(const json_allocator_type& jallocator = json_allocator_type()) + : string_allocator_(jallocator), + object_allocator_(jallocator), + array_allocator_(jallocator), + is_valid_(false) + + { + stack_offsets_.reserve(100); + stack_.reserve(1000); + } + + bool is_valid() const + { + return is_valid_; + } + + Json get_result() + { + is_valid_ = false; + return std::move(result_); + } + +#if !defined(JSONCONS_NO_DEPRECATED) + Json& root() + { + return result_; + } +#endif + +private: + + void push_object() + { + if (stack_offsets_.back().is_object_) + { + stack_.back().value_ = Json(object(object_allocator_)); + } + else + { + stack_.push_back(Json(object(object_allocator_))); + } + stack_offsets_.push_back({stack_.size()-1,true}); + } + + void pop_object() + { + stack_.erase(stack_.begin()+stack_offsets_.back().offset_+1, stack_.end()); + stack_offsets_.pop_back(); + } + + void push_array() + { + if (stack_offsets_.back().is_object_) + { + stack_.back().value_ = Json(array(array_allocator_)); + } + else + { + stack_.push_back(Json(array(array_allocator_))); + } + stack_offsets_.push_back({stack_.size()-1,false}); + } + + void pop_array() + { + stack_.erase(stack_.begin()+stack_offsets_.back().offset_+1, stack_.end()); + stack_offsets_.pop_back(); + } + + void do_begin_json() override + { + stack_offsets_.clear(); + stack_.clear(); + stack_offsets_.push_back({0,false}); + is_valid_ = false; + } + + void do_end_json() override + { + if (stack_.size() == 1) + { + result_.swap(stack_.front().value_); + stack_.pop_back(); + is_valid_ = true; + } + } + + void do_begin_object(const serializing_context&) override + { + push_object(); + } + + void do_end_object(const serializing_context&) override + { + end_structure(); + pop_object(); + } + + void do_begin_array(const serializing_context&) override + { + push_array(); + } + + void do_end_array(const serializing_context&) override + { + end_structure(); + pop_array(); + } + + void end_structure() + { + JSONCONS_ASSERT(stack_offsets_.size() > 0); + const size_t structure_index = stack_offsets_.back().offset_; + JSONCONS_ASSERT(stack_.size() > structure_index); + const size_t count = stack_.size() - (structure_index + 1); + + auto first = stack_.begin() + (structure_index+1); + auto last = first + count; + if (stack_offsets_.back().is_object_) + { + stack_[structure_index].value_.object_value().insert( + std::make_move_iterator(first), + std::make_move_iterator(last), + [](stack_item&& val){return key_value_pair_type(std::move(val.name_),std::move(val.value_));}); + } + else + { + auto& j = stack_[structure_index].value_; + j.reserve(count); + while (first != last) + { + j.push_back(std::move(first->value_)); + ++first; + } + } + } + + void do_name(const string_view_type& name, const serializing_context&) override + { + stack_.push_back(key_storage_type(name.begin(),name.end(),string_allocator_)); + } + + void do_string_value(const string_view_type& val, const serializing_context&) override + { + if (stack_offsets_.back().is_object_) + { + stack_.back().value_ = Json(val.data(),val.length(),string_allocator_); + } + else + { + stack_.push_back(Json(val.data(),val.length(),string_allocator_)); + } + } + + void do_byte_string_value(const uint8_t* data, size_t length, const serializing_context&) override + { + if (stack_offsets_.back().is_object_) + { + stack_.back().value_ = Json(data,length,string_allocator_); + } + else + { + stack_.push_back(Json(data,length,string_allocator_)); + } + } + + void do_integer_value(int64_t value, const serializing_context&) override + { + if (stack_offsets_.back().is_object_) + { + stack_.back().value_ = value; + } + else + { + stack_.push_back(Json(value)); + } + } + + void do_uinteger_value(uint64_t value, const serializing_context&) override + { + if (stack_offsets_.back().is_object_) + { + stack_.back().value_ = value; + } + else + { + stack_.push_back(Json(value)); + } + } + + void do_double_value(double value, const number_format& fmt, const serializing_context&) override + { + if (stack_offsets_.back().is_object_) + { + stack_.back().value_ = Json(value,fmt); + } + else + { + stack_.push_back(Json(value,fmt)); + } + } + + void do_bool_value(bool value, const serializing_context&) override + { + if (stack_offsets_.back().is_object_) + { + stack_.back().value_ = value; + } + else + { + stack_.push_back(Json(value)); + } + } + + void do_null_value(const serializing_context&) override + { + if (stack_offsets_.back().is_object_) + { + stack_.back().value_ = Json::null(); + } + else + { + stack_.push_back(Json(Json::null())); + } + } +}; + +} + +#endif diff --git a/direct-access-api/include/json_deserializer.hpp b/direct-access-api/include/json_deserializer.hpp new file mode 100644 index 0000000..05b44a4 --- /dev/null +++ b/direct-access-api/include/json_deserializer.hpp @@ -0,0 +1,12 @@ +// Copyright 2013-2016 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_DESERIALIZER_HPP +#define JSONCONS_JSON_DESERIALIZER_HPP + +#include + +#endif diff --git a/direct-access-api/include/json_error_category.hpp b/direct-access-api/include/json_error_category.hpp new file mode 100644 index 0000000..c09b847 --- /dev/null +++ b/direct-access-api/include/json_error_category.hpp @@ -0,0 +1,148 @@ +/// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_ERROR_CATEGORY_HPP +#define JSONCONS_JSON_ERROR_CATEGORY_HPP + +#include +#include + +namespace jsoncons { + + enum class json_parser_errc + { + ok = 0, + unexpected_eof = 1, + source_error, + invalid_json_text, + extra_character, + max_depth_exceeded, + single_quote, + illegal_character_in_string, + extra_comma, + expected_name, + expected_value, + invalid_value, + expected_colon, + illegal_control_character, + illegal_escaped_character, + expected_codepoint_surrogate_pair, + invalid_hex_escape_sequence, + invalid_unicode_escape_sequence, + leading_zero, + invalid_number, + expected_comma_or_right_brace, + expected_comma_or_right_bracket, + unexpected_right_bracket, + unexpected_right_brace, + illegal_comment, + expected_continuation_byte, + over_long_utf8_sequence, + illegal_codepoint, + illegal_surrogate_value, + unpaired_high_surrogate + }; + +class json_error_category_impl + : public std::error_category +{ +public: + virtual const char* name() const JSONCONS_NOEXCEPT + { + return "jsoncons"; + } + virtual std::string message(int ev) const + { + switch (static_cast(ev)) + { + case json_parser_errc::unexpected_eof: + return "Unexpected end of file"; + case json_parser_errc::source_error: + return "Source error"; + case json_parser_errc::invalid_json_text: + return "Invalid JSON text"; + case json_parser_errc::extra_character: + return "Unexpected non-whitespace character after JSON text"; + case json_parser_errc::max_depth_exceeded: + return "Maximum JSON depth exceeded"; + case json_parser_errc::single_quote: + return "JSON strings cannot be quoted with single quotes"; + case json_parser_errc::illegal_character_in_string: + return "Illegal character in string"; + case json_parser_errc::extra_comma: + return "Extra comma"; + case json_parser_errc::expected_name: + return "Expected object member name"; + case json_parser_errc::expected_value: + return "Expected value"; + case json_parser_errc::invalid_value: + return "Invalid value"; + case json_parser_errc::expected_colon: + return "Expected name separator ':'"; + case json_parser_errc::illegal_control_character: + return "Illegal control character in string"; + case json_parser_errc::illegal_escaped_character: + return "Illegal escaped character in string"; + case json_parser_errc::expected_codepoint_surrogate_pair: + return "Invalid codepoint, expected another \\u token to begin the second half of a codepoint surrogate pair."; + case json_parser_errc::invalid_hex_escape_sequence: + return "Invalid codepoint, expected hexadecimal digit."; + case json_parser_errc::invalid_unicode_escape_sequence: + return "Invalid codepoint, expected four hexadecimal digits."; + case json_parser_errc::leading_zero: + return "A number cannot have a leading zero"; + case json_parser_errc::invalid_number: + return "Invalid number"; + case json_parser_errc::expected_comma_or_right_brace: + return "Expected comma or right brace ']'"; + case json_parser_errc::expected_comma_or_right_bracket: + return "Expected comma or right bracket ']'"; + case json_parser_errc::unexpected_right_brace: + return "Unexpected right brace '}'"; + case json_parser_errc::unexpected_right_bracket: + return "Unexpected right bracket ']'"; + case json_parser_errc::illegal_comment: + return "Illegal comment"; + case json_parser_errc::expected_continuation_byte: + return "Expected continuation byte"; + case json_parser_errc::over_long_utf8_sequence: + return "Over long UTF-8 sequence"; + case json_parser_errc::illegal_codepoint: + return "Illegal codepoint (>= 0xd800 && <= 0xdfff)"; + case json_parser_errc::illegal_surrogate_value: + return "UTF-16 surrogate values are illegal in UTF-32"; + case json_parser_errc::unpaired_high_surrogate: + return "Expected low surrogate following the high surrogate"; + default: + return "Unknown JSON parser error"; + } + } +}; + +inline +const std::error_category& json_error_category() +{ + static json_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(json_parser_errc result) +{ + return std::error_code(static_cast(result),json_error_category()); +} + + +} + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +#endif diff --git a/direct-access-api/include/json_exception.hpp b/direct-access-api/include/json_exception.hpp new file mode 100644 index 0000000..668fa3c --- /dev/null +++ b/direct-access-api/include/json_exception.hpp @@ -0,0 +1,107 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSON_EXCEPTION_HPP +#define JSON_EXCEPTION_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +// json_exception + +class json_exception +{ +public: + virtual const char* what() const JSONCONS_NOEXCEPT = 0; +}; + +template +class json_exception_impl : public Base, public virtual json_exception +{ +public: + json_exception_impl(const std::string& s) JSONCONS_NOEXCEPT + : Base(""), message_(s) + { + } + ~json_exception_impl() JSONCONS_NOEXCEPT + { + } + const char* what() const JSONCONS_NOEXCEPT override + { + return message_.c_str(); + } +private: + std::string message_; +}; + +class key_not_found : public std::out_of_range, public virtual json_exception +{ +public: + template + explicit key_not_found(const CharT* key, size_t length) JSONCONS_NOEXCEPT + : std::out_of_range("") + { + buffer_.append("Key '"); + unicons::convert(key, key+length, std::back_inserter(buffer_), + unicons::conv_flags::strict); + buffer_.append("' not found"); + } + ~key_not_found() JSONCONS_NOEXCEPT + { + } + const char* what() const JSONCONS_NOEXCEPT override + { + return buffer_.c_str(); + } +private: + std::string buffer_; +}; + +class not_an_object : public std::runtime_error, public virtual json_exception +{ +public: + template + explicit not_an_object(const CharT* key, size_t length) JSONCONS_NOEXCEPT + : std::runtime_error("") + { + buffer_.append("Attempting to access or modify '"); + unicons::convert(key, key+length, std::back_inserter(buffer_), + unicons::conv_flags::strict); + buffer_.append("' on a value that is not an object"); + } + ~not_an_object() JSONCONS_NOEXCEPT + { + } + const char* what() const JSONCONS_NOEXCEPT override + { + return buffer_.c_str(); + } +private: + std::string buffer_; +}; + +#define JSONCONS_STR2(x) #x +#define JSONCONS_STR(x) JSONCONS_STR2(x) + +#define JSONCONS_ASSERT(x) if (!(x)) { \ + throw jsoncons::json_exception_impl("assertion '" #x "' failed at " __FILE__ ":" \ + JSONCONS_STR(__LINE__)); } + +#define JSONCONS_THROW(x) throw (x) + +} +#endif diff --git a/direct-access-api/include/json_filter.hpp b/direct-access-api/include/json_filter.hpp new file mode 100644 index 0000000..c5bb5ab --- /dev/null +++ b/direct-access-api/include/json_filter.hpp @@ -0,0 +1,204 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_FILTER_HPP +#define JSONCONS_JSON_FILTER_HPP + +#include + +#include +#include + +namespace jsoncons { + +template +class basic_json_filter : public basic_json_content_handler +{ +public: + using typename basic_json_content_handler::string_view_type ; +private: + basic_json_content_handler& content_handler_; + basic_json_content_handler& downstream_handler_; + + // noncopyable and nonmoveable + basic_json_filter(const basic_json_filter&) = delete; + basic_json_filter& operator=(const basic_json_filter&) = delete; +public: + basic_json_filter(basic_json_content_handler& handler) + : content_handler_(*this), + downstream_handler_(handler) + { + } + +#if !defined(JSONCONS_NO_DEPRECATED) + basic_json_content_handler& input_handler() + { + return downstream_handler_; + } +#endif + + basic_json_content_handler& downstream_handler() + { + return downstream_handler_; + } + +private: + void do_begin_json() override + { + downstream_handler_.begin_json(); + } + + void do_end_json() override + { + downstream_handler_.end_json(); + } + + void do_begin_object(const serializing_context& context) override + { + downstream_handler_.begin_object(context); + } + + void do_begin_object(size_t length, const serializing_context& context) override + { + downstream_handler_.begin_object(length, context); + } + + void do_end_object(const serializing_context& context) override + { + downstream_handler_.end_object(context); + } + + void do_begin_array(const serializing_context& context) override + { + downstream_handler_.begin_array(context); + } + + void do_begin_array(size_t length, const serializing_context& context) override + { + downstream_handler_.begin_array(length, context); + } + + void do_end_array(const serializing_context& context) override + { + downstream_handler_.end_array(context); + } + + void do_name(const string_view_type& name, + const serializing_context& context) override + { + downstream_handler_.name(name,context); + } + + void do_string_value(const string_view_type& value, + const serializing_context& context) override + { + downstream_handler_.string_value(value,context); + } + + void do_byte_string_value(const uint8_t* data, size_t length, + const serializing_context& context) override + { + downstream_handler_.byte_string_value(data, length, context); + } + + void do_double_value(double value, const number_format& fmt, + const serializing_context& context) override + { + downstream_handler_.double_value(value, fmt, context); + } + + void do_integer_value(int64_t value, + const serializing_context& context) override + { + downstream_handler_.integer_value(value,context); + } + + void do_uinteger_value(uint64_t value, + const serializing_context& context) override + { + downstream_handler_.uinteger_value(value,context); + } + + void do_bool_value(bool value, + const serializing_context& context) override + { + downstream_handler_.bool_value(value,context); + } + + void do_null_value(const serializing_context& context) override + { + downstream_handler_.null_value(context); + } + +}; + +// Filters out begin_json and end_json events +template +class basic_json_fragment_filter : public basic_json_filter +{ +public: + using typename basic_json_filter::string_view_type; + + basic_json_fragment_filter(basic_json_content_handler& handler) + : basic_json_filter(handler) + { + } +private: + void do_begin_json() override + { + } + + void do_end_json() override + { + } +}; + +template +class basic_rename_object_member_filter : public basic_json_filter +{ +public: + using typename basic_json_filter::string_view_type; + +private: + std::basic_string name_; + std::basic_string new_name_; +public: + basic_rename_object_member_filter(const std::basic_string& name, + const std::basic_string& new_name, + basic_json_content_handler& handler) + : basic_json_filter(handler), + name_(name), new_name_(new_name) + { + } + +private: + void do_name(const string_view_type& name, + const serializing_context& context) override + { + if (name == name_) + { + this->downstream_handler().name(new_name_,context); + } + else + { + this->downstream_handler().name(name,context); + } + } +}; + +typedef basic_json_filter json_filter; +typedef basic_json_filter wjson_filter; +typedef basic_rename_object_member_filter rename_object_member_filter; +typedef basic_rename_object_member_filter wrename_object_member_filter; + +#if !defined(JSONCONS_NO_DEPRECATED) +typedef basic_rename_object_member_filter rename_name_filter; +typedef basic_rename_object_member_filter wrename_name_filter; +#endif + +} + +#endif diff --git a/direct-access-api/include/json_fwd.hpp b/direct-access-api/include/json_fwd.hpp new file mode 100644 index 0000000..6ef5a5a --- /dev/null +++ b/direct-access-api/include/json_fwd.hpp @@ -0,0 +1,32 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONFWD_HPP +#define JSONCONS_JSONFWD_HPP + +#include + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" +#endif + +namespace jsoncons { + +struct sorted_policy; + +template > +class basic_json; + +} + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#endif diff --git a/direct-access-api/include/json_parser.hpp b/direct-access-api/include/json_parser.hpp new file mode 100644 index 0000000..d9a7039 --- /dev/null +++ b/direct-access-api/include/json_parser.hpp @@ -0,0 +1,2981 @@ +// Copyright 2015 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_PARSER_HPP +#define JSONCONS_JSON_PARSER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JSONCONS_ILLEGAL_CONTROL_CHARACTER \ + case 0x00:case 0x01:case 0x02:case 0x03:case 0x04:case 0x05:case 0x06:case 0x07:case 0x08:case 0x0b: \ + case 0x0c:case 0x0e:case 0x0f:case 0x10:case 0x11:case 0x12:case 0x13:case 0x14:case 0x15:case 0x16: \ + case 0x17:case 0x18:case 0x19:case 0x1a:case 0x1b:case 0x1c:case 0x1d:case 0x1e:case 0x1f + +namespace jsoncons { + +namespace detail { + +template +class replacement_filter : public basic_json_filter +{ + typedef typename basic_json_content_handler::string_view_type string_view_type; + + basic_null_json_content_handler default_content_handler_; + basic_json_serializing_options options_; +public: + replacement_filter() + : basic_json_filter(default_content_handler_) + { + } + + replacement_filter(basic_json_content_handler& handler, const basic_json_serializing_options& options) + : basic_json_filter(handler), options_(options) + { + } + + void do_string_value(const string_view_type& s, const serializing_context& context) override + { + if (options_.can_read_nan_replacement() && s == options_.nan_replacement().substr(1,options_.nan_replacement().length()-2)) + { + this->downstream_handler().double_value(std::nan(""), context); + } + else if (options_.can_read_pos_inf_replacement() && s == options_.pos_inf_replacement().substr(1,options_.pos_inf_replacement().length()-2)) + { + this->downstream_handler().double_value(std::numeric_limits::infinity(), context); + } + else if (options_.can_read_neg_inf_replacement() && s == options_.neg_inf_replacement().substr(1,options_.neg_inf_replacement().length()-2)) + { + this->downstream_handler().double_value(-std::numeric_limits::infinity(), context); + } + else + { + this->downstream_handler().string_value(s, context); + } + } + +}; + +} + +enum class parse_state : uint8_t +{ + root, + start, + slash, + slash_slash, + slash_star, + slash_star_star, + expect_comma_or_end, + object, + expect_member_name_or_end, + expect_member_name, + expect_colon, + expect_value_or_end, + expect_value, + array, + string_u1, + member_name, + escape, + escape_u1, + escape_u2, + escape_u3, + escape_u4, + escape_expect_surrogate_pair1, + escape_expect_surrogate_pair2, + escape_u6, + escape_u7, + escape_u8, + escape_u9, + minus, + zero, + integer, + fraction1, + fraction2, + exp1, + exp2, + exp3, + n, + nu, + nul, + t, + tr, + tru, + f, + fa, + fal, + fals, + cr, + lf, + done +}; + +template > +class basic_json_parser : private serializing_context +{ + static const size_t initial_string_buffer_capacity_ = 1024; + static const size_t initial_number_buffer_capacity_ = 64; + static const int default_initial_stack_capacity_ = 100; + typedef typename basic_json_content_handler::string_view_type string_view_type; + + detail::replacement_filter replacement_filter_; + basic_null_json_content_handler default_content_handler_; + default_parse_error_handler default_err_handler_; + + basic_json_content_handler& handler_; + parse_error_handler& err_handler_; + uint32_t cp_; + uint32_t cp2_; + + typedef Allocator allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc numeral_allocator_type; + + std::basic_string,char_allocator_type> string_buffer_; + std::basic_string,numeral_allocator_type> number_buffer_; + + bool is_negative_; + uint8_t precision_; + uint8_t decimal_places_; + + size_t line_; + size_t column_; + size_t nesting_depth_; + int initial_stack_capacity_; + + size_t max_nesting_depth_; + detail::string_to_double to_double_; + const CharT* begin_input_; + const CharT* input_end_; + const CharT* input_ptr_; + + parse_state state_; + typedef typename std::allocator_traits:: template rebind_alloc parse_state_allocator_type; + std::vector state_stack_; + + // Noncopyable and nonmoveable + basic_json_parser(const basic_json_parser&) = delete; + basic_json_parser& operator=(const basic_json_parser&) = delete; + +public: + + basic_json_parser() + : handler_(default_content_handler_), + err_handler_(default_err_handler_), + cp_(0), + cp2_(0), + is_negative_(false), + precision_(0), + decimal_places_(0), + line_(1), + column_(1), + nesting_depth_(0), + initial_stack_capacity_(default_initial_stack_capacity_), + begin_input_(nullptr), + input_end_(nullptr), + input_ptr_(nullptr), + state_(parse_state::start) + { + string_buffer_.reserve(initial_string_buffer_capacity_); + number_buffer_.reserve(initial_number_buffer_capacity_); + max_nesting_depth_ = (std::numeric_limits::max)(); + + state_stack_.reserve(initial_stack_capacity_); + push_state(parse_state::root); + } + + basic_json_parser(parse_error_handler& err_handler) + : handler_(default_content_handler_), + err_handler_(err_handler), + cp_(0), + cp2_(0), + is_negative_(false), + precision_(0), + decimal_places_(0), + line_(1), + column_(1), + nesting_depth_(0), + initial_stack_capacity_(default_initial_stack_capacity_), + begin_input_(nullptr), + input_end_(nullptr), + input_ptr_(nullptr), + state_(parse_state::start) + { + string_buffer_.reserve(initial_string_buffer_capacity_); + number_buffer_.reserve(initial_number_buffer_capacity_); + max_nesting_depth_ = (std::numeric_limits::max)(); + + state_stack_.reserve(initial_stack_capacity_); + push_state(parse_state::root); + } + + basic_json_parser(basic_json_content_handler& handler) + : handler_(handler), + err_handler_(default_err_handler_), + cp_(0), + cp2_(0), + is_negative_(false), + precision_(0), + decimal_places_(0), + line_(1), + column_(1), + nesting_depth_(0), + initial_stack_capacity_(default_initial_stack_capacity_), + begin_input_(nullptr), + input_end_(nullptr), + input_ptr_(nullptr), + state_(parse_state::start) + { + string_buffer_.reserve(initial_string_buffer_capacity_); + number_buffer_.reserve(initial_number_buffer_capacity_); + max_nesting_depth_ = (std::numeric_limits::max)(); + + state_stack_.reserve(initial_stack_capacity_); + push_state(parse_state::root); + } + + basic_json_parser(basic_json_content_handler& handler, + parse_error_handler& err_handler) + : handler_(handler), + err_handler_(err_handler), + cp_(0), + cp2_(0), + is_negative_(false), + precision_(0), + decimal_places_(0), + line_(1), + column_(1), + nesting_depth_(0), + initial_stack_capacity_(default_initial_stack_capacity_), + begin_input_(nullptr), + input_end_(nullptr), + input_ptr_(nullptr), + state_(parse_state::start) + { + string_buffer_.reserve(initial_string_buffer_capacity_); + number_buffer_.reserve(initial_number_buffer_capacity_); + max_nesting_depth_ = (std::numeric_limits::max)(); + + state_stack_.reserve(initial_stack_capacity_); + push_state(parse_state::root); + } + + basic_json_parser(const basic_json_serializing_options& options) + : handler_((options.can_read_nan_replacement() || options.can_read_pos_inf_replacement() || options.can_read_neg_inf_replacement()) ? replacement_filter_(default_content_handler_,options) : default_content_handler_), + err_handler_(default_err_handler_), + cp_(0), + cp2_(0), + is_negative_(false), + precision_(0), + decimal_places_(0), + line_(1), + column_(1), + nesting_depth_(0), + initial_stack_capacity_(default_initial_stack_capacity_), + begin_input_(nullptr), + input_end_(nullptr), + input_ptr_(nullptr), + state_(parse_state::start) + { + string_buffer_.reserve(initial_string_buffer_capacity_); + number_buffer_.reserve(initial_number_buffer_capacity_); + max_nesting_depth_ = options.max_nesting_depth(); + + state_stack_.reserve(initial_stack_capacity_); + push_state(parse_state::root); + } + + basic_json_parser(const basic_json_serializing_options& options, + parse_error_handler& err_handler) + : handler_((options.can_read_nan_replacement() || options.can_read_pos_inf_replacement() || options.can_read_neg_inf_replacement()) ? replacement_filter_(default_content_handler_,options) : default_content_handler_), + err_handler_(err_handler), + cp_(0), + cp2_(0), + is_negative_(false), + precision_(0), + decimal_places_(0), + line_(1), + column_(1), + nesting_depth_(0), + initial_stack_capacity_(default_initial_stack_capacity_), + begin_input_(nullptr), + input_end_(nullptr), + input_ptr_(nullptr), + state_(parse_state::start) + { + string_buffer_.reserve(initial_string_buffer_capacity_); + number_buffer_.reserve(initial_number_buffer_capacity_); + max_nesting_depth_ = options.max_nesting_depth(); + + state_stack_.reserve(initial_stack_capacity_); + push_state(parse_state::root); + } + + basic_json_parser(basic_json_content_handler& handler, + const basic_json_serializing_options& options) + : replacement_filter_(handler,options), + handler_((options.can_read_nan_replacement() || options.can_read_pos_inf_replacement() || options.can_read_neg_inf_replacement()) ? replacement_filter_ : handler), + err_handler_(default_err_handler_), + cp_(0), + cp2_(0), + is_negative_(false), + precision_(0), + decimal_places_(0), + line_(1), + column_(1), + nesting_depth_(0), + initial_stack_capacity_(default_initial_stack_capacity_), + begin_input_(nullptr), + input_end_(nullptr), + input_ptr_(nullptr), + state_(parse_state::start) + { + string_buffer_.reserve(initial_string_buffer_capacity_); + number_buffer_.reserve(initial_number_buffer_capacity_); + max_nesting_depth_ = options.max_nesting_depth(); + + state_stack_.reserve(initial_stack_capacity_); + push_state(parse_state::root); + } + + basic_json_parser(basic_json_content_handler& handler, + const basic_json_serializing_options& options, + parse_error_handler& err_handler) + : replacement_filter_(handler,options), + handler_((options.can_read_nan_replacement() || options.can_read_pos_inf_replacement() || options.can_read_neg_inf_replacement()) ? replacement_filter_ : handler), + err_handler_(err_handler), + cp_(0), + cp2_(0), + is_negative_(false), + precision_(0), + decimal_places_(0), + line_(1), + column_(1), + nesting_depth_(0), + initial_stack_capacity_(default_initial_stack_capacity_), + begin_input_(nullptr), + input_end_(nullptr), + input_ptr_(nullptr), + state_(parse_state::start) + { + string_buffer_.reserve(initial_string_buffer_capacity_); + number_buffer_.reserve(initial_number_buffer_capacity_); + max_nesting_depth_ = options.max_nesting_depth(); + + state_stack_.reserve(initial_stack_capacity_); + push_state(parse_state::root); + } + + size_t line_number() const + { + return line_; + } + + size_t column_number() const + { + return column_; + } + + void set_column_number(size_t column) + { + column_ = column; + } + + bool source_exhausted() const + { + return input_ptr_ == input_end_; + } + + ~basic_json_parser() + { + } + +#if !defined(JSONCONS_NO_DEPRECATED) + size_t max_nesting_depth() const + { + return max_nesting_depth_; + } + + void max_nesting_depth(size_t value) + { + max_nesting_depth_ = value; + } +#endif + parse_state parent() const + { + JSONCONS_ASSERT(state_stack_.size() >= 1); + return state_stack_.back(); + } + + bool done() const + { + return state_ == parse_state::done; + } + + void skip_whitespace() + { + const CharT* local_input_end = input_end_; + for (;;) + { + if (JSONCONS_UNLIKELY(input_ptr_ == local_input_end)) + { + return; + } + else if (*input_ptr_ == ' ' || *input_ptr_ == '\t') + { + ++input_ptr_; + ++column_; + } + else + { + return; + } + } + } + + void begin_object(std::error_code& ec) + { + if (++nesting_depth_ >= max_nesting_depth_) + { + if (err_handler_.error(json_parser_errc::max_depth_exceeded, *this)) + { + ec = json_parser_errc::max_depth_exceeded; + return; + } + } + push_state(parse_state::object); + state_ = parse_state::expect_member_name_or_end; + handler_.begin_object(*this); + } + + void end_object(std::error_code& ec) + { + JSONCONS_ASSERT(nesting_depth_ >= 1); + --nesting_depth_; + state_ = pop_state(); + if (state_ == parse_state::object) + { + handler_.end_object(*this); + } + else if (state_ == parse_state::array) + { + err_handler_.fatal_error(json_parser_errc::expected_comma_or_right_bracket, *this); + ec = json_parser_errc::expected_comma_or_right_bracket; + return; + } + else + { + err_handler_.fatal_error(json_parser_errc::unexpected_right_brace, *this); + ec = json_parser_errc::unexpected_right_brace; + return; + } + + if (parent() == parse_state::root) + { + state_ = parse_state::done; + handler_.end_json(); + } + else + { + state_ = parse_state::expect_comma_or_end; + } + } + + void begin_array(std::error_code& ec) + { + if (++nesting_depth_ >= max_nesting_depth_) + { + if (err_handler_.error(json_parser_errc::max_depth_exceeded, *this)) + { + ec = json_parser_errc::max_depth_exceeded; + return; + } + + } + push_state(parse_state::array); + state_ = parse_state::expect_value_or_end; + handler_.begin_array(*this); + } + + void end_array(std::error_code& ec) + { + JSONCONS_ASSERT(nesting_depth_ >= 1); + --nesting_depth_; + state_ = pop_state(); + if (state_ == parse_state::array) + { + handler_.end_array(*this); + } + else if (state_ == parse_state::object) + { + err_handler_.fatal_error(json_parser_errc::expected_comma_or_right_brace, *this); + ec = json_parser_errc::expected_comma_or_right_brace; + return; + } + else + { + err_handler_.fatal_error(json_parser_errc::unexpected_right_bracket, *this); + ec = json_parser_errc::unexpected_right_bracket; + return; + } + if (parent() == parse_state::root) + { + state_ = parse_state::done; + handler_.end_json(); + } + else + { + state_ = parse_state::expect_comma_or_end; + } + } + + void reset() + { + state_stack_.clear(); + state_stack_.reserve(initial_stack_capacity_); + push_state(parse_state::root); + state_ = parse_state::start; + line_ = 1; + column_ = 1; + nesting_depth_ = 0; + } + + void check_done() + { + std::error_code ec; + check_done(ec); + if (ec) + { + throw parse_error(ec,line_,column_); + } + } + + void check_done(std::error_code& ec) + { + if (state_ != parse_state::done) + { + if (err_handler_.error(json_parser_errc::unexpected_eof, *this)) + { + ec = json_parser_errc::unexpected_eof; + return; + } + } + for (; input_ptr_ != input_end_; ++input_ptr_) + { + CharT curr_char_ = *input_ptr_; + switch (curr_char_) + { + case '\n': + case '\r': + case '\t': + case ' ': + break; + default: + if (err_handler_.error(json_parser_errc::extra_character, *this)) + { + ec = json_parser_errc::extra_character; + return; + } + break; + } + } + } + + void parse_some(std::error_code& ec) + { + const CharT* local_input_end = input_end_; + + while ((input_ptr_ < local_input_end) && (state_ != parse_state::done)) + { + switch (state_) + { + case parse_state::cr: + ++line_; + column_ = 1; + switch (*input_ptr_) + { + case '\n': + state_ = pop_state(); + ++input_ptr_; + break; + default: + state_ = pop_state(); + break; + } + break; + case parse_state::lf: + ++line_; + column_ = 1; + state_ = pop_state(); + break; + case parse_state::start: + { + handler_.begin_json(); + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + if (err_handler_.error(json_parser_errc::illegal_control_character, *this)) + { + ec = json_parser_errc::illegal_control_character; + return; + } + break; + case '\r': + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::lf; + break; + case ' ':case '\t': + skip_whitespace(); + break; + case '/': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::slash; + break; + case '{': + begin_object(ec); + if (ec) return; + ++input_ptr_; + ++column_; + break; + case '[': + begin_array(ec); + if (ec) return; + ++input_ptr_; + ++column_; + break; + case '\"': + state_ = parse_state::string_u1; + ++input_ptr_; + ++column_; + break; + case '-': + number_buffer_.clear(); + is_negative_ = true; + precision_ = 0; + ++input_ptr_; + ++column_; + state_ = parse_state::minus; + parse_number(ec); + if (ec) {return;} + break; + case '0': + number_buffer_.clear(); + is_negative_ = false; + precision_ = 1; + number_buffer_.push_back(static_cast(*input_ptr_)); + state_ = parse_state::zero; + ++input_ptr_; + ++column_; + parse_number(ec); + if (ec) {return;} + break; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + number_buffer_.clear(); + is_negative_ = false; + precision_ = 1; + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + state_ = parse_state::integer; + parse_number(ec); + if (ec) {return;} + break; + case 'n': + parse_null(ec); + if (ec) {return;} + break; + case 't': + parse_true(ec); + if (ec) {return;} + break; + case 'f': + parse_false(ec); + if (ec) {return;} + break; + case '}': + err_handler_.fatal_error(json_parser_errc::unexpected_right_brace, *this); + ec = json_parser_errc::unexpected_right_brace; + return; + case ']': + err_handler_.fatal_error(json_parser_errc::unexpected_right_bracket, *this); + ec = json_parser_errc::unexpected_right_bracket; + return; + default: + err_handler_.fatal_error(json_parser_errc::invalid_json_text, *this); + ec = json_parser_errc::invalid_json_text; + return; + } + } + break; + + case parse_state::expect_comma_or_end: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + if (err_handler_.error(json_parser_errc::illegal_control_character, *this)) + { + ec = json_parser_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++column_; + break; + case '\r': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::lf; + break; + case ' ':case '\t': + skip_whitespace(); + break; + case '/': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::slash; + break; + case '}': + end_object(ec); + if (ec) return; + ++input_ptr_; + ++column_; + break; + case ']': + end_array(ec); + if (ec) return; + ++input_ptr_; + ++column_; + break; + case ',': + begin_member_or_element(ec); + if (ec) return; + ++input_ptr_; + ++column_; + break; + default: + if (parent() == parse_state::array) + { + if (err_handler_.error(json_parser_errc::expected_comma_or_right_bracket, *this)) + { + ec = json_parser_errc::expected_comma_or_right_bracket; + return; + } + } + else if (parent() == parse_state::object) + { + if (err_handler_.error(json_parser_errc::expected_comma_or_right_brace, *this)) + { + ec = json_parser_errc::expected_comma_or_right_brace; + return; + } + } + ++input_ptr_; + ++column_; + break; + } + } + break; + case parse_state::expect_member_name_or_end: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + if (err_handler_.error(json_parser_errc::illegal_control_character, *this)) + { + ec = json_parser_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++column_; + break; + case '\r': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::lf; + break; + case ' ':case '\t': + skip_whitespace(); + break; + case '/': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::slash; + break; + case '}': + end_object(ec); + if (ec) return; + ++input_ptr_; + ++column_; + break; + case '\"': + ++input_ptr_; + ++column_; + push_state(parse_state::member_name); + state_ = parse_state::string_u1; + break; + case '\'': + if (err_handler_.error(json_parser_errc::single_quote, *this)) + { + ec = json_parser_errc::single_quote; + return; + } + ++input_ptr_; + ++column_; + break; + default: + if (err_handler_.error(json_parser_errc::expected_name, *this)) + { + ec = json_parser_errc::expected_name; + return; + } + ++input_ptr_; + ++column_; + break; + } + } + break; + case parse_state::expect_member_name: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + if (err_handler_.error(json_parser_errc::illegal_control_character, *this)) + { + ec = json_parser_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++column_; + break; + case '\r': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::lf; + break; + case ' ':case '\t': + skip_whitespace(); + break; + case '/': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::slash; + break; + case '\"': + ++input_ptr_; + ++column_; + push_state(parse_state::member_name); + state_ = parse_state::string_u1; + break; + case '}': + if (err_handler_.error(json_parser_errc::extra_comma, *this)) + { + ec = json_parser_errc::extra_comma; + return; + } + end_object(ec); // Recover + if (ec) return; + ++input_ptr_; + ++column_; + break; + case '\'': + if (err_handler_.error(json_parser_errc::single_quote, *this)) + { + ec = json_parser_errc::single_quote; + return; + } + ++input_ptr_; + ++column_; + break; + default: + if (err_handler_.error(json_parser_errc::expected_name, *this)) + { + ec = json_parser_errc::expected_name; + return; + } + ++input_ptr_; + ++column_; + break; + } + } + break; + case parse_state::expect_colon: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + if (err_handler_.error(json_parser_errc::illegal_control_character, *this)) + { + ec = json_parser_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++column_; + break; + case '\r': + push_state(state_); + state_ = parse_state::cr; + ++input_ptr_; + ++column_; + break; + case '\n': + push_state(state_); + state_ = parse_state::lf; + ++input_ptr_; + ++column_; + break; + case ' ':case '\t': + skip_whitespace(); + break; + case '/': + push_state(state_); + state_ = parse_state::slash; + ++input_ptr_; + ++column_; + break; + case ':': + state_ = parse_state::expect_value; + ++input_ptr_; + ++column_; + break; + default: + if (err_handler_.error(json_parser_errc::expected_colon, *this)) + { + ec = json_parser_errc::expected_colon; + return; + } + ++input_ptr_; + ++column_; + break; + } + } + break; + + case parse_state::expect_value: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + if (err_handler_.error(json_parser_errc::illegal_control_character, *this)) + { + ec = json_parser_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++column_; + break; + case '\r': + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::cr; + break; + case '\n': + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::lf; + break; + case ' ':case '\t': + skip_whitespace(); + break; + case '/': + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::slash; + break; + case '{': + begin_object(ec); + if (ec) return; + ++input_ptr_; + ++column_; + break; + case '[': + begin_array(ec); + if (ec) return; + ++input_ptr_; + ++column_; + break; + case '\"': + ++input_ptr_; + ++column_; + state_ = parse_state::string_u1; + break; + case '-': + number_buffer_.clear(); + is_negative_ = true; + precision_ = 0; + ++input_ptr_; + ++column_; + state_ = parse_state::minus; + parse_number(ec); + if (ec) {return;} + break; + case '0': + number_buffer_.clear(); + is_negative_ = false; + precision_ = 1; + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + state_ = parse_state::zero; + parse_number(ec); + if (ec) {return;} + break; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + number_buffer_.clear(); + is_negative_ = false; + precision_ = 1; + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + state_ = parse_state::integer; + parse_number(ec); + if (ec) {return;} + break; + case 'n': + parse_null(ec); + if (ec) {return;} + break; + case 't': + parse_true(ec); + if (ec) {return;} + break; + case 'f': + parse_false(ec); + if (ec) {return;} + break; + case ']': + if (parent() == parse_state::array) + { + if (err_handler_.error(json_parser_errc::extra_comma, *this)) + { + ec = json_parser_errc::extra_comma; + return; + } + end_array(ec); // Recover + if (ec) return; + } + else + { + if (err_handler_.error(json_parser_errc::expected_value, *this)) + { + ec = json_parser_errc::expected_value; + return; + } + } + ++input_ptr_; + ++column_; + break; + case '\'': + if (err_handler_.error(json_parser_errc::single_quote, *this)) + { + ec = json_parser_errc::single_quote; + return; + } + ++input_ptr_; + ++column_; + break; + default: + if (err_handler_.error(json_parser_errc::expected_value, *this)) + { + ec = json_parser_errc::expected_value; + return; + } + ++input_ptr_; + ++column_; + break; + } + } + break; + case parse_state::expect_value_or_end: + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + if (err_handler_.error(json_parser_errc::illegal_control_character, *this)) + { + ec = json_parser_errc::illegal_control_character; + return; + } + ++input_ptr_; + ++column_; + break; + case '\r': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::cr; + break; + case '\n': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::lf; + break; + case ' ':case '\t': + skip_whitespace(); + break; + case '/': + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::slash; + break; + case '{': + begin_object(ec); + if (ec) return; + ++input_ptr_; + ++column_; + break; + case '[': + begin_array(ec); + if (ec) return; + ++input_ptr_; + ++column_; + break; + case ']': + end_array(ec); + if (ec) return; + ++input_ptr_; + ++column_; + break; + case '\"': + ++input_ptr_; + ++column_; + state_ = parse_state::string_u1; + break; + case '-': + number_buffer_.clear(); + is_negative_ = true; + precision_ = 0; + ++input_ptr_; + ++column_; + state_ = parse_state::minus; + parse_number(ec); + if (ec) {return;} + break; + case '0': + number_buffer_.clear(); + is_negative_ = false; + precision_ = 1; + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + state_ = parse_state::zero; + parse_number(ec); + if (ec) {return;} + break; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + number_buffer_.clear(); + is_negative_ = false; + precision_ = 1; + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + state_ = parse_state::integer; + parse_number(ec); + if (ec) {return;} + break; + case 'n': + parse_null(ec); + if (ec) {return;} + break; + case 't': + parse_true(ec); + if (ec) {return;} + break; + case 'f': + parse_false(ec); + if (ec) {return;} + break; + case '\'': + if (err_handler_.error(json_parser_errc::single_quote, *this)) + { + ec = json_parser_errc::single_quote; + return; + } + ++input_ptr_; + ++column_; + break; + default: + if (err_handler_.error(json_parser_errc::expected_value, *this)) + { + ec = json_parser_errc::expected_value; + return; + } + ++input_ptr_; + ++column_; + break; + } + } + break; + case parse_state::string_u1: + case parse_state::escape: + case parse_state::escape_u1: + case parse_state::escape_u2: + case parse_state::escape_u3: + case parse_state::escape_u4: + case parse_state::escape_expect_surrogate_pair1: + case parse_state::escape_expect_surrogate_pair2: + case parse_state::escape_u6: + case parse_state::escape_u7: + case parse_state::escape_u8: + case parse_state::escape_u9: + parse_string(ec); + if (ec) return; + break; + case parse_state::minus: + case parse_state::zero: + case parse_state::integer: + case parse_state::fraction1: + case parse_state::fraction2: + case parse_state::exp1: + case parse_state::exp2: + case parse_state::exp3: + parse_number(ec); + if (ec) return; + break; + case parse_state::t: + switch (*input_ptr_) + { + case 'r': + ++input_ptr_; + ++column_; + state_ = parse_state::tr; + break; + default: + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + break; + case parse_state::tr: + switch (*input_ptr_) + { + case 'u': + state_ = parse_state::tru; + break; + default: + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + ++input_ptr_; + ++column_; + break; + case parse_state::tru: + switch (*input_ptr_) + { + case 'e': + handler_.bool_value(true,*this); + if (parent() == parse_state::root) + { + state_ = parse_state::done; + handler_.end_json(); + } + else + { + state_ = parse_state::expect_comma_or_end; + } + break; + default: + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + ++input_ptr_; + ++column_; + break; + case parse_state::f: + switch (*input_ptr_) + { + case 'a': + ++input_ptr_; + ++column_; + state_ = parse_state::fa; + break; + default: + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + break; + case parse_state::fa: + switch (*input_ptr_) + { + case 'l': + state_ = parse_state::fal; + break; + default: + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + ++input_ptr_; + ++column_; + break; + case parse_state::fal: + switch (*input_ptr_) + { + case 's': + state_ = parse_state::fals; + break; + default: + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + ++input_ptr_; + ++column_; + break; + case parse_state::fals: + switch (*input_ptr_) + { + case 'e': + handler_.bool_value(false,*this); + if (parent() == parse_state::root) + { + state_ = parse_state::done; + handler_.end_json(); + } + else + { + state_ = parse_state::expect_comma_or_end; + } + break; + default: + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + ++input_ptr_; + ++column_; + break; + case parse_state::n: + switch (*input_ptr_) + { + case 'u': + ++input_ptr_; + ++column_; + state_ = parse_state::nu; + break; + default: + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + break; + case parse_state::nu: + switch (*input_ptr_) + { + case 'l': + state_ = parse_state::nul; + break; + default: + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + ++input_ptr_; + ++column_; + break; + case parse_state::nul: + switch (*input_ptr_) + { + case 'l': + handler_.null_value(*this); + if (parent() == parse_state::root) + { + state_ = parse_state::done; + handler_.end_json(); + } + else + { + state_ = parse_state::expect_comma_or_end; + } + break; + default: + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + ++input_ptr_; + ++column_; + break; + case parse_state::slash: + { + switch (*input_ptr_) + { + case '*': + state_ = parse_state::slash_star; + if (err_handler_.error(json_parser_errc::illegal_comment, *this)) + { + ec = json_parser_errc::illegal_comment; + return; + } + break; + case '/': + state_ = parse_state::slash_slash; + if (err_handler_.error(json_parser_errc::illegal_comment, *this)) + { + ec = json_parser_errc::illegal_comment; + return; + } + break; + default: + if (err_handler_.error(json_parser_errc::invalid_json_text, *this)) + { + ec = json_parser_errc::invalid_json_text; + return; + } + break; + } + } + ++input_ptr_; + ++column_; + break; + case parse_state::slash_star: + { + switch (*input_ptr_) + { + case '\r': + push_state(state_); + state_ = parse_state::cr; + break; + case '\n': + push_state(state_); + state_ = parse_state::lf; + break; + case '*': + state_ = parse_state::slash_star_star; + break; + } + } + ++input_ptr_; + ++column_; + break; + case parse_state::slash_slash: + { + switch (*input_ptr_) + { + case '\r': + state_ = pop_state(); + break; + case '\n': + state_ = pop_state(); + break; + default: + ++input_ptr_; + ++column_; + } + } + break; + case parse_state::slash_star_star: + { + switch (*input_ptr_) + { + case '/': + state_ = pop_state(); + break; + default: + state_ = parse_state::slash_star; + break; + } + } + ++input_ptr_; + ++column_; + break; + default: + JSONCONS_ASSERT(false); + break; + } + } + } + + void parse_true(std::error_code& ec) + { + if (JSONCONS_LIKELY(input_end_ - input_ptr_ >= 4)) + { + if (*(input_ptr_+1) == 'r' && *(input_ptr_+2) == 'u' && *(input_ptr_+3) == 'e') + { + handler_.bool_value(true,*this); + input_ptr_ += 4; + column_ += 4; + if (parent() == parse_state::root) + { + handler_.end_json(); + state_ = parse_state::done; + } + else + { + state_ = parse_state::expect_comma_or_end; + } + } + else + { + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + } + else + { + ++input_ptr_; + ++column_; + state_ = parse_state::t; + } + } + + void parse_null(std::error_code& ec) + { + if (JSONCONS_LIKELY(input_end_ - input_ptr_ >= 4)) + { + if (*(input_ptr_+1) == 'u' && *(input_ptr_+2) == 'l' && *(input_ptr_+3) == 'l') + { + handler_.null_value(*this); + input_ptr_ += 4; + column_ += 4; + if (parent() == parse_state::root) + { + handler_.end_json(); + state_ = parse_state::done; + } + else + { + state_ = parse_state::expect_comma_or_end; + } + } + else + { + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + } + else + { + ++input_ptr_; + ++column_; + state_ = parse_state::n; + } + } + + void parse_false(std::error_code& ec) + { + if (JSONCONS_LIKELY(input_end_ - input_ptr_ >= 5)) + { + if (*(input_ptr_+1) == 'a' && *(input_ptr_+2) == 'l' && *(input_ptr_+3) == 's' && *(input_ptr_+4) == 'e') + { + handler_.bool_value(false,*this); + input_ptr_ += 5; + column_ += 5; + if (parent() == parse_state::root) + { + handler_.end_json(); + state_ = parse_state::done; + } + else + { + state_ = parse_state::expect_comma_or_end; + } + } + else + { + err_handler_.error(json_parser_errc::invalid_value, *this); + ec = json_parser_errc::invalid_value; + return; + } + } + else + { + ++input_ptr_; + ++column_; + state_ = parse_state::f; + } + } + + void parse_number(std::error_code& ec) + { + const CharT* local_input_end = input_end_; + + switch (state_) + { + case parse_state::minus: + goto minus_sign; + case parse_state::zero: + goto zero; + case parse_state::integer: + goto integer; + case parse_state::fraction1: + goto fraction1; + case parse_state::fraction2: + goto fraction2; + case parse_state::exp1: + goto exp1; + case parse_state::exp2: + goto exp2; + case parse_state::exp3: + goto exp3; + default: + JSONCONS_UNREACHABLE(); + } +minus_sign: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::minus; + return; + } + switch (*input_ptr_) + { + case '0': + number_buffer_.push_back(static_cast(*input_ptr_)); + ++precision_; + ++input_ptr_; + ++column_; + goto zero; + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + number_buffer_.push_back(static_cast(*input_ptr_)); + ++precision_; + ++input_ptr_; + ++column_; + goto integer; + default: + err_handler_.error(json_parser_errc::expected_value, *this); + ec = json_parser_errc::expected_value; + return; + } +zero: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::zero; + return; + } + switch (*input_ptr_) + { + case '\r': + end_integer_value(ec); + if (ec) return; + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::cr; + return; + case '\n': + end_integer_value(ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::lf; + return; + case ' ':case '\t': + end_integer_value(ec); + if (ec) return; + skip_whitespace(); + return; + case '/': + end_integer_value(ec); + if (ec) return; + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::slash; + return; + case '}': + end_integer_value(ec); + if (ec) return; + end_object(ec); + ++input_ptr_; + ++column_; + if (ec) return; + return; + case ']': + end_integer_value(ec); + if (ec) return; + end_array(ec); + ++input_ptr_; + ++column_; + if (ec) return; + return; + case '.': + decimal_places_ = 0; + JSONCONS_ASSERT(precision_ == number_buffer_.length()); + number_buffer_.push_back(to_double_.get_decimal_point()); + ++input_ptr_; + ++column_; + goto fraction1; + case 'e':case 'E': + JSONCONS_ASSERT(precision_ == number_buffer_.length()); + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + goto exp1; + case ',': + end_integer_value(ec); + if (ec) return; + begin_member_or_element(ec); + if (ec) return; + ++input_ptr_; + ++column_; + return; + case '0': case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + err_handler_.error(json_parser_errc::leading_zero, *this); + ec = json_parser_errc::leading_zero; + state_ = parse_state::zero; + return; + default: + err_handler_.error(json_parser_errc::invalid_number, *this); + ec = json_parser_errc::invalid_number; + state_ = parse_state::zero; + return; + } +integer: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::integer; + return; + } + switch (*input_ptr_) + { + case '\r': + end_integer_value(ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::cr; + return; + case '\n': + end_integer_value(ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::lf; + return; + case ' ':case '\t': + end_integer_value(ec); + if (ec) return; + skip_whitespace(); + return; + case '/': + end_integer_value(ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::slash; + return; + case '}': + end_integer_value(ec); + if (ec) return; + end_object(ec); + if (ec) return; + ++input_ptr_; + ++column_; + return; + case ']': + end_integer_value(ec); + if (ec) return; + end_array(ec); + if (ec) return; + ++input_ptr_; + ++column_; + return; + case '0': case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + number_buffer_.push_back(static_cast(*input_ptr_)); + ++precision_; + ++input_ptr_; + ++column_; + goto integer; + case '.': + decimal_places_ = 0; + JSONCONS_ASSERT(precision_ == number_buffer_.length()); + number_buffer_.push_back(to_double_.get_decimal_point()); + ++input_ptr_; + ++column_; + goto fraction1; + case 'e':case 'E': + JSONCONS_ASSERT(precision_ == number_buffer_.length()); + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + goto exp1; + case ',': + end_integer_value(ec); + if (ec) return; + begin_member_or_element(ec); + if (ec) return; + ++input_ptr_; + ++column_; + return; + default: + err_handler_.error(json_parser_errc::invalid_number, *this); + ec = json_parser_errc::invalid_number; + state_ = parse_state::integer; + return; + } +fraction1: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::fraction1; + return; + } + switch (*input_ptr_) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + ++precision_; + ++decimal_places_; + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + goto fraction2; + default: + err_handler_.error(json_parser_errc::invalid_number, *this); + ec = json_parser_errc::invalid_number; + state_ = parse_state::fraction1; + return; + } +fraction2: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::fraction2; + return; + } + switch (*input_ptr_) + { + case '\r': + end_fraction_value(chars_format::fixed,ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::cr; + return; + case '\n': + end_fraction_value(chars_format::fixed,ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::lf; + return; + case ' ':case '\t': + end_fraction_value(chars_format::fixed,ec); + if (ec) return; + skip_whitespace(); + return; + case '/': + end_fraction_value(chars_format::fixed,ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::slash; + return; + case '}': + end_fraction_value(chars_format::fixed,ec); + if (ec) return; + end_object(ec); + if (ec) return; + ++input_ptr_; + ++column_; + return; + case ']': + end_fraction_value(chars_format::fixed,ec); + if (ec) return; + end_array(ec); + if (ec) return; + ++input_ptr_; + ++column_; + return; + case ',': + end_fraction_value(chars_format::fixed,ec); + if (ec) return; + begin_member_or_element(ec); + if (ec) return; + ++input_ptr_; + ++column_; + return; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + ++precision_; + ++decimal_places_; + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + goto fraction2; + case 'e':case 'E': + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + goto exp1; + default: + err_handler_.error(json_parser_errc::invalid_number, *this); + ec = json_parser_errc::invalid_number; + state_ = parse_state::fraction2; + return; + } +exp1: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::exp1; + return; + } + switch (*input_ptr_) + { + case '+': + ++input_ptr_; + ++column_; + goto exp2; + case '-': + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + goto exp2; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + goto exp3; + default: + err_handler_.error(json_parser_errc::expected_value, *this); + ec = json_parser_errc::expected_value; + state_ = parse_state::exp1; + return; + } +exp2: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::exp2; + return; + } + switch (*input_ptr_) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + goto exp3; + default: + err_handler_.error(json_parser_errc::expected_value, *this); + ec = json_parser_errc::expected_value; + state_ = parse_state::exp2; + return; + } + +exp3: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::exp3; + return; + } + switch (*input_ptr_) + { + case '\r': + end_fraction_value(chars_format::scientific,ec); + if (ec) return; + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::cr; + return; + case '\n': + end_fraction_value(chars_format::scientific,ec); + if (ec) return; + ++input_ptr_; + ++column_; + push_state(state_); + state_ = parse_state::lf; + return; + case ' ':case '\t': + end_fraction_value(chars_format::scientific,ec); + if (ec) return; + skip_whitespace(); + return; + case '/': + end_fraction_value(chars_format::scientific,ec); + if (ec) return; + push_state(state_); + ++input_ptr_; + ++column_; + state_ = parse_state::slash; + return; + case '}': + end_fraction_value(chars_format::scientific,ec); + if (ec) return; + end_object(ec); + if (ec) return; + ++input_ptr_; + ++column_; + return; + case ']': + end_fraction_value(chars_format::scientific,ec); + if (ec) return; + end_array(ec); + if (ec) return; + ++input_ptr_; + ++column_; + return; + case ',': + end_fraction_value(chars_format::scientific,ec); + if (ec) return; + begin_member_or_element(ec); + if (ec) return; + ++input_ptr_; + ++column_; + return; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + number_buffer_.push_back(static_cast(*input_ptr_)); + ++input_ptr_; + ++column_; + goto exp3; + default: + err_handler_.error(json_parser_errc::invalid_number, *this); + ec = json_parser_errc::invalid_number; + state_ = parse_state::exp3; + return; + } + + JSONCONS_UNREACHABLE(); + } + + void parse_string(std::error_code& ec) + { + const CharT* local_input_end = input_end_; + const CharT* sb = input_ptr_; + + switch (state_) + { + case parse_state::string_u1: + goto string_u1; + case parse_state::escape: + goto escape; + case parse_state::escape_u1: + goto escape_u1; + case parse_state::escape_u2: + goto escape_u2; + case parse_state::escape_u3: + goto escape_u3; + case parse_state::escape_u4: + goto escape_u4; + case parse_state::escape_expect_surrogate_pair1: + goto escape_expect_surrogate_pair1; + case parse_state::escape_expect_surrogate_pair2: + goto escape_expect_surrogate_pair2; + case parse_state::escape_u6: + goto escape_u6; + case parse_state::escape_u7: + goto escape_u7; + case parse_state::escape_u8: + goto escape_u8; + case parse_state::escape_u9: + goto escape_u9; + default: + JSONCONS_UNREACHABLE(); + } + +string_u1: + while (input_ptr_ < local_input_end) + { + switch (*input_ptr_) + { + JSONCONS_ILLEGAL_CONTROL_CHARACTER: + { + column_ += (input_ptr_ - sb + 1); + if (err_handler_.error(json_parser_errc::illegal_control_character, *this)) + { + ec = json_parser_errc::illegal_control_character; + state_ = parse_state::string_u1; + return; + } + // recovery - skip + auto result = unicons::validate(sb,input_ptr_); + if (result.ec != unicons::conv_errc()) + { + translate_conv_errc(result.ec,ec); + column_ += (result.it - sb); + return; + } + string_buffer_.append(sb,input_ptr_-sb); + ++input_ptr_; + state_ = parse_state::string_u1; + return; + } + case '\r': + { + column_ += (input_ptr_ - sb + 1); + if (err_handler_.error(json_parser_errc::illegal_character_in_string, *this)) + { + ec = json_parser_errc::illegal_character_in_string; + state_ = parse_state::string_u1; + return; + } + // recovery - keep + auto result = unicons::validate(sb,input_ptr_); + if (result.ec != unicons::conv_errc()) + { + translate_conv_errc(result.ec,ec); + column_ += (result.it - sb); + return; + } + string_buffer_.append(sb, input_ptr_ - sb + 1); + ++input_ptr_; + push_state(state_); + state_ = parse_state::cr; + return; + } + case '\n': + { + column_ += (input_ptr_ - sb + 1); + if (err_handler_.error(json_parser_errc::illegal_character_in_string, *this)) + { + ec = json_parser_errc::illegal_character_in_string; + state_ = parse_state::string_u1; + return; + } + // recovery - keep + auto result = unicons::validate(sb,input_ptr_); + if (result.ec != unicons::conv_errc()) + { + translate_conv_errc(result.ec,ec); + column_ += (result.it - sb); + return; + } + string_buffer_.append(sb, input_ptr_ - sb + 1); + ++input_ptr_; + push_state(state_); + state_ = parse_state::lf; + return; + } + case '\t': + { + column_ += (input_ptr_ - sb + 1); + if (err_handler_.error(json_parser_errc::illegal_character_in_string, *this)) + { + ec = json_parser_errc::illegal_character_in_string; + state_ = parse_state::string_u1; + return; + } + // recovery - keep + auto result = unicons::validate(sb,input_ptr_); + if (result.ec != unicons::conv_errc()) + { + translate_conv_errc(result.ec,ec); + column_ += (result.it - sb); + return; + } + string_buffer_.append(sb, input_ptr_ - sb + 1); + ++input_ptr_; + state_ = parse_state::string_u1; + return; + } + case '\\': + { + auto result = unicons::validate(sb,input_ptr_); + if (result.ec != unicons::conv_errc()) + { + translate_conv_errc(result.ec,ec); + column_ += (result.it - sb); + return; + } + string_buffer_.append(sb,input_ptr_-sb); + column_ += (input_ptr_ - sb + 1); + ++input_ptr_; + goto escape; + } + case '\"': + { + auto result = unicons::validate(sb,input_ptr_); + if (result.ec != unicons::conv_errc()) + { + translate_conv_errc(result.ec,ec); + column_ += (result.it - sb); + return; + } + if (string_buffer_.length() == 0) + { + end_string_value(sb,input_ptr_-sb, ec); + if (ec) {return;} + } + else + { + string_buffer_.append(sb,input_ptr_-sb); + end_string_value(string_buffer_.data(),string_buffer_.length(), ec); + string_buffer_.clear(); + if (ec) {return;} + } + column_ += (input_ptr_ - sb + 1); + ++input_ptr_; + return; + } + default: + break; + } + ++input_ptr_; + } + + // Buffer exhausted + { + auto result = unicons::validate(sb,input_ptr_); + if (result.ec != unicons::conv_errc()) + { + translate_conv_errc(result.ec,ec); + column_ += (result.it - sb); + return; + } + string_buffer_.append(sb,input_ptr_-sb); + column_ += (input_ptr_ - sb + 1); + state_ = parse_state::string_u1; + return; + } + +escape: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::escape; + return; + } + switch (*input_ptr_) + { + case '\"': + string_buffer_.push_back('\"'); + sb = ++input_ptr_; + ++column_; + goto string_u1; + case '\\': + string_buffer_.push_back('\\'); + sb = ++input_ptr_; + ++column_; + goto string_u1; + case '/': + string_buffer_.push_back('/'); + sb = ++input_ptr_; + ++column_; + goto string_u1; + case 'b': + string_buffer_.push_back('\b'); + sb = ++input_ptr_; + ++column_; + goto string_u1; + case 'f': + string_buffer_.push_back('\f'); + sb = ++input_ptr_; + ++column_; + goto string_u1; + case 'n': + string_buffer_.push_back('\n'); + sb = ++input_ptr_; + ++column_; + goto string_u1; + case 'r': + string_buffer_.push_back('\r'); + sb = ++input_ptr_; + ++column_; + goto string_u1; + case 't': + string_buffer_.push_back('\t'); + sb = ++input_ptr_; + ++column_; + goto string_u1; + case 'u': + cp_ = 0; + ++input_ptr_; + ++column_; + goto escape_u1; + default: + err_handler_.error(json_parser_errc::illegal_escaped_character, *this); + ec = json_parser_errc::illegal_escaped_character; + state_ = parse_state::escape; + return; + } + +escape_u1: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::escape_u1; + return; + } + { + append_codepoint(*input_ptr_,ec); + if (ec) + { + state_ = parse_state::escape_u1; + return; + } + ++input_ptr_; + ++column_; + goto escape_u2; + } + +escape_u2: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::escape_u2; + return; + } + { + append_codepoint(*input_ptr_, ec); + if (ec) + { + state_ = parse_state::escape_u2; + return; + } + ++input_ptr_; + ++column_; + goto escape_u3; + } + +escape_u3: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::escape_u3; + return; + } + { + append_codepoint(*input_ptr_, ec); + if (ec) + { + state_ = parse_state::escape_u3; + return; + } + ++input_ptr_; + ++column_; + goto escape_u4; + } + +escape_u4: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::escape_u4; + return; + } + { + append_codepoint(*input_ptr_, ec); + if (ec) + { + state_ = parse_state::escape_u4; + return; + } + if (unicons::is_high_surrogate(cp_)) + { + ++input_ptr_; + ++column_; + goto escape_expect_surrogate_pair1; + } + else + { + unicons::convert(&cp_, &cp_ + 1, std::back_inserter(string_buffer_)); + sb = ++input_ptr_; + ++column_; + state_ = parse_state::string_u1; + return; + } + } + +escape_expect_surrogate_pair1: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::escape_expect_surrogate_pair1; + return; + } + { + switch (*input_ptr_) + { + case '\\': + cp2_ = 0; + ++input_ptr_; + ++column_; + goto escape_expect_surrogate_pair2; + default: + err_handler_.error(json_parser_errc::expected_codepoint_surrogate_pair, *this); + ec = json_parser_errc::expected_codepoint_surrogate_pair; + state_ = parse_state::escape_expect_surrogate_pair1; + return; + } + } + +escape_expect_surrogate_pair2: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::escape_expect_surrogate_pair2; + return; + } + { + switch (*input_ptr_) + { + case 'u': + ++input_ptr_; + ++column_; + goto escape_u6; + default: + err_handler_.error(json_parser_errc::expected_codepoint_surrogate_pair, *this); + ec = json_parser_errc::expected_codepoint_surrogate_pair; + state_ = parse_state::escape_expect_surrogate_pair2; + return; + } + } + +escape_u6: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::escape_u6; + return; + } + { + append_second_codepoint(*input_ptr_, ec); + if (ec) + { + state_ = parse_state::escape_u6; + return; + } + } + ++input_ptr_; + ++column_; + goto escape_u7; + +escape_u7: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::escape_u7; + return; + } + { + append_second_codepoint(*input_ptr_, ec); + if (ec) + { + state_ = parse_state::escape_u7; + return; + } + ++input_ptr_; + ++column_; + goto escape_u8; + } + +escape_u8: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::escape_u8; + return; + } + { + append_second_codepoint(*input_ptr_, ec); + if (ec) + { + state_ = parse_state::escape_u8; + return; + } + ++input_ptr_; + ++column_; + goto escape_u9; + } + +escape_u9: + if (JSONCONS_UNLIKELY(input_ptr_ >= local_input_end)) // Buffer exhausted + { + state_ = parse_state::escape_u9; + return; + } + { + append_second_codepoint(*input_ptr_, ec); + if (ec) + { + state_ = parse_state::escape_u9; + return; + } + uint32_t cp = 0x10000 + ((cp_ & 0x3FF) << 10) + (cp2_ & 0x3FF); + unicons::convert(&cp, &cp + 1, std::back_inserter(string_buffer_)); + sb = ++input_ptr_; + ++column_; + goto string_u1; + } + + JSONCONS_UNREACHABLE(); + } + + void translate_conv_errc(unicons::conv_errc result, std::error_code& ec) + { + switch (result) + { + case unicons::conv_errc(): + break; + case unicons::conv_errc::over_long_utf8_sequence: + if (err_handler_.error(json_parser_errc::over_long_utf8_sequence, *this)) + { + ec = json_parser_errc::over_long_utf8_sequence; + return; + } + break; + case unicons::conv_errc::unpaired_high_surrogate: + if (err_handler_.error(json_parser_errc::unpaired_high_surrogate, *this)) + { + ec = json_parser_errc::unpaired_high_surrogate; + return; + } + break; + case unicons::conv_errc::expected_continuation_byte: + if (err_handler_.error(json_parser_errc::expected_continuation_byte, *this)) + { + ec = json_parser_errc::expected_continuation_byte; + return; + } + break; + case unicons::conv_errc::illegal_surrogate_value: + if (err_handler_.error(json_parser_errc::illegal_surrogate_value, *this)) + { + ec = json_parser_errc::illegal_surrogate_value; + return; + } + break; + default: + if (err_handler_.error(json_parser_errc::illegal_codepoint, *this)) + { + ec = json_parser_errc::illegal_codepoint; + return; + } + break; + } + } + + void parse_some() + { + std::error_code ec; + parse_some(ec); + if (ec) + { + throw parse_error(ec,line_,column_); + } + } + + void end_parse() + { + std::error_code ec; + end_parse(ec); + if (ec) + { + throw parse_error(ec,line_,column_); + } + } + + void end_parse(std::error_code& ec) + { + if (parent() == parse_state::root) + { + switch (state_) + { + case parse_state::zero: + case parse_state::integer: + end_integer_value(ec); + if (ec) return; + break; + case parse_state::fraction2: + end_fraction_value(chars_format::fixed,ec); + if (ec) return; + break; + case parse_state::exp3: + end_fraction_value(chars_format::scientific,ec); + if (ec) return; + break; + default: + break; + } + } + if (state_ == parse_state::lf || state_ == parse_state::cr) + { + state_ = pop_state(); + } + if (!(state_ == parse_state::done || state_ == parse_state::start)) + { + if (err_handler_.error(json_parser_errc::unexpected_eof, *this)) + { + ec = json_parser_errc::unexpected_eof; + return; + } + } + } + + parse_state state() const + { + return state_; + } + + void set_source(const CharT* input, size_t length) + { + begin_input_ = input; + input_end_ = input + length; + input_ptr_ = begin_input_; + } +private: + + void end_integer_value(std::error_code& ec) + { + if (is_negative_) + { + end_negative_value(ec); + } + else + { + end_positive_value(ec); + } + } + + void end_negative_value(std::error_code& ec) + { + static const int64_t min_value = (std::numeric_limits::min)(); + static const int64_t min_value_div_10 = min_value / 10; + + const char* s = number_buffer_.data(); + size_t length = number_buffer_.length(); + int64_t n = 0; + bool overflow = false; + const char* end = s + length; + for (; s < end; ++s) + { + int64_t x = *s - '0'; + if (n < min_value_div_10) + { + overflow = true; + break; + } + n = n * 10; + if (n < min_value + x) + { + overflow = true; + break; + } + + n -= x; + } + + if (!overflow) + { + handler_.integer_value(n, *this); + + switch (parent()) + { + case parse_state::array: + case parse_state::object: + state_ = parse_state::expect_comma_or_end; + break; + case parse_state::root: + state_ = parse_state::done; + handler_.end_json(); + break; + default: + if (err_handler_.error(json_parser_errc::invalid_json_text, *this)) + { + ec = json_parser_errc::invalid_json_text; + return; + } + break; + } + } + else + { + end_fraction_value(chars_format::general,ec); + } + } + + void end_positive_value(std::error_code& ec) + { + static const uint64_t max_value = (std::numeric_limits::max)(); + static const uint64_t max_value_div_10 = max_value / 10; + uint64_t n = 0; + bool overflow = false; + const char* s = number_buffer_.data(); + size_t length = number_buffer_.length(); + + const char* end = s + length; + for (; s < end; ++s) + { + uint64_t x = *s - '0'; + if (n > max_value_div_10) + { + overflow = true; + break; + } + n = n * 10; + if (n > max_value - x) + { + overflow = true; + break; + } + + n += x; + } + + if (!overflow) + { + handler_.uinteger_value(n, *this); + + switch (parent()) + { + case parse_state::array: + case parse_state::object: + state_ = parse_state::expect_comma_or_end; + break; + case parse_state::root: + state_ = parse_state::done; + handler_.end_json(); + break; + default: + if (err_handler_.error(json_parser_errc::invalid_json_text, *this)) + { + ec = json_parser_errc::invalid_json_text; + return; + } + break; + } + } + else + { + end_fraction_value(chars_format::general,ec); + } + } + + void end_fraction_value(chars_format format, std::error_code& ec) + { + try + { + double d = to_double_(number_buffer_.c_str(), number_buffer_.length()); + if (is_negative_) + d = -d; + + if (precision_ > std::numeric_limits::max_digits10) + { + handler_.double_value(d, number_format(format,std::numeric_limits::max_digits10, decimal_places_), *this); + } + else + { + handler_.double_value(d, number_format(format,static_cast(precision_), decimal_places_), *this); + } + } + catch (...) + { + if (err_handler_.error(json_parser_errc::invalid_number, *this)) + { + ec = json_parser_errc::invalid_number; + return; + } + handler_.null_value(*this); // recovery + } + + switch (parent()) + { + case parse_state::array: + case parse_state::object: + state_ = parse_state::expect_comma_or_end; + break; + case parse_state::root: + state_ = parse_state::done; + handler_.end_json(); + break; + default: + if (err_handler_.error(json_parser_errc::invalid_json_text, *this)) + { + ec = json_parser_errc::invalid_json_text; + return; + } + break; + } + } + + void append_codepoint(int c, std::error_code& ec) + { + switch (c) + { + case '0': case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + cp_ = append_to_codepoint(cp_, c, ec); + if (ec) return; + break; + default: + if (err_handler_.error(json_parser_errc::expected_value, *this)) + { + ec = json_parser_errc::expected_value; + return; + } + break; + } + } + + void append_second_codepoint(int c, std::error_code& ec) + { + switch (c) + { + case '0': + case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8': case '9': + case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': + case 'A':case 'B':case 'C':case 'D':case 'E':case 'F': + cp2_ = append_to_codepoint(cp2_, c, ec); + if (ec) return; + break; + default: + if (err_handler_.error(json_parser_errc::expected_value, *this)) + { + ec = json_parser_errc::expected_value; + return; + } + break; + } + } + + void end_string_value(const CharT* s, size_t length, std::error_code& ec) + { + switch (parent()) + { + case parse_state::member_name: + handler_.name(string_view_type(s, length), *this); + state_ = pop_state(); + state_ = parse_state::expect_colon; + break; + case parse_state::object: + case parse_state::array: + handler_.string_value(string_view_type(s, length), *this); + state_ = parse_state::expect_comma_or_end; + break; + case parse_state::root: + handler_.string_value(string_view_type(s, length), *this); + state_ = parse_state::done; + handler_.end_json(); + break; + default: + if (err_handler_.error(json_parser_errc::invalid_json_text, *this)) + { + ec = json_parser_errc::invalid_json_text; + return; + } + break; + } + } + + void begin_member_or_element(std::error_code& ec) + { + switch (parent()) + { + case parse_state::object: + state_ = parse_state::expect_member_name; + break; + case parse_state::array: + state_ = parse_state::expect_value; + break; + case parse_state::root: + break; + default: + if (err_handler_.error(json_parser_errc::invalid_json_text, *this)) + { + ec = json_parser_errc::invalid_json_text; + return; + } + break; + } + } + + void push_state(parse_state state) + { + state_stack_.push_back(state); + } + + parse_state pop_state() + { + JSONCONS_ASSERT(!state_stack_.empty()) + parse_state state = state_stack_.back(); + state_stack_.pop_back(); + return state; + } + + uint32_t append_to_codepoint(uint32_t cp, int c, std::error_code& ec) + { + cp *= 16; + if (c >= '0' && c <= '9') + { + cp += c - '0'; + } + else if (c >= 'a' && c <= 'f') + { + cp += c - 'a' + 10; + } + else if (c >= 'A' && c <= 'F') + { + cp += c - 'A' + 10; + } + else + { + if (err_handler_.error(json_parser_errc::invalid_hex_escape_sequence, *this)) + { + ec = json_parser_errc::invalid_hex_escape_sequence; + return cp; + } + } + return cp; + } + + size_t do_line_number() const override + { + return line_; + } + + size_t do_column_number() const override + { + return column_; + } +}; + +typedef basic_json_parser json_parser; +typedef basic_json_parser wjson_parser; + +} + +#endif + diff --git a/direct-access-api/include/json_query.hpp b/direct-access-api/include/json_query.hpp new file mode 100644 index 0000000..8356d56 --- /dev/null +++ b/direct-access-api/include/json_query.hpp @@ -0,0 +1,1119 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATH_JSONQUERY_HPP +#define JSONCONS_JSONPATH_JSONQUERY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include "jsonpath_filter.hpp" +#include "jsonpath_error_category.hpp" + +namespace jsoncons { namespace jsonpath { + +enum class result_type {value,path}; + +template +Json json_query(const Json& root, const typename Json::string_view_type& path, result_type result_t = result_type::value) +{ + if (result_t == result_type::value) + { + detail::jsonpath_evaluator> evaluator; + evaluator.evaluate(root,path.data(),path.length()); + return evaluator.get_values(); + } + else + { + detail::jsonpath_evaluator> evaluator; + evaluator.evaluate(root,path.data(),path.length()); + return evaluator.get_normalized_paths(); + } +} + +template +void json_replace(Json& root, const typename Json::string_view_type& path, T&& new_value) +{ + detail::jsonpath_evaluator> evaluator; + evaluator.evaluate(root,path.data(),path.length()); + evaluator.replace(std::forward(new_value)); +} + +namespace detail { + +template +bool try_string_to_index(const CharT *s, size_t length, size_t* value, bool* positive) +{ + static const size_t max_value = (std::numeric_limits::max)(); + static const size_t max_value_div_10 = max_value / 10; + + size_t start = 0; + size_t n = 0; + if (length > 0) + { + if (s[start] == '-') + { + *positive = false; + ++start; + } + else + { + *positive = true; + } + } + if (length > start) + { + for (size_t i = start; i < length; ++i) + { + CharT c = s[i]; + switch (c) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + { + size_t x = c - '0'; + if (n > max_value_div_10) + { + return false; + } + n = n * 10; + if (n > max_value - x) + { + return false; + } + + n += x; + } + break; + default: + return false; + break; + } + } + *value = n; + return true; + } + else + { + return false; + } +} + +enum class path_state +{ + start, + cr, + lf, + expect_dot_or_left_bracket, + expect_unquoted_name_or_left_bracket, + unquoted_name, + left_bracket_single_quoted_string, + left_bracket_double_quoted_string, + left_bracket, + left_bracket_start, + left_bracket_end, + left_bracket_end2, + left_bracket_step, + left_bracket_step2, + expect_comma_or_right_bracket, + dot +}; + +template> +class jsonpath_evaluator : private serializing_context +{ +private: + typedef typename Json::char_type char_type; + typedef typename Json::char_traits_type char_traits_type; + typedef std::basic_string string_type; + typedef typename Json::string_view_type string_view_type; + typedef JsonReference reference; + using pointer = typename std::conditional::type>::value,typename Json::const_pointer,typename Json::pointer>::type; + struct node_type + { + node_type() = default; + node_type(const string_type& p, const pointer& valp) + : skip_contained_object(false),path(p),val_ptr(valp) + { + } + node_type(string_type&& p, pointer&& valp) + : skip_contained_object(false),path(std::move(p)),val_ptr(valp) + { + } + node_type(const node_type&) = default; + node_type(node_type&&) = default; + + bool skip_contained_object; + string_type path; + pointer val_ptr; + }; + typedef std::vector node_set; + + static string_view_type length_literal() + { + static const char_type data[] = {'l','e','n','g','t','h'}; + return string_view_type{data,sizeof(data)/sizeof(char_type)}; + } + + class selector + { + public: + virtual ~selector() + { + } + virtual void select(node_type& node, const string_type& path, reference val, + node_set& nodes, std::vector>& temp_json_values) = 0; + }; + + class expr_selector final : public selector + { + private: + jsonpath_filter_expr result_; + public: + expr_selector(const jsonpath_filter_expr& result) + : result_(result) + { + } + + void select(node_type& node, const string_type& path, reference val, + node_set& nodes, std::vector>& temp_json_values) override + { + auto index = result_.eval(val); + if (index.template is()) + { + size_t start = index. template as(); + if (val.is_array() && start < val.size()) + { + nodes.emplace_back(PathCons()(path,start),std::addressof(val[start])); + } + } + else if (index.is_string()) + { + name_selector selector(index.as_string_view(),true); + selector.select(node, path, val, nodes, temp_json_values); + } + } + }; + + class filter_selector final : public selector + { + private: + jsonpath_filter_expr result_; + public: + filter_selector(const jsonpath_filter_expr& result) + : result_(result) + { + } + + void select(node_type& node, const string_type& path, reference val, + node_set& nodes, std::vector>&) override + { + if (val.is_array()) + { + node.skip_contained_object =true; + for (size_t i = 0; i < val.size(); ++i) + { + if (result_.exists(val[i])) + { + nodes.emplace_back(PathCons()(path,i),std::addressof(val[i])); + } + } + } + else if (val.is_object()) + { + if (!node.skip_contained_object) + { + if (result_.exists(val)) + { + nodes.emplace_back(path, std::addressof(val)); + } + } + else + { + node.skip_contained_object = false; + } + } + } + }; + + class name_selector final : public selector + { + private: + string_type name_; + bool positive_start_; + public: + name_selector(const string_view_type& name, bool positive_start) + : name_(name), positive_start_(positive_start) + { + } + + void select(node_type& node, const string_type& path, reference val, + node_set& nodes, + std::vector>& temp_json_values) override + { + if (val.is_object() && val.count(name_) > 0) + { + nodes.emplace_back(PathCons()(path,name_),std::addressof(val.at(name_))); + } + else if (val.is_array()) + { + size_t pos = 0; + if (try_string_to_index(name_.data(), name_.size(), &pos, &positive_start_)) + { + size_t index = positive_start_ ? pos : val.size() - pos; + if (index < val.size()) + { + nodes.emplace_back(PathCons()(path,index),std::addressof(val[index])); + } + } + else if (name_ == length_literal() && val.size() > 0) + { + auto temp = std::make_shared(val.size()); + temp_json_values.push_back(temp); + nodes.emplace_back(PathCons()(path,name_),temp.get()); + } + } + else if (val.is_string()) + { + size_t pos = 0; + string_view_type sv = val.as_string_view(); + if (try_string_to_index(name_.data(), name_.size(), &pos, &positive_start_)) + { + size_t index = positive_start_ ? pos : sv.size() - pos; + auto sequence = unicons::sequence_at(sv.data(), sv.data() + sv.size(), index); + if (sequence.length() > 0) + { + auto temp = std::make_shared(sequence.begin(),sequence.length()); + temp_json_values.push_back(temp); + nodes.emplace_back(PathCons()(path,index),temp.get()); + } + } + else if (name_ == length_literal() && sv.size() > 0) + { + size_t count = unicons::u32_length(sv.begin(),sv.end()); + auto temp = std::make_shared(count); + temp_json_values.push_back(temp); + nodes.emplace_back(PathCons()(path,name_),temp.get()); + } + } + } + }; + + class array_slice_selector final : public selector + { + private: + size_t start_; + bool positive_start_; + size_t end_; + bool positive_end_; + bool undefined_end_; + size_t step_; + bool positive_step_; + public: + array_slice_selector(size_t start, bool positive_start, + size_t end, bool positive_end, + size_t step, bool positive_step, + bool undefined_end) + : start_(start), positive_start_(positive_start), + end_(end), positive_end_(positive_end),undefined_end_(undefined_end), + step_(step), positive_step_(positive_step) + { + } + + void select(node_type& node, const string_type& path, reference val, + node_set& nodes, + std::vector>&) override + { + if (positive_step_) + { + end_array_slice1(path, val, nodes); + } + else + { + end_array_slice2(path, val, nodes); + } + } + + void end_array_slice1(const string_type& path, reference val, node_set& nodes) + { + if (val.is_array()) + { + size_t start = positive_start_ ? start_ : val.size() - start_; + size_t end; + if (!undefined_end_) + { + end = positive_end_ ? end_ : val.size() - end_; + } + else + { + end = val.size(); + } + for (size_t j = start; j < end; j += step_) + { + if (j < val.size()) + { + nodes.emplace_back(PathCons()(path,j),std::addressof(val[j])); + } + } + } + } + + void end_array_slice2(const string_type& path, reference val, node_set& nodes) + { + if (val.is_array()) + { + size_t start = positive_start_ ? start_ : val.size() - start_; + size_t end; + if (!undefined_end_) + { + end = positive_end_ ? end_ : val.size() - end_; + } + else + { + end = val.size(); + } + + size_t j = end + step_ - 1; + while (j > (start+step_-1)) + { + j -= step_; + if (j < val.size()) + { + nodes.emplace_back(PathCons()(path,j),std::addressof(val[j])); + } + } + } + } + }; + + default_parse_error_handler default_err_handler_; + parse_error_handler *err_handler_; + path_state state_; + string_type buffer_; + size_t start_; + bool positive_start_; + size_t end_; + bool positive_end_; + bool undefined_end_; + size_t step_; + bool positive_step_; + bool recursive_descent_; + node_set nodes_; + std::vector stack_; + std::vector> temp_json_values_; + size_t line_; + size_t column_; + const char_type* begin_input_; + const char_type* end_input_; + const char_type* p_; + std::vector> selectors_; + +public: + jsonpath_evaluator() + : err_handler_(&default_err_handler_), + state_(path_state::start), + start_(0), positive_start_(true), + end_(0), positive_end_(true), undefined_end_(false), + step_(0), positive_step_(true), + recursive_descent_(false), + line_(0), column_(0), + begin_input_(nullptr), end_input_(nullptr), + p_(nullptr) + { + } + + Json get_values() const + { + Json result = typename Json::array(); + + if (stack_.size() > 0) + { + result.reserve(stack_.back().size()); + for (const auto& p : stack_.back()) + { + result.push_back(*(p.val_ptr)); + } + } + return result; + } + + Json get_normalized_paths() const + { + Json result = typename Json::array(); + if (stack_.size() > 0) + { + result.reserve(stack_.back().size()); + for (const auto& p : stack_.back()) + { + result.push_back(p.path); + } + } + return result; + } + + template + void replace(T&& new_value) + { + if (stack_.size() > 0) + { + for (size_t i = 0; i < stack_.back().size(); ++i) + { + *(stack_.back()[i].val_ptr) = new_value; + } + } + } + + void evaluate(reference root, const string_view_type& path) + { + evaluate(root,path.data(),path.length()); + } + void evaluate(reference root, const char_type* path) + { + evaluate(root,path,char_traits_type::length(path)); + } + + void evaluate(reference root, + const char_type* path, + size_t length) + { + std::error_code ec; + evaluate(root, path, length, ec); + if (ec) + { + throw parse_error(ec,line_,column_); + } + } + + void evaluate(reference root, + const char_type* path, + size_t length, + std::error_code& ec) + { + path_state pre_line_break_state = path_state::start; + + begin_input_ = path; + end_input_ = path + length; + p_ = begin_input_; + + line_ = 1; + column_ = 1; + state_ = path_state::start; + + recursive_descent_ = false; + + clear_index(); + + while (p_ < end_input_) + { + switch (state_) + { + case path_state::cr: + ++line_; + column_ = 1; + switch (*p_) + { + case '\n': + state_ = pre_line_break_state; + ++p_; + ++column_; + break; + default: + state_ = pre_line_break_state; + break; + } + break; + case path_state::lf: + ++line_; + column_ = 1; + state_ = pre_line_break_state; + break; + case path_state::start: + switch (*p_) + { + case ' ':case '\t': + break; + case '$': + case '@': + { + string_type s; + s.push_back('$'); + node_set v; + v.emplace_back(std::move(s),std::addressof(root)); + stack_.push_back(v); + + state_ = path_state::expect_dot_or_left_bracket; + } + break; + default: + err_handler_->fatal_error(jsonpath_parser_errc::expected_root, *this); + ec = jsonpath_parser_errc::expected_root; + return; + }; + ++p_; + ++column_; + break; + case path_state::dot: + switch (*p_) + { + case '.': + recursive_descent_ = true; + ++p_; + ++column_; + state_ = path_state::expect_unquoted_name_or_left_bracket; + break; + default: + state_ = path_state::expect_unquoted_name_or_left_bracket; + break; + } + break; + case path_state::expect_unquoted_name_or_left_bracket: + switch (*p_) + { + case '.': + err_handler_->fatal_error(jsonpath_parser_errc::expected_name, *this); + ec = jsonpath_parser_errc::expected_name; + return; + case '*': + end_all(); + transfer_nodes(); + state_ = path_state::expect_dot_or_left_bracket; + ++p_; + ++column_; + break; + case '[': + state_ = path_state::left_bracket; + ++p_; + ++column_; + break; + default: + buffer_.clear(); + state_ = path_state::unquoted_name; + break; + } + break; + case path_state::expect_dot_or_left_bracket: + switch (*p_) + { + case ' ':case '\t': + break; + case '.': + state_ = path_state::dot; + break; + case '[': + state_ = path_state::left_bracket; + break; + default: + err_handler_->fatal_error(jsonpath_parser_errc::expected_separator, *this); + ec = jsonpath_parser_errc::expected_separator; + return; + }; + ++p_; + ++column_; + break; + case path_state::expect_comma_or_right_bracket: + switch (*p_) + { + case ',': + state_ = path_state::left_bracket; + break; + case ']': + apply_selectors(); + state_ = path_state::expect_dot_or_left_bracket; + break; + case ' ':case '\t': + break; + default: + err_handler_->fatal_error(jsonpath_parser_errc::expected_right_bracket, *this); + ec = jsonpath_parser_errc::expected_right_bracket; + return; + } + ++p_; + ++column_; + break; + case path_state::left_bracket: + switch (*p_) + { + case ' ':case '\t': + ++p_; + ++column_; + break; + case '(': + { + jsonpath_filter_parser parser(line_,column_); + auto result = parser.parse(root, p_,end_input_,&p_); + line_ = parser.line(); + column_ = parser.column(); + selectors_.push_back(std::make_shared(result)); + state_ = path_state::expect_comma_or_right_bracket; + } + break; + case '?': + { + jsonpath_filter_parser parser(line_,column_); + auto result = parser.parse(root,p_,end_input_,&p_); + line_ = parser.line(); + column_ = parser.column(); + selectors_.push_back(std::make_shared(result)); + state_ = path_state::expect_comma_or_right_bracket; + } + break; + case ':': + clear_index(); + state_ = path_state::left_bracket_end; + ++p_; + ++column_; + break; + case '*': + end_all(); + state_ = path_state::expect_comma_or_right_bracket; + ++p_; + ++column_; + break; + case '\'': + state_ = path_state::left_bracket_single_quoted_string; + ++p_; + ++column_; + break; + case '\"': + state_ = path_state::left_bracket_double_quoted_string; + ++p_; + ++column_; + break; + default: + clear_index(); + buffer_.push_back(*p_); + state_ = path_state::left_bracket_start; + ++p_; + ++column_; + break; + } + break; + case path_state::left_bracket_start: + switch (*p_) + { + case ':': + if (!try_string_to_index(buffer_.data(), buffer_.size(), &start_, &positive_start_)) + { + err_handler_->fatal_error(jsonpath_parser_errc::expected_index, *this); + ec = jsonpath_parser_errc::expected_index; + return; + } + state_ = path_state::left_bracket_end; + break; + case ',': + selectors_.push_back(std::make_shared(buffer_,positive_start_)); + buffer_.clear(); + state_ = path_state::left_bracket; + break; + case ']': + selectors_.push_back(std::make_shared(buffer_,positive_start_)); + buffer_.clear(); + apply_selectors(); + state_ = path_state::expect_dot_or_left_bracket; + break; + default: + buffer_.push_back(*p_); + break; + } + ++p_; + ++column_; + break; + case path_state::left_bracket_end: + switch (*p_) + { + case '-': + positive_end_ = false; + state_ = path_state::left_bracket_end2; + break; + case ':': + step_ = 0; + state_ = path_state::left_bracket_step; + break; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + undefined_end_ = false; + end_ = static_cast(*p_-'0'); + state_ = path_state::left_bracket_end2; + break; + case ',': + selectors_.push_back(std::make_shared(start_,positive_start_,end_,positive_end_,step_,positive_step_,undefined_end_)); + state_ = path_state::left_bracket; + break; + case ']': + selectors_.push_back(std::make_shared(start_,positive_start_,end_,positive_end_,step_,positive_step_,undefined_end_)); + apply_selectors(); + state_ = path_state::expect_dot_or_left_bracket; + break; + } + ++p_; + ++column_; + break; + case path_state::left_bracket_end2: + switch (*p_) + { + case ':': + step_ = 0; + state_ = path_state::left_bracket_step; + break; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + undefined_end_ = false; + end_ = end_*10 + static_cast(*p_-'0'); + break; + case ',': + selectors_.push_back(std::make_shared(start_,positive_start_,end_,positive_end_,step_,positive_step_,undefined_end_)); + state_ = path_state::left_bracket; + break; + case ']': + selectors_.push_back(std::make_shared(start_,positive_start_,end_,positive_end_,step_,positive_step_,undefined_end_)); + apply_selectors(); + state_ = path_state::expect_dot_or_left_bracket; + break; + } + ++p_; + ++column_; + break; + case path_state::left_bracket_step: + switch (*p_) + { + case '-': + positive_step_ = false; + state_ = path_state::left_bracket_step2; + break; + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + step_ = static_cast(*p_-'0'); + state_ = path_state::left_bracket_step2; + break; + case ',': + selectors_.push_back(std::make_shared(start_,positive_start_,end_,positive_end_,step_,positive_step_,undefined_end_)); + state_ = path_state::left_bracket; + break; + case ']': + selectors_.push_back(std::make_shared(start_,positive_start_,end_,positive_end_,step_,positive_step_,undefined_end_)); + apply_selectors(); + state_ = path_state::expect_dot_or_left_bracket; + break; + } + ++p_; + ++column_; + break; + case path_state::left_bracket_step2: + switch (*p_) + { + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + step_ = step_*10 + static_cast(*p_-'0'); + break; + case ',': + selectors_.push_back(std::make_shared(start_,positive_start_,end_,positive_end_,step_,positive_step_,undefined_end_)); + state_ = path_state::left_bracket; + break; + case ']': + selectors_.push_back(std::make_shared(start_,positive_start_,end_,positive_end_,step_,positive_step_,undefined_end_)); + apply_selectors(); + state_ = path_state::expect_dot_or_left_bracket; + break; + } + ++p_; + ++column_; + break; + case path_state::unquoted_name: + switch (*p_) + { + case '[': + apply_unquoted_string(buffer_); + transfer_nodes(); + start_ = 0; + state_ = path_state::left_bracket; + break; + case '.': + apply_unquoted_string(buffer_); + transfer_nodes(); + state_ = path_state::dot; + break; + case ' ':case '\t': + apply_unquoted_string(buffer_); + transfer_nodes(); + state_ = path_state::expect_dot_or_left_bracket; + break; + case '\r': + apply_unquoted_string(buffer_); + transfer_nodes(); + pre_line_break_state = path_state::expect_dot_or_left_bracket; + state_= path_state::cr; + break; + case '\n': + apply_unquoted_string(buffer_); + transfer_nodes(); + pre_line_break_state = path_state::expect_dot_or_left_bracket; + state_= path_state::lf; + break; + default: + buffer_.push_back(*p_); + break; + }; + ++p_; + ++column_; + break; + case path_state::left_bracket_single_quoted_string: + switch (*p_) + { + case '\'': + selectors_.push_back(std::make_shared(buffer_,positive_start_)); + buffer_.clear(); + state_ = path_state::expect_comma_or_right_bracket; + break; + case '\\': + buffer_.push_back(*p_); + if (p_+1 < end_input_) + { + ++p_; + ++column_; + buffer_.push_back(*p_); + } + break; + default: + buffer_.push_back(*p_); + break; + }; + ++p_; + ++column_; + break; + case path_state::left_bracket_double_quoted_string: + switch (*p_) + { + case '\"': + selectors_.push_back(std::make_shared(buffer_,positive_start_)); + buffer_.clear(); + state_ = path_state::expect_comma_or_right_bracket; + break; + case '\\': + buffer_.push_back(*p_); + if (p_+1 < end_input_) + { + ++p_; + ++column_; + buffer_.push_back(*p_); + } + break; + default: + buffer_.push_back(*p_); + break; + }; + ++p_; + ++column_; + break; + default: + ++p_; + ++column_; + break; + } + } + switch (state_) + { + case path_state::unquoted_name: + { + apply_unquoted_string(buffer_); + transfer_nodes(); + } + break; + default: + break; + } + } + + void clear_index() + { + buffer_.clear(); + start_ = 0; + positive_start_ = true; + end_ = 0; + positive_end_ = true; + undefined_end_ = true; + step_ = 1; + positive_step_ = true; + } + + void end_all() + { + for (size_t i = 0; i < stack_.back().size(); ++i) + { + const auto& path = stack_.back()[i].path; + pointer p = stack_.back()[i].val_ptr; + + if (p->is_array()) + { + for (auto it = p->array_range().begin(); it != p->array_range().end(); ++it) + { + nodes_.emplace_back(PathCons()(path,it - p->array_range().begin()),std::addressof(*it)); + } + } + else if (p->is_object()) + { + for (auto it = p->object_range().begin(); it != p->object_range().end(); ++it) + { + nodes_.emplace_back(PathCons()(path,it->key()),std::addressof(it->value())); + } + } + + } + start_ = 0; + } + + void apply_unquoted_string(const string_view_type& name) + { + if (name.length() > 0) + { + for (size_t i = 0; i < stack_.back().size(); ++i) + { + apply_unquoted_string(stack_.back()[i].path, *(stack_.back()[i].val_ptr), name); + } + } + buffer_.clear(); + } + + void apply_unquoted_string(const string_type& path, reference val, const string_view_type& name) + { + if (val.is_object()) + { + if (val.count(name) > 0) + { + nodes_.emplace_back(PathCons()(path,name),std::addressof(val.at(name))); + } + if (recursive_descent_) + { + for (auto it = val.object_range().begin(); it != val.object_range().end(); ++it) + { + if (it->value().is_object() || it->value().is_array()) + { + apply_unquoted_string(path, it->value(), name); + } + } + } + } + else if (val.is_array()) + { + size_t pos = 0; + if (try_string_to_index(name.data(),name.size(),&pos, &positive_start_)) + { + size_t index = positive_start_ ? pos : val.size() - pos; + if (index < val.size()) + { + nodes_.emplace_back(PathCons()(path,index),std::addressof(val[index])); + } + } + else if (name == length_literal() && val.size() > 0) + { + auto temp = std::make_shared(val.size()); + temp_json_values_.push_back(temp); + nodes_.emplace_back(PathCons()(path,name),temp.get()); + } + if (recursive_descent_) + { + for (auto it = val.array_range().begin(); it != val.array_range().end(); ++it) + { + if (it->is_object() || it->is_array()) + { + apply_unquoted_string(path, *it, name); + } + } + } + } + else if (val.is_string()) + { + string_view_type sv = val.as_string_view(); + size_t pos = 0; + if (try_string_to_index(name.data(),name.size(),&pos, &positive_start_)) + { + auto sequence = unicons::sequence_at(sv.data(), sv.data() + sv.size(), pos); + if (sequence.length() > 0) + { + auto temp = std::make_shared(sequence.begin(),sequence.length()); + temp_json_values_.push_back(temp); + nodes_.emplace_back(PathCons()(path,pos),temp.get()); + } + } + else if (name == length_literal() && sv.size() > 0) + { + size_t count = unicons::u32_length(sv.begin(),sv.end()); + auto temp = std::make_shared(count); + temp_json_values_.push_back(temp); + nodes_.emplace_back(PathCons()(path,name),temp.get()); + } + } + } + + void apply_selectors() + { + if (selectors_.size() > 0) + { + for (size_t i = 0; i < stack_.back().size(); ++i) + { + node_type& node = stack_.back()[i]; + apply_selectors(node, node.path, *(node.val_ptr)); + } + selectors_.clear(); + } + transfer_nodes(); + } + + void apply_selectors(node_type& node, const string_type& path, reference val) + { + for (const auto& selector : selectors_) + { + selector->select(node, path, val, nodes_, temp_json_values_); + } + if (recursive_descent_) + { + if (val.is_object()) + { + for (auto& nvp : val.object_range()) + { + if (nvp.value().is_object() || nvp.value().is_array()) + { + apply_selectors(node,PathCons()(path,nvp.key()),nvp.value()); + } + } + } + else if (val.is_array()) + { + for (auto& elem : val.array_range()) + { + if (elem.is_object() || elem.is_array()) + { + apply_selectors(node,path, elem); + } + } + } + } + } + + void transfer_nodes() + { + stack_.push_back(nodes_); + nodes_.clear(); + recursive_descent_ = false; + } + + size_t do_line_number() const override + { + return line_; + } + + size_t do_column_number() const override + { + return column_; + } + +}; + +} + +}} + +#endif diff --git a/direct-access-api/include/json_reader.hpp b/direct-access-api/include/json_reader.hpp new file mode 100644 index 0000000..e7c32bd --- /dev/null +++ b/direct-access-api/include/json_reader.hpp @@ -0,0 +1,456 @@ +// Copyright 2015 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_READER_HPP +#define JSONCONS_JSON_READER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +// utf8_other_json_input_adapter + +template +class json_utf8_other_content_handler_adapter : public json_content_handler +{ +public: + using json_content_handler::string_view_type; +private: + basic_null_json_content_handler default_content_handler_; + basic_json_content_handler& other_handler_; + //parse_error_handler& err_handler_; + + // noncopyable and nonmoveable + json_utf8_other_content_handler_adapter(const json_utf8_other_content_handler_adapter&) = delete; + json_utf8_other_content_handler_adapter& operator=(const json_utf8_other_content_handler_adapter&) = delete; + +public: + json_utf8_other_content_handler_adapter() + : other_handler_(default_content_handler_) + { + } + + json_utf8_other_content_handler_adapter(basic_json_content_handler& other_handler/*, + parse_error_handler& err_handler*/) + : other_handler_(other_handler)/*, + err_handler_(err_handler)*/ + { + } + +private: + + void do_begin_json() override + { + other_handler_.begin_json(); + } + + void do_end_json() override + { + other_handler_.end_json(); + } + + void do_begin_object(const serializing_context& context) override + { + other_handler_.begin_object(context); + } + + void do_end_object(const serializing_context& context) override + { + other_handler_.end_object(context); + } + + void do_begin_array(const serializing_context& context) override + { + other_handler_.begin_array(context); + } + + void do_end_array(const serializing_context& context) override + { + other_handler_.end_array(context); + } + + void do_name(const string_view_type& name, const serializing_context& context) override + { + std::basic_string target; + auto result = unicons::convert( + name.begin(), name.end(), std::back_inserter(target), + unicons::conv_flags::strict); + if (result.ec != unicons::conv_errc()) + { + throw parse_error(result.ec,context.line_number(),context.column_number()); + } + other_handler_.name(target, context); + } + + void do_string_value(const string_view_type& value, const serializing_context& context) override + { + std::basic_string target; + auto result = unicons::convert( + value.begin(), value.end(), std::back_inserter(target), + unicons::conv_flags::strict); + if (result.ec != unicons::conv_errc()) + { + throw parse_error(result.ec,context.line_number(),context.column_number()); + } + other_handler_.string_value(target, context); + } + + void do_integer_value(int64_t value, const serializing_context& context) override + { + other_handler_.integer_value(value, context); + } + + void do_uinteger_value(uint64_t value, const serializing_context& context) override + { + other_handler_.uinteger_value(value, context); + } + + void do_double_value(double value, const number_format& fmt, const serializing_context& context) override + { + other_handler_.double_value(value, fmt, context); + } + + void do_bool_value(bool value, const serializing_context& context) override + { + other_handler_.bool_value(value, context); + } + + void do_null_value(const serializing_context& context) override + { + other_handler_.null_value(context); + } +}; + +template> +class basic_json_reader +{ + static const size_t default_max_buffer_length = 16384; + + typedef CharT char_type; + typedef Allocator allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; + + basic_json_parser parser_; + std::basic_istream& is_; + bool eof_; + std::vector buffer_; + size_t buffer_length_; + bool begin_; + + // Noncopyable and nonmoveable + basic_json_reader(const basic_json_reader&) = delete; + basic_json_reader& operator=(const basic_json_reader&) = delete; + +public: + + basic_json_reader(std::basic_istream& is) + : parser_(), + is_(is), + eof_(false), + buffer_length_(default_max_buffer_length), + begin_(true) + { + buffer_.reserve(buffer_length_); + } + + basic_json_reader(std::basic_istream& is, + parse_error_handler& err_handler) + : parser_(err_handler), + is_(is), + eof_(false), + buffer_length_(default_max_buffer_length), + begin_(true) + { + buffer_.reserve(buffer_length_); + } + + basic_json_reader(std::basic_istream& is, + basic_json_content_handler& handler) + : parser_(handler), + is_(is), + eof_(false), + buffer_length_(default_max_buffer_length), + begin_(true) + { + buffer_.reserve(buffer_length_); + } + + basic_json_reader(std::basic_istream& is, + basic_json_content_handler& handler, + parse_error_handler& err_handler) + : parser_(handler,err_handler), + is_(is), + eof_(false), + buffer_length_(default_max_buffer_length), + begin_(true) + { + buffer_.reserve(buffer_length_); + } + + basic_json_reader(std::basic_istream& is, + const basic_json_serializing_options& options) + : parser_(options), + is_(is), + eof_(false), + buffer_length_(default_max_buffer_length), + begin_(true) + { + buffer_.reserve(buffer_length_); + } + + basic_json_reader(std::basic_istream& is, + const basic_json_serializing_options& options, + parse_error_handler& err_handler) + : parser_(options,err_handler), + is_(is), + eof_(false), + buffer_length_(default_max_buffer_length), + begin_(true) + { + buffer_.reserve(buffer_length_); + } + + basic_json_reader(std::basic_istream& is, + const basic_json_serializing_options& options, + basic_json_content_handler& handler) + : parser_(handler,options), + is_(is), + eof_(false), + buffer_length_(default_max_buffer_length), + begin_(true) + { + buffer_.reserve(buffer_length_); + } + + basic_json_reader(std::basic_istream& is, + basic_json_content_handler& handler, + const basic_json_serializing_options& options, + parse_error_handler& err_handler) + : parser_(handler,options,err_handler), + is_(is), + eof_(false), + buffer_length_(default_max_buffer_length), + begin_(true) + { + buffer_.reserve(buffer_length_); + } + + size_t buffer_length() const + { + return buffer_length_; + } + + void buffer_length(size_t length) + { + buffer_length_ = length; + buffer_.reserve(buffer_length_); + } +#if !defined(JSONCONS_NO_DEPRECATED) + size_t max_nesting_depth() const + { + return parser_.max_nesting_depth(); + } + + void max_nesting_depth(size_t depth) + { + parser_.max_nesting_depth(depth); + } +#endif + void read_next() + { + std::error_code ec; + read_next(ec); + if (ec) + { + throw parse_error(ec,parser_.line_number(),parser_.column_number()); + } + } + + void read_buffer(std::error_code& ec) + { + buffer_.clear(); + buffer_.resize(buffer_length_); + is_.read(buffer_.data(), buffer_length_); + buffer_.resize(static_cast(is_.gcount())); + if (buffer_.size() == 0) + { + eof_ = true; + } + else if (begin_) + { + auto result = unicons::skip_bom(buffer_.begin(), buffer_.end()); + if (result.ec != unicons::encoding_errc()) + { + ec = result.ec; + return; + } + size_t offset = result.it - buffer_.begin(); + parser_.set_source(buffer_.data()+offset,buffer_.size()-offset); + begin_ = false; + } + else + { + parser_.set_source(buffer_.data(),buffer_.size()); + } + } + + void read_next(std::error_code& ec) + { + parser_.reset(); + while (!eof_ && !parser_.done()) + { + if (parser_.source_exhausted()) + { + if (!is_.eof()) + { + if (is_.fail()) + { + ec = json_parser_errc::source_error; + return; + } + read_buffer(ec); + if (ec) return; + } + else + { + eof_ = true; + } + } + if (!eof_) + { + parser_.parse_some(ec); + if (ec) return; + } + } + if (eof_) + { + parser_.end_parse(ec); + if (ec) return; + } + } + + void check_done() + { + std::error_code ec; + check_done(ec); + if (ec) + { + throw parse_error(ec,parser_.line_number(),parser_.column_number()); + } + } + + size_t line_number() const + { + return parser_.line_number(); + } + + size_t column_number() const + { + return parser_.column_number(); + } + + void check_done(std::error_code& ec) + { + if (eof_) + { + parser_.check_done(ec); + if (ec) return; + } + else + { + while (!eof_) + { + if (parser_.source_exhausted()) + { + if (!is_.eof()) + { + if (is_.fail()) + { + ec = json_parser_errc::source_error; + return; + } + read_buffer(ec); + if (ec) return; + } + else + { + eof_ = true; + } + } + if (!eof_) + { + parser_.check_done(ec); + if (ec) return; + } + } + } + } + + bool eof() const + { + return eof_; + } + + void read() + { + read_next(); + check_done(); + } + + void read(std::error_code& ec) + { + read_next(ec); + if (!ec) + { + check_done(ec); + } + } + +#if !defined(JSONCONS_NO_DEPRECATED) + + size_t buffer_capacity() const + { + return buffer_length_; + } + + void buffer_capacity(size_t length) + { + buffer_length_ = length; + buffer_.reserve(buffer_length_); + } + size_t max_depth() const + { + return parser_.max_nesting_depth(); + } + + void max_depth(size_t depth) + { + parser_.max_nesting_depth(depth); + } +#endif + +private: +}; + +typedef basic_json_reader json_reader; +typedef basic_json_reader wjson_reader; + +} + +#endif + diff --git a/direct-access-api/include/json_serializer.hpp b/direct-access-api/include/json_serializer.hpp new file mode 100644 index 0000000..d801ea8 --- /dev/null +++ b/direct-access-api/include/json_serializer.hpp @@ -0,0 +1,674 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_SERIALIZER_HPP +#define JSONCONS_JSON_SERIALIZER_HPP + +#include +#include +#include +#include +#include +#include +#include // std::numeric_limits +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +template> +class basic_json_serializer final : public basic_json_content_handler +{ +public: + using typename basic_json_content_handler::string_view_type; + typedef Writer writer_type; + typedef typename Writer::output_type output_type; + +private: + static const size_t default_buffer_length = 16384; + + enum class structure_type {object, array}; + + class line_split_context + { + structure_type type_; + size_t count_; + line_split_kind split_lines_; + bool indent_before_; + bool unindent_after_; + public: + line_split_context(structure_type type) + : type_(type), count_(0), split_lines_(line_split_kind::same_line), indent_before_(false), unindent_after_(false) + { + } + line_split_context(structure_type type, line_split_kind split_lines, bool indent_once) + : type_(type), count_(0), split_lines_(split_lines), indent_before_(indent_once), unindent_after_(false) + { + } + + size_t count() const + { + return count_; + } + + void increment_count() + { + ++count_; + } + + bool unindent_after() const + { + return unindent_after_; + } + + void unindent_after(bool value) + { + unindent_after_ = value; + } + + bool is_object() const + { + return type_ == structure_type::object; + } + + bool is_array() const + { + return type_ == structure_type::array; + } + + bool is_same_line() const + { + return split_lines_ == line_split_kind::same_line; + } + + bool is_new_line() const + { + return split_lines_ == line_split_kind::new_line; + } + + bool is_multi_line() const + { + return split_lines_ == line_split_kind::multi_line; + } + + bool is_indent_once() const + { + return count_ == 0 ? indent_before_ : false; + } + + }; + basic_json_serializing_options options_; + std::vector stack_; + int indent_; + bool indenting_; + detail::print_double fp_; + Writer writer_; + + // Noncopyable and nonmoveable + basic_json_serializer(const basic_json_serializer&) = delete; + basic_json_serializer& operator=(const basic_json_serializer&) = delete; +public: + basic_json_serializer(output_type& os) + : indent_(0), + indenting_(false), + fp_(options_.precision()), + writer_(os) + { + } + + basic_json_serializer(output_type& os, indenting line_indent) + : indent_(0), + indenting_(line_indent == indenting::indent), + fp_(options_.precision()), + writer_(os) + { + } + + basic_json_serializer(output_type& os, const basic_json_serializing_options& options) + : options_(options), + indent_(0), + indenting_(false), + fp_(options_.precision()), + writer_(os) + { + } + + basic_json_serializer(output_type& os, + const basic_json_serializing_options& options, + indenting line_indent) + : options_(options), + indent_(0), + indenting_(line_indent == indenting::indent), + fp_(options_.precision()), + writer_(os) + { + } + + ~basic_json_serializer() + { + } + + +#if !defined(JSONCONS_NO_DEPRECATED) + + basic_json_serializer(output_type& os, bool pprint) + : indent_(0), + indenting_(pprint), + fp_(options_.precision()), + writer_(os) + { + } + + basic_json_serializer(output_type& os, const basic_json_serializing_options& options, bool pprint) + : options_(options), + indent_(0), + indenting_(pprint), + fp_(options_.precision()), + writer_(os) + { + } +#endif + +private: + void escape_string(const CharT* s, + size_t length, + const basic_json_serializing_options& options, + writer_type& writer) + { + const CharT* begin = s; + const CharT* end = s + length; + for (const CharT* it = begin; it != end; ++it) + { + CharT c = *it; + switch (c) + { + case '\\': + writer.put('\\'); + writer.put('\\'); + break; + case '"': + writer.put('\\'); + writer.put('\"'); + break; + case '\b': + writer.put('\\'); + writer.put('b'); + break; + case '\f': + writer.put('\\'); + writer.put('f'); + break; + case '\n': + writer.put('\\'); + writer.put('n'); + break; + case '\r': + writer.put('\\'); + writer.put('r'); + break; + case '\t': + writer.put('\\'); + writer.put('t'); + break; + default: + if (options.escape_solidus() && c == '/') + { + writer.put('\\'); + writer.put('/'); + } + else if (is_control_character(c) || options.escape_all_non_ascii()) + { + // convert utf8 to codepoint + unicons::sequence_generator g(it,end,unicons::conv_flags::strict); + if (g.done() || g.status() != unicons::conv_errc()) + { + JSONCONS_THROW(json_exception_impl("Invalid codepoint")); + } + uint32_t cp = g.get().codepoint(); + it += (g.get().length() - 1); + if (is_non_ascii_codepoint(cp) || is_control_character(c)) + { + if (cp > 0xFFFF) + { + cp -= 0x10000; + uint32_t first = (cp >> 10) + 0xD800; + uint32_t second = ((cp & 0x03FF) + 0xDC00); + + writer.put('\\'); + writer.put('u'); + writer.put(to_hex_character(first >> 12 & 0x000F)); + writer.put(to_hex_character(first >> 8 & 0x000F)); + writer.put(to_hex_character(first >> 4 & 0x000F)); + writer.put(to_hex_character(first & 0x000F)); + writer.put('\\'); + writer.put('u'); + writer.put(to_hex_character(second >> 12 & 0x000F)); + writer.put(to_hex_character(second >> 8 & 0x000F)); + writer.put(to_hex_character(second >> 4 & 0x000F)); + writer.put(to_hex_character(second & 0x000F)); + } + else + { + writer.put('\\'); + writer.put('u'); + writer.put(to_hex_character(cp >> 12 & 0x000F)); + writer.put(to_hex_character(cp >> 8 & 0x000F)); + writer.put(to_hex_character(cp >> 4 & 0x000F)); + writer.put(to_hex_character(cp & 0x000F)); + } + } + else + { + writer.put(c); + } + } + else + { + writer.put(c); + } + break; + } + } + } + // Implementing methods + void do_begin_json() override + { + } + + void do_end_json() override + { + writer_.flush(); + } + + void do_begin_object(const serializing_context&) override + { + if (!stack_.empty() && stack_.back().is_array()) + { + if (!stack_.empty()) + { + if (stack_.back().count() > 0) + { + writer_. put(','); + } + } + } + + if (indenting_) + { + if (!stack_.empty() && stack_.back().is_object()) + { + stack_.push_back(line_split_context(structure_type::object,options_.object_object_split_lines(), false)); + } + else if (!stack_.empty()) + { + if (options_.array_object_split_lines() != line_split_kind::same_line) + { + stack_.back().unindent_after(true); + stack_.push_back(line_split_context(structure_type::object,options_.array_object_split_lines(), false)); + write_indent1(); + } + else + { + stack_.push_back(line_split_context(structure_type::object,options_.array_object_split_lines(), false)); + } + } + else + { + stack_.push_back(line_split_context(structure_type::object, line_split_kind::multi_line, false)); + } + indent(); + } + else + { + stack_.push_back(line_split_context(structure_type::object)); + } + writer_.put('{'); + } + + void do_end_object(const serializing_context&) override + { + JSONCONS_ASSERT(!stack_.empty()); + if (indenting_) + { + unindent(); + if (stack_.back().unindent_after()) + { + write_indent(); + } + } + stack_.pop_back(); + writer_.put('}'); + + end_value(); + } + + + void do_begin_array(const serializing_context&) override + { + if (!stack_.empty() && stack_.back().is_array()) + { + if (!stack_.empty()) + { + if (stack_.back().count() > 0) + { + writer_. put(','); + } + } + } + if (indenting_) + { + if (!stack_.empty()) + { + if (stack_.back().is_object()) + { + writer_.put('['); + indent(); + if (options_.object_array_split_lines() != line_split_kind::same_line) + { + stack_.push_back(line_split_context(structure_type::array,options_.object_array_split_lines(),true)); + } + else + { + stack_.push_back(line_split_context(structure_type::array,options_.object_array_split_lines(),false)); + } + } + else // array + { + if (options_.array_array_split_lines() == line_split_kind::same_line) + { + if (stack_.back().is_multi_line()) + { + write_indent(); + } + stack_.push_back(line_split_context(structure_type::array,line_split_kind::same_line, false)); + indent(); + } + else if (options_.array_array_split_lines() == line_split_kind::multi_line) + { + write_indent(); + stack_.push_back(line_split_context(structure_type::array,options_.array_array_split_lines(), false)); + indent(); + } + else // new_line + { + write_indent(); + stack_.push_back(line_split_context(structure_type::array,options_.array_array_split_lines(), false)); + indent(); + } + writer_.put('['); + } + } + else + { + stack_.push_back(line_split_context(structure_type::array, line_split_kind::multi_line, false)); + writer_.put('['); + indent(); + } + } + else + { + stack_.push_back(line_split_context(structure_type::array)); + writer_.put('['); + } + } + + void do_end_array(const serializing_context&) override + { + JSONCONS_ASSERT(!stack_.empty()); + if (indenting_) + { + unindent(); + if (stack_.back().unindent_after()) + { + write_indent(); + } + } + stack_.pop_back(); + writer_.put(']'); + end_value(); + } + + void do_name(const string_view_type& name, const serializing_context&) override + { + if (!stack_.empty()) + { + if (stack_.back().count() > 0) + { + writer_. put(','); + } + if (indenting_) + { + if (stack_.back().is_multi_line()) + { + write_indent(); + } + } + } + + writer_.put('\"'); + escape_string(name.data(), name.length(), options_, writer_); + writer_.put('\"'); + writer_.put(':'); + if (indenting_) + { + writer_.put(' '); + } + } + + void do_null_value(const serializing_context&) override + { + if (!stack_.empty() && stack_.back().is_array()) + { + begin_scalar_value(); + } + + auto buf = detail::null_literal(); + writer_.write(buf, 4); + + end_value(); + } + + void do_string_value(const string_view_type& value, const serializing_context&) override + { + if (!stack_.empty() && stack_.back().is_array()) + { + begin_scalar_value(); + } + + writer_. put('\"'); + escape_string(value.data(), value.length(), options_, writer_); + writer_. put('\"'); + + end_value(); + } + + void do_byte_string_value(const uint8_t* data, size_t length, const serializing_context& context) override + { + std::basic_string s; + encode_base64url(data,data+length,s); + do_string_value(s, context); + } + + void do_double_value(double value, const number_format& fmt, const serializing_context&) override + { + if (!stack_.empty() && stack_.back().is_array()) + { + begin_scalar_value(); + } + + if ((std::isnan)(value)) + { + if (options_.can_write_nan_replacement()) + { + writer_.write(options_.nan_replacement()); + } + else + { + writer_.write(detail::null_literal()); + } + } + else if (value == std::numeric_limits::infinity()) + { + if (options_.can_write_pos_inf_replacement()) + { + writer_.write(options_.pos_inf_replacement()); + } + else + { + writer_.write(detail::null_literal()); + } + } + else if (!(std::isfinite)(value)) + { + if (options_.can_write_neg_inf_replacement()) + { + writer_.write(options_.neg_inf_replacement()); + } + else + { + writer_.write(detail::null_literal()); + } + } + else + { + fp_(value, fmt.precision(), writer_); + } + + end_value(); + } + + void do_integer_value(int64_t value, const serializing_context&) override + { + if (!stack_.empty() && stack_.back().is_array()) + { + begin_scalar_value(); + } + detail::print_integer(value, writer_); + end_value(); + } + + void do_uinteger_value(uint64_t value, const serializing_context&) override + { + if (!stack_.empty() && stack_.back().is_array()) + { + begin_scalar_value(); + } + detail::print_uinteger(value, writer_); + end_value(); + } + + void do_bool_value(bool value, const serializing_context&) override + { + if (!stack_.empty() && stack_.back().is_array()) + { + begin_scalar_value(); + } + + if (value) + { + auto buf = detail::true_literal(); + writer_.write(buf,4); + } + else + { + auto buf = detail::false_literal(); + writer_.write(buf,5); + } + + end_value(); + } + + void begin_scalar_value() + { + if (!stack_.empty()) + { + if (stack_.back().count() > 0) + { + writer_. put(','); + } + if (indenting_) + { + if (stack_.back().is_multi_line() || stack_.back().is_indent_once()) + { + write_indent(); + } + } + } + } + + void begin_value() + { + if (!stack_.empty()) + { + if (stack_.back().count() > 0) + { + writer_. put(','); + } + if (indenting_) + { + if (!stack_.back().is_same_line()) + { + write_indent(); + } + } + } + } + + void end_value() + { + if (!stack_.empty()) + { + stack_.back().increment_count(); + } + } + + void indent() + { + indent_ += static_cast(options_.indent()); + } + + void unindent() + { + indent_ -= static_cast(options_.indent()); + } + + void write_indent() + { + if (!stack_.empty()) + { + stack_.back().unindent_after(true); + } + writer_. put('\n'); + for (int i = 0; i < indent_; ++i) + { + writer_. put(' '); + } + } + + void write_indent1() + { + writer_. put('\n'); + for (int i = 0; i < indent_; ++i) + { + writer_. put(' '); + } + } +}; + +typedef basic_json_serializer> json_serializer; +typedef basic_json_serializer> wjson_serializer; + +typedef basic_json_serializer> json_string_serializer; +typedef basic_json_serializer> wjson_string_serializer; + +} +#endif diff --git a/direct-access-api/include/json_serializing_options.hpp b/direct-access-api/include/json_serializing_options.hpp new file mode 100644 index 0000000..7c5be78 --- /dev/null +++ b/direct-access-api/include/json_serializing_options.hpp @@ -0,0 +1,339 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_SERIALIZING_OPTIONS_HPP +#define JSONCONS_JSON_SERIALIZING_OPTIONS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +enum class indenting {no_indent = 0, indent = 1}; + +#if !defined(JSONCONS_NO_DEPRECATED) +enum class block_options {next_line,same_line}; +#endif + +enum class line_split_kind{same_line,new_line,multi_line}; + +template > +class basic_json_serializing_options +{ +public: + typedef basic_string_view_ext string_view_type; + typedef CharT char_type; + typedef Allocator allocator_type; + typedef typename std::allocator_traits:: template rebind_alloc char_allocator_type; + typedef std::basic_string,char_allocator_type> string_type; +private: + int indent_; + uint8_t precision_; + uint8_t decimal_places_; + bool can_read_nan_replacement_; + bool can_read_pos_inf_replacement_; + bool can_read_neg_inf_replacement_; + string_type nan_replacement_; + string_type pos_inf_replacement_; + string_type neg_inf_replacement_; + bool escape_all_non_ascii_; + bool escape_solidus_; + + line_split_kind object_object_split_lines_; + line_split_kind object_array_split_lines_; + line_split_kind array_array_split_lines_; + line_split_kind array_object_split_lines_; + + chars_format floating_point_format_; + size_t max_nesting_depth_; +public: + static const size_t default_indent = 4; + +// Constructors + + basic_json_serializing_options() + : indent_(default_indent), + precision_(0), + can_read_nan_replacement_(false), + can_read_pos_inf_replacement_(false), + can_read_neg_inf_replacement_(false), + escape_all_non_ascii_(false), + escape_solidus_(false), + object_object_split_lines_(line_split_kind::multi_line), + object_array_split_lines_(line_split_kind::same_line), + array_array_split_lines_(line_split_kind::new_line), + array_object_split_lines_(line_split_kind::multi_line), + max_nesting_depth_((std::numeric_limits::max)()) + { + } + +// Accessors + line_split_kind object_object_split_lines() const {return object_object_split_lines_;} + line_split_kind array_object_split_lines() const {return array_object_split_lines_;} + line_split_kind object_array_split_lines() const {return object_array_split_lines_;} + line_split_kind array_array_split_lines() const {return array_array_split_lines_;} + + basic_json_serializing_options& object_object_split_lines(line_split_kind value) {object_object_split_lines_ = value; return *this;} + basic_json_serializing_options& array_object_split_lines(line_split_kind value) {array_object_split_lines_ = value; return *this;} + basic_json_serializing_options& object_array_split_lines(line_split_kind value) {object_array_split_lines_ = value; return *this;} + basic_json_serializing_options& array_array_split_lines(line_split_kind value) {array_array_split_lines_ = value; return *this;} + +#if !defined(JSONCONS_NO_DEPRECATED) + block_options array_array_block_option() + { + return (array_array_split_lines_ == line_split_kind::same_line) ? block_options::same_line : block_options::next_line; + } + + basic_json_serializing_options& array_array_block_option(block_options value) + { + array_array_split_lines_ = (value == block_options::same_line) ? line_split_kind::same_line : line_split_kind::new_line; + return *this; + } + + block_options array_object_block_option() + { + return (array_object_split_lines_ == line_split_kind::same_line) ? block_options::same_line : block_options::next_line; + } + + basic_json_serializing_options& array_object_block_option(block_options value) + { + array_object_split_lines_ = (value == block_options::same_line) ? line_split_kind::same_line : line_split_kind::new_line; + return *this; + } + + block_options object_array_block_option() + { + return (object_array_split_lines_ == line_split_kind::same_line) ? block_options::same_line : block_options::next_line; + } + + basic_json_serializing_options& object_array_block_option(block_options value) + { + object_array_split_lines_ = (value == block_options::same_line) ? line_split_kind::same_line : line_split_kind::new_line; + return *this; + } + + block_options object_object_block_option() + { + return (object_object_split_lines_ == line_split_kind::same_line) ? block_options::same_line : block_options::next_line; + } + + basic_json_serializing_options& object_object_block_option(block_options value) + { + object_object_split_lines_ = (value == block_options::same_line) ? line_split_kind::same_line : line_split_kind::new_line; + return *this; + } +#endif + + int indent() const + { + return indent_; + } + + basic_json_serializing_options& indent(int value) + { + indent_ = value; + return *this; + } + + chars_format floating_point_format() const + { + return floating_point_format_; + } + + basic_json_serializing_options& floating_point_format(chars_format value) + { + floating_point_format_ = value; + return *this; + } + + uint8_t precision() const + { + return precision_; + } + + basic_json_serializing_options& precision(uint8_t value) + { + precision_ = value; + return *this; + } + + uint8_t decimal_places() const + { + return decimal_places_; + } + + basic_json_serializing_options& decimal_places(uint8_t value) + { + decimal_places_ = value; + return *this; + } + + bool escape_all_non_ascii() const + { + return escape_all_non_ascii_; + } + + basic_json_serializing_options& escape_all_non_ascii(bool value) + { + escape_all_non_ascii_ = value; + return *this; + } + + bool escape_solidus() const + { + return escape_solidus_; + } + + basic_json_serializing_options& escape_solidus(bool value) + { + escape_solidus_ = value; + return *this; + } + + bool can_read_nan_replacement() const {return can_read_nan_replacement_;} + + bool can_read_pos_inf_replacement() const {return can_read_pos_inf_replacement_;} + + bool can_read_neg_inf_replacement() const {return can_read_neg_inf_replacement_;} + + bool can_write_nan_replacement() const {return !nan_replacement_.empty();} + + bool can_write_pos_inf_replacement() const {return !pos_inf_replacement_.empty();} + + bool can_write_neg_inf_replacement() const {return !neg_inf_replacement_.empty();} + + basic_json_serializing_options& replace_inf(bool replace) + { + can_read_pos_inf_replacement_ = replace; + can_read_neg_inf_replacement_ = replace; + return *this; + } + + basic_json_serializing_options& replace_pos_inf(bool replace) + { + can_read_pos_inf_replacement_ = replace; + return *this; + } + + basic_json_serializing_options& replace_neg_inf(bool replace) + { + can_read_neg_inf_replacement_ = replace; + return *this; + } + + const string_type& nan_replacement() const + { + return nan_replacement_; + } + + basic_json_serializing_options& nan_replacement(const string_type& replacement) + { + nan_replacement_ = replacement; + + can_read_nan_replacement_ = is_string(replacement); + + return *this; + } + + const string_type& pos_inf_replacement() const + { + return pos_inf_replacement_; + } + + basic_json_serializing_options& pos_inf_replacement(const string_type& replacement) + { + pos_inf_replacement_ = replacement; + can_read_pos_inf_replacement_ = is_string(replacement); + return *this; + } + + const string_type& neg_inf_replacement() const + { + return neg_inf_replacement_; + } + + basic_json_serializing_options& neg_inf_replacement(const string_type& replacement) + { + neg_inf_replacement_ = replacement; + can_read_neg_inf_replacement_ = is_string(replacement); + return *this; + } + + size_t max_nesting_depth() const + { + return max_nesting_depth_; + } + + void max_nesting_depth(size_t value) + { + max_nesting_depth_ = value; + } +private: + enum class input_state {initial,begin_quote,character,end_quote,escape,error}; + bool is_string(const string_view_type& s) const + { + input_state state = input_state::initial; + for (CharT c : s) + { + switch (c) + { + case '\t': case ' ': case '\n': case'\r': + break; + case '\\': + state = input_state::escape; + break; + case '\"': + switch (state) + { + case input_state::initial: + state = input_state::begin_quote; + break; + case input_state::begin_quote: + state = input_state::end_quote; + break; + case input_state::character: + state = input_state::end_quote; + break; + case input_state::end_quote: + state = input_state::error; + break; + case input_state::escape: + state = input_state::character; + break; + default: + state = input_state::character; + break; + } + default: + break; + } + + } + return state == input_state::end_quote; + } +}; + +typedef basic_json_serializing_options json_serializing_options; +typedef basic_json_serializing_options wjson_serializing_options; + +#if !defined(JSONCONS_NO_DEPRECATED) +typedef basic_json_serializing_options output_format; +typedef basic_json_serializing_options woutput_format; +typedef basic_json_serializing_options serialization_options; +typedef basic_json_serializing_options wserialization_options; +#endif + +} +#endif diff --git a/direct-access-api/include/json_structures.hpp b/direct-access-api/include/json_structures.hpp new file mode 100644 index 0000000..74aa150 --- /dev/null +++ b/direct-access-api/include/json_structures.hpp @@ -0,0 +1,1864 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSON_STRUCTURES_HPP +#define JSONCONS_JSON_STRUCTURES_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +// json_array + +template +class Json_array_base_ +{ +public: + typedef typename Json::allocator_type allocator_type; + +public: + Json_array_base_() + : self_allocator_() + { + } + Json_array_base_(const allocator_type& allocator) + : self_allocator_(allocator) + { + } + + allocator_type get_allocator() const + { + return self_allocator_; + } + + allocator_type self_allocator_; +}; + +// json_array + +template +class json_array: public Json_array_base_ +{ +public: + typedef typename Json::allocator_type allocator_type; + typedef Json value_type; + typedef typename std::allocator_traits:: template rebind_alloc val_allocator_type; + + typedef typename Json::array_storage_type array_storage_type; + + typedef typename array_storage_type::iterator iterator; + typedef typename array_storage_type::const_iterator const_iterator; + + typedef typename std::iterator_traits::reference reference; + typedef typename std::iterator_traits::reference const_reference; + + using Json_array_base_::get_allocator; + + json_array() + : Json_array_base_(), + elements_() + { + } + + explicit json_array(const allocator_type& allocator) + : Json_array_base_(allocator), + elements_(val_allocator_type(allocator)) + { + } + + explicit json_array(size_t n, + const allocator_type& allocator = allocator_type()) + : Json_array_base_(allocator), + elements_(n,Json(),val_allocator_type(allocator)) + { + } + + explicit json_array(size_t n, + const Json& value, + const allocator_type& allocator = allocator_type()) + : Json_array_base_(allocator), + elements_(n,value,val_allocator_type(allocator)) + { + } + + template + json_array(InputIterator begin, InputIterator end, const allocator_type& allocator = allocator_type()) + : Json_array_base_(allocator), + elements_(begin,end,val_allocator_type(allocator)) + { + } + json_array(const json_array& val) + : Json_array_base_(val.get_allocator()), + elements_(val.elements_) + { + } + json_array(const json_array& val, const allocator_type& allocator) + : Json_array_base_(allocator), + elements_(val.elements_,val_allocator_type(allocator)) + { + } + + json_array(json_array&& val) JSONCONS_NOEXCEPT + : Json_array_base_(val.get_allocator()), + elements_(std::move(val.elements_)) + { + } + json_array(json_array&& val, const allocator_type& allocator) + : Json_array_base_(allocator), + elements_(std::move(val.elements_),val_allocator_type(allocator)) + { + } + + json_array(std::initializer_list init) + : Json_array_base_(), + elements_(std::move(init)) + { + } + + json_array(std::initializer_list init, + const allocator_type& allocator) + : Json_array_base_(allocator), + elements_(std::move(init),val_allocator_type(allocator)) + { + } + ~json_array() + { + } + + void swap(json_array& val) + { + elements_.swap(val.elements_); + } + + size_t size() const {return elements_.size();} + + size_t capacity() const {return elements_.capacity();} + + void clear() {elements_.clear();} + + void shrink_to_fit() + { + for (size_t i = 0; i < elements_.size(); ++i) + { + elements_[i].shrink_to_fit(); + } + elements_.shrink_to_fit(); + } + + void reserve(size_t n) {elements_.reserve(n);} + + void resize(size_t n) {elements_.resize(n);} + + void resize(size_t n, const Json& val) {elements_.resize(n,val);} + + void remove_range(size_t from_index, size_t to_index) + { + JSONCONS_ASSERT(from_index <= to_index); + JSONCONS_ASSERT(to_index <= elements_.size()); + elements_.erase(elements_.begin()+from_index,elements_.begin()+to_index); + } + + void erase(const_iterator pos) + { + elements_.erase(pos); + } + + void erase(const_iterator first, const_iterator last) + { + elements_.erase(first,last); + } + + Json& operator[](size_t i) {return elements_[i];} + + const Json& operator[](size_t i) const {return elements_[i];} + + // push_back + + template + typename std::enable_if::value,void>::type + push_back(T&& value) + { + elements_.emplace_back(std::forward(value)); + } + + template + typename std::enable_if::value,void>::type + push_back(T&& value) + { + elements_.emplace_back(std::forward(value),get_allocator()); + } + + template + typename std::enable_if::value,iterator>::type + insert(const_iterator pos, T&& value) + { +#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 9 + // work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54577 + iterator it = elements_.begin() + (pos - elements_.begin()); + return elements_.emplace(it, std::forward(value)); +#else + return elements_.emplace(pos, std::forward(value)); +#endif + } + template + typename std::enable_if::value,iterator>::type + insert(const_iterator pos, T&& value) + { +#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 9 + // work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54577 + iterator it = elements_.begin() + (pos - elements_.begin()); + return elements_.emplace(it, std::forward(value), get_allocator()); +#else + return elements_.emplace(pos, std::forward(value), get_allocator()); +#endif + } + + template + iterator insert(const_iterator pos, InputIt first, InputIt last) + { +#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 9 + // work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54577 + iterator it = elements_.begin() + (pos - elements_.begin()); + return elements_.insert(it, first, last); +#else + return elements_.insert(pos, first, last); +#endif + } + +#if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 9 + // work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54577 + template + typename std::enable_if::value,iterator>::type + emplace(const_iterator pos, Args&&... args) + { + iterator it = elements_.begin() + (pos - elements_.begin()); + return elements_.emplace(it, std::forward(args)...); + } +#else + template + typename std::enable_if::value,iterator>::type + emplace(const_iterator pos, Args&&... args) + { + return elements_.emplace(pos, std::forward(args)...); + } +#endif + template + Json& emplace_back(Args&&... args) + { + elements_.emplace_back(std::forward(args)...); + return elements_.back(); + } + + iterator begin() {return elements_.begin();} + + iterator end() {return elements_.end();} + + const_iterator begin() const {return elements_.begin();} + + const_iterator end() const {return elements_.end();} + + bool operator==(const json_array& rhs) const + { + if (size() != rhs.size()) + { + return false; + } + for (size_t i = 0; i < size(); ++i) + { + if (elements_[i] != rhs.elements_[i]) + { + return false; + } + } + return true; + } +private: + array_storage_type elements_; + + json_array& operator=(const json_array&) = delete; +}; + +// json_object + +template +BidirectionalIt last_wins_unique_sequence(BidirectionalIt first, BidirectionalIt last, BinaryPredicate compare) +{ + + if (first == last) + { + return last; + } + + typedef typename BidirectionalIt::value_type value_type; + typedef typename BidirectionalIt::pointer pointer; + std::vector dups; + { + std::vector v(std::distance(first,last)); + auto p = v.begin(); + for (auto it = first; it != last; ++it) + { + *p++ = &(*it); + } + std::sort(v.begin(), v.end(), [&](pointer a, pointer b){return compare(*a,*b)<0;}); + auto it = v.begin(); + auto end = v.end(); + for (auto begin = it+1; begin != end; ++it, ++begin) + { + if (compare(*(*it),*(*begin)) == 0) + { + dups.push_back(*(*it)); + } + } + } + if (dups.size() == 0) + { + return last; + } + + auto it = last; + for (auto p = first; p != last && p != it; ) + { + bool no_dup = true; + if (dups.size() > 0) + { + for (auto q = dups.begin(); no_dup && q != dups.end();) + { + if (compare(*p,*q) == 0) + { + dups.erase(q); + no_dup = false; + } + else + { + ++q; + } + } + } + if (!no_dup) + { + --it; + for (auto r = p; r != it; ++r) + { + *r = std::move(*(r+1)); + } + } + else + { + ++p; + } + } + + return it; +} + +template +class key_value_pair +{ +public: + typedef KeyT key_storage_type; + typedef typename KeyT::value_type char_type; + typedef typename KeyT::allocator_type allocator_type; + typedef typename ValueT::string_view_type string_view_type; + + key_value_pair() + { + } + + key_value_pair(const key_storage_type& name, const ValueT& val) + : key_(name), value_(val) + { + } + + template + key_value_pair(key_storage_type&& name, T&& val) + : key_(std::forward(name)), + value_(std::forward(val)) + { + } + + template + key_value_pair(key_storage_type&& name, + T&& val, + const allocator_type& allocator) + : key_(std::forward(name)), value_(std::forward(val), allocator) + { + } + + key_value_pair(const key_value_pair& member) + : key_(member.key_), value_(member.value_) + { + } + + key_value_pair(key_value_pair&& member) + : key_(std::move(member.key_)), value_(std::move(member.value_)) + { + } + + string_view_type key() const + { + return string_view_type(key_.data(),key_.size()); + } + + ValueT& value() + { + return value_; + } + + const ValueT& value() const + { + return value_; + } + + template + void value(T&& value) + { + value_ = std::forward(value); + } + + void swap(key_value_pair& member) + { + key_.swap(member.key_); + value_.swap(member.value_); + } + + key_value_pair& operator=(const key_value_pair& member) + { + if (this != & member) + { + key_ = member.key_; + value_ = member.value_; + } + return *this; + } + + key_value_pair& operator=(key_value_pair&& member) + { + if (this != &member) + { + key_.swap(member.key_); + value_.swap(member.value_); + } + return *this; + } + + void shrink_to_fit() + { + key_.shrink_to_fit(); + value_.shrink_to_fit(); + } +#if !defined(JSONCONS_NO_DEPRECATED) + const key_storage_type& name() const + { + return key_; + } +#endif +private: + key_storage_type key_; + ValueT value_; +}; + +template +class Json_object_ +{ +public: + typedef typename Json::allocator_type allocator_type; + typedef typename Json::char_type char_type; + typedef typename Json::char_allocator_type char_allocator_type; + typedef KeyT key_storage_type; + typedef typename Json::string_view_type string_view_type; + typedef key_value_pair value_type; + + typedef typename std::allocator_traits:: template rebind_alloc kvp_allocator_type; + typedef typename Json::object_storage_type object_storage_type; + + typedef typename object_storage_type::iterator iterator; + typedef typename object_storage_type::const_iterator const_iterator; + +protected: + allocator_type self_allocator_; + object_storage_type members_; +public: + Json_object_() + : self_allocator_(), members_() + { + } + Json_object_(const allocator_type& allocator) + : self_allocator_(allocator), + members_(kvp_allocator_type(allocator)) + { + } + + Json_object_(const Json_object_& val) + : self_allocator_(val.get_allocator()), members_(val.members_) + { + } + + Json_object_(Json_object_&& val) + : self_allocator_(val.get_allocator()), + members_(std::move(val.members_)) + { + } + + Json_object_(const Json_object_& val, const allocator_type& allocator) : + self_allocator_(allocator), + members_(val.members_,kvp_allocator_type(allocator)) + { + } + + Json_object_(Json_object_&& val,const allocator_type& allocator) : + self_allocator_(allocator), members_(std::move(val.members_),kvp_allocator_type(allocator)) + { + } + + void swap(Json_object_& val) + { + members_.swap(val.members_); + } + + allocator_type get_allocator() const + { + return this->self_allocator_; + } +}; + +// json_object + +template +class json_object +{ +}; + +// Do not preserve order +template +class json_object final : public Json_object_ +{ +public: + using typename Json_object_::allocator_type; + using typename Json_object_::char_type; + using typename Json_object_::char_allocator_type; + using typename Json_object_::key_storage_type; + using typename Json_object_::string_view_type; + using typename Json_object_::value_type; + using typename Json_object_::kvp_allocator_type; + using typename Json_object_::object_storage_type; + using typename Json_object_::iterator; + using typename Json_object_::const_iterator; + using Json_object_::get_allocator; + + json_object() + : Json_object_() + { + } + json_object(const allocator_type& allocator) + : Json_object_(allocator) + { + } + + json_object(const json_object& val) + : Json_object_(val) + { + } + + json_object(json_object&& val) + : Json_object_(std::forward(val)) + { + } + + json_object(const json_object& val, const allocator_type& allocator) + : Json_object_(val,allocator) + { + } + + json_object(json_object&& val,const allocator_type& allocator) + : Json_object_(std::forward(val),allocator) + { + } + + json_object(std::initializer_list> init) + : Json_object_() + { + this->members_.reserve(init.size()); + for (auto& item : init) + { + insert_or_assign(item.first, std::move(item.second)); + } + } + + json_object(std::initializer_list> init, + const allocator_type& allocator) + : Json_object_(allocator) + { + this->members_.reserve(init.size()); + for (auto& item : init) + { + insert_or_assign(item.first, std::move(item.second), allocator); + } + } + + void swap(json_object& val) + { + Json_object_::swap(val); + } + + iterator begin() + { + return this->members_.begin(); + } + + iterator end() + { + return this->members_.end(); + } + + const_iterator begin() const + { + return this->members_.begin(); + } + + const_iterator end() const + { + return this->members_.end(); + } + + size_t size() const {return this->members_.size();} + + size_t capacity() const {return this->members_.capacity();} + + void clear() {this->members_.clear();} + + void shrink_to_fit() + { + for (size_t i = 0; i < this->members_.size(); ++i) + { + this->members_[i].shrink_to_fit(); + } + this->members_.shrink_to_fit(); + } + + void reserve(size_t n) {this->members_.reserve(n);} + + Json& at(size_t i) + { + if (i >= this->members_.size()) + { + JSONCONS_THROW(json_exception_impl("Invalid array subscript")); + } + return this->members_[i].value(); + } + + const Json& at(size_t i) const + { + if (i >= this->members_.size()) + { + JSONCONS_THROW(json_exception_impl("Invalid array subscript")); + } + return this->members_[i].value(); + } + + iterator find(const string_view_type& name) + { + auto it = std::lower_bound(this->members_.begin(),this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + auto result = (it != this->members_.end() && it->key() == name) ? it : this->members_.end(); + return result; + } + + const_iterator find(const string_view_type& name) const + { + auto it = std::lower_bound(this->members_.begin(),this->members_.end(), + name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + auto result = (it != this->members_.end() && it->key() == name) ? it : this->members_.end(); + return result; + } + + void erase(const_iterator pos) + { + this->members_.erase(pos); + } + + void erase(const_iterator first, const_iterator last) + { + this->members_.erase(first,last); + } + + void erase(const string_view_type& name) + { + auto it = std::lower_bound(this->members_.begin(),this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + if (it != this->members_.end() && it->key() == name) + { + this->members_.erase(it); + } + } + + template + void insert(InputIt first, InputIt last, UnaryPredicate pred) + { + size_t count = std::distance(first,last); + this->members_.reserve(this->members_.size() + count); + for (auto s = first; s != last; ++s) + { + this->members_.emplace_back(pred(*s)); + } + std::stable_sort(this->members_.begin(),this->members_.end(), + [](const value_type& a, const value_type& b){return a.key().compare(b.key()) < 0;}); + auto it = std::unique(this->members_.rbegin(), this->members_.rend(), + [](const value_type& a, const value_type& b){ return !(a.key().compare(b.key()));}); + this->members_.erase(this->members_.begin(),it.base()); + } + + // merge + + void merge(const json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + try_emplace(it->key(),it->value()); + } + } + + void merge(json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = std::lower_bound(this->members_.begin(),this->members_.end(), it->key(), + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + if (pos == this->members_.end() ) + { + this->members_.emplace_back(*it); + } + else if (it->key() != pos->key()) + { + this->members_.emplace(pos,*it); + } + } + } + + void merge(iterator hint, const json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = try_emplace(hint, it->key(),it->value()); + } + } + + void merge(iterator hint, json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + iterator pos; + if (hint != this->members_.end() && hint->key() <= it->key()) + { + pos = std::lower_bound(hint,this->members_.end(), it->key(), + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + else + { + pos = std::lower_bound(this->members_.begin(),this->members_.end(), it->key(), + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + if (pos == this->members_.end() ) + { + this->members_.emplace_back(*it); + hint = this->members_.begin() + (this->members_.size() - 1); + } + else if (it->key() != pos->key()) + { + hint = this->members_.emplace(pos,*it); + } + } + } + + // merge_or_update + + void merge_or_update(const json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + insert_or_assign(it->key(),it->value()); + } + } + + void merge_or_update(json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = std::lower_bound(this->members_.begin(),this->members_.end(), it->key(), + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + if (pos == this->members_.end() ) + { + this->members_.emplace_back(*it); + } + else + { + pos->value(it->value()); + } + } + } + + void merge_or_update(iterator hint, const json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = insert_or_assign(hint, it->key(),it->value()); + } + } + + void merge_or_update(iterator hint, json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + iterator pos; + if (hint != this->members_.end() && hint->key() <= it->key()) + { + pos = std::lower_bound(hint,this->members_.end(), it->key(), + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + else + { + pos = std::lower_bound(this->members_.begin(),this->members_.end(), it->key(), + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + if (pos == this->members_.end() ) + { + this->members_.emplace_back(*it); + hint = this->members_.begin() + (this->members_.size() - 1); + } + else + { + pos->value(it->value()); + hint = pos; + } + } + } + + // insert_or_assign + + template + typename std::enable_if::value,std::pair>::type + insert_or_assign(const string_view_type& name, T&& value) + { + bool inserted; + auto it = std::lower_bound(this->members_.begin(),this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(name.begin(),name.end()), + std::forward(value)); + inserted = true; + it = this->members_.begin() + this->members_.size() - 1; + } + else if (it->key() == name) + { + it->value(Json(std::forward(value))); + inserted = false; // assigned + } + else + { + it = this->members_.emplace(it, + key_storage_type(name.begin(),name.end()), + std::forward(value)); + inserted = true; + } + return std::make_pair(it,inserted); + } + + template + typename std::enable_if::value,std::pair>::type + insert_or_assign(const string_view_type& name, T&& value) + { + bool inserted; + auto it = std::lower_bound(this->members_.begin(),this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(name.begin(),name.end(), get_allocator()), + std::forward(value),get_allocator()); + inserted = true; + it = this->members_.begin() + this->members_.size() - 1; + } + else if (it->key() == name) + { + it->value(Json(std::forward(value), get_allocator())); + inserted = false; // assigned + } + else + { + it = this->members_.emplace(it, + key_storage_type(name.begin(),name.end(), get_allocator()), + std::forward(value),get_allocator()); + inserted = true; + } + return std::make_pair(it,inserted); + } + + // try_emplace + + template + typename std::enable_if::value,std::pair>::type + try_emplace(const string_view_type& name, Args&&... args) + { + bool inserted; + auto it = std::lower_bound(this->members_.begin(),this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(name.begin(),name.end()), + std::forward(args)...); + it = this->members_.begin() + this->members_.size() - 1; + inserted = true; + } + else if (it->key() == name) + { + inserted = false; + } + else + { + it = this->members_.emplace(it, + key_storage_type(name.begin(),name.end()), + std::forward(args)...); + inserted = true; + } + return std::make_pair(it,inserted); + } + + template + typename std::enable_if::value,std::pair>::type + try_emplace(const string_view_type& name, Args&&... args) + { + bool inserted; + auto it = std::lower_bound(this->members_.begin(),this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(name.begin(),name.end(), get_allocator()), + std::forward(args)...); + it = this->members_.begin() + this->members_.size() - 1; + inserted = true; + } + else if (it->key() == name) + { + inserted = false; + } + else + { + it = this->members_.emplace(it, + key_storage_type(name.begin(),name.end(), get_allocator()), + std::forward(args)...); + inserted = true; + } + return std::make_pair(it,inserted); + } + + template + typename std::enable_if::value,iterator>::type + try_emplace(iterator hint, const string_view_type& name, Args&&... args) + { + iterator it; + if (hint != this->members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + else + { + it = std::lower_bound(this->members_.begin(),this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(name.begin(),name.end()), + std::forward(args)...); + it = this->members_.begin() + (this->members_.size() - 1); + } + else if (it->key() == name) + { + } + else + { + it = this->members_.emplace(it, + key_storage_type(name.begin(),name.end()), + std::forward(args)...); + } + return it; + } + + template + typename std::enable_if::value,iterator>::type + try_emplace(iterator hint, const string_view_type& name, Args&&... args) + { + iterator it; + if (hint != this->members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + else + { + it = std::lower_bound(this->members_.begin(),this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(name.begin(),name.end(), get_allocator()), + std::forward(args)...); + it = this->members_.begin() + (this->members_.size() - 1); + } + else if (it->key() == name) + { + } + else + { + it = this->members_.emplace(it, + key_storage_type(name.begin(),name.end(), get_allocator()), + std::forward(args)...); + } + return it; + } + + // set_ + + template + typename std::enable_if::value,void>::type + set_(key_storage_type&& name, T&& value) + { + string_view_type s(name.data(), name.size()); + auto it = std::lower_bound(this->members_.begin(),this->members_.end(), s, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + if (it == this->members_.end()) + { + this->members_.emplace_back(std::forward(name), + std::forward(value)); + } + else if (string_view_type(it->key().data(),it->key().length()) == s) + { + it->value(Json(std::forward(value))); + } + else + { + this->members_.emplace(it, + std::forward(name), + std::forward(value)); + } + } + + template + typename std::enable_if::value,void>::type + set_(key_storage_type&& name, T&& value) + { + string_view_type s(name.data(), name.size()); + auto it = std::lower_bound(this->members_.begin(),this->members_.end(), s, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + if (it == this->members_.end()) + { + this->members_.emplace_back(std::forward(name), + std::forward(value),get_allocator() ); + } + else if (string_view_type(it->key().data(), it->key().length()) == s) + { + it->value(Json(std::forward(value),get_allocator() )); + } + else + { + this->members_.emplace(it, + std::forward(name), + std::forward(value),get_allocator() ); + } + } + + template + typename std::enable_if::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& name, T&& value) + { + iterator it; + if (hint != this->members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + else + { + it = std::lower_bound(this->members_.begin(),this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(name.begin(),name.end()), + std::forward(value)); + it = this->members_.begin() + (this->members_.size() - 1); + } + else if (it->key() == name) + { + it->value(Json(std::forward(value))); + } + else + { + it = this->members_.emplace(it, + key_storage_type(name.begin(),name.end()), + std::forward(value)); + } + return it; + } + + template + typename std::enable_if::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& name, T&& value) + { + iterator it; + if (hint != this->members_.end() && hint->key() <= name) + { + it = std::lower_bound(hint,this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + else + { + it = std::lower_bound(this->members_.begin(),this->members_.end(), name, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(name.begin(),name.end(), get_allocator()), + std::forward(value),get_allocator()); + it = this->members_.begin() + (this->members_.size() - 1); + } + else if (it->key() == name) + { + it->value(Json(std::forward(value),get_allocator())); + } + else + { + it = this->members_.emplace(it, + key_storage_type(name.begin(),name.end(), get_allocator()), + std::forward(value),get_allocator()); + } + return it; + } + + template + typename std::enable_if::value,iterator>::type + set_(iterator hint, key_storage_type&& name, T&& value) + { + string_view_type s(name.data(), name.size()); + iterator it; + if (hint != this->members_.end() && hint->key() <= s) + { + it = std::lower_bound(hint,this->members_.end(), s, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + else + { + it = std::lower_bound(this->members_.begin(),this->members_.end(), s, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + + if (it == this->members_.end()) + { + this->members_.emplace_back(std::forward(name), + std::forward(value)); + it = this->members_.begin() + (this->members_.size() - 1); + } + else if (string_view_type(it->key().data(), it->key().length()) == s) + { + it->value(Json(std::forward(value))); + } + else + { + it = this->members_.emplace(it, + std::forward(name), + std::forward(value)); + } + return it; + } + + template + typename std::enable_if::value,iterator>::type + set_(iterator hint, key_storage_type&& name, T&& value) + { + string_view_type s(name.data(), name.size()); + iterator it; + if (hint != this->members_.end() && hint->key() <= s) + { + it = std::lower_bound(hint,this->members_.end(), s, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + else + { + it = std::lower_bound(this->members_.begin(),this->members_.end(), s, + [](const value_type& a, const string_view_type& k){return a.key().compare(k) < 0;}); + } + + if (it == this->members_.end()) + { + this->members_.emplace_back(std::forward(name), + std::forward(value),get_allocator() ); + it = this->members_.begin() + (this->members_.size() - 1); + } + else if (string_view_type(it->key().data(), it->key().length()) == s) + { + it->value(Json(std::forward(value),get_allocator() )); + } + else + { + it = this->members_.emplace(it, + std::forward(name), + std::forward(value),get_allocator() ); + } + return it; + } + + bool operator==(const json_object& rhs) const + { + if (size() != rhs.size()) + { + return false; + } + for (auto it = this->members_.begin(); it != this->members_.end(); ++it) + { + + auto rhs_it = std::lower_bound(rhs.begin(), rhs.end(), *it, + [](const value_type& a, const value_type& b){return a.key().compare(b.key()) < 0;}); + if (rhs_it == rhs.end() || rhs_it->key() != it->key() || rhs_it->value() != it->value()) + { + return false; + } + } + return true; + } +private: + json_object& operator=(const json_object&) = delete; +}; + +// Preserve order +template +class json_object final : public Json_object_ +{ +public: + using typename Json_object_::allocator_type; + using typename Json_object_::char_type; + using typename Json_object_::char_allocator_type; + using typename Json_object_::key_storage_type; + using typename Json_object_::string_view_type; + using typename Json_object_::value_type; + using typename Json_object_::kvp_allocator_type; + using typename Json_object_::object_storage_type; + using typename Json_object_::iterator; + using typename Json_object_::const_iterator; + using Json_object_::get_allocator; + + json_object() + : Json_object_() + { + } + json_object(const allocator_type& allocator) + : Json_object_(allocator) + { + } + + json_object(const json_object& val) + : Json_object_(val) + { + } + + json_object(json_object&& val) + : Json_object_(std::forward(val)) + { + } + + json_object(const json_object& val, const allocator_type& allocator) + : Json_object_(val,allocator) + { + } + + json_object(json_object&& val,const allocator_type& allocator) + : Json_object_(std::forward(val),allocator) + { + } + + json_object(std::initializer_list init) + : Json_object_() + { + for (const auto& element : init) + { + if (element.size() != 2 || !element[0].is_string()) + { + JSONCONS_THROW(json_exception_impl("Cannot create object from initializer list")); + break; + } + } + for (auto& element : init) + { + insert_or_assign(element[0].as_string_view(), std::move(element[1])); + } + } + + json_object(std::initializer_list init, + const allocator_type& allocator) + : Json_object_(allocator) + { + for (const auto& element : init) + { + if (element.size() != 2 || !element[0].is_string()) + { + JSONCONS_THROW(json_exception_impl("Cannot create object from initializer list")); + break; + } + } + for (auto& element : init) + { + insert_or_assign(element[0].as_string_view(), std::move(element[1])); + } + } + + void swap(json_object& val) + { + Json_object_::swap(val); + } + + iterator begin() + { + return this->members_.begin(); + } + + iterator end() + { + return this->members_.end(); + } + + const_iterator begin() const + { + return this->members_.begin(); + } + + const_iterator end() const + { + return this->members_.end(); + } + + size_t size() const {return this->members_.size();} + + size_t capacity() const {return this->members_.capacity();} + + void clear() {this->members_.clear();} + + void shrink_to_fit() + { + for (size_t i = 0; i < this->members_.size(); ++i) + { + this->members_[i].shrink_to_fit(); + } + this->members_.shrink_to_fit(); + } + + void reserve(size_t n) {this->members_.reserve(n);} + + Json& at(size_t i) + { + if (i >= this->members_.size()) + { + JSONCONS_THROW(json_exception_impl("Invalid array subscript")); + } + return this->members_[i].value(); + } + + const Json& at(size_t i) const + { + if (i >= this->members_.size()) + { + JSONCONS_THROW(json_exception_impl("Invalid array subscript")); + } + return this->members_[i].value(); + } + + iterator find(const string_view_type& name) + { + return std::find_if(this->members_.begin(),this->members_.end(), + [name](const value_type& kv){return kv.key() == name;}); + } + + const_iterator find(const string_view_type& name) const + { + return std::find_if(this->members_.begin(),this->members_.end(), + [name](const value_type& kv){return kv.key() == name;}); + } + + void erase(const_iterator first, const_iterator last) + { + this->members_.erase(first,last); + } + + void erase(const string_view_type& name) + { + auto it = std::find_if(this->members_.begin(),this->members_.end(), + [name](const value_type& kv){return kv.key() == name;}); + if (it != this->members_.end()) + { + this->members_.erase(it); + } + } + + template + void insert(InputIt first, InputIt last, UnaryPredicate pred) + { + size_t count = std::distance(first,last); + this->members_.reserve(this->members_.size() + count); + for (auto s = first; s != last; ++s) + { + this->members_.emplace_back(pred(*s)); + } + auto it = last_wins_unique_sequence(this->members_.begin(), this->members_.end(), + [](const value_type& a, const value_type& b){ return a.key().compare(b.key());}); + this->members_.erase(it,this->members_.end()); + } + + // insert_or_assign + + template + typename std::enable_if::value,std::pair>::type + insert_or_assign(const string_view_type& name, T&& value) + { + bool inserted; + auto it = std::find_if(this->members_.begin(),this->members_.end(), + [name](const value_type& a){return a.key() == name;}); + + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(name.begin(),name.end()), + std::forward(value)); + it = this->members_.begin() + this->members_.size() - 1; + inserted = true; + } + else + { + it->value(Json(std::forward(value))); + inserted = false; // assigned + } + return std::make_pair(it,inserted); + } + + template + typename std::enable_if::value,std::pair>::type + insert_or_assign(const string_view_type& name, T&& value) + { + bool inserted; + auto it = std::find_if(this->members_.begin(),this->members_.end(), + [name](const value_type& a){return a.key() == name;}); + + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(name.begin(),name.end(),get_allocator()), + std::forward(value),get_allocator()); + it = this->members_.begin() + this->members_.size() - 1; + inserted = true; + } + else + { + it->value(Json(std::forward(value),get_allocator())); + inserted = false; // assigned + } + return std::make_pair(it,inserted); + } + + template + typename std::enable_if::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& key, T&& value) + { + iterator it; + if (hint == this->members_.end()) + { + auto result = insert_or_assign(key, std::forward(value)); + it = result.first; + } + else + { + it = std::find_if(this->members_.begin(),this->members_.end(), + [key](const value_type& a){return a.key() == key;}); + + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(key.begin(),key.end()), + std::forward(value)); + it = this->members_.begin() + this->members_.size() - 1; + } + else + { + it->value(Json(std::forward(value))); + } + } + return it; + } + + template + typename std::enable_if::value,iterator>::type + insert_or_assign(iterator hint, const string_view_type& key, T&& value) + { + iterator it; + if (hint == this->members_.end()) + { + auto result = insert_or_assign(key, std::forward(value)); + it = result.first; + } + else + { + it = std::find_if(this->members_.begin(),this->members_.end(), + [key](const value_type& a){return a.key() == key;}); + + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(key.begin(),key.end(),get_allocator()), + std::forward(value),get_allocator()); + it = this->members_.begin() + this->members_.size() - 1; + } + else + { + it->value(Json(std::forward(value),get_allocator())); + } + } + return it; + } + + // merge + + void merge(const json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + try_emplace(it->key(),it->value()); + } + } + + void merge(json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = std::find_if(this->members_.begin(),this->members_.end(), + [it](const value_type& a){return a.key() == it->key();}); + if (pos == this->members_.end() ) + { + this->members_.emplace_back(*it); + } + } + } + + void merge(iterator hint, const json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = try_emplace(hint, it->key(),it->value()); + } + } + + void merge(iterator hint, json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = std::find_if(this->members_.begin(),this->members_.end(), + [it](const value_type& a){return a.key() == it->key();}); + if (pos == this->members_.end() ) + { + hint = this->members_.emplace(hint,*it); + } + } + } + + // merge_or_update + + void merge_or_update(const json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + insert_or_assign(it->key(),it->value()); + } + } + + void merge_or_update(json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = std::find_if(this->members_.begin(),this->members_.end(), + [it](const value_type& a){return a.key() == it->key();}); + if (pos == this->members_.end() ) + { + this->members_.emplace_back(*it); + } + else + { + pos->value(it->value()); + } + } + } + + void merge_or_update(iterator hint, const json_object& source) + { + for (auto it = source.begin(); it != source.end(); ++it) + { + hint = insert_or_assign(hint, it->key(),it->value()); + } + } + + void merge_or_update(iterator hint, json_object&& source) + { + auto it = std::make_move_iterator(source.begin()); + auto end = std::make_move_iterator(source.end()); + for (; it != end; ++it) + { + auto pos = std::find_if(this->members_.begin(),this->members_.end(), + [it](const value_type& a){return a.key() == it->key();}); + if (pos == this->members_.end() ) + { + hint = this->members_.emplace(hint,*it); + } + else + { + pos->value(it->value()); + hint = pos; + } + } + } + + // try_emplace + template + typename std::enable_if::value,std::pair>::type + try_emplace(const string_view_type& key, Args&&... args) + { + bool inserted; + auto it = std::find_if(this->members_.begin(),this->members_.end(), + [key](const value_type& a){return a.key() == key;}); + + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(key.begin(),key.end()), + std::forward(args)...); + it = this->members_.begin() + this->members_.size() - 1; + inserted = true; + + } + else + { + inserted = false; + } + return std::make_pair(it,inserted); + } + + template + typename std::enable_if::value,std::pair>::type + try_emplace(const string_view_type& key, Args&&... args) + { + bool inserted; + auto it = std::find_if(this->members_.begin(),this->members_.end(), + [key](const value_type& a){return a.key() == key;}); + + if (it == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(key.begin(),key.end(), get_allocator()), + std::forward(args)...); + it = this->members_.begin() + this->members_.size() - 1; + inserted = true; + + } + else + { + inserted = false; + } + return std::make_pair(it,inserted); + } + + template + typename std::enable_if::value,iterator>::type + try_emplace(iterator hint, const string_view_type& key, Args&&... args) + { + auto it = std::find_if(this->members_.begin(),this->members_.end(), + [key](const value_type& a){return a.key() == key;}); + + if (it == this->members_.end()) + { + if (hint == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(key.begin(),key.end()), + std::forward(args)...); + it = this->members_.begin() + (this->members_.size() - 1); + } + else + { + it = this->members_.emplace(hint, + key_storage_type(key.begin(),key.end()), + std::forward(args)...); + } + } + return it; + } + + template + typename std::enable_if::value,iterator>::type + try_emplace(iterator hint, const string_view_type& key, Args&&... args) + { + auto it = std::find_if(this->members_.begin(),this->members_.end(), + [key](const value_type& a){return a.key() == key;}); + + if (it == this->members_.end()) + { + if (hint == this->members_.end()) + { + this->members_.emplace_back(key_storage_type(key.begin(),key.end(), get_allocator()), + std::forward(args)...); + it = this->members_.begin() + (this->members_.size() - 1); + } + else + { + it = this->members_.emplace(hint, + key_storage_type(key.begin(),key.end(), get_allocator()), + std::forward(args)...); + } + } + return it; + } + + // set_ + + template + typename std::enable_if::value,void>::type + set_(key_storage_type&& key, T&& value) + { + string_view_type s(key.data(),key.size()); + auto it = std::find_if(this->members_.begin(),this->members_.end(), + [s](const value_type& a){return a.key().compare(s) == 0;}); + + if (it == this->members_.end()) + { + this->members_.emplace_back(std::forward(key), + std::forward(value)); + } + else + { + it->value(Json(std::forward(value))); + } + } + + template + typename std::enable_if::value,void>::type + set_(key_storage_type&& key, T&& value) + { + string_view_type s(key.data(),key.size()); + auto it = std::find_if(this->members_.begin(),this->members_.end(), + [s](const value_type& a){return a.key().compare(s) == 0;}); + + if (it == this->members_.end()) + { + this->members_.emplace_back(std::forward(key), + std::forward(value),get_allocator()); + } + else + { + it->value(Json(std::forward(value),get_allocator())); + } + } + + template + typename std::enable_if::value,iterator>::type + set_(iterator hint, key_storage_type&& key, T&& value) + { + iterator it = hint; + + if (it == this->members_.end()) + { + this->members_.emplace_back(std::forward(key), + std::forward(value)); + it = this->members_.begin() + (this->members_.size() - 1); + } + else if (it->key() == key) + { + it->value(Json(std::forward(value))); + } + else + { + it = this->members_.emplace(it, + std::forward(key), + std::forward(value)); + } + return it; + } + + template + typename std::enable_if::value,iterator>::type + set_(iterator hint, key_storage_type&& key, T&& value) + { + iterator it = hint; + + if (it == this->members_.end()) + { + this->members_.emplace_back(std::forward(key), + std::forward(value), get_allocator()); + it = this->members_.begin() + (this->members_.size() - 1); + } + else if (it->key() == key) + { + it->value(Json(std::forward(value), get_allocator())); + } + else + { + it = this->members_.emplace(it, + std::forward(key), + std::forward(value), get_allocator()); + } + return it; + } + + bool operator==(const json_object& rhs) const + { + if (size() != rhs.size()) + { + return false; + } + for (auto it = this->members_.begin(); it != this->members_.end(); ++it) + { + auto rhs_it = std::find_if(rhs.begin(),rhs.end(), + [it](const value_type& a){return a.key() == it->key();}); + if (rhs_it == rhs.end() || rhs_it->key() != it->key() || rhs_it->value() != it->value()) + { + return false; + } + } + return true; + } +private: + json_object& operator=(const json_object&) = delete; +}; + +} + +#endif diff --git a/direct-access-api/include/json_type_traits.hpp b/direct-access-api/include/json_type_traits.hpp new file mode 100644 index 0000000..3f53ca8 --- /dev/null +++ b/direct-access-api/include/json_type_traits.hpp @@ -0,0 +1,958 @@ +// Copyright 2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_JSON_TYPE_TRAITS_HPP +#define JSONCONS_DETAIL_JSON_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" +#endif + +namespace jsoncons { namespace detail { + +// null_type + +struct null_type +{ +}; + +// json_type_traits + +template +struct json_type_traits +{ + static const bool is_compatible = false; + + static bool is(const Json&) + { + return false; + } +}; + +// is_incompatible +template +struct is_incompatible : std::false_type {}; + + +// is_incompatible +template +struct is_incompatible::is_compatible>::value>::type +> : std::true_type {}; + +// is_compatible_string_type +template +struct is_compatible_string_type : std::false_type {}; + +template +struct is_compatible_string_type::value && + detail::is_string_like::value && + !is_incompatible::value_type>::value +>::type> : std::true_type {}; + +// is_compatible_array_type +template +struct is_compatible_array_type : std::false_type {}; + +template +struct is_compatible_array_type::value && + detail::is_vector_like::value && + !is_incompatible::value_type>::value +>::type> : std::true_type {}; + +// is_compatible_object_type +template +struct is_compatible_object_type : std::false_type {}; + +template +struct is_compatible_object_type::value +>::type> : std::true_type {}; + +// is_std_array +template +struct is_std_array : std::false_type {}; + +template +struct is_std_array> : std::true_type {}; + +template +class json_array_input_iterator +{ +public: + typedef typename Json::const_array_iterator iterator_base; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::difference_type difference_type; + typedef typename std::iterator_traits::pointer pointer; + typedef T reference; + typedef std::input_iterator_tag iterator_category; + + json_array_input_iterator() + { + } + + json_array_input_iterator(iterator_base it) + : it_(it) + { + } + + json_array_input_iterator& operator=(json_array_input_iterator rhs) + { + swap(*this,rhs); + return *this; + } + + json_array_input_iterator& operator++() + { + ++it_; + return *this; + } + + json_array_input_iterator operator++(int) // postfix increment + { + json_array_input_iterator temp(*this); + ++it_; + return temp; + } + + json_array_input_iterator& operator--() + { + --it_; + return *this; + } + + json_array_input_iterator operator--(int) + { + json_array_input_iterator temp(*this); + --it_; + return temp; + } + + reference operator*() const + { + return typename Traits::base_traits::as(*it_); + } + + friend bool operator==(const json_array_input_iterator& it1, const json_array_input_iterator& it2) + { + return it1.it_ == it2.it_; + } + friend bool operator!=(const json_array_input_iterator& it1, const json_array_input_iterator& it2) + { + return !(it1.it_ == it2.it_); + } + friend void swap(json_array_input_iterator& lhs, json_array_input_iterator& rhs) + { + using std::swap; + swap(lhs.it_,rhs.it_); + swap(lhs.empty_,rhs.empty_); + } + +private: + iterator_base it_; +}; + +template +class json_object_input_iterator +{ +public: + typedef typename Json::const_object_iterator iterator_base; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::difference_type difference_type; + typedef typename std::iterator_traits::pointer pointer; + typedef T reference; + typedef std::input_iterator_tag iterator_category; + typedef typename T::first_type key_type; + typedef typename T::second_type mapped_type; + + json_object_input_iterator() + { + } + + json_object_input_iterator(iterator_base it) + : it_(it) + { + } + + json_object_input_iterator& operator=(json_object_input_iterator rhs) + { + swap(*this,rhs); + return *this; + } + + json_object_input_iterator& operator++() + { + ++it_; + return *this; + } + + json_object_input_iterator operator++(int) // postfix increment + { + json_object_input_iterator temp(*this); + ++it_; + return temp; + } + + json_object_input_iterator& operator--() + { + --it_; + return *this; + } + + json_object_input_iterator operator--(int) + { + json_object_input_iterator temp(*this); + --it_; + return temp; + } + + reference operator*() const + { + return T(key_type(it_->key()),typename Traits::base_traits::as(it_->value())); + } + + friend bool operator==(const json_object_input_iterator& it1, const json_object_input_iterator& it2) + { + return it1.it_ == it2.it_; + } + friend bool operator!=(const json_object_input_iterator& it1, const json_object_input_iterator& it2) + { + return !(it1.it_ == it2.it_); + } + friend void swap(json_object_input_iterator& lhs, json_object_input_iterator& rhs) + { + using std::swap; + swap(lhs.it_,rhs.it_); + swap(lhs.empty_,rhs.empty_); + } + +private: + iterator_base it_; +}; + +template +struct json_type_traits::const_pointer_type> +{ + typedef typename Json::char_type char_type; + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_string(); + } + static const char_type* as(const Json& j) + { + return j.as_cstring(); + } + template + static Json to_json(Args&&... args) + { + return Json(typename Json::variant(std::forward(args)...)); + } +}; + +template +struct json_type_traits::pointer_type> +{ + typedef typename Json::char_type char_type; + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_string(); + } + template + static Json to_json(Args&&... args) + { + return Json(typename Json::variant(std::forward(args)...)); + } +}; + +// integral + +template +struct json_type_traits::value +>::type> +{ + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + if (j.is_integer()) + { + return j.as_integer() >= (std::numeric_limits::min)() && j.as_integer() <= (std::numeric_limits::max)(); + } + else if (j.is_uinteger()) + { + return j.as_uinteger() <= static_cast((std::numeric_limits::max)()); + } + else + { + return false; + } + } + static T as(const Json& j) + { + return static_cast(j.as_integer()); + } + template + static Json to_json(Args&&... args) + { + return Json::from_integer(std::forward(args)...); + } +}; + +template +struct json_type_traits::value +>::type > +{ + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + if (j.is_integer()) + { + return j.as_integer() >= 0 && static_cast(j.as_integer()) <= (std::numeric_limits::max)(); + } + else if (j.is_uinteger()) + { + return j.as_uinteger() <= (std::numeric_limits::max)(); + } + else + { + return false; + } + } + static T as(const Json& j) + { + return static_cast(j.as_uinteger()); + } + + template + static Json to_json(Args&&... args) + { + return Json::from_uinteger(std::forward(args)...); + } +}; + +template +struct json_type_traits::value +>::type> +{ + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_double(); + } + static T as(const Json& j) + { + return static_cast(j.as_double()); + } + template + static Json to_json(Args&&... args) + { + return Json::from_floating_point(std::forward(args)...); + } +}; + +template +struct json_type_traits +{ + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_object(); + } + template + static Json to_json(Args&&... args) + { + return Json(typename Json::variant(std::forward(args)...)); + } +}; + +template +struct json_type_traits +{ + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_array(); + } + template + static Json to_json(Args&&... args) + { + return Json(typename Json::variant(std::forward(args)...)); + } +}; + +template +struct json_type_traits +{ + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json&) JSONCONS_NOEXCEPT + { + return true; + } + static Json as(Json j) + { + return j; + } + static Json to_json(const Json& val) + { + return val; + } + static Json to_json(const Json& val, allocator_type) + { + return val; + } +}; + +template +struct json_type_traits +{ + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_null(); + } + static typename jsoncons::null_type as(const Json& j) + { + JSONCONS_ASSERT(j.is_null()); + return jsoncons::null_type(); + } + static Json to_json(jsoncons::null_type) + { + return Json::null(); + } + static Json to_json(jsoncons::null_type, allocator_type) + { + return Json::null(); + } +}; + +template +struct json_type_traits +{ + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_bool(); + } + static bool as(const Json& j) + { + return j.as_bool(); + } + template + static Json to_json(Args&&... args) + { + return Json(typename Json::variant(std::forward(args)...)); + } +}; + +template +struct json_type_traits::const_reference>::value, + std::vector::const_reference, + void>::type>::value>::type> +{ + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_bool(); + } + static bool as(const Json& j) + { + return j.as_bool(); + } + template + static Json to_json(Args&&... args) + { + return Json(typename Json::variant(std::forward(args)...)); + } +}; + +template +struct json_type_traits::reference> +{ + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_bool(); + } + static bool as(const Json& j) + { + return j.as_bool(); + } + template + static Json to_json(Args&&... args) + { + return Json(typename Json::variant(std::forward(args)...)); + } +}; + +template +struct json_type_traits::value && + !detail::is_std_array::value>::type> +{ + typedef typename std::iterator_traits::value_type element_type; + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + bool result = j.is_array(); + if (result) + { + for (auto e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + template + static typename std::enable_if::value && !std::is_same::value),T>::type + as(const Json& j) + { + if (j.is_array()) + { + T v(detail::json_array_input_iterator(j.array_range().begin()), + detail::json_array_input_iterator(j.array_range().end())); + return v; + } + else + { + JSONCONS_THROW(json_exception_impl("Attempt to cast json non-array to array")); + } + } + + template + static typename std::enable_if::value && !std::is_same::value,T>::type + as(const Json& j) + { + if (j.is_array()) + { + T v(detail::json_array_input_iterator(j.array_range().begin()), + detail::json_array_input_iterator(j.array_range().end())); + return v; + } + else if (j.is_byte_string()) + { + T v(j.as_byte_string_view().begin(),j.as_byte_string_view().end()); + return v; + } + else + { + JSONCONS_THROW(json_exception_impl("Attempt to cast json non-array to array")); + } + } + + static Json to_json(const T& val) + { + Json j = typename Json::array(); + auto first = std::begin(val); + auto last = std::end(val); + size_t size = std::distance(first,last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + + static Json to_json(const T& val, const allocator_type& allocator) + { + Json j = typename Json::array(allocator); + auto first = std::begin(val); + auto last = std::end(val); + size_t size = std::distance(first, last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } +}; + +template +struct json_type_traits::value>::type> +{ + typedef typename std::iterator_traits::value_type element_type; + typedef typename Json::allocator_type allocator_type; + typedef typename T::allocator_type string_allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_string(); + } + + static T as(const Json& j) + { + if (j.is_string()) + { + return j.as_string(string_allocator_type()); + } + else + { + T s; + j.dump(s); + return s; + } + } + + static Json to_json(const T& val) + { + return Json(typename Json::variant(val.data(), val.size())); + } + + static Json to_json(const T& val, const allocator_type& allocator) + { + return Json(typename Json::variant(val.data(),val.size(),allocator)); + } +}; + +template +struct json_type_traits::value>::type +> +{ + typedef typename T::mapped_type mapped_type; + typedef typename T::value_type value_type; + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + bool result = j.is_object(); + for (auto member : j.object_range()) + { + if (!member.value().template is()) + { + result = false; + } + } + return result; + } + + static T as(const Json& j) + { + T v(detail::json_object_input_iterator(j.object_range().begin()), + detail::json_object_input_iterator(j.object_range().end())); + return v; + } + + static Json to_json(const T& val) + { + Json j; + j.reserve(val.size()); + for (auto p: val) + { + j.set(p.first, p.second); + } + return j; + } + + static Json to_json(const T& val, const allocator_type& allocator) + { + Json j(allocator); + j.reserve(val.size()); + for (auto p: val) + { + j.set(p.first, p.second); + } + return j; + } +}; + +template +struct json_type_traits> +{ + typedef typename Json::allocator_type allocator_type; + + typedef E element_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + bool result = j.is_array() && j.size() == N; + if (result) + { + for (auto e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + static std::array as(const Json& j) + { + std::array buff; + JSONCONS_ASSERT(j.size() == N); + for (size_t i = 0; i < N; i++) + { + buff[i] = j[i].template as(); + } + return buff; + } + + static Json to_json(const std::array& val) + { + Json j = typename Json::array(); + j.reserve(N); + for (auto it = val.begin(); it != val.end(); ++it) + { + j.push_back(*it); + } + return j; + } + + static Json to_json(const std::array& val, + const allocator_type& allocator) + { + Json j = typename Json::array(allocator); + j.reserve(N); + for (auto it = val.begin(); it != val.end(); ++it) + { + j.push_back(*it); + } + return j; + } +}; + +template +struct json_tuple_helper +{ + using element_type = typename std::tuple_element::type; + using next = json_tuple_helper; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + if(j[Pos - 1].template is()) + { + return next::is(j); + } + else + { + return false; + } + } + + static void as(Tuple& tuple, const Json& j) + { + std::get(tuple) = j[Pos - 1].template as(); + next::as(tuple, j); + } + + static void to_json(const Tuple& tuple, std::array::value>& jsons) + { + jsons[Pos - 1] = typename Traits::base_traits::to_json(std::get(tuple)); + next::to_json(tuple, jsons); + } +}; + +template +struct json_tuple_helper<0, Json, Tuple> +{ + static bool is(const Json&) JSONCONS_NOEXCEPT + { + return true; + } + + static void as(Tuple&, const Json&) + { + } + + static void to_json(const Tuple&, std::array::value>&) + { + } +}; + +template +struct json_type_traits> +{ +private: + using helper = detail::json_tuple_helper>; + +public: + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return helper::is(j); + } + + static std::tuple as(const Json& j) + { + std::tuple buff; + helper::as(buff, j); + return buff; + } + + static Json to_json(const std::tuple& val) + { + std::array buf; + helper::to_json(val, buf); + return Json(typename Json::array(buf.begin(), buf.end())); + } +}; + +template +struct json_type_traits> +{ +public: + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_array() && j.size() == 2; + } + + static std::pair as(const Json& j) + { + return std::make_pair(j[0]. template as(),j[1]. template as()); + } + + static Json to_json(const std::pair& val) + { + return typename Json::array{val.first,val.second}; + } +}; + +template +struct json_type_traits> +{ +public: + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + return j.is_byte_string(); + } + + static basic_byte_string as(const Json& j) + { + return basic_byte_string(j.as_byte_string_view()); + } + + static Json to_json(const basic_byte_string& val) + { + return Json(val.data(),val.length()); + } +}; + +// std::valarray + +template +struct json_type_traits> +{ + typedef typename Json::allocator_type allocator_type; + + static bool is(const Json& j) JSONCONS_NOEXCEPT + { + bool result = j.is_array(); + if (result) + { + for (auto e : j.array_range()) + { + if (!e.template is()) + { + result = false; + break; + } + } + } + return result; + } + + static std::valarray as(const Json& j) + { + if (j.is_array()) + { + std::valarray v(j.size()); + for (size_t i = 0; i < j.size(); ++i) + { + v[i] = j[i].template as(); + } + return v; + } + else + { + JSONCONS_THROW(json_exception_impl("Attempt to cast json non-array to array")); + } + } + + static Json to_json(const std::valarray& val) + { + Json j = typename Json::array(); + auto first = std::begin(val); + auto last = std::end(val); + size_t size = std::distance(first,last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } + + static Json to_json(const std::valarray& val, const allocator_type& allocator) + { + Json j = typename Json::array(allocator); + auto first = std::begin(val); + auto last = std::end(val); + size_t size = std::distance(first,last); + j.reserve(size); + for (auto it = first; it != last; ++it) + { + j.push_back(*it); + } + return j; + } +}; + +}} + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#endif + diff --git a/direct-access-api/include/jsoncons_config.hpp b/direct-access-api/include/jsoncons_config.hpp new file mode 100644 index 0000000..59c6b65 --- /dev/null +++ b/direct-access-api/include/jsoncons_config.hpp @@ -0,0 +1,106 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONCONS_CONFIG_HPP +#define JSONCONS_JSONCONS_CONFIG_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // std::numeric_limits + +// Uncomment the following line to suppress deprecated names (recommended for new code) +//#define JSONCONS_NO_DEPRECATED + +// The definitions below follow the definitions in compiler_support_p.h, https://github.com/01org/tinycbor +// MIT license + +#if defined(__GNUC__) || defined(__clang__) +#define JSONCONS_LIKELY(x) __builtin_expect(!!(x), 1) +#define JSONCONS_UNLIKELY(x) __builtin_expect(!!(x), 0) +#define JSONCONS_UNREACHABLE() __builtin_unreachable() +#elif defined(_MSC_VER) +#define JSONCONS_LIKELY(x) x +#define JSONCONS_UNLIKELY(x) x +#define JSONCONS_UNREACHABLE() __assume(0) +#else +#define JSONCONS_LIKELY(x) x +#define JSONCONS_UNLIKELY(x) x +#define JSONCONS_UNREACHABLE() do {} while (0) +#endif + +namespace jsoncons +{ + +#if _MSC_VER > 1800 // _MSC_VER == 1800 -> MS Visual Studio 2013 +#else +#define JSONCONS_NO_CONSTEXPR +#endif + +#define JSONCONS_NO_TO_CHARS + +#if defined(_MSC_VER) +#if _MSC_VER >= 1900 +#define JSONCONS_HAS_USER_DEFINED_LITERALS +#endif +#else +#define JSONCONS_HAS_USER_DEFINED_LITERALS +#endif + +//#define JSONCONS_HAS_STRING_VIEW + +#if defined(ANDROID) || defined(__ANDROID__) +#define JSONCONS_HAS_STRTOLD_L +#define JSONCONS_NO_LOCALECONV +#endif + +#if defined (__clang__) +#if defined(_GLIBCXX_USE_NOEXCEPT) +#define JSONCONS_NOEXCEPT _GLIBCXX_USE_NOEXCEPT +#else +#define JSONCONS_NOEXCEPT noexcept +#endif +#elif defined(__GNUC__) +#define JSONCONS_NOEXCEPT _GLIBCXX_USE_NOEXCEPT +#elif defined(_MSC_VER) +#if _MSC_VER >= 1900 +#define JSONCONS_NOEXCEPT noexcept +#else +#define JSONCONS_NOEXCEPT +#endif +#else +#define JSONCONS_NOEXCEPT +#endif + +#if defined(_MSC_VER) +#define JSONCONS_HAS_MSC__STRTOD_L +#define JSONCONS_HAS__ECVT_S +#define JSONCONS_HAS_FOPEN_S +#if _MSC_VER >= 1900 +#define JSONCONS_ALIGNOF alignof +#else +#define JSONCONS_ALIGNOF __alignof +#endif +#else +#define JSONCONS_ALIGNOF alignof +#endif + +#define JSONCONS_DEFINE_LITERAL( name, lit ) \ +template< class Ch > Ch const* name(); \ +template<> inline char const * name() { return lit; } \ +template<> inline wchar_t const* name() { return L ## lit; } + +} + +#endif diff --git a/direct-access-api/include/jsoncons_utilities.hpp b/direct-access-api/include/jsoncons_utilities.hpp new file mode 100644 index 0000000..28a9eb7 --- /dev/null +++ b/direct-access-api/include/jsoncons_utilities.hpp @@ -0,0 +1,954 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONCONSUTILITIES_HPP +#define JSONCONS_JSONCONSUTILITIES_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(JSONCONS_NO_TO_CHARS) +#include +#endif +#include + +namespace jsoncons +{ + +#if !defined(JSONCONS_HAS_STRING_VIEW) +template > +class Basic_string_view_ +{ +private: + const CharT* data_; + size_t length_; +public: + typedef CharT value_type; + typedef const CharT& const_reference; + typedef Traits traits_type; + typedef std::size_t size_type; + static const size_type npos = size_type(-1); + typedef const CharT* const_iterator; + typedef const CharT* iterator; + typedef std::reverse_iterator const_reverse_iterator; + + Basic_string_view_() + : data_(nullptr), length_(0) + { + } + Basic_string_view_(const CharT* data, size_t length) + : data_(data), length_(length) + { + } + Basic_string_view_(const CharT* data) + : data_(data), length_(Traits::length(data)) + { + } + Basic_string_view_(const Basic_string_view_& other) = default; + + template + Basic_string_view_(const std::basic_string& s) + : data_(s.data()), length_(s.length()) + { + } + + template + explicit operator std::basic_string() const + { + return std::basic_string(data_,length_); + } + + // iterator support + const_iterator begin() const JSONCONS_NOEXCEPT + { + return data_; + } + const_iterator end() const JSONCONS_NOEXCEPT + { + return data_ + length_; + } + const_iterator cbegin() const JSONCONS_NOEXCEPT + { + return data_; + } + const_iterator cend() const JSONCONS_NOEXCEPT + { + return data_ + length_; + } + const_reverse_iterator rbegin() const JSONCONS_NOEXCEPT + { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const JSONCONS_NOEXCEPT + { + return const_reverse_iterator(begin()); + } + const_reverse_iterator crbegin() const JSONCONS_NOEXCEPT + { + return const_reverse_iterator(end()); + } + const_reverse_iterator crend() const JSONCONS_NOEXCEPT + { + return const_reverse_iterator(begin()); + } + + // capacity + + size_t size() const + { + return length_; + } + + size_t length() const + { + return length_; + } + size_type max_size() const JSONCONS_NOEXCEPT + { + return length_; + } + bool empty() const JSONCONS_NOEXCEPT + { + return length_ == 0; + } + + // element access + + const_reference operator[](size_type pos) const + { + return data_[pos]; + } + + const_reference at(size_t pos) const + { + if (pos >= length_) + { + JSONCONS_THROW(json_exception_impl("pos exceeds length")); + } + return data_[pos]; + } + + const_reference front() const + { + return data_[0]; + } + const_reference back() const + { + return data_[length_-1]; + } + + const CharT* data() const + { + return data_; + } + + // string operations + + Basic_string_view_ substr(size_type pos, size_type n=npos) const + { + if (pos > length_) + { + JSONCONS_THROW(json_exception_impl("pos exceeds size")); + } + if (n == npos || pos + n > length_) + { + n = length_ - pos; + } + return Basic_string_view_(data_ + pos, n); + } + + int compare(Basic_string_view_ s) const + { + const int rc = Traits::compare(data_, s.data_, (std::min)(length_, s.length_)); + return rc != 0 ? rc : (length_ == s.length_ ? 0 : length_ < s.length_ ? -1 : 1); + } + + int compare(const CharT* data) const + { + const size_t length = Traits::length(data); + const int rc = Traits::compare(data_, data, (std::min)(length_, length)); + return rc != 0 ? rc : (length_ == length? 0 : length_ < length? -1 : 1); + } + + template + int compare(const std::basic_string& s) const + { + const int rc = Traits::compare(data_, s.data(), (std::min)(length_, s.length())); + return rc != 0 ? rc : (length_ == s.length() ? 0 : length_ < s.length() ? -1 : 1); + } + + size_type find(Basic_string_view_ s, size_type pos = 0) const JSONCONS_NOEXCEPT + { + if (pos > length_) + { + return npos; + } + if (s.length_ == 0) + { + return pos; + } + const_iterator it = std::search(cbegin() + pos, cend(), + s.cbegin(), s.cend(), Traits::eq); + return it == cend() ? npos : std::distance(cbegin(), it); + } + size_type find(CharT ch, size_type pos = 0) const JSONCONS_NOEXCEPT + { + return find(Basic_string_view_(&ch, 1), pos); + } + size_type find(const CharT* s, size_type pos, size_type n) const JSONCONS_NOEXCEPT + { + return find(Basic_string_view_(s, n), pos); + } + size_type find(const CharT* s, size_type pos = 0) const JSONCONS_NOEXCEPT + { + return find(Basic_string_view_(s), pos); + } + + size_type rfind(Basic_string_view_ s, size_type pos = npos) const JSONCONS_NOEXCEPT + { + if (length_ < s.length_) + { + return npos; + } + if (pos > length_ - s.length_) + { + pos = length_ - s.length_; + } + if (s.length_ == 0) + { + return pos; + } + for (const CharT* p = data_ + pos; true; --p) + { + if (Traits::compare(p, s.data_, s.length_) == 0) + { + return p - data_; + } + if (p == data_) + { + return npos; + } + }; + } + size_type rfind(CharT ch, size_type pos = npos) const JSONCONS_NOEXCEPT + { + return rfind(Basic_string_view_(&ch, 1), pos); + } + size_type rfind(const CharT* s, size_type pos, size_type n) const JSONCONS_NOEXCEPT + { + return rfind(Basic_string_view_(s, n), pos); + } + size_type rfind(const CharT* s, size_type pos = npos) const JSONCONS_NOEXCEPT + { + return rfind(Basic_string_view_(s), pos); + } + + size_type find_first_of(Basic_string_view_ s, size_type pos = 0) const JSONCONS_NOEXCEPT + { + if (pos >= length_ || s.length_ == 0) + { + return npos; + } + const_iterator it = std::find_first_of + (cbegin() + pos, cend(), s.cbegin(), s.cend(), Traits::eq); + return it == cend() ? npos : std::distance (cbegin(), it); + } + size_type find_first_of(CharT ch, size_type pos = 0) const JSONCONS_NOEXCEPT + { + return find_first_of(Basic_string_view_(&ch, 1), pos); + } + size_type find_first_of(const CharT* s, size_type pos, size_type n) const JSONCONS_NOEXCEPT + { + return find_first_of(Basic_string_view_(s, n), pos); + } + size_type find_first_of(const CharT* s, size_type pos = 0) const JSONCONS_NOEXCEPT + { + return find_first_of(Basic_string_view_(s), pos); + } + + size_type find_last_of(Basic_string_view_ s, size_type pos = npos) const JSONCONS_NOEXCEPT + { + if (s.length_ == 0) + { + return npos; + } + if (pos >= length_) + { + pos = 0; + } + else + { + pos = length_ - (pos+1); + } + const_reverse_iterator it = std::find_first_of + (crbegin() + pos, crend(), s.cbegin(), s.cend(), Traits::eq); + return it == crend() ? npos : (length_ - 1 - std::distance(crbegin(), it)); + } + size_type find_last_of(CharT ch, size_type pos = npos) const JSONCONS_NOEXCEPT + { + return find_last_of(Basic_string_view_(&ch, 1), pos); + } + size_type find_last_of(const CharT* s, size_type pos, size_type n) const JSONCONS_NOEXCEPT + { + return find_last_of(Basic_string_view_(s, n), pos); + } + size_type find_last_of(const CharT* s, size_type pos = npos) const JSONCONS_NOEXCEPT + { + return find_last_of(Basic_string_view_(s), pos); + } + + size_type find_first_not_of(Basic_string_view_ s, size_type pos = 0) const JSONCONS_NOEXCEPT + { + if (pos >= length_) + return npos; + if (s.length_ == 0) + return pos; + + const_iterator it = cend(); + for (auto p = cbegin()+pos; p != cend(); ++p) + { + if (Traits::find(s.data_, s.length_, *p) == 0) + { + it = p; + break; + } + } + return it == cend() ? npos : std::distance (cbegin(), it); + } + size_type find_first_not_of(CharT ch, size_type pos = 0) const JSONCONS_NOEXCEPT + { + return find_first_not_of(Basic_string_view_(&ch, 1), pos); + } + size_type find_first_not_of(const CharT* s, size_type pos, size_type n) const JSONCONS_NOEXCEPT + { + return find_first_not_of(Basic_string_view_(s, n), pos); + } + size_type find_first_not_of(const CharT* s, size_type pos = 0) const JSONCONS_NOEXCEPT + { + return find_first_not_of(Basic_string_view_(s), pos); + } + + size_type find_last_not_of(Basic_string_view_ s, size_type pos = npos) const JSONCONS_NOEXCEPT + { + if (pos >= length_) + { + pos = length_ - 1; + } + if (s.length_ == 0) + { + return pos; + } + pos = length_ - (pos+1); + + const_iterator it = crend(); + for (auto p = crbegin()+pos; p != crend(); ++p) + { + if (Traits::find(s.data_, s.length_, *p) == 0) + { + it = p; + break; + } + } + return it == crend() ? npos : (length_ - 1 - std::distance(crbegin(), it)); + } + size_type find_last_not_of(CharT ch, size_type pos = npos) const JSONCONS_NOEXCEPT + { + return find_last_not_of(Basic_string_view_(&ch, 1), pos); + } + size_type find_last_not_of(const CharT* s, size_type pos, size_type n) const JSONCONS_NOEXCEPT + { + return find_last_not_of(Basic_string_view_(s, n), pos); + } + size_type find_last_not_of(const CharT* s, size_type pos = npos) const JSONCONS_NOEXCEPT + { + return find_last_not_of(Basic_string_view_(s), pos); + } + + friend std::basic_ostream& operator<<(std::basic_ostream& os, const Basic_string_view_& sv) + { + os.write(sv.data_,sv.length_); + return os; + } +}; + +// == +template +bool operator==(const Basic_string_view_& lhs, + const Basic_string_view_& rhs) +{ + return lhs.compare(rhs) == 0; +} +template +bool operator==(const Basic_string_view_& lhs, + const std::basic_string& rhs) +{ + return lhs.compare(rhs) == 0; +} +template +bool operator==(const std::basic_string& lhs, + const Basic_string_view_& rhs) +{ + return rhs.compare(lhs) == 0; +} +template +bool operator==(const Basic_string_view_& lhs, + const CharT* rhs) +{ + return lhs.compare(rhs) == 0; +} +template +bool operator==(const CharT* lhs, + const Basic_string_view_& rhs) +{ + return rhs.compare(lhs) == 0; +} + +// != +template +bool operator!=(const Basic_string_view_& lhs, + const Basic_string_view_& rhs) +{ + return lhs.compare(rhs) != 0; +} +template +bool operator!=(const Basic_string_view_& lhs, + const std::basic_string& rhs) +{ + return lhs.compare(rhs) != 0; +} +template +bool operator!=(const std::basic_string& lhs, + const Basic_string_view_& rhs) +{ + return rhs.compare(lhs) != 0; +} +template +bool operator!=(const Basic_string_view_& lhs, + const CharT* rhs) +{ + return lhs.compare(rhs) != 0; +} +template +bool operator!=(const CharT* lhs, + const Basic_string_view_& rhs) +{ + return rhs.compare(lhs) != 0; +} + +// <= +template +bool operator<=(const Basic_string_view_& lhs, + const Basic_string_view_& rhs) +{ + return lhs.compare(rhs) <= 0; +} +template +bool operator<=(const Basic_string_view_& lhs, + const std::basic_string& rhs) +{ + return lhs.compare(rhs) <= 0; +} +template +bool operator<=(const std::basic_string& lhs, + const Basic_string_view_& rhs) +{ + return rhs.compare(lhs) >= 0; +} + +// < +template +bool operator<(const Basic_string_view_& lhs, + const Basic_string_view_& rhs) +{ + return lhs.compare(rhs) < 0; +} +template +bool operator<(const Basic_string_view_& lhs, + const std::basic_string& rhs) +{ + return lhs.compare(rhs) < 0; +} +template +bool operator<(const std::basic_string& lhs, + const Basic_string_view_& rhs) +{ + return rhs.compare(lhs) > 0; +} + +// >= +template +bool operator>=(const Basic_string_view_& lhs, + const Basic_string_view_& rhs) +{ + return lhs.compare(rhs) >= 0; +} +template +bool operator>=(const Basic_string_view_& lhs, + const std::basic_string& rhs) +{ + return lhs.compare(rhs) >= 0; +} +template +bool operator>=(const std::basic_string& lhs, + const Basic_string_view_& rhs) +{ + return rhs.compare(lhs) <= 0; +} + +// > +template +bool operator>(const Basic_string_view_& lhs, + const Basic_string_view_& rhs) +{ + return lhs.compare(rhs) > 0; +} +template +bool operator>(const Basic_string_view_& lhs, + const std::basic_string& rhs) +{ + return lhs.compare(rhs) > 0; +} +template +bool operator>(const std::basic_string& lhs, + const Basic_string_view_& rhs) +{ + return rhs.compare(lhs) < 0; +} +#endif + +#if !defined(JSONCONS_HAS_STRING_VIEW) +template > +using basic_string_view_ext = Basic_string_view_; +#else +#include +template > +using basic_string_view_ext = std::basic_string_view; +#endif + +#if !defined(JSONCONS_NO_TO_CHARS) +using chars_format = std::chars_format; +#else +enum class chars_format : uint8_t {fixed=1,scientific=2,hex=4,general=fixed|scientific}; +#endif + +// number_format + +class number_format +{ + chars_format format_; + uint8_t precision_; + uint8_t decimal_places_; +public: + number_format() + : format_(chars_format::general), precision_(0), decimal_places_(0) + { + } + + number_format(uint8_t precision, uint8_t decimal_places) + : format_(chars_format::general), precision_(precision), decimal_places_(decimal_places) + { + } + + number_format(chars_format format, uint8_t precision, uint8_t decimal_places) + : format_(format), precision_(precision), decimal_places_(decimal_places) + { + } + + number_format(chars_format format) + : format_(format), precision_(0), decimal_places_(0) + { + } + + number_format(const number_format&) = default; + number_format(number_format&&) = default; + number_format& operator=(const number_format& e) = default; + number_format& operator=(number_format&& e) = default; + + uint8_t precision() const + { + return precision_; + } + + uint8_t decimal_places() const + { + return decimal_places_; + } + + chars_format floating_point_format() const + { + return format_; + } +}; + +// byte_string_view +class byte_string_view +{ + const uint8_t* data_; + size_t length_; +public: + typedef const uint8_t* const_iterator; + typedef const uint8_t* iterator; + typedef std::size_t size_type; + + byte_string_view(const uint8_t* data, size_t length) + : data_(data), length_(length) + { + } + + const uint8_t* data() const + { + return data_; + } + + size_t length() const + { + return length_; + } + + size_t size() const + { + return length_; + } + + // iterator support + const_iterator begin() const JSONCONS_NOEXCEPT + { + return data_; + } + const_iterator end() const JSONCONS_NOEXCEPT + { + return data_ + length_; + } + + uint8_t operator[](size_type pos) const + { + return data_[pos]; + } + + friend bool operator==(const byte_string_view& lhs, const byte_string_view& rhs) + { + if (lhs.length() != rhs.length()) + { + return false; + } + for (size_t i = 0; i < lhs.length(); ++i) + { + if (lhs[i] != rhs[i]) + { + return false; + } + } + return true; + } + + friend bool operator!=(const byte_string_view& lhs, const byte_string_view& rhs) + { + return !(lhs == rhs); + } + + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, const byte_string_view& o) + { + std::basic_ostringstream ss; + ss.flags(std::ios::hex | std::ios::showbase); + for (auto b : o) + { + ss << (int)b; + } + os << ss.str(); + return os; + } +}; + +// basic_byte_string +template > +class basic_byte_string +{ + std::vector data_; +public: + typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::const_iterator iterator; + typedef std::size_t size_type; + + basic_byte_string() = default; + + explicit basic_byte_string(const Allocator& alloc) + : data_(alloc) + { + } + + basic_byte_string(std::initializer_list init) + : data_(std::move(init)) + { + } + + basic_byte_string(std::initializer_list init, const Allocator& alloc) + : data_(std::move(init), alloc) + { + } + + explicit basic_byte_string(const byte_string_view& v) + : data_(v.begin(),v.end()) + { + } + + basic_byte_string(const byte_string_view& v, const Allocator& alloc) + : data_(v.begin(),v.end(),alloc) + { + } + + basic_byte_string(const char* s) + { + while (*s) + { + data_.push_back(*s++); + } + } + + basic_byte_string(const uint8_t* data, size_t length) + { + data_.insert(data,data+length); + } + + basic_byte_string(const basic_byte_string& s) = default; + + basic_byte_string(basic_byte_string&& s) = default; + + basic_byte_string& operator=(const basic_byte_string& s) = default; + + basic_byte_string& operator=(basic_byte_string&& s) = default; + + operator byte_string_view() const JSONCONS_NOEXCEPT + { + return byte_string_view(data(),length()); + } + + void push_back(uint8_t b) + { + data_.push_back(b); + } + + void assign(const uint8_t* s, size_t count) + { + data_.clear(); + data_.insert(s, s+count); + } + + void append(const uint8_t* s, size_t count) + { + data_.insert(s, s+count); + } + + void clear() + { + data_.clear(); + } + + uint8_t operator[](size_type pos) const + { + return data_[pos]; + } + + // iterator support + const_iterator begin() const JSONCONS_NOEXCEPT + { + return data_.begin(); + } + const_iterator end() const JSONCONS_NOEXCEPT + { + return data_.end(); + } + + const uint8_t* data() const + { + return data_.data(); + } + + size_t size() const + { + return data_.size(); + } + + size_t length() const + { + return data_.size(); + } + + friend bool operator==(const basic_byte_string& lhs, const basic_byte_string& rhs) + { + return byte_string_view(lhs) == byte_string_view(rhs); + } + + friend bool operator!=(const basic_byte_string& lhs, const basic_byte_string& rhs) + { + return byte_string_view(lhs) != byte_string_view(rhs); + } + + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_byte_string& o) + { + os << byte_string_view(o); + return os; + } +}; + +typedef basic_byte_string> byte_string; + +static const std::string base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/" + "="; +static const std::string base64url_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-_" + "\0"; + +inline +static bool is_base64(uint8_t c) +{ + return isalnum(c) || c == '+' || c == '/'; +} + +template +void encode_base64(InputIt first, InputIt last, const std::string& alphabet, std::basic_string& result) +{ + unsigned char a3[3]; + unsigned char a4[4]; + unsigned char fill = alphabet.back(); + int i = 0; + int j = 0; + + while (first != last) + { + a3[i++] = *first++; + if (i == 3) + { + a4[0] = (a3[0] & 0xfc) >> 2; + a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); + a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); + a4[3] = a3[2] & 0x3f; + + for (i = 0; i < 4; i++) + { + result.push_back(alphabet[a4[i]]); + } + i = 0; + } + } + + if (i > 0) + { + for (j = i; j < 3; ++j) + { + a3[j] = 0; + } + + a4[0] = (a3[0] & 0xfc) >> 2; + a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); + a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); + + for (j = 0; j < i + 1; ++j) + { + result.push_back(alphabet[a4[j]]); + } + + if (fill != 0) + { + while (i++ < 3) + { + result.push_back(fill); + } + } + } +} + +template +void encode_base64url(InputIt first, InputIt last, std::basic_string& result) +{ + return encode_base64(first,last,base64url_alphabet,result); +} + +template +void encode_base64(InputIt first, InputIt last, std::basic_string& result) +{ + encode_base64(first,last,base64_alphabet,result); +} + +inline +std::string decode_base64(const std::string& base64_string) +{ + std::string result; + uint8_t a4[4], a3[3]; + uint8_t i = 0; + uint8_t j = 0; + + auto first = base64_string.begin(); + auto last = base64_string.end(); + + while (first != last && *first != '=') + { + JSONCONS_ASSERT(is_base64(*first)); + + a4[i++] = *first++; + if (i == 4) + { + for (i = 0; i < 4; ++i) + { + a4[i] = static_cast(base64_alphabet.find(a4[i])); + } + + a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); + a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); + a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; + + for (i = 0; i < 3; i++) + { + result.push_back(a3[i]); + } + i = 0; + } + } + + if (i > 0) + { + for (j = 0; j < i; ++j) + { + a4[j] = static_cast(base64_alphabet.find(a4[j])); + } + + a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); + a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); + + for (j = 0; j < i - 1; ++j) + { + result.push_back(a3[j]); + } + } + + return result; +} + +} + +#endif diff --git a/direct-access-api/include/jsonpath_error_category.hpp b/direct-access-api/include/jsonpath_error_category.hpp new file mode 100644 index 0000000..62aa588 --- /dev/null +++ b/direct-access-api/include/jsonpath_error_category.hpp @@ -0,0 +1,97 @@ +/// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATH_JSONPATH_ERROR_CATEGORY_HPP +#define JSONCONS_JSONPATH_JSONPATH_ERROR_CATEGORY_HPP + +#include +#include + +namespace jsoncons { namespace jsonpath { + +enum class jsonpath_parser_errc +{ + ok = 0, + expected_root = 1, + expected_right_bracket = 2, + expected_name = 3, + expected_separator = 4, + invalid_filter = 5, + invalid_filter_expected_slash = 6, + invalid_filter_unbalanced_paren = 7, + invalid_filter_unsupported_operator = 8, + invalid_filter_expected_right_brace = 9, + invalid_filter_expected_primary = 10, + expected_index = 11, + expected_left_bracket_token = 12, + unexpected_operator = 13 +}; + +class jsonpath_error_category_impl + : public std::error_category +{ +public: + virtual const char* name() const JSONCONS_NOEXCEPT + { + return "jsonpath"; + } + virtual std::string message(int ev) const + { + switch (static_cast(ev)) + { + case jsonpath_parser_errc::expected_root: + return "Expected $"; + case jsonpath_parser_errc::expected_right_bracket: + return "Expected ]"; + case jsonpath_parser_errc::expected_name: + return "Expected a name following a dot"; + case jsonpath_parser_errc::expected_index: + return "Expected index"; + case jsonpath_parser_errc::expected_separator: + return "Expected dot or left bracket separator"; + case jsonpath_parser_errc::invalid_filter: + return "Invalid path filter"; + case jsonpath_parser_errc::invalid_filter_expected_slash: + return "Invalid path filter, expected '/'"; + case jsonpath_parser_errc::invalid_filter_unbalanced_paren: + return "Invalid path filter, unbalanced parenthesis"; + case jsonpath_parser_errc::invalid_filter_unsupported_operator: + return "Unsupported operator"; + case jsonpath_parser_errc::invalid_filter_expected_right_brace: + return "Invalid path filter, expected right brace }"; + case jsonpath_parser_errc::invalid_filter_expected_primary: + return "Invalid path filter, expected primary expression."; + case jsonpath_parser_errc::expected_left_bracket_token: + return "Expected ?,',\",0-9,*"; + default: + return "Unknown jsonpath parser error"; + } + } +}; + +inline +const std::error_category& jsonpath_error_category() +{ + static jsonpath_error_category_impl instance; + return instance; +} + +inline +std::error_code make_error_code(jsonpath_parser_errc result) +{ + return std::error_code(static_cast(result),jsonpath_error_category()); +} + +}} + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; +} + +#endif diff --git a/direct-access-api/include/jsonpath_filter.hpp b/direct-access-api/include/jsonpath_filter.hpp new file mode 100644 index 0000000..c3af2ed --- /dev/null +++ b/direct-access-api/include/jsonpath_filter.hpp @@ -0,0 +1,1874 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_JSONPATH_FILTER_HPP +#define JSONCONS_JSONPATH_FILTER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "jsonpath_error_category.hpp" + +namespace jsoncons { namespace jsonpath { namespace detail { + +JSONCONS_DEFINE_LITERAL(eqtilde_literal,"=~") +JSONCONS_DEFINE_LITERAL(star_literal,"*") +JSONCONS_DEFINE_LITERAL(forwardslash_literal,"/") +JSONCONS_DEFINE_LITERAL(plus_literal,"+") +JSONCONS_DEFINE_LITERAL(minus_literal,"-") +JSONCONS_DEFINE_LITERAL(lt_literal,"<") +JSONCONS_DEFINE_LITERAL(lte_literal,"<=") +JSONCONS_DEFINE_LITERAL(gt_literal,">") +JSONCONS_DEFINE_LITERAL(gte_literal,">=") +JSONCONS_DEFINE_LITERAL(eq_literal,"==") +JSONCONS_DEFINE_LITERAL(ne_literal,"!=") +JSONCONS_DEFINE_LITERAL(ampamp_literal,"&&") +JSONCONS_DEFINE_LITERAL(pipepipe_literal,"||") +JSONCONS_DEFINE_LITERAL(max_literal,"max") +JSONCONS_DEFINE_LITERAL(min_literal,"min") + +template +struct PathConstructor +{ + typedef typename Json::char_type char_type; + typedef typename Json::string_view_type string_view_type; + typedef typename Json::string_type string_type; + + string_type operator()(const string_type& path, size_t index) const + { + char_type buf[255]; + char_type* p = buf; + do + { + *p++ = static_cast(48 + index % 10); + } while (index /= 10); + + string_type s; + s.append(path); + s.push_back('['); + while (--p >= buf) + { + s.push_back(*p); + } + s.push_back(']'); + return s; + } + + string_type operator()(const string_type& path, const string_view_type& sv) const + { + string_type s; + s.append(path); + s.push_back('['); + s.push_back('\''); + s.append(sv.data(),sv.length()); + s.push_back('\''); + s.push_back(']'); + return s; + } +}; + +template +struct VoidPathConstructor +{ + typedef typename Json::char_type char_type; + typedef typename Json::string_view_type string_view_type; + typedef typename Json::string_type string_type; + + string_type operator()(const string_type&, size_t) const + { + return string_type{}; + } + + string_type operator()(const string_type&, string_view_type) const + { + return string_type{}; + } +}; + +template +class jsonpath_evaluator; + +enum class filter_state +{ + start, + cr, + lf, + expect_right_round_bracket, + expect_oper_or_right_round_bracket, + expect_path_or_value_or_unary_op, + expect_regex, + regex, + single_quoted_text, + double_quoted_text, + unquoted_text, + path, + value, + oper, + function_argument, + done +}; + +enum class token_type +{ + operand, + unary_operator, + binary_operator, + lparen, + rparen +}; + +template +class term +{ +public: + typedef typename Json::string_type string_type; + typedef typename Json::char_type char_type; + + virtual ~term() {} + + virtual void initialize(const Json&) + { + } + virtual bool accept_single_node() const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual Json evaluate_single_node() const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool exclaim() const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool eq_term(const term&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool eq(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool ne_term(const term&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool ne(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool regex_term(const term&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool regex2(const string_type&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool ampamp_term(const term&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool ampamp(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool pipepipe_term(const term&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool pipepipe(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool lt_term(const term&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool lt(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool gt_term(const term&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual bool gt(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + + virtual Json minus_term(const term&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual Json minus(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + + virtual Json left_minus(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + + virtual Json unary_minus() const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual Json plus_term(const term&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual Json plus(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual Json mult_term(const term&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual Json mult(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + + virtual Json div_term(const term&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + virtual Json div(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } + + virtual Json left_div(const Json&) const + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,1,1); + } +}; + +template +struct operator_properties +{ + typedef std::function&, const term&)> operator_type; + + size_t precedence_level; + bool is_right_associative; + operator_type op; +}; + +template +struct function_properties +{ + typedef std::function&)> function_type; + + size_t precedence_level; + bool is_right_associative; + bool is_aggregate; + function_type op; +}; + +template +class token +{ + token_type type_; + size_t precedence_level_; + bool is_right_associative_; + bool is_aggregate_; + std::shared_ptr> operand_ptr_; + std::function&)> unary_operator_; + std::function&, const term&)> operator_; +public: + typedef std::function&)> unary_operator_type; + typedef std::function&, const term&)> operator_type; + + Json operator()(const term& a) + { + return unary_operator_(a); + } + + Json operator()(const term& a, const term& b) + { + return operator_(a,b); + } + + token(token_type type) + : type_(type),precedence_level_(0),is_right_associative_(false),is_aggregate_(false) + { + } + token(token_type type, std::shared_ptr> term_ptr) + : type_(type),precedence_level_(0),is_right_associative_(false),is_aggregate_(false),operand_ptr_(term_ptr) + { + } + token(size_t precedence_level, + bool is_right_associative, + std::function&)> unary_operator) + : type_(token_type::unary_operator), + precedence_level_(precedence_level), + is_right_associative_(is_right_associative), + is_aggregate_(false), + unary_operator_(unary_operator) + { + } + token(const operator_properties& properties) + : type_(token_type::binary_operator), + precedence_level_(properties.precedence_level), + is_right_associative_(properties.is_right_associative), + is_aggregate_(false), + operator_(properties.op) + { + } + token(const function_properties& properties) + : type_(token_type::unary_operator), + precedence_level_(properties.precedence_level), + is_right_associative_(properties.is_right_associative), + is_aggregate_(properties.is_aggregate), + unary_operator_(properties.op) + { + } + token(const token& t) = default; + //token(token&& t) = default; + + token& operator=(const token& val) = default; + //token& operator=(token&& val) = default; + + bool is_operator() const + { + return is_unary_operator() || is_binary_operator(); + } + + bool is_unary_operator() const + { + return type_ == token_type::unary_operator; + } + + bool is_binary_operator() const + { + return type_ == token_type::binary_operator; + } + + bool is_operand() const + { + return type_ == token_type::operand; + } + + bool is_lparen() const + { + return type_ == token_type::lparen; + } + + bool is_rparen() const + { + return type_ == token_type::rparen; + } + + size_t precedence_level() const + { + return precedence_level_; + } + + bool is_right_associative() const + { + return is_right_associative_; + } + + bool is_aggregate() const + { + return is_aggregate_; + } + + const term& operand() + { + JSONCONS_ASSERT(type_ == token_type::operand && operand_ptr_ != nullptr); + return *operand_ptr_; + } + + void initialize(const Json& context_node) + { + if (operand_ptr_.get() != nullptr) + { + operand_ptr_->initialize(context_node); + } + } +}; + +template +bool ampamp(const Json& lhs, const Json& rhs) +{ + return lhs.as_bool() && rhs.as_bool(); +} + +template +bool pipepipe(const Json& lhs, const Json& rhs) +{ + return lhs.as_bool() || rhs.as_bool(); +} + +template +bool lt(const Json& lhs, const Json& rhs) +{ + bool result = false; + if (lhs. template is() && rhs. template is()) + { + result = lhs. template as() < rhs. template as(); + } + else if (lhs. template is() && rhs. template is()) + { + result = lhs. template as() < rhs. template as(); + } + else if ((lhs.is_number() && rhs.is_double()) || (lhs.is_double() && rhs.is_number())) + { + result = lhs.as_double() < rhs.as_double(); + } + else if (lhs.is_string() && rhs.is_string()) + { + result = lhs.as_string_view() < rhs.as_string_view(); + } + return result; +} + +template +bool gt(const Json& lhs, const Json& rhs) +{ + return lt(rhs,lhs); +} + +template +Json plus(const Json& lhs, const Json& rhs) +{ + Json result = Json(jsoncons::null_type()); + if (lhs.is_integer() && rhs.is_integer()) + { + result = Json(((lhs.as_integer() + rhs.as_integer()))); + } + else if ((lhs.is_number() && rhs.is_double()) || (lhs.is_double() && rhs.is_number())) + { + result = Json((lhs.as_double() + rhs.as_double())); + } + else if (lhs.is_uinteger() && rhs.is_uinteger()) + { + result = Json((lhs.as_uinteger() + rhs.as_uinteger())); + } + return result; +} + +template +Json mult(const Json& lhs, const Json& rhs) +{ + Json result = Json(jsoncons::null_type()); + if (lhs.is_integer() && rhs.is_integer()) + { + result = Json(((lhs.as_integer() * rhs.as_integer()))); + } + else if ((lhs.is_number() && rhs.is_double()) || (lhs.is_double() && rhs.is_number())) + { + result = Json((lhs.as_double() * rhs.as_double())); + } + else if (lhs.is_uinteger() && rhs.is_uinteger()) + { + result = Json((lhs.as_uinteger() * rhs.as_uinteger())); + } + return result; +} + +template +Json div(const Json& lhs, const Json& rhs) +{ + Json result = Json(jsoncons::null_type()); + if (lhs.is_integer() && rhs.is_integer()) + { + result = Json((double)(lhs.as_integer() / (double)rhs.as_integer())); + } + else if ((lhs.is_number() && rhs.is_double()) || (lhs.is_double() && rhs.is_number())) + { + result = Json((lhs.as_double() / rhs.as_double())); + } + else if (lhs.is_uinteger() && rhs.is_uinteger()) + { + result = Json((double)(lhs.as_uinteger() / (double)rhs.as_uinteger())); + } + return result; +} + +template +Json unary_minus(const Json& lhs) +{ + Json result = Json::null(); + if (lhs.is_integer()) + { + result = -lhs.as_integer(); + } + else if (lhs.is_double()) + { + result = -lhs.as_double(); + } + return result; +} + +template +Json minus(const Json& lhs, const Json& rhs) +{ + Json result = Json::null(); + if (lhs.is_integer() && rhs.is_integer()) + { + result = ((lhs.as_integer() - rhs.as_integer())); + } + else if ((lhs.is_number() && rhs.is_double()) || (lhs.is_double() && rhs.is_number())) + { + result = (lhs.as_double() - rhs.as_double()); + } + else if (lhs.is_uinteger() && rhs.is_uinteger() && lt(rhs,lhs)) + { + result = (lhs.as_uinteger() - rhs.as_uinteger()); + } + return result; +} + +template +class value_term final : public term +{ + Json value_; +public: + template + value_term(const T& val) + : value_(val) + { + } + + bool accept_single_node() const override + { + return value_.as_bool(); + } + + Json evaluate_single_node() const override + { + return value_; + } + + bool exclaim() const override + { + return !value_.as_bool(); + } + + bool eq_term(const term& rhs) const override + { + return rhs.eq(value_); + } + + bool eq(const Json& rhs) const override + { + return value_ == rhs; + } + + bool ne_term(const term& rhs) const override + { + return rhs.ne(value_); + } + bool ne(const Json& rhs) const override + { + return value_ != rhs; + } + bool regex_term(const term& rhs) const override + { + return rhs.regex2(value_.as_string()); + } + bool ampamp_term(const term& rhs) const override + { + return rhs.ampamp(value_); + } + bool ampamp(const Json& rhs) const override + { + return jsoncons::jsonpath::detail::ampamp(value_,rhs); + } + bool pipepipe_term(const term& rhs) const override + { + return rhs.pipepipe(value_); + } + bool pipepipe(const Json& rhs) const override + { + return jsoncons::jsonpath::detail::pipepipe(value_,rhs); + } + + bool lt_term(const term& rhs) const override + { + return rhs.gt(value_); + } + bool lt(const Json& rhs) const override + { + return jsoncons::jsonpath::detail::lt(value_,rhs); + } + + bool gt_term(const term& rhs) const override + { + return rhs.lt(value_); + } + bool gt(const Json& rhs) const override + { + return jsoncons::jsonpath::detail::gt(value_,rhs); + } + + Json minus_term(const term& rhs) const override + { + return rhs.left_minus(value_); + } + Json minus(const Json& rhs) const override + { + return jsoncons::jsonpath::detail::minus(value_,rhs); + } + + Json left_minus(const Json& lhs) const override + { + return jsoncons::jsonpath::detail::minus(lhs,value_); + } + + Json unary_minus() const override + { + return jsoncons::jsonpath::detail::unary_minus(value_); + } + + Json plus_term(const term& rhs) const override + { + return rhs.plus(value_); + } + + Json plus(const Json& rhs) const override + { + return jsoncons::jsonpath::detail::plus(value_,rhs); + } + Json mult_term(const term& rhs) const override + { + return rhs.mult(value_); + } + + Json mult(const Json& rhs) const override + { + return jsoncons::jsonpath::detail::mult(value_,rhs); + } + + Json div_term(const term& rhs) const override + { + return rhs.left_div(value_); + } + Json div(const Json& rhs) const override + { + return jsoncons::jsonpath::detail::div(value_,rhs); + } + + Json left_div(const Json& lhs) const override + { + return jsoncons::jsonpath::detail::div(lhs,value_); + } +}; + +template +class regex_term final : public term +{ + typedef typename Json::char_type char_type; + typedef typename Json::string_type string_type; + const std::basic_regex pattern_; +public: + regex_term(const string_type& pattern, std::regex::flag_type flags) + : pattern_(pattern,flags) + { + } + + bool regex2(const string_type& subject) const override + { + return std::regex_match(subject, pattern_); + } +}; + +template +class path_term final : public term +{ + typedef typename Json::string_type string_type; + + string_type path_; + Json nodes_; +public: + path_term(const string_type& path) + : path_(path) + { + } + + void initialize(const Json& context_node) override + { + jsonpath_evaluator> evaluator; + evaluator.evaluate(context_node,path_); + nodes_ = evaluator.get_values(); + } + + bool accept_single_node() const override + { + return nodes_.size() != 0; + } + + Json evaluate_single_node() const override + { + return nodes_.size() == 1 ? nodes_[0] : nodes_; + } + + bool exclaim() const override + { + return nodes_.size() == 0; + } + + bool eq_term(const term& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = rhs.eq(nodes_[i]); + } + } + return result; + } + + bool eq(const Json& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = nodes_[i] == rhs; + } + } + return result; + } + + bool ne_term(const term& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = rhs.ne(nodes_[i]); + } + } + return result; + + } + bool ne(const Json& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = nodes_[i] != rhs; + } + } + return result; + } + bool regex_term(const term& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = rhs.regex2(nodes_[i].as_string()); + } + } + return result; + } + bool ampamp_term(const term& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = rhs.ampamp(nodes_[i]); + } + } + return result; + } + bool ampamp(const Json& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = jsoncons::jsonpath::detail::ampamp(nodes_[i],rhs); + } + } + return result; + } + bool pipepipe_term(const term& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = rhs.pipepipe(nodes_[i]); + } + } + return result; + } + bool pipepipe(const Json& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = jsoncons::jsonpath::detail::pipepipe(nodes_[i],rhs); + } + } + return result; + } + + bool lt(const Json& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = jsoncons::jsonpath::detail::lt(nodes_[i],rhs); + } + } + return result; + } + + bool lt_term(const term& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = rhs.gt(nodes_[i]); + } + } + return result; + } + + bool gt(const Json& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = jsoncons::jsonpath::detail::gt(nodes_[i],rhs); + } + } + return result; + } + + bool gt_term(const term& rhs) const override + { + bool result = false; + if (nodes_.size() > 0) + { + result = true; + for (size_t i = 0; result && i < nodes_.size(); ++i) + { + result = rhs.lt(nodes_[i]); + } + } + return result; + } + + Json minus_term(const term& rhs) const override + { + static auto a_null = Json(jsoncons::null_type()); + return nodes_.size() == 1 ? rhs.left_minus(nodes_[0]) : a_null; + } + Json minus(const Json& rhs) const override + { + return nodes_.size() == 1 ? jsoncons::jsonpath::detail::minus(nodes_[0],rhs) : Json(jsoncons::null_type()); + } + + Json left_minus(const Json& lhs) const override + { + static auto a_null = Json(jsoncons::null_type()); + return nodes_.size() == 1 ? jsoncons::jsonpath::detail::minus(lhs,nodes_[0]) : a_null; + } + + Json unary_minus() const override + { + return nodes_.size() == 1 ? jsoncons::jsonpath::detail::unary_minus(nodes_[0]) : Json::null(); + } + + Json plus_term(const term& rhs) const override + { + static auto a_null = Json(jsoncons::null_type()); + return nodes_.size() == 1 ? rhs.plus(nodes_[0]) : a_null; + } + Json plus(const Json& rhs) const override + { + static auto a_null = Json(jsoncons::null_type()); + return nodes_.size() == 1 ? jsoncons::jsonpath::detail::plus(nodes_[0],rhs) : a_null; + } + + Json mult_term(const term& rhs) const override + { + static auto a_null = Json(jsoncons::null_type()); + return nodes_.size() == 1 ? rhs.mult(nodes_[0]) : a_null; + } + Json mult(const Json& rhs) const override + { + static auto a_null = Json(jsoncons::null_type()); + return nodes_.size() == 1 ? jsoncons::jsonpath::detail::mult(nodes_[0],rhs) : a_null; + } + + Json div_term(const term& rhs) const override + { + static auto a_null = Json(jsoncons::null_type()); + return nodes_.size() == 1 ? rhs.left_div(nodes_[0]) : a_null; + } + Json div(const Json& rhs) const override + { + static auto a_null = Json(jsoncons::null_type()); + return nodes_.size() == 1 ? jsoncons::jsonpath::detail::div(nodes_[0],rhs) : a_null; + } + + Json left_div(const Json& lhs) const override + { + static auto a_null = Json(jsoncons::null_type()); + return nodes_.size() == 1 ? jsoncons::jsonpath::detail::div(lhs, nodes_[0]) : a_null; + } +}; + +template +token evaluate(const Json& context, std::vector>& tokens) +{ + for (auto it= tokens.begin(); it != tokens.end(); ++it) + { + it->initialize(context); + } + std::vector> stack; + for (auto t : tokens) + { + if (t.is_operand()) + { + stack.push_back(t); + } + else if (t.is_unary_operator()) + { + auto rhs = stack.back(); + stack.pop_back(); + Json val = t(rhs.operand()); + stack.push_back(token(token_type::operand,std::make_shared>(val))); + } + else if (t.is_binary_operator()) + { + auto rhs = stack.back(); + stack.pop_back(); + auto lhs = stack.back(); + stack.pop_back(); + Json val = t(lhs.operand(), rhs.operand()); + stack.push_back(token(token_type::operand,std::make_shared>(val))); + } + } + if (stack.size() != 1) + { + JSONCONS_THROW(json_exception_impl("Invalid state")); + } + + return stack.back(); +} + +template +class jsonpath_filter_expr +{ +public: + std::vector> tokens_; + size_t line_; + size_t column_; +public: + + jsonpath_filter_expr(const std::vector>& tokens, size_t line, size_t column) + : tokens_(tokens), line_(line), column_(column) + { + } + + Json eval(const Json& context_node) + { + try + { + auto t = evaluate(context_node,tokens_); + + return t.operand().evaluate_single_node(); + + } + catch (const parse_error& e) + { + throw parse_error(e.code(),line_,column_); + } + } + + bool exists(const Json& context_node) + { + try + { + auto t = evaluate(context_node,tokens_); + return t.operand().accept_single_node(); + } + catch (const parse_error& e) + { + throw parse_error(e.code(),line_,column_); + } + } +}; + +template +class jsonpath_filter_parser +{ + typedef typename Json::string_type string_type; + typedef typename Json::string_view_type string_view_type; + typedef typename Json::char_type char_type; + + std::vector> output_stack_; + std::vector> operator_stack_; + std::vector state_stack_; + + size_t line_; + size_t column_; + + static const operator_properties op_properties_[]; + + class function_table + { + typedef std::map> function_dictionary; + + const function_dictionary functions_ = + { + { + max_literal(),{1,true,true,[](const term& term) + { + Json a = term.evaluate_single_node(); + + double v = std::numeric_limits::lowest(); + for (const auto& elem : a.array_range()) + { + double x = elem. template as(); + if (x > v) + { + v = x; + } + } + return v; + } + } + }, + { + min_literal(),{1,true,true,[](const term& term) + { + Json a = term.evaluate_single_node(); + + double v = (std::numeric_limits::max)(); + for (const auto& elem : a.array_range()) + { + double x = elem. template as(); + if (x < v) + { + v = x; + } + } + return v; + } + } + } + }; + + public: + + typename function_dictionary::const_iterator find(const string_type& key) const + { + return functions_.find(key); + } + typename function_dictionary::const_iterator end() const + { + return functions_.end(); + } + }; + + class binary_operator_table + { + typedef std::map> binary_operator_map; + + const binary_operator_map operators = + { + {eqtilde_literal(),{2,false,[](const term& a, const term& b) {return Json(a.regex_term(b)); }}}, + {star_literal(),{3,false,[](const term& a, const term& b) {return a.mult_term(b); }}}, + {forwardslash_literal(),{3,false,[](const term& a, const term& b) {return a.div_term(b); }}}, + {plus_literal(),{4,false,[](const term& a, const term& b) {return a.plus_term(b); }}}, + {minus_literal(),{4,false,[](const term& a, const term& b) {return a.minus_term(b); }}}, + {lt_literal(),{5,false,[](const term& a, const term& b) {return Json(a.lt_term(b)); }}}, + {lte_literal(),{5,false,[](const term& a, const term& b) {return Json(a.lt_term(b) || a.eq_term(b)); }}}, + {gt_literal(),{5,false,[](const term& a, const term& b) {return Json(a.gt_term(b)); }}}, + {gte_literal(),{5,false,[](const term& a, const term& b) {return Json(a.gt_term(b) || a.eq_term(b)); }}}, + {eq_literal(),{6,false,[](const term& a, const term& b) {return Json(a.eq_term(b)); }}}, + {ne_literal(),{6,false,[](const term& a, const term& b) {return Json(a.ne_term(b)); }}}, + {ampamp_literal(),{7,false,[](const term& a, const term& b) {return Json(a.ampamp_term(b)); }}}, + {pipepipe_literal(),{8,false,[](const term& a, const term& b) {return Json(a.pipepipe_term(b)); }}} + }; + + public: + typename binary_operator_map::const_iterator find(const string_type& key) const + { + return operators.find(key); + } + typename binary_operator_map::const_iterator end() const + { + return operators.end(); + } + }; + + function_table functions_; + binary_operator_table binary_operators_; + +public: + jsonpath_filter_parser() + : line_(1), column_(1) + { + } + jsonpath_filter_parser(size_t line, size_t column) + : line_(line), column_(column) + { + } + + size_t line() const + { + return line_; + } + + size_t column() const + { + return column_; + } + + jsonpath_filter_expr parse(const Json& root, const char_type* p, size_t length, const char_type** end_ptr) + { + return parse(root, p,p+length, end_ptr); + } + + void push_state(filter_state state) + { + state_stack_.push_back(state); + } + + filter_state pop_state() + { + if (state_stack_.empty()) + { + JSONCONS_THROW(json_exception_impl("Invalid state")); + } + filter_state state = state_stack_.back(); + state_stack_.pop_back(); + return state; + } + + void add_token(token token) + { + if (token.is_operand()) + { + output_stack_.push_back(token); + } + else if (token.is_lparen()) + { + operator_stack_.push_back(token); + } + else if (token.is_rparen()) + { + auto it = operator_stack_.rbegin(); + while (it != operator_stack_.rend() && !it->is_lparen()) + { + output_stack_.push_back(*it); + ++it; + } + if (it == operator_stack_.rend()) + { + JSONCONS_THROW(json_exception_impl("Unbalanced parenthesis")); + } + operator_stack_.erase(it.base(),operator_stack_.end()); + operator_stack_.pop_back(); + } + else if (token.is_operator()) + { + if (operator_stack_.empty() || operator_stack_.back().is_lparen()) + { + operator_stack_.push_back(token); + } + else if (token.precedence_level() < operator_stack_.back().precedence_level() + || (token.precedence_level() == operator_stack_.back().precedence_level() && token.is_right_associative())) + { + operator_stack_.push_back(token); + } + else + { + auto it = operator_stack_.rbegin(); + while (it != operator_stack_.rend() && it->is_operator() + && (token.precedence_level() > it->precedence_level() + || (token.precedence_level() == it->precedence_level() && token.is_right_associative()))) + { + output_stack_.push_back(*it); + ++it; + } + + operator_stack_.erase(it.base(),operator_stack_.end()); + operator_stack_.push_back(token); + } + } + } + + jsonpath_filter_expr parse(const Json& root, const char_type* p, const char_type* end_expr, const char_type** end_ptr) + { + output_stack_.clear(); + operator_stack_.clear(); + state_stack_.clear(); + + string_type buffer; + + int depth = 0; + filter_state state = filter_state::start; + while (p < end_expr && state != filter_state::done) + { + switch (state) + { + case filter_state::cr: + ++line_; + column_ = 1; + switch (*p) + { + case '\n': + state = pop_state(); + ++p; + ++column_; + break; + default: + state = pop_state(); + break; + } + break; + case filter_state::lf: + ++line_; + column_ = 1; + state = pop_state(); + break; + case filter_state::start: + switch (*p) + { + case '\r': + push_state(state); + state = filter_state::cr; + break; + case '\n': + push_state(state); + state = filter_state::lf; + break; + case '(': + state = filter_state::expect_path_or_value_or_unary_op; + ++depth; + add_token(token(token_type::lparen)); + break; + case ')': + state = filter_state::expect_path_or_value_or_unary_op; + add_token(token(token_type::rparen)); + if (--depth == 0) + { + state = filter_state::done; + } + break; + } + ++p; + ++column_; + break; + case filter_state::function_argument: + { + switch (*p) + { + case '\r': + push_state(state); + state = filter_state::cr; + break; + case '\n': + push_state(state); + state = filter_state::lf; + break; + case ' ':case '\t': + break; + case ')': + if (buffer.length() > 0) + { + if (operator_stack_.back().is_aggregate()) + { + try + { + // path, parse against root, get value + jsonpath_evaluator> evaluator; + evaluator.evaluate(root,buffer.data(),buffer.length()); + auto result = evaluator.get_values(); + add_token(token(token_type::operand,std::make_shared>(result))); + } + catch (const parse_error& e) + { + throw parse_error(e.code(),line_,column_); + } + } + else + { + add_token(token(token_type::operand,std::make_shared>(buffer))); + } + buffer.clear(); + state = filter_state::expect_oper_or_right_round_bracket; + } + break; + default: + buffer.push_back(*p); + break; + } + ++p; + ++column_; + } + break; + case filter_state::oper: + switch (*p) + { + case '~': + { + buffer.push_back(*p); + ++p; + ++column_; + auto it = binary_operators_.find(buffer); + if (it == binary_operators_.end()) + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator, line_, column_); + } + buffer.clear(); + add_token(token(it->second)); + state = filter_state::expect_regex; + } + break; + case '=': + case '&': + case '|': + { + buffer.push_back(*p); + ++p; + ++column_; + auto it = binary_operators_.find(buffer); + if (it == binary_operators_.end()) + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator, line_, column_); + } + buffer.clear(); + add_token(token(it->second)); + state = filter_state::expect_path_or_value_or_unary_op; + } + break; + default: + { + auto it = binary_operators_.find(buffer); + if (it == binary_operators_.end()) + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator, line_, column_); + } + buffer.clear(); + add_token(token(it->second)); + state = filter_state::expect_path_or_value_or_unary_op; + } + break; + } + break; + case filter_state::unquoted_text: + { + switch (*p) + { + case ' ':case '\t': + if (buffer.length() > 0) + { + try + { + auto val = Json::parse(buffer); + add_token(token(token_type::operand,std::make_shared>(val))); + } + catch (const parse_error& e) + { + throw parse_error(e.code(),line_,column_); + } + buffer.clear(); + } + ++p; + ++column_; + break; + case '(': + { + auto it = functions_.find(buffer); + if (it == functions_.end()) + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unsupported_operator,line_,column_); + } + add_token(token(it->second)); + state = filter_state::function_argument; + buffer.clear(); + ++p; + ++column_; + break; + } + case '<': + case '>': + case '!': + case '=': + case '&': + case '|': + case '+': + case '-': + case '*': + case '/': + { + if (buffer.length() > 0) + { + try + { + auto val = Json::parse(buffer); + add_token(token(token_type::operand,std::make_shared>(val))); + } + catch (const parse_error& e) + { + throw parse_error(e.code(),line_,column_); + } + buffer.clear(); + } + buffer.push_back(*p); + state = filter_state::oper; + ++p; + ++column_; + } + break; + case ')': + if (buffer.length() > 0) + { + try + { + auto val = Json::parse(buffer); + add_token(token(token_type::operand,std::make_shared>(val))); + } + catch (const parse_error& e) + { + throw parse_error(e.code(),line_,column_); + } + buffer.clear(); + } + add_token(token(token_type::rparen)); + if (--depth == 0) + { + state = filter_state::done; + } + else + { + state = filter_state::expect_path_or_value_or_unary_op; + } + ++p; + ++column_; + break; + default: + buffer.push_back(*p); + ++p; + ++column_; + break; + } + } + break; + case filter_state::single_quoted_text: + { + switch (*p) + { + case '\\': + buffer.push_back(*p); + if (p+1 < end_expr) + { + ++p; + ++column_; + buffer.push_back(*p); + } + break; + case '\'': + buffer.push_back('\"'); + //if (buffer.length() > 0) + { + try + { + auto val = Json::parse(buffer); + add_token(token(token_type::operand,std::make_shared>(val))); + } + catch (const parse_error& e) + { + throw parse_error(e.code(),line_,column_); + } + buffer.clear(); + } + state = filter_state::expect_path_or_value_or_unary_op; + break; + + default: + buffer.push_back(*p); + break; + } + } + ++p; + ++column_; + break; + case filter_state::double_quoted_text: + { + switch (*p) + { + case '\\': + buffer.push_back(*p); + if (p+1 < end_expr) + { + ++p; + ++column_; + buffer.push_back(*p); + } + break; + case '\"': + buffer.push_back(*p); + //if (buffer.length() > 0) + { + try + { + auto val = Json::parse(buffer); + add_token(token(token_type::operand,std::make_shared>(val))); + } + catch (const parse_error& e) + { + throw parse_error(e.code(),line_,column_); + } + buffer.clear(); + } + state = filter_state::expect_path_or_value_or_unary_op; + break; + + default: + buffer.push_back(*p); + break; + } + } + ++p; + ++column_; + break; + case filter_state::expect_path_or_value_or_unary_op: + switch (*p) + { + case '\r': + push_state(state); + state = filter_state::cr; + ++p; + break; + case '\n': + push_state(state); + state = filter_state::lf; + ++p; + break; + case ' ':case '\t': + ++p; + ++column_; + break; + case '!': + { + std::function&)> f = [](const term& b) {return Json(b.exclaim());}; + add_token(token(1, true, f)); + ++p; + ++column_; + break; + } + case '-': + { + std::function&)> f = [](const term& b) {return b.unary_minus();}; + add_token(token(1, true, f)); + ++p; + ++column_; + break; + } + case '@': + buffer.push_back(*p); + state = filter_state::path; + ++p; + ++column_; + break; + case '\'': + buffer.push_back('\"'); + state = filter_state::single_quoted_text; + ++p; + ++column_; + break; + case '\"': + buffer.push_back(*p); + state = filter_state::double_quoted_text; + ++p; + ++column_; + break; + case '(': + ++depth; + add_token(token(token_type::lparen)); + ++p; + ++column_; + break; + case ')': + add_token(token(token_type::rparen)); + if (--depth == 0) + { + state = filter_state::done; + } + ++p; + ++column_; + break; + default: + // don't increment + state = filter_state::unquoted_text; + break; + }; + break; + case filter_state::expect_oper_or_right_round_bracket: + switch (*p) + { + case '\r': + push_state(state); + state = filter_state::cr; + ++p; + break; + case '\n': + push_state(state); + state = filter_state::lf; + ++p; + break; + case ' ':case '\t': + ++p; + ++column_; + break; + case ')': + add_token(token(token_type::rparen)); + if (--depth == 0) + { + state = filter_state::done; + ++p; // fix + } + break; + case '<': + case '>': + case '!': + case '=': + case '&': + case '|': + case '+': + case '-': + case '*': + case '/': + { + buffer.push_back(*p); + state = filter_state::oper; + ++p; + ++column_; + } + break; + default: + throw parse_error(jsonpath_parser_errc::invalid_filter,line_,column_); + break; + }; + break; + case filter_state::expect_right_round_bracket: + switch (*p) + { + case '\r': + push_state(state); + state = filter_state::cr; + break; + case '\n': + push_state(state); + state = filter_state::lf; + break; + case ' ':case '\t': + break; + case ')': + add_token(token(token_type::rparen)); + if (--depth == 0) + { + state = filter_state::done; + } + else + { + state = filter_state::expect_oper_or_right_round_bracket; + } + break; + default: + throw parse_error(jsonpath_parser_errc::invalid_filter,line_,column_); + break; + }; + ++p; + ++column_; + break; + case filter_state::path: + switch (*p) + { + case '<': + case '>': + case '!': + case '=': + case '&': + case '|': + case '+': + case '-': + case '*': + case '/': + { + if (buffer.length() > 0) + { + add_token(token(token_type::operand,std::make_shared>(buffer))); + buffer.clear(); + } + buffer.push_back(*p); + ++p; + ++column_; + state = filter_state::oper; + } + break; + case ')': + if (buffer.length() > 0) + { + add_token(token(token_type::operand,std::make_shared>(buffer))); + add_token(token(token_type::rparen)); + buffer.clear(); + } + if (--depth == 0) + { + state = filter_state::done; + } + else + { + state = filter_state::expect_path_or_value_or_unary_op; + } + ++p; + ++column_; + break; + default: + buffer.push_back(*p); + ++p; + ++column_; + break; + }; + break; + case filter_state::expect_regex: + switch (*p) + { + case '\r': + push_state(state); + state = filter_state::cr; + break; + case '\n': + push_state(state); + state = filter_state::lf; + break; + case ' ':case '\t': + break; + case '/': + state = filter_state::regex; + break; + default: + throw parse_error(jsonpath_parser_errc::invalid_filter_expected_slash,line_,column_); + break; + }; + ++p; + ++column_; + break; + case filter_state::regex: + { + switch (*p) + { + case '/': + //if (buffer.length() > 0) + { + std::regex::flag_type flags = std::regex_constants::ECMAScript; + if (p+1 < end_expr && *(p+1) == 'i') + { + ++p; + ++column_; + flags |= std::regex_constants::icase; + } + add_token(token(token_type::operand,std::make_shared>(buffer,flags))); + buffer.clear(); + } + state = filter_state::expect_path_or_value_or_unary_op; + break; + + default: + buffer.push_back(*p); + break; + } + } + ++p; + ++column_; + break; + default: + ++p; + ++column_; + break; + } + } + if (depth != 0) + { + throw parse_error(jsonpath_parser_errc::invalid_filter_unbalanced_paren,line_,column_); + } + *end_ptr = p; + + return jsonpath_filter_expr(output_stack_,line_,column_); + } +}; + +template +const operator_properties jsonpath_filter_parser::op_properties_[] = +{ + {2,false,[](const term& a, const term& b) {return Json(a.regex_term(b));}}, + {3,false,[](const term& a, const term& b) {return a.mult_term(b);}}, + {3,false,[](const term& a, const term& b) {return a.div_term(b);}}, + {4,false,[](const term& a, const term& b) {return a.plus_term(b);}}, + {4,false,[](const term& a, const term& b) {return a.minus_term(b);}}, + {5,false,[](const term& a, const term& b) {return Json(a.lt_term(b));}}, + {5,false,[](const term& a, const term& b) {return Json(a.lt_term(b) || a.eq_term(b));}}, + {5,false,[](const term& a, const term& b) {return Json(a.gt_term(b));}}, + {5,false,[](const term& a, const term& b) {return Json(a.gt_term(b) || a.eq_term(b));}}, + {6,false,[](const term& a, const term& b) {return Json(a.eq_term(b)); }}, + {6,false,[](const term& a, const term& b) {return Json(a.ne_term(b)); }}, + {7,false,[](const term& a, const term& b) {return Json(a.ampamp_term(b));}}, + {8,false,[](const term& a, const term& b) {return Json(a.pipepipe_term(b));}} +}; + +}}} + +#endif \ No newline at end of file diff --git a/direct-access-api/include/number_parsers.hpp b/direct-access-api/include/number_parsers.hpp new file mode 100644 index 0000000..1a8fc0a --- /dev/null +++ b/direct-access-api/include/number_parsers.hpp @@ -0,0 +1,266 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_NUMBERPARSERS_HPP +#define JSONCONS_DETAIL_NUMBERPARSERS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace detail { + +struct to_integer_result +{ + int64_t value; + bool overflow; +}; + +// Precondition: s satisfies + +// digit +// digit1-digits +// - digit +// - digit1-digits + +template +to_integer_result to_integer(const CharT* s, size_t length) +{ + JSONCONS_ASSERT(length > 0); + + int64_t n = 0; + bool overflow = false; + const CharT* end = s + length; + if (*s == '-') + { + static const int64_t min_value = (std::numeric_limits::min)(); + static const int64_t min_value_div_10 = min_value / 10; + ++s; + for (; s < end; ++s) + { + int64_t x = *s - '0'; + if (n < min_value_div_10) + { + overflow = true; + break; + } + n = n * 10; + if (n < min_value + x) + { + overflow = true; + break; + } + + n -= x; + } + } + else + { + static const int64_t max_value = (std::numeric_limits::max)(); + static const int64_t max_value_div_10 = max_value / 10; + for (; s < end; ++s) + { + int64_t x = *s - '0'; + if (n > max_value_div_10) + { + overflow = true; + break; + } + n = n * 10; + if (n > max_value - x) + { + overflow = true; + break; + } + + n += x; + } + } + + return to_integer_result({ n,overflow }); +} + +struct to_uinteger_result +{ + uint64_t value; + bool overflow; +}; + +// Precondition: s satisfies + +// digit +// digit1-digits +// - digit +// - digit1-digits + +template +to_uinteger_result to_uinteger(const CharT* s, size_t length) +{ + JSONCONS_ASSERT(length > 0); + + static const uint64_t max_value = (std::numeric_limits::max)(); + static const uint64_t max_value_div_10 = max_value / 10; + uint64_t n = 0; + bool overflow = false; + + const CharT* end = s + length; + for (; s < end; ++s) + { + uint64_t x = *s - '0'; + if (n > max_value_div_10) + { + overflow = true; + break; + } + n = n * 10; + if (n > max_value - x) + { + overflow = true; + break; + } + + n += x; + } + return to_uinteger_result{ n,overflow }; +} + +#if defined(JSONCONS_HAS_MSC__STRTOD_L) + +class string_to_double +{ +private: + _locale_t locale_; +public: + string_to_double() + { + locale_ = _create_locale(LC_NUMERIC, "C"); + } + ~string_to_double() + { + _free_locale(locale_); + } + + char get_decimal_point() const + { + return '.'; + } + + double operator()(const char* s, size_t) const + { + const char *begin = s; + char *end = nullptr; + double val = _strtod_l(begin, &end, locale_); + if (begin == end) + { + JSONCONS_THROW(json_exception_impl("Invalid float value")); + } + return val; + } +private: + // noncopyable and nonmoveable + string_to_double(const string_to_double&) = delete; + string_to_double& operator=(const string_to_double&) = delete; +}; + +#elif defined(JSONCONS_HAS_STRTOLD_L) + +class string_to_double +{ +private: + locale_t locale_; +public: + string_to_double() + { + locale_ = newlocale(LC_ALL_MASK, "C", (locale_t) 0); + } + ~string_to_double() + { + freelocale(locale_); + } + + char get_decimal_point() const + { + return '.'; + } + + double operator()(const char* s, size_t length) const + { + const char *begin = s; + char *end = nullptr; + double val = strtold_l(begin, &end, locale_); + if (begin == end) + { + JSONCONS_THROW(json_exception_impl("Invalid float value")); + } + return val; + } + +private: + // noncopyable and nonmoveable + string_to_double(const string_to_double& fr) = delete; + string_to_double& operator=(const string_to_double& fr) = delete; +}; + +#else +class string_to_double +{ +private: + std::vector buffer_; + char decimal_point_; +public: + string_to_double() + : buffer_() + { + struct lconv * lc = localeconv(); + if (lc != nullptr && lc->decimal_point[0] != 0) + { + decimal_point_ = lc->decimal_point[0]; + } + else + { + decimal_point_ = '.'; + } + buffer_.reserve(100); + } + + char get_decimal_point() const + { + return decimal_point_; + } + + double operator()(const char* s, size_t /*length*/) const + { + char *end = nullptr; + double val = strtod(s, &end); + if (s == end) + { + JSONCONS_THROW(json_exception_impl("string_to_double failed")); + } + return val; + } + +private: + // noncopyable and nonmoveable + string_to_double(const string_to_double& fr) = delete; + string_to_double& operator=(const string_to_double& fr) = delete; +}; +#endif + +}} + +#endif diff --git a/direct-access-api/include/number_printers.hpp b/direct-access-api/include/number_printers.hpp new file mode 100644 index 0000000..5877928 --- /dev/null +++ b/direct-access-api/include/number_printers.hpp @@ -0,0 +1,374 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_NUMBERPRINTERS_HPP +#define JSONCONS_DETAIL_NUMBERPRINTERS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace detail { + +// print_integer + +template +void print_integer(int64_t value, Writer& os) +{ + typedef typename Writer::char_type char_type; + + char_type buf[255]; + uint64_t u = (value < 0) ? static_cast(-value) : static_cast(value); + char_type* p = buf; + do + { + *p++ = static_cast(48 + u%10); + } + while (u /= 10); + if (value < 0) + { + os.put('-'); + } + while (--p >= buf) + { + os.put(*p); + } +} + +// print_uinteger + +template +void print_uinteger(uint64_t value, Writer& os) +{ + typedef typename Writer::char_type char_type; + + char_type buf[255]; + char_type* p = buf; + do + { + *p++ = static_cast(48 + value % 10); + } while (value /= 10); + while (--p >= buf) + { + os.put(*p); + } +} + +// print_double + +#if defined(JSONCONS_HAS__ECVT_S) + +class print_double +{ +private: + uint8_t precision_override_; +public: + print_double(uint8_t precision) + : precision_override_(precision) + { + } + + template + void operator()(double val, uint8_t precision, Writer& writer) + { + typedef typename Writer::char_type char_type; + + char buf[_CVTBUFSIZE]; + int decimal_point = 0; + int sign = 0; + + int prec; + if (precision_override_ != 0) + { + prec = precision_override_; + } + else if (precision != 0) + { + prec = precision; + } + else + { + prec = std::numeric_limits::digits10; + } + + int err = _ecvt_s(buf, _CVTBUFSIZE, val, prec, &decimal_point, &sign); + if (err != 0) + { + JSONCONS_THROW(json_exception_impl("Failed attempting double to string conversion")); + } + //std::cout << "prec:" << prec << ", buf:" << buf << std::endl; + char* s = buf; + char* se = s + prec; + + int i, k; + int j; + + if (sign) + { + writer.put('-'); + } + if (decimal_point <= -4 || decimal_point > se - s + 5) + { + writer.put(*s++); + if (s < se) + { + writer.put('.'); + while ((se-1) > s && *(se-1) == '0') + { + --se; + } + + while(s < se) + { + writer.put(*s++); + } + } + writer.put('e'); + /* sprintf(b, "%+.2d", decimal_point - 1); */ + if (--decimal_point < 0) { + writer.put('-'); + decimal_point = -decimal_point; + } + else + writer.put('+'); + for(j = 2, k = 10; 10*k <= decimal_point; j++, k *= 10); + for(;;) + { + i = decimal_point / k; + writer.put(static_cast(i) + '0'); + if (--j <= 0) + break; + decimal_point -= i*k; + decimal_point *= 10; + } + } + else if (decimal_point <= 0) + { + writer.put('0'); + writer.put('.'); + while ((se-1) > s && *(se-1) == '0') + { + --se; + } + for(; decimal_point < 0; decimal_point++) + { + writer.put('0'); + } + while(s < se) + { + writer.put(*s++); + } + } + else { + while(s < se) + { + writer.put(*s++); + if ((--decimal_point == 0) && s < se) + { + writer.put('.'); + while ((se-1) > s && *(se-1) == '0') + { + --se; + } + } + } + for(; decimal_point > 0; decimal_point--) + { + writer.put('0'); + } + } + } +}; + +#elif defined(JSONCONS_NO_LOCALECONV) + +class print_double +{ +private: + uint8_t precision_override_; + basic_obufferedstream os_; +public: + print_double(uint8_t precision) + : precision_override_(precision) + { + os_.imbue(std::locale::classic()); + os_.precision(precision); + } + + template + void operator()(double val, uint8_t precision, Writer& writer) + { + typedef typename Writer::char_type char_type; + + int prec; + if (precision_override_ != 0) + { + prec = precision_override_; + } + else if (precision != 0) + { + prec = precision; + } + else + { + prec = std::numeric_limits::digits10; + } + + os_.clear_sequence(); + os_.precision(prec); + os_ << val; + + //std::cout << "precision_override_:" << (int)precision_override_ << ", precision:" << (int)precision << ", buf:" << os_.data() << std::endl; + + const char_type* sbeg = os_.data(); + const char_type* send = sbeg + os_.length(); + const char_type* pexp = send; + + if (sbeg != send) + { + bool dot = false; + for (pexp = sbeg; *pexp != 'e' && *pexp != 'E' && pexp < send; ++pexp) + { + } + + const char_type* qend = pexp; + while (qend >= sbeg+2 && *(qend-1) == '0' && *(qend-2) != '.') + { + --qend; + } + if (pexp == send) + { + qend = ((qend >= sbeg+2) && *(qend-2) == '.') ? qend : send; + } + + for (const char_type* q = sbeg; q < qend; ++q) + { + if (*q == '.') + { + dot = true; + } + writer.put(*q); + } + if (!dot) + { + writer.put('.'); + writer.put('0'); + dot = true; + } + for (const char_type* q = pexp; q < send; ++q) + { + writer.put(*q); + } + } + } +}; +#else + +class print_double +{ +private: + uint8_t precision_override_; + char decimal_point_; +public: + print_double(uint8_t precision) + : precision_override_(precision) + { + struct lconv * lc = localeconv(); + if (lc != nullptr && lc->decimal_point[0] != 0) + { + decimal_point_ = lc->decimal_point[0]; + } + else + { + decimal_point_ = '.'; + } + } + + template + void operator()(double val, uint8_t precision, Writer& writer) + { + typedef typename Writer::char_type char_type; + + int prec; + if (precision_override_ != 0) + { + prec = precision_override_; + } + else if (precision != 0) + { + prec = precision; + } + else + { + prec = std::numeric_limits::digits10; + } + + char number_buffer[100]; + int length = snprintf(number_buffer, 100, "%1.*g", prec, val); + if (length < 0) + { + JSONCONS_THROW(json_exception_impl("print_double failed.")); + } + + const char* sbeg = number_buffer; + const char* send = sbeg + length; + const char* pexp = send; + + if (sbeg != send) + { + bool dot = false; + for (pexp = sbeg; *pexp != 'e' && *pexp != 'E' && pexp < send; ++pexp) + { + } + + for (const char* q = sbeg; q < pexp; ++q) + { + switch (*q) + { + case '-':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': + writer.put(*q); + break; + default: + if (*q == decimal_point_) + { + dot = true; + writer.put('.'); + } + break; + } + } + if (!dot) + { + writer.put('.'); + writer.put('0'); + dot = true; + } + for (const char* q = pexp; q < send; ++q) + { + writer.put(*q); + } + } + } +}; + +#endif + +}} + +#endif diff --git a/direct-access-api/include/obeclient.hpp b/direct-access-api/include/obeclient.hpp index 277db0a..d237af9 100644 --- a/direct-access-api/include/obeclient.hpp +++ b/direct-access-api/include/obeclient.hpp @@ -30,12 +30,15 @@ #include "dbmanager.hpp" #include "vcanhandler.hpp" -#define BUFFER_SIZE 100 +#define BUFFER_SIZE 99999 + e_result obe_client_init(); -void handle_incoming_msg(char* daa_msg); +void handle_incoming_msg(char* framestr); +void handle_incoming_msgs(string daa_msgs); char* prepare_outgoing_msg(char* can_msg, struct can_frame* frame); -void parse_buffer(char* msg); +void parse_buffer(string buffer); void* obe_listen(void* notused); +void* parse_obe_queue(void* notused); #endif // OBECLIENT_HPP diff --git a/direct-access-api/include/obufferedstream.hpp b/direct-access-api/include/obufferedstream.hpp new file mode 100644 index 0000000..7b499f4 --- /dev/null +++ b/direct-access-api/include/obufferedstream.hpp @@ -0,0 +1,264 @@ +// Copyright 2016 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_OBUFFEREDSTREAM_HPP +#define JSONCONS_DETAIL_OBUFFEREDSTREAM_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +template< + class CharT, + class Traits = std::char_traits +> class basic_obufferedstream; + +template +class basic_ovectorbuf + : public std::basic_streambuf +{ +private: + std::ios_base::openmode mode_; + std::vector buf_; + +public: + typedef CharT char_type; + typedef typename Traits::int_type int_type; + typedef typename Traits::pos_type pos_type; + typedef typename Traits::off_type off_type; + typedef Traits traits_type; + +public: + + explicit basic_ovectorbuf() JSONCONS_NOEXCEPT + : mode_(std::ios_base::out | std::ios_base::binary), + buf_(100u) + { + // Set write position to beginning of buffer. + this->setp(buf_.data(), buf_.data() + buf_.size()); + this->setg(buf_.data(), 0, buf_.data()); + } + + explicit basic_ovectorbuf(std::size_t length) JSONCONS_NOEXCEPT + : mode_(std::ios_base::out | std::ios_base::binary), + buf_(length) + { + // Set write position to beginning of buffer. + this->setp(buf_.data(), buf_.data() + buf_.size()); + this->setg(buf_.data(), 0, buf_.data()); + } + + virtual ~basic_ovectorbuf() JSONCONS_NOEXCEPT {} + + basic_ovectorbuf(const basic_ovectorbuf&) = delete; + + //basic_ovectorbuf(basic_ovectorbuf&&) = default; + + basic_ovectorbuf& operator=(const basic_ovectorbuf&) = delete; + + //basic_ovectorbuf& operator=(basic_ovectorbuf&&) = default; + + const CharT* data() const + { + return buf_.data(); + } + + size_t length() const + { + return this->pptr() - this->pbase(); + } + + virtual int sync() override + { + return EOF; + } + +protected: + int_type underflow() override + { + return this->gptr() != this->egptr() ? + Traits::to_int_type(*this->gptr()) : Traits::eof(); + } + + int_type pbackfail(int_type c = Traits::eof()) override + { + if (this->gptr() != this->eback()) + { + if (!Traits::eq_int_type(c, Traits::eof())) + { + if (Traits::eq(Traits::to_char_type(c), this->gptr()[-1])) + { + this->gbump(-1); + return c; + } + this->gbump(-1); + *this->gptr() = static_cast(c); + return c; + } + else + { + this->gbump(-1); + return Traits::not_eof(c); + } + } + else + { + return Traits::eof(); + } + } + + int_type overflow(int_type c = Traits::eof()) override + { + if (!Traits::eq_int_type(c, Traits::eof())) + { + size_t pos = buf_.size()+1; + buf_.resize(pos*2); + this->setp(buf_.data(), buf_.data() + buf_.size()); + this->pubseekpos(pos, std::ios_base::out); + *this->pptr() = Traits::to_char_type(c); + this->pbump(1); + this->pubsync(); + return c; + } + else + { + return Traits::not_eof(c); + } + } + + pos_type seekoff(off_type off, std::ios_base::seekdir dir, + std::ios_base::openmode mode = std::ios_base::out) override + { + (void)mode; // Always out + + std::streamoff newoff; + switch (dir) + { + case std::ios_base::beg: + newoff = 0; + break; + case std::ios_base::end: + newoff = static_cast(buf_.size()); + break; + case std::ios_base::cur: + newoff = static_cast(this->pptr() - this->pbase()); + break; + default: + return pos_type(off_type(-1)); + } + + off += newoff; + + std::ptrdiff_t n = this->epptr() - this->pbase(); + + if (off < 0 || off > n) return pos_type(off_type(-1)); + else + { + this->setp(this->pbase(), this->pbase() + n); + this->pbump(static_cast(off)); + } + + return pos_type(off); + } + + pos_type seekoff_beg(off_type off) + { + std::ptrdiff_t n = this->epptr() - this->pbase(); + + if (off < 0 || off > n) + { + return pos_type(off_type(-1)); + } + else + { + this->setp(this->pbase(), this->pbase() + n); + this->pbump(static_cast(off)); + } + + return pos_type(off); + } + + pos_type seekpos(pos_type pos, std::ios_base::openmode mode + = std::ios_base::out) override + { + + (void)mode; // Always out + + return seekoff_beg(pos - pos_type(off_type(0))); + } +}; + +template +class basic_obufferedstream : + public std::basic_ostream +{ +public: + typedef typename std::basic_ios::char_type char_type; + typedef typename std::basic_ios::int_type int_type; + typedef typename std::basic_ios::pos_type pos_type; + typedef typename std::basic_ios::off_type off_type; + typedef typename std::basic_ios::traits_type traits_type; + +private: + typedef basic_ovectorbuf base_ouputbuf; + typedef std::basic_ios base_ios; + + basic_ovectorbuf buf_; + +public: + basic_obufferedstream() JSONCONS_NOEXCEPT + : std::basic_ostream( (std::basic_streambuf*)(&buf_)), + buf_() + {} + basic_obufferedstream(std::size_t length) JSONCONS_NOEXCEPT + : std::basic_ostream( (std::basic_streambuf*)(&buf_)), + buf_(length) + {} + + basic_obufferedstream(const basic_obufferedstream&) = delete; + + //basic_obufferedstream(basic_obufferedstream&&) = default; + + virtual ~basic_obufferedstream() JSONCONS_NOEXCEPT + { + } + + basic_obufferedstream& operator=(const basic_obufferedstream&) = delete; + + //basic_obufferedstream& operator=(basic_obufferedstream&&) = default; + + const CharT* data() const + { + return buf_.data(); + } + + size_t length() const + { + return buf_.length(); + } + + void set_locale(const std::locale& loc) + { + std::locale result = std::basic_ostream::imbue(loc); + //this->pubimbue(loc); + } + + void clear_sequence() + { + this->clear(); + this->seekp(0, std::ios::beg); + } +}; + +} + +#endif diff --git a/direct-access-api/include/parse_error_handler.hpp b/direct-access-api/include/parse_error_handler.hpp new file mode 100644 index 0000000..cd79e0f --- /dev/null +++ b/direct-access-api/include/parse_error_handler.hpp @@ -0,0 +1,147 @@ +/// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_PARSE_ERROR_HANDLER_HPP +#define JSONCONS_PARSE_ERROR_HANDLER_HPP + +#include +#include +#include +#include + +namespace jsoncons { + +class parse_error : public std::exception, public virtual json_exception +{ +public: + parse_error() + : line_number_(0), + column_number_(0) + { + } + parse_error(std::error_code ec, + size_t line, + size_t column) + : error_code_(ec), + line_number_(line), + column_number_(column) + { + } + parse_error(const parse_error& other) + : error_code_(other.error_code_), + line_number_(other.line_number_), + column_number_(other.column_number_) + { + } + + const char* what() const JSONCONS_NOEXCEPT override + { + try + { + std::ostringstream os; + os << error_code_.message() << " at line " << line_number_ << " and column " << column_number_; + const_cast(buffer_) = os.str(); + return buffer_.c_str(); + } + catch (...) + { + return ""; + } + } + + const std::error_code code() const + { + return error_code_; + } + + size_t line_number() const + { + return line_number_; + } + + size_t column_number() const + { + return column_number_; + } + + parse_error& operator=(const parse_error& e) + { + error_code_ = e.error_code_; + line_number_ = e.line_number_; + column_number_ = e.column_number_; + return *this; + } +private: + std::error_code error_code_; + std::string buffer_; + size_t line_number_; + size_t column_number_; +}; + +#if !defined(JSONCONS_NO_DEPRECATED) +typedef parse_error json_parse_exception; +typedef parse_error parse_exception; +#endif + +class parse_error_handler +{ +public: + virtual ~parse_error_handler() + { + } + + bool error(std::error_code ec, + const serializing_context& context) JSONCONS_NOEXCEPT + { + return do_error(ec,context); + } + + void fatal_error(std::error_code ec, + const serializing_context& context) JSONCONS_NOEXCEPT + { + do_fatal_error(ec,context); + } + +private: + virtual bool do_error(std::error_code, + const serializing_context& context) JSONCONS_NOEXCEPT = 0; + + virtual void do_fatal_error(std::error_code, + const serializing_context&) JSONCONS_NOEXCEPT + { + } +}; + +class default_parse_error_handler : public parse_error_handler +{ +private: + bool do_error(std::error_code code, + const serializing_context&) JSONCONS_NOEXCEPT override + { + static const std::error_code illegal_comment = make_error_code(json_parser_errc::illegal_comment); + + if (code == illegal_comment) + { + return false; + } + else + { + return true; + } + } +}; + +class strict_parse_error_handler : public parse_error_handler +{ +private: + bool do_error(std::error_code, const serializing_context&) JSONCONS_NOEXCEPT override + { + return true; + } +}; + +} +#endif diff --git a/direct-access-api/include/serialization_traits.hpp b/direct-access-api/include/serialization_traits.hpp new file mode 100644 index 0000000..1c2159f --- /dev/null +++ b/direct-access-api/include/serialization_traits.hpp @@ -0,0 +1,315 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_SERIALIZATION_TRAITS_HPP +#define JSONCONS_SERIALIZATION_TRAITS_HPP + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { + +// serialization_traits + +template +struct serialization_traits +{ + template + static void encode(const T&, basic_json_content_handler&) + { + } +}; + +// dump + +template +void dump(const T& val, basic_json_content_handler& handler) +{ + handler.begin_json(); + serialization_traits::encode(val,handler); + handler.end_json(); +} + +#if !defined(JSONCONS_NO_DEPRECATED) +template +void dump_body(const T& val, basic_json_content_handler& handler) +{ + dump_fragment(val,handler); +} +#endif + +template +void dump_fragment(const T& val, basic_json_content_handler& handler) +{ + serialization_traits::encode(val,handler); +} + +template +void dump(const T& val, std::basic_ostream& os) +{ + basic_json_serializer serializer(os); + dump(val, serializer); +} + +template +void dump(const T& val, const basic_json_serializing_options& options, + std::basic_ostream& os) +{ + basic_json_serializer serializer(os, options); + dump(val, serializer); +} + +template +void dump(const T& val, std::basic_ostream& os, bool pprint) +{ + basic_json_serializer serializer(os, pprint); + dump(val, serializer); +} + +template +void dump(const T& val, const basic_json_serializing_options& options, + std::basic_ostream& os, bool pprint) +{ + basic_json_serializer serializer(os, options, pprint); + dump(val, serializer); +} + +// integer + +template +struct serialization_traits::value +>::type> +{ + template + static void encode(T val, basic_json_content_handler& handler) + { + handler.integer_value(val); + } +}; + +// uinteger + +template +struct serialization_traits::value +>::type> +{ + template + static void encode(T val, basic_json_content_handler& handler) + { + handler.uinteger_value(val); + } +}; + +// double + +template +struct serialization_traits::value +>::type> +{ + template + static void encode(T val, basic_json_content_handler& handler) + { + handler.double_value(val); + } +}; + +// bool + +template<> +struct serialization_traits +{ + template + static void encode(bool val, basic_json_content_handler& handler) + { + handler.bool_value(val); + } +}; + +// string + +template +struct serialization_traits::value +>::type> +{ + template + static void encode(const T& val, basic_json_content_handler& handler) + { + handler.string_value(val); + } +}; + +/*template<> +struct serialization_traits::const_pointer_type> +{ + template + static void encode(typename type_wrapper::const_pointer_type val, basic_json_content_handler& handler) + { + handler.string_value(val); + } +};*/ + +// sequence container (except string and array) + +template +struct serialization_traits::value +>::type> +{ + typedef typename std::iterator_traits::value_type value_type; + + template + static void encode(const T& val, basic_json_content_handler& handler) + { + handler.begin_array(); + for (auto it = std::begin(val); it != std::end(val); ++it) + { + serialization_traits::encode(*it,handler); + } + handler.end_array(); + } +}; + +// std::array + +template +struct serialization_traits> +{ + typedef typename std::array::value_type value_type; +public: + + template + static void encode(const std::array& val, basic_json_content_handler& handler) + { + handler.begin_array(); + for (auto it = std::begin(val); it != std::end(val); ++it) + { + serialization_traits::encode(*it,handler); + } + handler.end_array(); + } +}; + +// associative container + +template +struct serialization_traits::value +>::type> +{ + typedef typename T::mapped_type mapped_type; + typedef typename T::value_type value_type; + + template + static void encode(const T& val, basic_json_content_handler& handler) + { + handler.begin_object(); + for (auto it = std::begin(val); it != std::end(val); ++it) + { + handler.name(it->first); + serialization_traits::encode(it->second,handler); + } + handler.end_object(); + } +}; + +namespace detail { namespace streaming { + +template +struct tuple_helper +{ + using element_type = typename std::tuple_element::value - Pos, Tuple>::type; + using next = tuple_helper; + + template + static void encode(const Tuple& tuple, basic_json_content_handler& handler) + { + serialization_traits::encode(std::get::value - Pos>(tuple),handler); + next::encode(tuple, handler); + } +}; + +template +struct tuple_helper<0, Tuple> +{ + template + static void encode(const Tuple&, basic_json_content_handler&) + { + } +}; + +}} + +template +struct serialization_traits> +{ +private: + using helper = detail::streaming::tuple_helper>; + +public: + template + static void encode(const std::tuple& value, basic_json_content_handler& handler) + { + handler.begin_array(); + helper::encode(value, handler); + handler.end_array(); + } +}; + +template +struct serialization_traits> +{ +public: + + template + static void encode(const std::pair& value, basic_json_content_handler& handler) + { + handler.begin_array(); + serialization_traits::encode(value.first, handler); + serialization_traits::encode(value.second, handler); + handler.end_array(); + } +}; + +#if !defined(JSONCONS_NO_DEPRECATED) +template +struct serialization_traits> +{ +public: + + template + static void encode(std::shared_ptr p, basic_json_content_handler& handler) + { + serialization_traits::encode(*p, handler); + } +}; +#endif + +} + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#endif + + diff --git a/direct-access-api/include/serializing_context.hpp b/direct-access-api/include/serializing_context.hpp new file mode 100644 index 0000000..77af770 --- /dev/null +++ b/direct-access-api/include/serializing_context.hpp @@ -0,0 +1,44 @@ +/// Copyright 2013-2018 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_SERIALIZING_CONTEXT_HPP +#define JSONCONS_SERIALIZING_CONTEXT_HPP + +namespace jsoncons { + +class serializing_context +{ +public: + virtual ~serializing_context() = default; + + size_t line_number() const + { + return do_line_number(); + } + size_t column_number() const + { + return do_column_number(); + } + +private: + virtual size_t do_line_number() const = 0; + virtual size_t do_column_number() const = 0; +}; + +class null_serializing_context : public serializing_context +{ +private: + size_t do_line_number() const override { return 0; } + + size_t do_column_number() const override { return 0; } +}; + +#if !defined(JSONCONS_NO_DEPRECATED) +typedef serializing_context serializing_context; +#endif + +} +#endif diff --git a/direct-access-api/include/server_ws.hpp b/direct-access-api/include/server_ws.hpp new file mode 100644 index 0000000..2a74f9a --- /dev/null +++ b/direct-access-api/include/server_ws.hpp @@ -0,0 +1,802 @@ +#ifndef SERVER_WS_HPP +#define SERVER_WS_HPP + +#include "crypto.hpp" +#include "utility.hpp" +#include "wschannel.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_STANDALONE_ASIO +#include +#include +namespace SimpleWeb { + using error_code = std::error_code; + using errc = std::errc; + namespace make_error_code = std; +} // namespace SimpleWeb +#else +#include +#include +namespace SimpleWeb { + namespace asio = boost::asio; + using error_code = boost::system::error_code; + namespace errc = boost::system::errc; + namespace make_error_code = boost::system::errc; +} // namespace SimpleWeb +#endif + +// Late 2017 TODO: remove the following checks and always use std::regex +#ifdef USE_BOOST_REGEX +#include +namespace SimpleWeb { + namespace regex = boost; +} +#else +#include +namespace SimpleWeb { + namespace regex = std; +} +#endif + +namespace SimpleWeb { + template + class SocketServer; + + template + class SocketServerBase { + public: + class Message : public std::istream { + friend class SocketServerBase; + + public: + unsigned char fin_rsv_opcode; + std::size_t size() noexcept { + return length; + } + + /// Convenience function to return std::string. The stream buffer is consumed. + std::string string() noexcept { + try { + std::stringstream ss; + ss << rdbuf(); + return ss.str(); + } + catch(...) { + return std::string(); + } + } + + private: + Message() noexcept : std::istream(&streambuf), length(0) {} + Message(unsigned char fin_rsv_opcode, std::size_t length) noexcept : std::istream(&streambuf), fin_rsv_opcode(fin_rsv_opcode), length(length) {} + std::size_t length; + asio::streambuf streambuf; + }; + + /// The buffer is not consumed during send operations. + /// Do not alter while sending. + class SendStream : public std::ostream { + friend class SocketServerBase; + + asio::streambuf streambuf; + + public: + SendStream() noexcept : std::ostream(&streambuf) {} + + /// Returns the size of the buffer + std::size_t size() const noexcept { + return streambuf.size(); + } + }; + + class Connection : public std::enable_shared_from_this { + friend class SocketServerBase; + friend class SocketServer; + + public: + Connection(std::unique_ptr &&socket) noexcept : socket(std::move(socket)), timeout_idle(0), strand(this->socket->get_io_service()), closed(false) {} + + std::string method, path, query_string, http_version; + + CaseInsensitiveMultimap header; + + wschannel channel; + + regex::smatch path_match; + + asio::ip::tcp::endpoint remote_endpoint; + + std::string remote_endpoint_address() noexcept { + try { + return remote_endpoint.address().to_string(); + } + catch(...) { + return std::string(); + } + } + + unsigned short remote_endpoint_port() noexcept { + return remote_endpoint.port(); + } + + private: + template + Connection(std::shared_ptr handler_runner, long timeout_idle, Args &&... args) noexcept + : handler_runner(std::move(handler_runner)), socket(new socket_type(std::forward(args)...)), timeout_idle(timeout_idle), strand(socket->get_io_service()), closed(false) {} + + std::shared_ptr handler_runner; + + std::unique_ptr socket; // Socket must be unique_ptr since asio::ssl::stream is not movable + std::mutex socket_close_mutex; + + asio::streambuf read_buffer; + std::shared_ptr fragmented_message; + + long timeout_idle; + std::unique_ptr timer; + std::mutex timer_mutex; + + void close() noexcept { + error_code ec; + std::unique_lock lock(socket_close_mutex); // The following operations seems to be needed to run sequentially + socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec); + socket->lowest_layer().close(ec); + } + + void set_timeout(long seconds = -1) noexcept { + bool use_timeout_idle = false; + if(seconds == -1) { + use_timeout_idle = true; + seconds = timeout_idle; + } + + std::unique_lock lock(timer_mutex); + + if(seconds == 0) { + timer = nullptr; + return; + } + + timer = std::unique_ptr(new asio::steady_timer(socket->get_io_service())); + timer->expires_from_now(std::chrono::seconds(seconds)); + std::weak_ptr connection_weak(this->shared_from_this()); // To avoid keeping Connection instance alive longer than needed + timer->async_wait([connection_weak, use_timeout_idle](const error_code &ec) { + if(!ec) { + if(auto connection = connection_weak.lock()) { + if(use_timeout_idle) + connection->send_close(1000, "idle timeout"); // 1000=normal closure + else + connection->close(); + } + } + }); + } + + void cancel_timeout() noexcept { + std::unique_lock lock(timer_mutex); + if(timer) { + error_code ec; + timer->cancel(ec); + } + } + + bool generate_handshake(const std::shared_ptr &write_buffer) { + std::ostream handshake(write_buffer.get()); + + auto header_it = header.find("Sec-WebSocket-Key"); + if(header_it == header.end()) + return false; + + static auto ws_magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + auto sha1 = Crypto::sha1(header_it->second + ws_magic_string); + + handshake << "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"; + handshake << "Upgrade: websocket\r\n"; + handshake << "Connection: Upgrade\r\n"; + handshake << "Sec-WebSocket-Accept: " << Crypto::Base64::encode(sha1) << "\r\n"; + handshake << "\r\n"; + + return true; + } + + asio::io_service::strand strand; + + class SendData { + public: + SendData(std::shared_ptr header_stream, std::shared_ptr message_stream, + std::function &&callback) noexcept + : header_stream(std::move(header_stream)), message_stream(std::move(message_stream)), callback(std::move(callback)) {} + std::shared_ptr header_stream; + std::shared_ptr message_stream; + std::function callback; + }; + + std::list send_queue; + + void send_from_queue() { + auto self = this->shared_from_this(); + strand.post([self]() { + asio::async_write(*self->socket, self->send_queue.begin()->header_stream->streambuf, self->strand.wrap([self](const error_code &ec, std::size_t /*bytes_transferred*/) { + auto lock = self->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + asio::async_write(*self->socket, self->send_queue.begin()->message_stream->streambuf.data(), self->strand.wrap([self](const error_code &ec, std::size_t /*bytes_transferred*/) { + auto lock = self->handler_runner->continue_lock(); + if(!lock) + return; + auto send_queued = self->send_queue.begin(); + if(send_queued->callback) + send_queued->callback(ec); + if(!ec) { + self->send_queue.erase(send_queued); + if(self->send_queue.size() > 0) + self->send_from_queue(); + } + else + self->send_queue.clear(); + })); + } + else { + auto send_queued = self->send_queue.begin(); + if(send_queued->callback) + send_queued->callback(ec); + self->send_queue.clear(); + } + })); + }); + } + + std::atomic closed; + + void read_remote_endpoint() noexcept { + try { + remote_endpoint = socket->lowest_layer().remote_endpoint(); + } + catch(...) { + } + } + + public: + /// fin_rsv_opcode: 129=one fragment, text, 130=one fragment, binary, 136=close connection. + /// See http://tools.ietf.org/html/rfc6455#section-5.2 for more information + void send(const std::shared_ptr &send_stream, const std::function &callback = nullptr, + unsigned char fin_rsv_opcode = 129) { + cancel_timeout(); + set_timeout(); + + auto header_stream = std::make_shared(); + + std::size_t length = send_stream->size(); + + header_stream->put(static_cast(fin_rsv_opcode)); + // Unmasked (first length byte<128) + if(length >= 126) { + std::size_t num_bytes; + if(length > 0xffff) { + num_bytes = 8; + header_stream->put(127); + } + else { + num_bytes = 2; + header_stream->put(126); + } + + for(std::size_t c = num_bytes - 1; c != static_cast(-1); c--) + header_stream->put((static_cast(length) >> (8 * c)) % 256); + } + else + header_stream->put(static_cast(length)); + + auto self = this->shared_from_this(); + strand.post([self, header_stream, send_stream, callback]() { + self->send_queue.emplace_back(header_stream, send_stream, callback); + if(self->send_queue.size() == 1) + self->send_from_queue(); + }); + } + + void send_close(int status, const std::string &reason = "", const std::function &callback = nullptr) { + // Send close only once (in case close is initiated by server) + if(closed) + return; + closed = true; + + auto send_stream = std::make_shared(); + + send_stream->put(status >> 8); + send_stream->put(status % 256); + + *send_stream << reason; + + // fin_rsv_opcode=136: message close + send(send_stream, callback, 136); + } + }; + + class Endpoint { + friend class SocketServerBase; + + private: + std::unordered_set> connections; + std::mutex connections_mutex; + + public: + std::function)> on_open; + std::function, std::shared_ptr)> on_message; + std::function, int, const std::string &)> on_close; + std::function, const error_code &)> on_error; + std::function)> on_ping; + std::function)> on_pong; + + std::unordered_set> get_connections() noexcept { + std::unique_lock lock(connections_mutex); + auto copy = connections; + return copy; + } + }; + + class Config { + friend class SocketServerBase; + + private: + Config(unsigned short port) noexcept : port(port) {} + + public: + /// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS. + unsigned short port; + /// If io_service is not set, number of threads that the server will use when start() is called. + /// Defaults to 1 thread. + std::size_t thread_pool_size = 1; + /// Timeout on request handling. Defaults to 5 seconds. + long timeout_request = 5; + /// Idle timeout. Defaults to no timeout. + long timeout_idle = 0; + /// Maximum size of incoming messages. Defaults to architecture maximum. + /// Exceeding this limit will result in a message_size error code and the connection will be closed. + std::size_t max_message_size = std::numeric_limits::max(); + /// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation. + /// If empty, the address will be any address. + std::string address; + /// Set to false to avoid binding the socket to an address that is already in use. Defaults to true. + bool reuse_address = true; + }; + /// Set before calling start(). + Config config; + + private: + class regex_orderable : public regex::regex { + std::string str; + + public: + regex_orderable(const char *regex_cstr) : regex::regex(regex_cstr), str(regex_cstr) {} + regex_orderable(const std::string ®ex_str) : regex::regex(regex_str), str(regex_str) {} + bool operator<(const regex_orderable &rhs) const noexcept { + return str < rhs.str; + } + }; + + public: + /// Warning: do not add or remove endpoints after start() is called + std::map endpoint; + + virtual void start() { + if(!io_service) { + io_service = std::make_shared(); + internal_io_service = true; + } + + if(io_service->stopped()) + io_service->reset(); + + asio::ip::tcp::endpoint endpoint; + if(config.address.size() > 0) + endpoint = asio::ip::tcp::endpoint(asio::ip::address::from_string(config.address), config.port); + else + endpoint = asio::ip::tcp::endpoint(asio::ip::tcp::v4(), config.port); + + if(!acceptor) + acceptor = std::unique_ptr(new asio::ip::tcp::acceptor(*io_service)); + acceptor->open(endpoint.protocol()); + acceptor->set_option(asio::socket_base::reuse_address(config.reuse_address)); + acceptor->bind(endpoint); + acceptor->listen(); + + accept(); + + if(internal_io_service) { + // If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling + threads.clear(); + for(std::size_t c = 1; c < config.thread_pool_size; c++) { + threads.emplace_back([this]() { + io_service->run(); + }); + } + // Main thread + if(config.thread_pool_size > 0) + io_service->run(); + + // Wait for the rest of the threads, if any, to finish as well + for(auto &t : threads) + t.join(); + } + } + + void stop() noexcept { + if(acceptor) { + error_code ec; + acceptor->close(ec); + + for(auto &pair : endpoint) { + std::unique_lock lock(pair.second.connections_mutex); + for(auto &connection : pair.second.connections) + connection->close(); + pair.second.connections.clear(); + } + + if(internal_io_service) + io_service->stop(); + } + } + + virtual ~SocketServerBase() noexcept {} + + std::unordered_set> get_connections() noexcept { + std::unordered_set> all_connections; + for(auto &e : endpoint) { + std::unique_lock lock(e.second.connections_mutex); + all_connections.insert(e.second.connections.begin(), e.second.connections.end()); + } + return all_connections; + } + + /** + * Upgrades a request, from for instance Simple-Web-Server, to a WebSocket connection. + * The parameters are moved to the Connection object. + * See also Server::on_upgrade in the Simple-Web-Server project. + * The socket's io_service is used, thus running start() is not needed. + * + * Example use: + * server.on_upgrade=[&socket_server] (auto socket, auto request) { + * auto connection=std::make_shared::Connection>(std::move(socket)); + * connection->method=std::move(request->method); + * connection->path=std::move(request->path); + * connection->query_string=std::move(request->query_string); + * connection->http_version=std::move(request->http_version); + * connection->header=std::move(request->header); + * connection->remote_endpoint=std::move(*request->remote_endpoint); + * socket_server.upgrade(connection); + * } + */ + void upgrade(const std::shared_ptr &connection) { + connection->handler_runner = handler_runner; + connection->timeout_idle = config.timeout_idle; + write_handshake(connection); + } + + /// If you have your own asio::io_service, store its pointer here before running start(). + std::shared_ptr io_service; + + protected: + bool internal_io_service = false; + + std::unique_ptr acceptor; + std::vector threads; + + std::shared_ptr handler_runner; + + SocketServerBase(unsigned short port) noexcept : config(port), handler_runner(new ScopeRunner()) {} + + virtual void accept() = 0; + + void read_handshake(const std::shared_ptr &connection) { + connection->read_remote_endpoint(); + + connection->set_timeout(config.timeout_request); + asio::async_read_until(*connection->socket, connection->read_buffer, "\r\n\r\n", [this, connection](const error_code &ec, std::size_t /*bytes_transferred*/) { + connection->cancel_timeout(); + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + std::istream stream(&connection->read_buffer); + if(RequestMessage::parse(stream, connection->method, connection->path, connection->query_string, connection->http_version, connection->header)) + write_handshake(connection); + } + }); + } + + void write_handshake(const std::shared_ptr &connection) { + for(auto ®ex_endpoint : endpoint) { + regex::smatch path_match; + if(regex::regex_match(connection->path, path_match, regex_endpoint.first)) { + auto write_buffer = std::make_shared(); + + if(connection->generate_handshake(write_buffer)) { + connection->path_match = std::move(path_match); + connection->set_timeout(config.timeout_request); + asio::async_write(*connection->socket, *write_buffer, [this, connection, write_buffer, ®ex_endpoint](const error_code &ec, std::size_t /*bytes_transferred*/) { + connection->cancel_timeout(); + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + connection_open(connection, regex_endpoint.second); + read_message(connection, regex_endpoint.second); + } + else + connection_error(connection, regex_endpoint.second, ec); + }); + } + return; + } + } + } + + void read_message(const std::shared_ptr &connection, Endpoint &endpoint) const { + asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(2), [this, connection, &endpoint](const error_code &ec, std::size_t bytes_transferred) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + if(bytes_transferred == 0) { // TODO: why does this happen sometimes? + read_message(connection, endpoint); + return; + } + std::istream stream(&connection->read_buffer); + + std::array first_bytes; + stream.read((char *)&first_bytes[0], 2); + + unsigned char fin_rsv_opcode = first_bytes[0]; + + // Close connection if unmasked message from client (protocol error) + if(first_bytes[1] < 128) { + const std::string reason("message from client not masked"); + connection->send_close(1002, reason); + connection_close(connection, endpoint, 1002, reason); + return; + } + + std::size_t length = (first_bytes[1] & 127); + + if(length == 126) { + // 2 next bytes is the size of content + asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(2), [this, connection, &endpoint, fin_rsv_opcode](const error_code &ec, std::size_t /*bytes_transferred*/) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + std::istream stream(&connection->read_buffer); + + std::array length_bytes; + stream.read((char *)&length_bytes[0], 2); + + std::size_t length = 0; + std::size_t num_bytes = 2; + for(std::size_t c = 0; c < num_bytes; c++) + length += static_cast(length_bytes[c]) << (8 * (num_bytes - 1 - c)); + + read_message_content(connection, length, endpoint, fin_rsv_opcode); + } + else + connection_error(connection, endpoint, ec); + }); + } + else if(length == 127) { + // 8 next bytes is the size of content + asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(8), [this, connection, &endpoint, fin_rsv_opcode](const error_code &ec, std::size_t /*bytes_transferred*/) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + std::istream stream(&connection->read_buffer); + + std::array length_bytes; + stream.read((char *)&length_bytes[0], 8); + + std::size_t length = 0; + std::size_t num_bytes = 8; + for(std::size_t c = 0; c < num_bytes; c++) + length += static_cast(length_bytes[c]) << (8 * (num_bytes - 1 - c)); + + read_message_content(connection, length, endpoint, fin_rsv_opcode); + } + else + connection_error(connection, endpoint, ec); + }); + } + else + read_message_content(connection, length, endpoint, fin_rsv_opcode); + } + else + connection_error(connection, endpoint, ec); + }); + } + + void read_message_content(const std::shared_ptr &connection, std::size_t length, Endpoint &endpoint, unsigned char fin_rsv_opcode) const { + if(length + (connection->fragmented_message ? connection->fragmented_message->length : 0) > config.max_message_size) { + connection_error(connection, endpoint, make_error_code::make_error_code(errc::message_size)); + const int status = 1009; + const std::string reason = "message too big"; + connection->send_close(status, reason); + connection_close(connection, endpoint, status, reason); + return; + } + asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(4 + length), [this, connection, length, &endpoint, fin_rsv_opcode](const error_code &ec, std::size_t /*bytes_transferred*/) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + std::istream istream(&connection->read_buffer); + + // Read mask + std::array mask; + istream.read((char *)&mask[0], 4); + + std::shared_ptr message; + + // If fragmented message + if((fin_rsv_opcode & 0x80) == 0 || (fin_rsv_opcode & 0x0f) == 0) { + if(!connection->fragmented_message) { + connection->fragmented_message = std::shared_ptr(new Message(fin_rsv_opcode, length)); + connection->fragmented_message->fin_rsv_opcode |= 0x80; + } + else + connection->fragmented_message->length += length; + message = connection->fragmented_message; + } + else + message = std::shared_ptr(new Message(fin_rsv_opcode, length)); + std::ostream ostream(&message->streambuf); + for(std::size_t c = 0; c < length; c++) + ostream.put(istream.get() ^ mask[c % 4]); + + // If connection close + if((fin_rsv_opcode & 0x0f) == 8) { + connection->cancel_timeout(); + connection->set_timeout(); + + int status = 0; + if(length >= 2) { + unsigned char byte1 = message->get(); + unsigned char byte2 = message->get(); + status = (static_cast(byte1) << 8) + byte2; + } + + auto reason = message->string(); + connection->send_close(status, reason); + this->connection_close(connection, endpoint, status, reason); + } + // If ping + else if((fin_rsv_opcode & 0x0f) == 9) { + connection->cancel_timeout(); + connection->set_timeout(); + + // Send pong + auto empty_send_stream = std::make_shared(); + connection->send(empty_send_stream, nullptr, fin_rsv_opcode + 1); + + if(endpoint.on_ping) + endpoint.on_ping(connection); + + // Next message + this->read_message(connection, endpoint); + } + // If pong + else if((fin_rsv_opcode & 0x0f) == 10) { + connection->cancel_timeout(); + connection->set_timeout(); + + if(endpoint.on_pong) + endpoint.on_pong(connection); + + // Next message + this->read_message(connection, endpoint); + } + // If fragmented message and not final fragment + else if((fin_rsv_opcode & 0x80) == 0) { + // Next message + this->read_message(connection, endpoint); + } + else { + connection->cancel_timeout(); + connection->set_timeout(); + + if(endpoint.on_message) + endpoint.on_message(connection, message); + + // Next message + // Only reset fragmented_message for non-control frames (control frames can be in between a fragmented message) + connection->fragmented_message = nullptr; + this->read_message(connection, endpoint); + } + } + else + this->connection_error(connection, endpoint, ec); + }); + } + + void connection_open(const std::shared_ptr &connection, Endpoint &endpoint) const { + connection->cancel_timeout(); + connection->set_timeout(); + + { + std::unique_lock lock(endpoint.connections_mutex); + endpoint.connections.insert(connection); + } + + if(endpoint.on_open) + endpoint.on_open(connection); + } + + void connection_close(const std::shared_ptr &connection, Endpoint &endpoint, int status, const std::string &reason) const { + connection->cancel_timeout(); + connection->set_timeout(); + + { + std::unique_lock lock(endpoint.connections_mutex); + endpoint.connections.erase(connection); + } + + if(endpoint.on_close) + endpoint.on_close(connection, status, reason); + } + + void connection_error(const std::shared_ptr &connection, Endpoint &endpoint, const error_code &ec) const { + connection->cancel_timeout(); + connection->set_timeout(); + + { + std::unique_lock lock(endpoint.connections_mutex); + endpoint.connections.erase(connection); + } + + if(endpoint.on_error) + endpoint.on_error(connection, ec); + } + }; + + template + class SocketServer : public SocketServerBase {}; + + using WS = asio::ip::tcp::socket; + + template <> + class SocketServer : public SocketServerBase { + public: + SocketServer() noexcept : SocketServerBase(80) {} + + protected: + void accept() override { + std::shared_ptr connection(new Connection(handler_runner, config.timeout_idle, *io_service)); + + acceptor->async_accept(*connection->socket, [this, connection](const error_code &ec) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + // Immediately start accepting a new connection (if io_service hasn't been stopped) + if(ec != asio::error::operation_aborted) + accept(); + + if(!ec) { + asio::ip::tcp::no_delay option(true); + connection->socket->set_option(option); + + read_handshake(connection); + } + }); + } + }; +} // namespace SimpleWeb + +#endif /* SERVER_WS_HPP */ diff --git a/direct-access-api/include/server_wss.hpp b/direct-access-api/include/server_wss.hpp new file mode 100644 index 0000000..7538493 --- /dev/null +++ b/direct-access-api/include/server_wss.hpp @@ -0,0 +1,81 @@ +#ifndef SERVER_WSS_HPP +#define SERVER_WSS_HPP + +#include "server_ws.hpp" +#include +#include + +#ifdef USE_STANDALONE_ASIO +#include +#else +#include +#endif + + +namespace SimpleWeb { + using WSS = asio::ssl::stream; + + template <> + class SocketServer : public SocketServerBase { + std::string session_id_context; + bool set_session_id_context = false; + + public: + SocketServer(const std::string &cert_file, const std::string &private_key_file, const std::string &verify_file = std::string()) + : SocketServerBase(443), context(asio::ssl::context::tlsv12) { + context.use_certificate_chain_file(cert_file); + context.use_private_key_file(private_key_file, asio::ssl::context::pem); + + if(verify_file.size() > 0) { + context.load_verify_file(verify_file); + context.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert | + asio::ssl::verify_client_once); + set_session_id_context = true; + } + } + + void start() override { + if(set_session_id_context) { + // Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH + session_id_context = std::to_string(config.port) + ':'; + session_id_context.append(config.address.rbegin(), config.address.rend()); + SSL_CTX_set_session_id_context(context.native_handle(), reinterpret_cast(session_id_context.data()), + std::min(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH)); + } + SocketServerBase::start(); + } + + protected: + asio::ssl::context context; + + void accept() override { + std::shared_ptr connection(new Connection(handler_runner, config.timeout_idle, *io_service, context)); + + acceptor->async_accept(connection->socket->lowest_layer(), [this, connection](const error_code &ec) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + // Immediately start accepting a new connection (if io_service hasn't been stopped) + if(ec != asio::error::operation_aborted) + accept(); + + if(!ec) { + asio::ip::tcp::no_delay option(true); + connection->socket->lowest_layer().set_option(option); + + connection->set_timeout(config.timeout_request); + connection->socket->async_handshake(asio::ssl::stream_base::server, [this, connection](const error_code &ec) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + connection->cancel_timeout(); + if(!ec) + read_handshake(connection); + }); + } + }); + } + }; +} // namespace SimpleWeb + +#endif /* SERVER_WSS_HPP */ diff --git a/direct-access-api/include/status_code.hpp b/direct-access-api/include/status_code.hpp new file mode 100644 index 0000000..0792727 --- /dev/null +++ b/direct-access-api/include/status_code.hpp @@ -0,0 +1,167 @@ +#ifndef SIMPLE_WEB_STATUS_CODE_HPP +#define SIMPLE_WEB_STATUS_CODE_HPP + +#include +#include +#include +#include + +namespace SimpleWeb { + enum class StatusCode { + unknown = 0, + information_continue = 100, + information_switching_protocols, + information_processing, + success_ok = 200, + success_created, + success_accepted, + success_non_authoritative_information, + success_no_content, + success_reset_content, + success_partial_content, + success_multi_status, + success_already_reported, + success_im_used = 226, + redirection_multiple_choices = 300, + redirection_moved_permanently, + redirection_found, + redirection_see_other, + redirection_not_modified, + redirection_use_proxy, + redirection_switch_proxy, + redirection_temporary_redirect, + redirection_permanent_redirect, + client_error_bad_request = 400, + client_error_unauthorized, + client_error_payment_required, + client_error_forbidden, + client_error_not_found, + client_error_method_not_allowed, + client_error_not_acceptable, + client_error_proxy_authentication_required, + client_error_request_timeout, + client_error_conflict, + client_error_gone, + client_error_length_required, + client_error_precondition_failed, + client_error_payload_too_large, + client_error_uri_too_long, + client_error_unsupported_media_type, + client_error_range_not_satisfiable, + client_error_expectation_failed, + client_error_im_a_teapot, + client_error_misdirection_required = 421, + client_error_unprocessable_entity, + client_error_locked, + client_error_failed_dependency, + client_error_upgrade_required = 426, + client_error_precondition_required = 428, + client_error_too_many_requests, + client_error_request_header_fields_too_large = 431, + client_error_unavailable_for_legal_reasons = 451, + server_error_internal_server_error = 500, + server_error_not_implemented, + server_error_bad_gateway, + server_error_service_unavailable, + server_error_gateway_timeout, + server_error_http_version_not_supported, + server_error_variant_also_negotiates, + server_error_insufficient_storage, + server_error_loop_detected, + server_error_not_extended = 510, + server_error_network_authentication_required + }; + + inline const std::map &status_code_strings() { + static const std::map status_code_strings = { + {StatusCode::unknown, ""}, + {StatusCode::information_continue, "100 Continue"}, + {StatusCode::information_switching_protocols, "101 Switching Protocols"}, + {StatusCode::information_processing, "102 Processing"}, + {StatusCode::success_ok, "200 OK"}, + {StatusCode::success_created, "201 Created"}, + {StatusCode::success_accepted, "202 Accepted"}, + {StatusCode::success_non_authoritative_information, "203 Non-Authoritative Information"}, + {StatusCode::success_no_content, "204 No Content"}, + {StatusCode::success_reset_content, "205 Reset Content"}, + {StatusCode::success_partial_content, "206 Partial Content"}, + {StatusCode::success_multi_status, "207 Multi-Status"}, + {StatusCode::success_already_reported, "208 Already Reported"}, + {StatusCode::success_im_used, "226 IM Used"}, + {StatusCode::redirection_multiple_choices, "300 Multiple Choices"}, + {StatusCode::redirection_moved_permanently, "301 Moved Permanently"}, + {StatusCode::redirection_found, "302 Found"}, + {StatusCode::redirection_see_other, "303 See Other"}, + {StatusCode::redirection_not_modified, "304 Not Modified"}, + {StatusCode::redirection_use_proxy, "305 Use Proxy"}, + {StatusCode::redirection_switch_proxy, "306 Switch Proxy"}, + {StatusCode::redirection_temporary_redirect, "307 Temporary Redirect"}, + {StatusCode::redirection_permanent_redirect, "308 Permanent Redirect"}, + {StatusCode::client_error_bad_request, "400 Bad Request"}, + {StatusCode::client_error_unauthorized, "401 Unauthorized"}, + {StatusCode::client_error_payment_required, "402 Payment Required"}, + {StatusCode::client_error_forbidden, "403 Forbidden"}, + {StatusCode::client_error_not_found, "404 Not Found"}, + {StatusCode::client_error_method_not_allowed, "405 Method Not Allowed"}, + {StatusCode::client_error_not_acceptable, "406 Not Acceptable"}, + {StatusCode::client_error_proxy_authentication_required, "407 Proxy Authentication Required"}, + {StatusCode::client_error_request_timeout, "408 Request Timeout"}, + {StatusCode::client_error_conflict, "409 Conflict"}, + {StatusCode::client_error_gone, "410 Gone"}, + {StatusCode::client_error_length_required, "411 Length Required"}, + {StatusCode::client_error_precondition_failed, "412 Precondition Failed"}, + {StatusCode::client_error_payload_too_large, "413 Payload Too Large"}, + {StatusCode::client_error_uri_too_long, "414 URI Too Long"}, + {StatusCode::client_error_unsupported_media_type, "415 Unsupported Media Type"}, + {StatusCode::client_error_range_not_satisfiable, "416 Range Not Satisfiable"}, + {StatusCode::client_error_expectation_failed, "417 Expectation Failed"}, + {StatusCode::client_error_im_a_teapot, "418 I'm a teapot"}, + {StatusCode::client_error_misdirection_required, "421 Misdirected Request"}, + {StatusCode::client_error_unprocessable_entity, "422 Unprocessable Entity"}, + {StatusCode::client_error_locked, "423 Locked"}, + {StatusCode::client_error_failed_dependency, "424 Failed Dependency"}, + {StatusCode::client_error_upgrade_required, "426 Upgrade Required"}, + {StatusCode::client_error_precondition_required, "428 Precondition Required"}, + {StatusCode::client_error_too_many_requests, "429 Too Many Requests"}, + {StatusCode::client_error_request_header_fields_too_large, "431 Request Header Fields Too Large"}, + {StatusCode::client_error_unavailable_for_legal_reasons, "451 Unavailable For Legal Reasons"}, + {StatusCode::server_error_internal_server_error, "500 Internal Server Error"}, + {StatusCode::server_error_not_implemented, "501 Not Implemented"}, + {StatusCode::server_error_bad_gateway, "502 Bad Gateway"}, + {StatusCode::server_error_service_unavailable, "503 Service Unavailable"}, + {StatusCode::server_error_gateway_timeout, "504 Gateway Timeout"}, + {StatusCode::server_error_http_version_not_supported, "505 HTTP Version Not Supported"}, + {StatusCode::server_error_variant_also_negotiates, "506 Variant Also Negotiates"}, + {StatusCode::server_error_insufficient_storage, "507 Insufficient Storage"}, + {StatusCode::server_error_loop_detected, "508 Loop Detected"}, + {StatusCode::server_error_not_extended, "510 Not Extended"}, + {StatusCode::server_error_network_authentication_required, "511 Network Authentication Required"}}; + return status_code_strings; + } + + inline StatusCode status_code(const std::string &status_code_string) noexcept { + class StringToStatusCode : public std::unordered_map { + public: + StringToStatusCode() { + for(auto &status_code : status_code_strings()) + emplace(status_code.second, status_code.first); + } + }; + static StringToStatusCode string_to_status_code; + auto pos = string_to_status_code.find(status_code_string); + if(pos == string_to_status_code.end()) + return StatusCode::unknown; + return pos->second; + } + + inline const std::string &status_code(StatusCode status_code_enum) noexcept { + auto pos = status_code_strings().find(status_code_enum); + if(pos == status_code_strings().end()) { + static std::string empty_string; + return empty_string; + } + return pos->second; + } +} // namespace SimpleWeb + +#endif // SIMPLE_WEB_STATUS_CODE_HPP diff --git a/direct-access-api/include/type_traits_helper.hpp b/direct-access-api/include/type_traits_helper.hpp new file mode 100644 index 0000000..4dce8e6 --- /dev/null +++ b/direct-access-api/include/type_traits_helper.hpp @@ -0,0 +1,226 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_TYPETRAITSHELPER_HPP +#define JSONCONS_DETAIL_TYPETRAITSHELPER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons +{ +// static_max + +template +struct static_max; + +template +struct static_max +{ + static const size_t value = arg; +}; + +template +struct static_max +{ + static const size_t value = arg1 >= arg2 ? + static_max::value : + static_max::value; +}; + +// type_wrapper + +template +struct type_wrapper +{ + typedef T* pointer_type; + typedef const T* const_pointer_type; + typedef T value_type; + typedef T& reference; + typedef const T& const_reference; +}; + +template +struct type_wrapper +{ + typedef T* pointer_type; + typedef const T* const_pointer_type; + typedef T value_type; + typedef T& reference; + typedef const T& const_reference; +}; + +template +struct type_wrapper +{ + typedef T* pointer_type; + typedef const T* const_pointer_type; + typedef T value_type; + typedef T& reference; + typedef const T& const_reference; +}; + +template +struct type_wrapper +{ + typedef T* pointer_type; + typedef const T* const_pointer_type; + typedef T value_type; + typedef T& reference; + typedef const T& const_reference; +}; + +// json_literals + +namespace detail { +JSONCONS_DEFINE_LITERAL(null_literal,"null") +JSONCONS_DEFINE_LITERAL(true_literal,"true") +JSONCONS_DEFINE_LITERAL(false_literal,"false") +} + +inline +unsigned char to_hex_character(unsigned char c) +{ + JSONCONS_ASSERT(c <= 0xF); + + return (c < 10) ? ('0' + c) : ('A' - 10 + c); +} + +inline +bool is_control_character(uint32_t c) +{ + return c <= 0x1F || c == 0x7f; +} + +inline +bool is_non_ascii_codepoint(uint32_t cp) +{ + return cp >= 0x80; +} + +template +struct is_stateless + : public std::integral_constant::value && + std::is_empty::value)> +{}; + +// type traits extensions + + +namespace detail { + +// to_plain_pointer + +template inline +typename std::pointer_traits::element_type* to_plain_pointer(Pointer ptr) +{ + return (std::addressof(*ptr)); +} + +template inline +T * to_plain_pointer(T * ptr) +{ + return (ptr); +} + +// is_string_like + +template +struct is_string_like : std::false_type {}; + +template +struct is_string_like::value +>::type> : std::true_type {}; + +// is_integer_like + +template +struct is_integer_like : std::false_type {}; + +template +struct is_integer_like::value && + std::is_signed::value && + !std::is_same::value>::type> : std::true_type {}; + +// is_uinteger_like + +template +struct is_uinteger_like : std::false_type {}; + +template +struct is_uinteger_like::value && + !std::is_signed::value && + !std::is_same::value>::type> : std::true_type {}; + +// is_floating_point_like + +template +struct is_floating_point_like : std::false_type {}; + +template +struct is_floating_point_like::value>::type> : std::true_type {}; + +// is_map_like + +template +struct is_map_like : std::false_type {}; + +template +struct is_map_like::value>::type> + : std::true_type {}; + +// is_array_like +template +struct is_array_like : std::false_type {}; + +template +struct is_array_like> : std::true_type {}; + +// is_vector_like + +template +struct is_vector_like : std::false_type {}; + +template +struct is_vector_like::value && + !is_array_like::value && + !is_string_like::value && + !is_map_like::value +>::type> + : std::true_type {}; + +} + +} + +#endif diff --git a/direct-access-api/include/unicode_traits.hpp b/direct-access-api/include/unicode_traits.hpp new file mode 100644 index 0000000..affbb9b --- /dev/null +++ b/direct-access-api/include/unicode_traits.hpp @@ -0,0 +1,1463 @@ +// Copyright 2016 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/unicode_traits for latest version + +/* + * Includes code derived from Unicode, Inc decomposition code in ConvertUTF.h and ConvertUTF.c + * http://www.unicode.org/ + * + * "Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard." +*/ + +#ifndef UNICONS_UNICODE_TRAITS_HPP +#define UNICONS_UNICODE_TRAITS_HPP + +#if defined (__clang__) +#if defined(_GLIBCXX_USE_NOEXCEPT) +#define UNICONS_NOEXCEPT _GLIBCXX_USE_NOEXCEPT +#else +#define UNICONS_NOEXCEPT noexcept +#endif +#elif defined(__GNUC__) +#define UNICONS_NOEXCEPT _GLIBCXX_USE_NOEXCEPT +#elif defined(_MSC_VER) +#if _MSC_VER >= 1900 +#define UNICONS_NOEXCEPT noexcept +#else +#define UNICONS_NOEXCEPT +#endif +#else +#define UNICONS_NOEXCEPT +#endif + +#include +#include +#include +#include + +namespace unicons { + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. Source: ConvertUTF.c + */ +const uint32_t offsets_from_utf8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + +/* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. Source: ConvertUTF.c + */ +const uint8_t first_byte_mark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. Source: ConvertUTF.c + */ +const uint8_t trailing_bytes_for_utf8[256] = { + 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, + 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, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +// Some fundamental constants. Source: ConvertUTF.h +const uint32_t replacement_char = 0x0000FFFD; +const uint32_t max_bmp = 0x0000FFFF; +const uint32_t max_utf16 = 0x0010FFFF; +const uint32_t max_utf32 = 0x7FFFFFFF; +const uint32_t max_legal_utf32 = 0x0010FFFF; + +const int half_shift = 10; // used for shifting by 10 bits +const uint32_t half_base = 0x0010000UL; +const uint32_t half_mask = 0x3FFUL; + +const uint16_t sur_high_start = 0xD800; +const uint16_t sur_high_end = 0xDBFF; +const uint16_t sur_low_start = 0xDC00; +const uint16_t sur_low_end = 0xDFFF; + +inline +static bool is_continuation_byte(unsigned char ch) +{ + return (ch & 0xC0) == 0x80; +} + +inline +bool is_high_surrogate(uint32_t ch) UNICONS_NOEXCEPT +{ + return (ch >= sur_high_start && ch <= sur_high_end); +} + +inline +bool is_low_surrogate(uint32_t ch) UNICONS_NOEXCEPT +{ + return (ch >= sur_low_start && ch <= sur_low_end); +} + +inline +bool is_surrogate(uint32_t ch) UNICONS_NOEXCEPT +{ + return (ch >= sur_high_start && ch <= sur_low_end); +} + +enum class conv_flags +{ + strict = 0, + lenient +}; + +// conv_errc + +enum class conv_errc +{ + ok = 0, + over_long_utf8_sequence = 1, // over long utf8 sequence + expected_continuation_byte, // expected continuation byte + unpaired_high_surrogate, // unpaired high surrogate UTF-16 + illegal_surrogate_value, // UTF-16 surrogate values are illegal in UTF-32 + source_exhausted, // partial character in source, but hit end + source_illegal // source sequence is illegal/malformed +}; + +class Unicode_traits_error_category_impl_ + : public std::error_category +{ +public: + virtual const char* name() const UNICONS_NOEXCEPT + { + return "unicons conversion error"; + } + virtual std::string message(int ev) const + { + switch (static_cast(ev)) + { + case conv_errc::over_long_utf8_sequence: + return "Over long utf8 sequence"; + case conv_errc::expected_continuation_byte: + return "Expected continuation byte"; + case conv_errc::unpaired_high_surrogate: + return "Unpaired high surrogate UTF-16"; + case conv_errc::illegal_surrogate_value: + return "UTF-16 surrogate values are illegal in UTF-32"; + case conv_errc::source_exhausted: + return "Partial character in source, but hit end"; + case conv_errc::source_illegal: + return "Source sequence is illegal/malformed"; + default: + return ""; + break; + } + } +}; + +inline +const std::error_category& unicode_traits_error_category() +{ + static Unicode_traits_error_category_impl_ instance; + return instance; +} + +inline +std::error_code make_error_code(conv_errc result) +{ + return std::error_code(static_cast(result),unicode_traits_error_category()); +} + +// encoding_errc + +enum class encoding_errc +{ + ok = 0, + expected_u8_found_u16 = 1, + expected_u8_found_u32, + expected_u16_found_fffe, + expected_u32_found_fffe +}; + +class Encoding_errc_impl_ + : public std::error_category +{ +public: + virtual const char* name() const UNICONS_NOEXCEPT + { + return "unicons encoding error"; + } + virtual std::string message(int ev) const + { + switch (static_cast(ev)) + { + case encoding_errc::expected_u8_found_u16: + return "Expected UTF-8, found UTF-16"; + case encoding_errc::expected_u8_found_u32: + return "Expected UTF-8, found UTF-32"; + case encoding_errc::expected_u16_found_fffe: + return "Expected UTF-16, found non character"; + case encoding_errc::expected_u32_found_fffe: + return "Expected UTF-32, found non character"; + default: + return ""; + break; + } + } +}; + +inline +const std::error_category& encoding_error_category() +{ + static Encoding_errc_impl_ instance; + return instance; +} + +inline +std::error_code make_error_code(encoding_errc result) +{ + return std::error_code(static_cast(result),encoding_error_category()); +} +} + +namespace std { + template<> + struct is_error_code_enum : public true_type + { + }; + template<> + struct is_error_code_enum : public true_type + { + }; +} + +namespace unicons { + +// utf8 + +template +typename std::enable_if::value_type>::value + && sizeof(typename std::iterator_traits::value_type) == sizeof(uint8_t), + conv_errc >::type +is_legal_utf8(Iterator first, size_t length) +{ + uint8_t a; + Iterator srcptr = first+length; + switch (length) { + default: + return conv_errc::over_long_utf8_sequence; + case 4: + if (((a = (*--srcptr))& 0xC0) != 0x80) + return conv_errc::expected_continuation_byte; + // FALLTHRU + case 3: + if (((a = (*--srcptr))& 0xC0) != 0x80) + return conv_errc::expected_continuation_byte; + // FALLTHRU + case 2: + if (((a = (*--srcptr))& 0xC0) != 0x80) + return conv_errc::expected_continuation_byte; + + switch (static_cast(*first)) + { + /* no fall-through in this inner switch */ + case 0xE0: if (a < 0xA0) return conv_errc::source_illegal; break; + case 0xED: if (a > 0x9F) return conv_errc::source_illegal; break; + case 0xF0: if (a < 0x90) return conv_errc::source_illegal; break; + case 0xF4: if (a > 0x8F) return conv_errc::source_illegal; break; + default: if (a < 0x80) return conv_errc::source_illegal; + } + + // FALLTHRU + case 1: + if (static_cast(*first) >= 0x80 && static_cast(*first) < 0xC2) + return conv_errc::source_illegal; + // FALLTHRU + } + if (static_cast(*first) > 0xF4) + return conv_errc::source_illegal; + + return conv_errc(); +} + +template using void_t = void; + +template +struct is_output_iterator : std::false_type {}; + +template +struct is_output_iterator::iterator_category, + decltype(*std::declval() = std::declval())>> : std::true_type {}; + +// is_same_size fixes issue with vs2013 + +// primary template +template +struct is_same_size : std::false_type +{ +}; + +// specialization for non void types +template +struct is_same_size::value && !std::is_void::value>::type> +{ + static const bool value = (sizeof(T1) == sizeof(T2)); +}; + +template +struct is_compatible_output_iterator : std::false_type {}; + +template +struct is_compatible_output_iterator::value + && std::is_void::value_type>::value + && std::is_integral::value + && !std::is_void::value + && is_same_size::value>::type +> : std::true_type {}; + +template +struct is_compatible_output_iterator::value + && std::is_integral::value_type>::value + && is_same_size::value_type,CharT>::value>::type +> : std::true_type {}; + +template +struct is_compatible_output_iterator::value + && std::is_void::value_type>::value + && is_same_size::value>::type +> : std::true_type {}; + +// convert + +template +struct convert_result +{ + Iterator it; + conv_errc ec; +}; + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint8_t) + && is_compatible_output_iterator::value,convert_result>::type +convert(InputIt first, InputIt last, OutputIt target, conv_flags flags=conv_flags::strict) +{ + (void)flags; + + conv_errc result = conv_errc(); + while (first != last) + { + size_t length = trailing_bytes_for_utf8[static_cast(*first)] + 1; + if (length > (size_t)(last - first)) + { + return convert_result{first, conv_errc::source_exhausted}; + } + if ((result=is_legal_utf8(first, length)) != conv_errc()) + { + return convert_result{first,result}; + } + + switch (length) { + case 4: *target++ = (static_cast(*first++)); + case 3: *target++ = (static_cast(*first++)); + case 2: *target++ = (static_cast(*first++)); + case 1: *target++ = (static_cast(*first++)); + } + } + return convert_result{first,result} ; +} + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint8_t) + && is_compatible_output_iterator::value,convert_result>::type +convert(InputIt first, InputIt last, + OutputIt target, + conv_flags flags = conv_flags::strict) +{ + conv_errc result = conv_errc(); + + while (first != last) + { + uint32_t ch = 0; + unsigned short extra_bytes_to_read = trailing_bytes_for_utf8[static_cast(*first)]; + if (extra_bytes_to_read >= last - first) + { + result = conv_errc::source_exhausted; + break; + } + /* Do this check whether lenient or strict */ + if ((result=is_legal_utf8(first, extra_bytes_to_read+1)) != conv_errc()) + { + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extra_bytes_to_read) { + case 5: ch += static_cast(*first++); ch <<= 6; /* remember, illegal UTF-8 */ + case 4: ch += static_cast(*first++); ch <<= 6; /* remember, illegal UTF-8 */ + case 3: ch += static_cast(*first++); ch <<= 6; + case 2: ch += static_cast(*first++); ch <<= 6; + case 1: ch += static_cast(*first++); ch <<= 6; + case 0: ch += static_cast(*first++); + } + ch -= offsets_from_utf8[extra_bytes_to_read]; + + if (ch <= max_bmp) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_surrogate(ch) ) { + if (flags == conv_flags::strict) { + first -= (extra_bytes_to_read+1); /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } else { + *target++ = (replacement_char); + } + } else { + *target++ = ((uint16_t)ch); /* normal case */ + } + } else if (ch > max_utf16) { + if (flags == conv_flags::strict) { + result = conv_errc::source_illegal; + first -= (extra_bytes_to_read+1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } else { + *target++ = (replacement_char); + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + ch -= half_base; + *target++ = ((uint16_t)((ch >> half_shift) + sur_high_start)); + *target++ = ((uint16_t)((ch & half_mask) + sur_low_start)); + } + } + return convert_result{first,result} ; +} + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint8_t) + && is_compatible_output_iterator::value,convert_result>::type +convert(InputIt first, InputIt last, + OutputIt target, + conv_flags flags = conv_flags::strict) +{ + conv_errc result = conv_errc(); + + while (first < last) + { + uint32_t ch = 0; + unsigned short extra_bytes_to_read = trailing_bytes_for_utf8[static_cast(*first)]; + if (extra_bytes_to_read >= last - first) + { + result = conv_errc::source_exhausted; + break; + } + /* Do this check whether lenient or strict */ + if ((result=is_legal_utf8(first, extra_bytes_to_read+1)) != conv_errc()) { + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extra_bytes_to_read) { + case 5: ch += static_cast(*first++); ch <<= 6; + case 4: ch += static_cast(*first++); ch <<= 6; + case 3: ch += static_cast(*first++); ch <<= 6; + case 2: ch += static_cast(*first++); ch <<= 6; + case 1: ch += static_cast(*first++); ch <<= 6; + case 0: ch += static_cast(*first++); + } + ch -= offsets_from_utf8[extra_bytes_to_read]; + + if (ch <= max_legal_utf32) { + /* + * UTF-16 surrogate values are illegal in UTF-32, and anything + * over Plane 17 (> 0x10FFFF) is illegal. + */ + if (is_surrogate(ch) ) { + if (flags == conv_flags::strict) { + first -= (extra_bytes_to_read+1); /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } else { + *target++ = (replacement_char); + } + } else { + *target++ = (ch); + } + } else { /* i.e., ch > max_legal_utf32 */ + result = conv_errc::source_illegal; + *target++ = (replacement_char); + } + } + return convert_result{first,result} ; +} + +// utf16 + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint16_t) + && is_compatible_output_iterator::value,convert_result>::type +convert(InputIt first, InputIt last, + OutputIt target, + conv_flags flags = conv_flags::strict) { + conv_errc result = conv_errc(); + while (first < last) { + unsigned short bytes_to_write = 0; + const uint32_t byteMask = 0xBF; + const uint32_t byteMark = 0x80; + uint32_t ch = *first++; + /* If we have a surrogate pair, convert to uint32_t first. */ + if (is_high_surrogate(ch)) { + /* If the 16 bits following the high surrogate are in the first buffer... */ + if (first < last) { + uint32_t ch2 = *first; + /* If it's a low surrogate, convert to uint32_t. */ + if (ch2 >= sur_low_start && ch2 <= sur_low_end) { + ch = ((ch - sur_high_start) << half_shift) + + (ch2 - sur_low_start) + half_base; + ++first; + } else if (flags == conv_flags::strict) { /* it's an unpaired high surrogate */ + --first; /* return to the illegal value itself */ + result = conv_errc::unpaired_high_surrogate; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --first; /* return to the high surrogate */ + result = conv_errc::source_exhausted; + break; + } + } else if (flags == conv_flags::strict) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_low_surrogate(ch)) { + --first; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } + } + /* Figure out how many bytes the result will require */ + if (ch < (uint32_t)0x80) { + bytes_to_write = 1; + } else if (ch < (uint32_t)0x800) { + bytes_to_write = 2; + } else if (ch < (uint32_t)0x10000) { + bytes_to_write = 3; + } else if (ch < (uint32_t)0x110000) { + bytes_to_write = 4; + } else { + bytes_to_write = 3; + ch = replacement_char; + } + + uint8_t byte1 = 0; + uint8_t byte2 = 0; + uint8_t byte3 = 0; + uint8_t byte4 = 0; + + switch (bytes_to_write) { // note: everything falls through + case 4: byte4 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + case 3: byte3 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + case 2: byte2 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + case 1: byte1 = (uint8_t)(ch | first_byte_mark[bytes_to_write]); + } + switch (bytes_to_write) + { + case 4: + *target++ = (byte1); + *target++ = (byte2); + *target++ = (byte3); + *target++ = (byte4); + break; + case 3: + *target++ = (byte1); + *target++ = (byte2); + *target++ = (byte3); + break; + case 2: + *target++ = (byte1); + *target++ = (byte2); + break; + case 1: + *target++ = (byte1); + break; + } + } + return convert_result{first,result} ; +} + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint16_t) + && is_compatible_output_iterator::value,convert_result>::type +convert(InputIt first, InputIt last, + OutputIt target, + conv_flags flags = conv_flags::strict) +{ + conv_errc result = conv_errc(); + + while (first != last) + { + uint32_t ch = *first++; + /* If we have a surrogate pair, convert to uint32_t first. */ + if (is_high_surrogate(ch)) + { + /* If the 16 bits following the high surrogate are in the first buffer... */ + if (first < last) { + uint32_t ch2 = *first; + /* If it's a low surrogate, */ + if (ch2 >= sur_low_start && ch2 <= sur_low_end) { + *target++ = ((uint16_t)ch); + *target++ = ((uint16_t)ch2); + ++first; + } else if (flags == conv_flags::strict) { /* it's an unpaired high surrogate */ + --first; /* return to the illegal value itself */ + result = conv_errc::unpaired_high_surrogate; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --first; /* return to the high surrogate */ + result = conv_errc::source_exhausted; + break; + } + } else if (is_low_surrogate(ch)) + { + // illegal leading low surrogate + if (flags == conv_flags::strict) { + --first; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } + else + { + *target++ = ((uint16_t)ch); + } + } + else + { + *target++ = ((uint16_t)ch); + } + } + return convert_result{first,result} ; +} + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint16_t) + && is_compatible_output_iterator::value,convert_result>::type +convert(InputIt first, InputIt last, + OutputIt target, + conv_flags flags = conv_flags::strict) +{ + conv_errc result = conv_errc(); + + while (first != last) + { + uint32_t ch = *first++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (is_high_surrogate(ch)) { + /* If the 16 bits following the high surrogate are in the first buffer... */ + if (first < last) { + uint32_t ch2 = *first; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= sur_low_start && ch2 <= sur_low_end ) { + ch = ((ch - sur_high_start) << half_shift) + + (ch2 - sur_low_start) + half_base; + ++first; + } else if (flags == conv_flags::strict) { /* it's an unpaired high surrogate */ + --first; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --first; /* return to the high surrogate */ + result = conv_errc::source_exhausted; + break; + } + } else if (flags == conv_flags::strict) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_low_surrogate(ch) ) { + --first; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } + } + *target++ = (ch); + } + return convert_result{first,result} ; +} + +// utf32 + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint32_t) + && is_compatible_output_iterator::value,convert_result>::type +convert(InputIt first, InputIt last, + OutputIt target, + conv_flags flags = conv_flags::strict) +{ + conv_errc result = conv_errc(); + while (first < last) { + unsigned short bytes_to_write = 0; + const uint32_t byteMask = 0xBF; + const uint32_t byteMark = 0x80; + uint32_t ch = *first++; + if (flags == conv_flags::strict ) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_surrogate(ch)) { + --first; /* return to the illegal value itself */ + result = conv_errc::illegal_surrogate_value; + break; + } + } + /* + * Figure out how many bytes the result will require. Turn any + * illegally large UTF32 things (> Plane 17) into replacement chars. + */ + if (ch < (uint32_t)0x80) { bytes_to_write = 1; + } else if (ch < (uint32_t)0x800) { bytes_to_write = 2; + } else if (ch < (uint32_t)0x10000) { bytes_to_write = 3; + } else if (ch <= max_legal_utf32) { bytes_to_write = 4; + } else { + bytes_to_write = 3; + ch = replacement_char; + result = conv_errc::source_illegal; + } + + uint8_t byte1 = 0; + uint8_t byte2 = 0; + uint8_t byte3 = 0; + uint8_t byte4 = 0; + + switch (bytes_to_write) { + case 4: + byte4 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + // FALLTHRU + case 3: + byte3 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + // FALLTHRU + case 2: + byte2 = (uint8_t)((ch | byteMark) & byteMask); ch >>= 6; + // FALLTHRU + case 1: + byte1 = (uint8_t) (ch | first_byte_mark[bytes_to_write]); + } + + switch (bytes_to_write) + { + case 4: + *target++ = (byte1); + *target++ = (byte2); + *target++ = (byte3); + *target++ = (byte4); + break; + case 3: + *target++ = (byte1); + *target++ = (byte2); + *target++ = (byte3); + break; + case 2: + *target++ = (byte1); + *target++ = (byte2); + break; + case 1: + *target++ = (byte1); + break; + } + } + return convert_result{first,result} ; +} + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint32_t) + && is_compatible_output_iterator::value,convert_result>::type +convert(InputIt first, InputIt last, + OutputIt target, + conv_flags flags = conv_flags::strict) +{ + conv_errc result = conv_errc(); + + while (first != last) + { + uint32_t ch = *first++; + if (ch <= max_bmp) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ + if (is_surrogate(ch) ) { + if (flags == conv_flags::strict) { + --first; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } else { + *target++ = (replacement_char); + } + } else { + *target++ = ((uint16_t)ch); /* normal case */ + } + } else if (ch > max_legal_utf32) { + if (flags == conv_flags::strict) { + result = conv_errc::source_illegal; + } else { + *target++ = (replacement_char); + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + ch -= half_base; + *target++ = ((uint16_t)((ch >> half_shift) + sur_high_start)); + *target++ = ((uint16_t)((ch & half_mask) + sur_low_start)); + } + } + return convert_result{first,result} ; +} + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint32_t) + && is_compatible_output_iterator::value,convert_result>::type +convert(InputIt first, InputIt last, + OutputIt target, + conv_flags flags = conv_flags::strict) +{ + conv_errc result = conv_errc(); + + while (first != last) + { + uint32_t ch = *first++; + if (flags == conv_flags::strict ) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_surrogate(ch)) { + --first; /* return to the illegal value itself */ + result = conv_errc::illegal_surrogate_value; + break; + } + } + if (ch <= max_legal_utf32) + { + *target++ = (ch); + } + else + { + *target++ = (replacement_char); + result = conv_errc::source_illegal; + } + } + return convert_result{first,result} ; +} + +// validate + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint8_t) + ,convert_result>::type +validate(InputIt first, InputIt last) UNICONS_NOEXCEPT +{ + conv_errc result = conv_errc(); + while (first != last) + { + size_t length = trailing_bytes_for_utf8[static_cast(*first)] + 1; + if (length > (size_t)(last - first)) + { + return convert_result{first, conv_errc::source_exhausted}; + } + if ((result=is_legal_utf8(first, length)) != conv_errc()) + { + return convert_result{first,result} ; + } + first += length; + } + return convert_result{first,result} ; +} + +// utf16 + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint16_t) + ,convert_result>::type +validate(InputIt first, InputIt last) UNICONS_NOEXCEPT +{ + conv_errc result = conv_errc(); + + while (first != last) + { + uint32_t ch = *first++; + /* If we have a surrogate pair, validate to uint32_t first. */ + if (is_high_surrogate(ch)) + { + /* If the 16 bits following the high surrogate are in the first buffer... */ + if (first < last) { + uint32_t ch2 = *first; + /* If it's a low surrogate, */ + if (ch2 >= sur_low_start && ch2 <= sur_low_end) { + ++first; + } else { + --first; /* return to the illegal value itself */ + result = conv_errc::unpaired_high_surrogate; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --first; /* return to the high surrogate */ + result = conv_errc::source_exhausted; + break; + } + } else if (is_low_surrogate(ch)) + { + /* UTF-16 surrogate values are illegal in UTF-32 */ + --first; /* return to the illegal value itself */ + result = conv_errc::source_illegal; + break; + } + } + return convert_result{first,result} ; +} + + +// utf32 + + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint32_t) + ,convert_result>::type +validate(InputIt first, InputIt last) UNICONS_NOEXCEPT +{ + conv_errc result = conv_errc(); + + while (first != last) + { + uint32_t ch = *first++; + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_surrogate(ch)) { + --first; /* return to the illegal value itself */ + result = conv_errc::illegal_surrogate_value; + break; + } + if (!(ch <= max_legal_utf32)) + { + result = conv_errc::source_illegal; + } + } + return convert_result{first,result} ; +} + +// sequence + +template +class sequence +{ + Iterator first_; + size_t length_; +public: + sequence(Iterator first, size_t length) + : first_(first), length_(length) + { + } + + Iterator begin() const + { + return first_; + } + + size_t length() const + { + return length_; + } + + template ::value_type> + typename std::enable_if::type + codepoint() const UNICONS_NOEXCEPT + { + uint32_t ch = 0; + Iterator it = first_; + switch (length_) + { + default: + return replacement_char; + break; + case 4: + ch += static_cast(*it++); ch <<= 6; + // FALLTHRU + case 3: + ch += static_cast(*it++); ch <<= 6; + // FALLTHRU + case 2: + ch += static_cast(*it++); ch <<= 6; + // FALLTHRU + case 1: + ch += static_cast(*it++); + ch -= offsets_from_utf8[length_ - 1]; + break; + } + if (ch <= max_legal_utf32) + { + if (is_surrogate(ch)) + { + ch = replacement_char; + } + } + else // ch > max_legal_utf32 + { + ch = replacement_char; + } + return ch; + } + + template ::value_type> + typename std::enable_if::type + codepoint() const UNICONS_NOEXCEPT + { + if (length_ == 0) + { + return replacement_char; + } + if (length_ == 2) + { + uint32_t ch = *first_; + uint32_t ch2 = *(first_+ 1); + ch = ((ch - sur_high_start) << half_shift) + + (ch2 - sur_low_start) + half_base; + return ch; + } + else + { + return *first_; + } + } + + template ::value_type> + typename std::enable_if::type + codepoint() const UNICONS_NOEXCEPT + { + if (length_ == 0) + { + return replacement_char; + } + return *(first_); + } +}; + +// sequence_generator + +template +class sequence_generator +{ + Iterator begin_; + Iterator last_; + conv_flags flags_; + size_t length_; + conv_errc err_cd_; +public: + typedef sequence sequence_type; + + sequence_generator(Iterator first, Iterator last, + conv_flags flags = conv_flags::strict) UNICONS_NOEXCEPT + : begin_(first), last_(last), flags_(flags), + length_(0), err_cd_(conv_errc()) + { + next(); + } + + bool done() const UNICONS_NOEXCEPT + { + return err_cd_ != conv_errc() || begin_ == last_; + } + + conv_errc status() const UNICONS_NOEXCEPT + { + return err_cd_; + } + + sequence_type get() const UNICONS_NOEXCEPT + { + return sequence(begin_,length_); + } + + template ::value_type> + typename std::enable_if::type + next() UNICONS_NOEXCEPT + { + begin_ += length_; + if (begin_ != last_) + { + size_t length = trailing_bytes_for_utf8[static_cast(*begin_)] + 1; + if (length > (size_t)(last_ - begin_)) + { + err_cd_ = conv_errc::source_exhausted; + } + else if ((err_cd_ = is_legal_utf8(begin_, length)) != conv_errc()) + { + } + else + { + length_ = length; + } + } + } + + template ::value_type> + typename std::enable_if::type + next() UNICONS_NOEXCEPT + { + begin_ += length_; + if (begin_ != last_) + { + if (begin_ != last_) + { + + Iterator it = begin_; + + uint32_t ch = *it++; + /* If we have a surrogate pair, validate to uint32_t it. */ + if (is_high_surrogate(ch)) + { + /* If the 16 bits following the high surrogate are in the it buffer... */ + if (it < last_) { + uint32_t ch2 = *it; + /* If it's a low surrogate, */ + if (ch2 >= sur_low_start && ch2 <= sur_low_end) + { + ++it; + length_ = 2; + } + else + { + err_cd_ = conv_errc::unpaired_high_surrogate; + } + } + else + { + // We don't have the 16 bits following the high surrogate. + err_cd_ = conv_errc::source_exhausted; + } + } + else if (is_low_surrogate(ch)) + { + /* leading low surrogate */ + err_cd_ = conv_errc::source_illegal; + } + else + { + length_ = 1; + } + } + } + } + + template ::value_type> + typename std::enable_if::type + next() UNICONS_NOEXCEPT + { + begin_ += length_; + length_ = 1; + } +}; + +template +sequence_generator make_sequence_generator(Iterator first, Iterator last, + conv_flags flags = conv_flags::strict) +{ + return sequence_generator(first, last, flags); +} + +template +typename std::enable_if::value_type>::value + && (sizeof(typename std::iterator_traits::value_type) == sizeof(uint8_t) || sizeof(typename std::iterator_traits::value_type) == sizeof(uint16_t)), + sequence>::type +sequence_at(InputIt first, InputIt last, size_t index) +{ + sequence_generator g(first, last, unicons::conv_flags::strict); + + size_t count = 0; + while (!g.done() && count < index) + { + g.next(); + ++count; + } + return (!g.done() && count == index) ? g.get() : sequence(last,0); +} + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint32_t), + sequence>::type +sequence_at(InputIt first, InputIt last, size_t index) +{ + size_t size = std::distance(first,last); + return index < size ? sequence(first+index,1) : sequence(last,0); +} + +// u8_length + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint8_t),size_t>::type +u8_length(InputIt first, InputIt last) UNICONS_NOEXCEPT +{ + return std::distance(first,last); +} + +// utf16 + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint16_t),size_t>::type +u8_length(InputIt first, InputIt last) UNICONS_NOEXCEPT +{ + conv_flags flags = conv_flags::strict; + size_t count = 0; + for (InputIt p = first; p != last; ++p) + { + uint32_t ch = *p; + if (is_high_surrogate(ch)) { + /* If the 16 bits following the high surrogate are in the p buffer... */ + if (p < last) { + uint32_t ch2 = *(++p); + /* If it's a low surrogate, convert to uint32_t. */ + if (ch2 >= sur_low_start && ch2 <= sur_low_end) { + ch = ((ch - sur_high_start) << half_shift) + + (ch2 - sur_low_start) + half_base; + + } else if (flags == conv_flags::strict) { /* it's an unpaired high surrogate */ + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + break; + } + } else if (flags == conv_flags::strict) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (is_low_surrogate(ch)) { + break; + } + } + if (ch < (uint32_t)0x80) { + ++count; + } else if (ch < (uint32_t)0x800) { + count += 2; + } else if (ch < (uint32_t)0x10000) { + count += 3; + } else if (ch < (uint32_t)0x110000) { + count += 4; + } else { + count += 3; + } + } + return count; +} + + +// utf32 + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint32_t),size_t>::type +u8_length(InputIt first, InputIt last) UNICONS_NOEXCEPT +{ + size_t count = 0; + for (InputIt p = first; p < last; ++p) + { + uint32_t ch = *p; + if (ch < (uint32_t)0x80) { + ++count; + } else if (ch < (uint32_t)0x800) { + count += 2; + } else if (ch < (uint32_t)0x10000) { + count += 3; + } else if (ch <= max_legal_utf32) { + count += 4; + } else { + count += 3; + } + } + return count; +} + +// u32_length + +template +typename std::enable_if::value_type>::value + && (sizeof(typename std::iterator_traits::value_type) == sizeof(uint8_t) || sizeof(typename std::iterator_traits::value_type) == sizeof(uint16_t)), + size_t>::type +u32_length(InputIt first, InputIt last) UNICONS_NOEXCEPT +{ + sequence_generator g(first, last, unicons::conv_flags::strict); + + size_t count = 0; + while (!g.done()) + { + g.next(); + ++count; + } + return count; +} + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint32_t), + size_t>::type +u32_length(InputIt first, InputIt last) UNICONS_NOEXCEPT +{ + return std::distance(first,last); +} + +enum class encoding {u8,u16le,u16be,u32le,u32be,undetected}; + +template +struct detect_encoding_result +{ + Iterator it; + encoding ec; +}; + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint8_t), + detect_encoding_result>::type +detect_encoding(Iterator first, Iterator last) UNICONS_NOEXCEPT +{ + Iterator it1 = first; + if (std::distance(first,last) < 4) + { + if (std::distance(first,last) == 3) + { + Iterator it2 = ++first; + Iterator it3 = ++first; + if (static_cast(*it1) == 0xEF && static_cast(*it2) == 0xBB && static_cast(*it3) == 0xBF) + { + return detect_encoding_result{last,encoding::u8}; + } + } + return detect_encoding_result{it1,encoding::undetected}; + } + else + { + Iterator it2 = ++first; + Iterator it3 = ++first; + Iterator it4 = ++first; + + uint32_t bom = static_cast(*it1) | (static_cast(*it2) << 8) | (static_cast(*it3) << 16) | (static_cast(*it4) << 24); + if (bom == 0xFFFE0000) + { + return detect_encoding_result{it4++,encoding::u32be}; + } + else if (bom == 0x0000FEFF) + { + return detect_encoding_result{first,encoding::u32le}; + } + else if ((bom & 0xFFFF) == 0xFFFE) + { + return detect_encoding_result{it3,encoding::u16be}; + } + else if ((bom & 0xFFFF) == 0xFEFF) + { + return detect_encoding_result{it3,encoding::u16le}; + } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) + { + return detect_encoding_result{it4,encoding::u8}; + } + else + { + uint32_t pattern = (static_cast(*it1) ? 1 : 0) | (static_cast(*it2) ? 2 : 0) | (static_cast(*it3) ? 4 : 0) | (static_cast(*it4) ? 8 : 0); + switch (pattern) { + case 0x08: + return detect_encoding_result{it1,encoding::u32be}; + case 0x0A: + return detect_encoding_result{it1,encoding::u16be}; + case 0x01: + return detect_encoding_result{it1,encoding::u32le}; + case 0x05: + return detect_encoding_result{it1,encoding::u16le}; + case 0x0F: + return detect_encoding_result{it1,encoding::u8}; + default: + return detect_encoding_result{it1,encoding::undetected}; + } + } + } +} + +template +struct skip_bom_result +{ + Iterator it; + encoding_errc ec; +}; + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint8_t), + skip_bom_result>::type +skip_bom(Iterator first, Iterator last) UNICONS_NOEXCEPT +{ + auto result = unicons::detect_encoding(first,last); + switch (result.ec) + { + case unicons::encoding::u8: + return skip_bom_result{result.it,encoding_errc()}; + break; + case unicons::encoding::u16le: + case unicons::encoding::u16be: + return skip_bom_result{result.it,encoding_errc::expected_u8_found_u16}; + break; + case unicons::encoding::u32le: + case unicons::encoding::u32be: + return skip_bom_result{result.it,encoding_errc::expected_u8_found_u32}; + break; + default: + return skip_bom_result{result.it,encoding_errc()}; + break; + } +} + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint16_t), + skip_bom_result>::type +skip_bom(Iterator first, Iterator last) UNICONS_NOEXCEPT +{ + if (first == last) + { + return skip_bom_result{first,encoding_errc()}; + } + uint16_t bom = static_cast(*first); + if (bom == 0xFEFF) + { + return skip_bom_result{++first,encoding_errc()}; + } + else if (bom == 0xFFFE) + { + return skip_bom_result{last,encoding_errc::expected_u16_found_fffe}; + } + else + { + return skip_bom_result{first,encoding_errc()}; + } +} + +template +typename std::enable_if::value_type>::value && sizeof(typename std::iterator_traits::value_type) == sizeof(uint32_t), + skip_bom_result>::type +skip_bom(Iterator first, Iterator last) UNICONS_NOEXCEPT +{ + if (first == last) + { + return skip_bom_result{first,encoding_errc()}; + } + uint32_t bom = static_cast(*first); + if (bom == 0xFEFF0000) + { + return skip_bom_result{++first,encoding_errc()}; + } + else if (bom == 0xFFFE0000) + { + return skip_bom_result{last,encoding_errc::expected_u32_found_fffe}; + } + else + { + return skip_bom_result{first,encoding_errc()}; + } +} + +} + +#endif + diff --git a/direct-access-api/include/utility.hpp b/direct-access-api/include/utility.hpp new file mode 100644 index 0000000..b1b33e2 --- /dev/null +++ b/direct-access-api/include/utility.hpp @@ -0,0 +1,357 @@ +#ifndef SIMPLE_WEB_UTILITY_HPP +#define SIMPLE_WEB_UTILITY_HPP + +#include "status_code.hpp" +#include +#include +#include +#include +#include + +namespace SimpleWeb { + inline bool case_insensitive_equal(const std::string &str1, const std::string &str2) noexcept { + return str1.size() == str2.size() && + std::equal(str1.begin(), str1.end(), str2.begin(), [](char a, char b) { + return tolower(a) == tolower(b); + }); + } + class CaseInsensitiveEqual { + public: + bool operator()(const std::string &str1, const std::string &str2) const noexcept { + return case_insensitive_equal(str1, str2); + } + }; + // Based on https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x/2595226#2595226 + class CaseInsensitiveHash { + public: + std::size_t operator()(const std::string &str) const noexcept { + std::size_t h = 0; + std::hash hash; + for(auto c : str) + h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2); + return h; + } + }; + + using CaseInsensitiveMultimap = std::unordered_multimap; + + /// Percent encoding and decoding + class Percent { + public: + /// Returns percent-encoded string + static std::string encode(const std::string &value) noexcept { + static auto hex_chars = "0123456789ABCDEF"; + + std::string result; + result.reserve(value.size()); // Minimum size of result + + for(auto &chr : value) { + if(chr == ' ') + result += '+'; + else if(chr == '!' || chr == '#' || chr == '$' || (chr >= '&' && chr <= ',') || (chr >= '/' && chr <= ';') || chr == '=' || chr == '?' || chr == '@' || chr == '[' || chr == ']') + result += std::string("%") + hex_chars[chr >> 4] + hex_chars[chr & 15]; + else + result += chr; + } + + return result; + } + + /// Returns percent-decoded string + static std::string decode(const std::string &value) noexcept { + std::string result; + result.reserve(value.size() / 3 + (value.size() % 3)); // Minimum size of result + + for(std::size_t i = 0; i < value.size(); ++i) { + auto &chr = value[i]; + if(chr == '%' && i + 2 < value.size()) { + auto hex = value.substr(i + 1, 2); + auto decoded_chr = static_cast(std::strtol(hex.c_str(), nullptr, 16)); + result += decoded_chr; + i += 2; + } + else if(chr == '+') + result += ' '; + else + result += chr; + } + + return result; + } + }; + + /// Query string creation and parsing + class QueryString { + public: + /// Returns query string created from given field names and values + static std::string create(const CaseInsensitiveMultimap &fields) noexcept { + std::string result; + + bool first = true; + for(auto &field : fields) { + result += (!first ? "&" : "") + field.first + '=' + Percent::encode(field.second); + first = false; + } + + return result; + } + + /// Returns query keys with percent-decoded values. + static CaseInsensitiveMultimap parse(const std::string &query_string) noexcept { + CaseInsensitiveMultimap result; + + if(query_string.empty()) + return result; + + std::size_t name_pos = 0; + auto name_end_pos = std::string::npos; + auto value_pos = std::string::npos; + for(std::size_t c = 0; c < query_string.size(); ++c) { + if(query_string[c] == '&') { + auto name = query_string.substr(name_pos, (name_end_pos == std::string::npos ? c : name_end_pos) - name_pos); + if(!name.empty()) { + auto value = value_pos == std::string::npos ? std::string() : query_string.substr(value_pos, c - value_pos); + result.emplace(std::move(name), Percent::decode(value)); + } + name_pos = c + 1; + name_end_pos = std::string::npos; + value_pos = std::string::npos; + } + else if(query_string[c] == '=') { + name_end_pos = c; + value_pos = c + 1; + } + } + if(name_pos < query_string.size()) { + auto name = query_string.substr(name_pos, name_end_pos - name_pos); + if(!name.empty()) { + auto value = value_pos >= query_string.size() ? std::string() : query_string.substr(value_pos); + result.emplace(std::move(name), Percent::decode(value)); + } + } + + return result; + } + }; + + class HttpHeader { + public: + /// Parse header fields + static CaseInsensitiveMultimap parse(std::istream &stream) noexcept { + CaseInsensitiveMultimap result; + std::string line; + getline(stream, line); + std::size_t param_end; + while((param_end = line.find(':')) != std::string::npos) { + std::size_t value_start = param_end + 1; + if(value_start < line.size()) { + if(line[value_start] == ' ') + value_start++; + if(value_start < line.size()) + result.emplace(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - 1)); + } + + getline(stream, line); + } + return result; + } + + class FieldValue { + public: + class SemicolonSeparatedAttributes { + public: + /// Parse Set-Cookie or Content-Disposition header field value. Attribute values are percent-decoded. + static CaseInsensitiveMultimap parse(const std::string &str) { + CaseInsensitiveMultimap result; + + std::size_t name_start_pos = std::string::npos; + std::size_t name_end_pos = std::string::npos; + std::size_t value_start_pos = std::string::npos; + for(std::size_t c = 0; c < str.size(); ++c) { + if(name_start_pos == std::string::npos) { + if(str[c] != ' ' && str[c] != ';') + name_start_pos = c; + } + else { + if(name_end_pos == std::string::npos) { + if(str[c] == ';') { + result.emplace(str.substr(name_start_pos, c - name_start_pos), std::string()); + name_start_pos = std::string::npos; + } + else if(str[c] == '=') + name_end_pos = c; + } + else { + if(value_start_pos == std::string::npos) { + if(str[c] == '"' && c + 1 < str.size()) + value_start_pos = c + 1; + else + value_start_pos = c; + } + else if(str[c] == '"' || str[c] == ';') { + result.emplace(str.substr(name_start_pos, name_end_pos - name_start_pos), Percent::decode(str.substr(value_start_pos, c - value_start_pos))); + name_start_pos = std::string::npos; + name_end_pos = std::string::npos; + value_start_pos = std::string::npos; + } + } + } + } + if(name_start_pos != std::string::npos) { + if(name_end_pos == std::string::npos) + result.emplace(str.substr(name_start_pos), std::string()); + else if(value_start_pos != std::string::npos) { + if(str.back() == '"') + result.emplace(str.substr(name_start_pos, name_end_pos - name_start_pos), Percent::decode(str.substr(value_start_pos, str.size() - 1))); + else + result.emplace(str.substr(name_start_pos, name_end_pos - name_start_pos), Percent::decode(str.substr(value_start_pos))); + } + } + + return result; + } + }; + }; + }; // namespace SimpleWeb + + class RequestMessage { + public: + /// Parse request line and header fields + static bool parse(std::istream &stream, std::string &method, std::string &path, std::string &query_string, std::string &version, CaseInsensitiveMultimap &header) noexcept { + header.clear(); + std::string line; + getline(stream, line); + std::size_t method_end; + if((method_end = line.find(' ')) != std::string::npos) { + method = line.substr(0, method_end); + + std::size_t query_start = std::string::npos; + std::size_t path_and_query_string_end = std::string::npos; + for(std::size_t i = method_end + 1; i < line.size(); ++i) { + if(line[i] == '?' && (i + 1) < line.size()) + query_start = i + 1; + else if(line[i] == ' ') { + path_and_query_string_end = i; + break; + } + } + if(path_and_query_string_end != std::string::npos) { + if(query_start != std::string::npos) { + path = line.substr(method_end + 1, query_start - method_end - 2); + query_string = line.substr(query_start, path_and_query_string_end - query_start); + } + else + path = line.substr(method_end + 1, path_and_query_string_end - method_end - 1); + + std::size_t protocol_end; + if((protocol_end = line.find('/', path_and_query_string_end + 1)) != std::string::npos) { + if(line.compare(path_and_query_string_end + 1, protocol_end - path_and_query_string_end - 1, "HTTP") != 0) + return false; + version = line.substr(protocol_end + 1, line.size() - protocol_end - 2); + } + else + return false; + + header = HttpHeader::parse(stream); + } + else + return false; + } + else + return false; + return true; + } + }; + + class ResponseMessage { + public: + /// Parse status line and header fields + static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept { + header.clear(); + std::string line; + getline(stream, line); + std::size_t version_end = line.find(' '); + if(version_end != std::string::npos) { + if(5 < line.size()) + version = line.substr(5, version_end - 5); + else + return false; + if((version_end + 1) < line.size()) + status_code = line.substr(version_end + 1, line.size() - (version_end + 1) - 1); + else + return false; + + header = HttpHeader::parse(stream); + } + else + return false; + return true; + } + }; +} // namespace SimpleWeb + +#ifdef __SSE2__ +#include +namespace SimpleWeb { + inline void spin_loop_pause() noexcept { _mm_pause(); } +} // namespace SimpleWeb +// TODO: need verification that the following checks are correct: +#elif defined(_MSC_VER) && _MSC_VER >= 1800 && (defined(_M_X64) || defined(_M_IX86)) +#include +namespace SimpleWeb { + inline void spin_loop_pause() noexcept { _mm_pause(); } +} // namespace SimpleWeb +#else +namespace SimpleWeb { + inline void spin_loop_pause() noexcept {} +} // namespace SimpleWeb +#endif + +namespace SimpleWeb { + /// Makes it possible to for instance cancel Asio handlers without stopping asio::io_service + class ScopeRunner { + /// Scope count that is set to -1 if scopes are to be canceled + std::atomic count; + + public: + class SharedLock { + friend class ScopeRunner; + std::atomic &count; + SharedLock(std::atomic &count) noexcept : count(count) {} + SharedLock &operator=(const SharedLock &) = delete; + SharedLock(const SharedLock &) = delete; + + public: + ~SharedLock() noexcept { + count.fetch_sub(1); + } + }; + + ScopeRunner() noexcept : count(0) {} + + /// Returns nullptr if scope should be exited, or a shared lock otherwise + std::unique_ptr continue_lock() noexcept { + long expected = count; + while(expected >= 0 && !count.compare_exchange_weak(expected, expected + 1)) + spin_loop_pause(); + + if(expected < 0) + return nullptr; + else + return std::unique_ptr(new SharedLock(count)); + } + + /// Blocks until all shared locks are released, then prevents future shared locks + void stop() noexcept { + long expected = 0; + while(!count.compare_exchange_weak(expected, -1)) { + if(expected < 0) + return; + expected = 0; + spin_loop_pause(); + } + } + }; +} // namespace SimpleWeb + +#endif // SIMPLE_WEB_UTILITY_HPP diff --git a/direct-access-api/include/vcanhandler.hpp b/direct-access-api/include/vcanhandler.hpp index 5a5539d..8ddaee8 100644 --- a/direct-access-api/include/vcanhandler.hpp +++ b/direct-access-api/include/vcanhandler.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include "common.hpp" #include "dbmanager.hpp" #include "obeclient.hpp" @@ -41,7 +42,6 @@ #define MAX_RAND_TRY_LIMIT 10 #define MIN_VCAN_NO 0 #define VCAN_NO_RANGE 100 -#define BUFFER_SIZE 100 using namespace std; @@ -74,6 +74,20 @@ class vcanlistener { void vcan_read(string vcan_name); e_result vcan_socket_init(); void vcan_write_frame(struct can_frame* frame); + void vcan_close(int can_sock); +}; + +class vcanwriter { // ToDo: add thread safety + + private: + static unordered_map vcan_conn_map; + static e_result vcan_socket_init(string vcan_name); + static int get_connection(string vcan_name); + public: + vcanwriter(); + ~vcanwriter(); + static void vcan_write_frame(string vcan_name, struct can_frame* frame); + static void vcan_close(string vcan_name); }; #endif // VCANHANDLER_H diff --git a/direct-access-api/include/version.hpp b/direct-access-api/include/version.hpp new file mode 100644 index 0000000..19a2888 --- /dev/null +++ b/direct-access-api/include/version.hpp @@ -0,0 +1,50 @@ +// Copyright 2017 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_VERSION_HPP +#define JSONCONS_VERSION_HPP + +#include + +namespace jsoncons { + +struct versioning_info +{ + versioning_info(unsigned int major, + unsigned int minor, + unsigned int patch) + : major_(major), + minor_(minor), + patch_(patch) + {} + + unsigned int const major_; + unsigned int const minor_; + unsigned int const patch_; + + friend std::ostream& operator<<(std::ostream& os, const versioning_info& ver) + { + os << ver.major_ << '.' + << ver.minor_ << '.' + << ver.patch_; + return os; + } + + versioning_info(const versioning_info&) = default; + versioning_info() = delete; + versioning_info& operator=(const versioning_info&) = delete; +}; + +inline +versioning_info version() +{ + static versioning_info ver(0, 105, 0); + return ver; +} + +} + +#endif diff --git a/direct-access-api/include/writer.hpp b/direct-access-api/include/writer.hpp new file mode 100644 index 0000000..c832f06 --- /dev/null +++ b/direct-access-api/include/writer.hpp @@ -0,0 +1,155 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_DETAIL_WRITERS_HPP +#define JSONCONS_DETAIL_WRITERS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jsoncons { namespace detail { + +template +class ostream_buffered_writer +{ +public: + typedef basic_string_view_ext string_view_type; + typedef CharT char_type; + typedef std::basic_ostream output_type; +private: + static const size_t default_buffer_length = 16384; + + std::basic_ostream& os_; + std::vector buffer_; + CharT * begin_buffer_; + const CharT* end_buffer_; + CharT* p_; + + // Noncopyable and nonmoveable + ostream_buffered_writer(const ostream_buffered_writer&) = delete; + ostream_buffered_writer& operator=(const ostream_buffered_writer&) = delete; + +public: + ostream_buffered_writer(std::basic_ostream& os) + : os_(os), buffer_(default_buffer_length), begin_buffer_(buffer_.data()), end_buffer_(begin_buffer_+buffer_.size()), p_(begin_buffer_) + { + } + ostream_buffered_writer(std::basic_ostream& os, size_t buflen) + : os_(os), buffer_(buflen), begin_buffer_(buffer_.data()), end_buffer_(begin_buffer_+buffer_.size()), p_(begin_buffer_) + { + } + ~ostream_buffered_writer() + { + os_.write(begin_buffer_, buffer_length()); + os_.flush(); + } + + void flush() + { + os_.write(begin_buffer_, buffer_length()); + p_ = buffer_.data(); + } + + void write(const CharT* s, size_t length) + { + size_t diff = end_buffer_ - p_; + if (diff >= length) + { + std::memcpy(p_, s, length*sizeof(CharT)); + p_ += length; + } + else + { + os_.write(begin_buffer_, buffer_length()); + os_.write(s,length); + p_ = begin_buffer_; + } + } + + void write(const string_view_type& s) + { + write(s.data(),s.length()); + } + + void put(CharT ch) + { + if (p_ < end_buffer_) + { + *p_++ = ch; + } + else + { + os_.write(begin_buffer_, buffer_length()); + p_ = begin_buffer_; + put(ch); + } + } +private: + + size_t buffer_length() const + { + return p_ - begin_buffer_; + } +}; + +template +class string_writer +{ +public: + typedef basic_string_view_ext string_view_type; + typedef CharT char_type; + typedef std::basic_string output_type; +private: + std::basic_string& s_; + + // Noncopyable and nonmoveable + string_writer(const string_writer&) = delete; + string_writer& operator=(const string_writer&) = delete; +public: + + string_writer(std::basic_string& s) + : s_(s) + { + } + + void flush() + { + } + + void write(const CharT* s, size_t length) + { + s_.append(s,length); + } + + void write(const string_view_type& s) + { + s_.append(s.data(),s.length()); + } + + void put(CharT ch) + { + s_.push_back(ch); + } +}; + +}} + +#endif diff --git a/direct-access-api/src/dbmanager.cpp b/direct-access-api/src/dbmanager.cpp index 2f5335f..ef83e79 100644 --- a/direct-access-api/src/dbmanager.cpp +++ b/direct-access-api/src/dbmanager.cpp @@ -1,17 +1,3 @@ -/* - * ****************************************************************************** - * Copyright (c) 2019 KocSistem Bilgi ve Iletisim Hizmetleri A.S.. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/org/documents/epl-2.0/index.php - * - * Contributors: - * Initial functionality - Ismail Burak Oksuzoglu, Erdem Ergen, Aslihan Cura (KocSistem Bilgi ve Iletisim Hizmetleri A.S.) - * ***************************************************************************** - */ - #include "dbmanager.hpp" dbmanager::dbmanager() { @@ -37,7 +23,7 @@ void dbmanager::insert_into_read_table(string vcan_name, int canid) { cout << "SQL error: " << sqlite3_errmsg(db) << endl; sqlite3_free(zErrMsg); } else { - cout << "SQL: insert_into_vcan_table successful" << endl; + cout << "SQL: insert_into_read_table successful" << endl; } } @@ -52,7 +38,7 @@ void dbmanager::insert_into_write_table(string vcan_name, int canid) { cout << "SQL error: " << sqlite3_errmsg(db) << endl; sqlite3_free(zErrMsg); } else { - cout << "SQL: insert_into_vcan_table successful" << endl; + cout << "SQL: insert_into_write_table successful" << endl; } } @@ -208,15 +194,65 @@ vector dbmanager::select_vcans_from_write_table(int canid) { } void dbmanager::store_read_rules(string vcan_name, json can_json) { - auto perms = can_json["read"].as>(); + auto perms = can_json["can_id_read"].as>(); for (auto r : perms) { this->insert_into_read_table(vcan_name, stoi(r)); } } void dbmanager::store_write_rules(string vcan_name, json can_json) { - auto perms = can_json["write"].as>(); + auto perms = can_json["can_id_write"].as>(); for (auto r : perms) { this->insert_into_write_table(vcan_name, stoi(r)); } } + +int maintest() { + dbmanager db; + + string vcan_name = "vcan0"; + int canid = 1; + vector vcan_list; + vector read_list; + vector write_list; + + /* + db.insert_into_read_table(vcan_name, 2); + db.insert_into_write_table(vcan_name, 2); + db.insert_into_read_table(vcan_name, 3); + db.insert_into_write_table(vcan_name, 3); + db.insert_into_read_table(vcan_name, 4); + */ + + vcan_list = db.get_vcan_names(); + + for (string vcan : vcan_list) { + cout << "vcan: " << vcan << endl; + } + + read_list = db.get_read_rules(vcan_name); + + for (int r : read_list) { + cout << "read: " << r << endl; + } + + write_list = db.get_write_rules(vcan_name); + + for (int w : write_list) { + cout << "write: " << w << endl; + } + + + vcan_list = db.select_vcans_from_read_table(canid); + + for (string vcan : vcan_list) { + cout << "vcanr: " << vcan << endl; + } + vcan_list = db.select_vcans_from_write_table(canid); + for (string vcan : vcan_list) { + cout << "vcanw: " << vcan << endl; + } + + db.delete_vcan(vcan_name); + +} diff --git a/direct-access-api/src/directAccessProcessor.cpp b/direct-access-api/src/directAccessProcessor.cpp index 84a3a15..bf36fc9 100644 --- a/direct-access-api/src/directAccessProcessor.cpp +++ b/direct-access-api/src/directAccessProcessor.cpp @@ -28,58 +28,16 @@ directAccessProcessor::directAccessProcessor(class authenticator* vdator) { vcanHandler = new vcanhandler(); } -string directAccessProcessor::processCreate(class wschannel& channel, - string rchannel) { -#ifdef DEBUG -// cout<< "Create :: path received from client = "<< rchannel <createVcan(); - - if (vcan == "-1") { // failed to create vcan - json result; - json error; - result["action"] = "create"; - error["reason"] = "Failed to create VCAN"; - result["error"] = error; - result["timestamp"] = time(NULL); +string directAccessProcessor::processCreate(json perm_json, string rchannel) { - std::stringstream ss; - ss << pretty_print(result); - return ss.str(); - } - // create vcan entry on db - dbmanager db; - db.insert_into_vcan_table(vcan); - - // get read/write permissions from token - json can_json = tokenValidator->get_payload(this->ks_token); - db.store_read_rules(vcan, can_json); - db.store_write_rules(vcan, can_json); - - // start vcan listener - vcanHandler->start_vcan_listener(vcan); - - json res; - res["action"] = "create"; - res["vcan"] = vcan; - res["rcan"] = rchannel; - res["timestamp"] = time(NULL); - stringstream ss; - ss << pretty_print(res); - cout << " Created VCAN Channel " << res["vcan"].as() - << " for Real CAN " << rchannel - << " with access on : " << channel.accesses << endl; - return ss.str(); - } else { + string vcan = vcanHandler->createVcan(); + + if (vcan == "-1") { // failed to create vcan json result; json error; result["action"] = "create"; - - error["reason"] = "not authorized to create " + rchannel; - + error["reason"] = "Failed to create VCAN"; result["error"] = error; result["timestamp"] = time(NULL); @@ -87,6 +45,33 @@ string directAccessProcessor::processCreate(class wschannel& channel, ss << pretty_print(result); return ss.str(); } + + // create vcan entry on db + dbmanager db; + db.insert_into_vcan_table(vcan); + + // set read/write permissions + json can_json = perm_json["roles"]; + + db.store_read_rules(vcan, can_json); + db.store_write_rules(vcan, can_json); + + + // start vcan listener + vcanHandler->start_vcan_listener(vcan); + + json res; + res["action"] = "create"; + res["vcan"] = vcan; + res["rcan"] = rchannel; + res["timestamp"] = time(NULL); + stringstream ss; + ss << pretty_print(res); + cout << " Created VCAN Channel " << res["vcan"].as() + << " for Real CAN " << rchannel + << " with access on : " << can_json << endl; + return ss.str(); + } string directAccessProcessor::processDelete(string vchannel) { @@ -110,89 +95,128 @@ string directAccessProcessor::processDelete(string vchannel) { dbmanager db; db.delete_vcan(vchannel); + answer["vcan"] = vchannel; + + std::stringstream ss; ss << pretty_print(answer); return ss.str(); } -string directAccessProcessor::processAuthorize(string token, - class wschannel& channel) { - int ttl = tokenValidator->validate(channel, token); - if (ttl == -1) { - json result; - json error; - result["action"] = "authorize"; - error["number"] = 401; - error["reason"] = "Invalid Token"; - error["message"] = "Check the JWT token passed"; +string directAccessProcessor::processQuery(string req_json, + class wschannel& channel) { + + json result; + json error; + json root; + root = json::parse(req_json); + string action = root["action"].as(); + string clientid = root["appid"].as(); + string secret = root["secret"].as(); + root["daaid"] = "daa_client"; + root["daasecret"] = "5f84495a-72f0-4414-be4f-5eec082e5732"; + root["api"] = "1"; + + cout << "incoming: " << root << endl; + + // create socket + int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) { + printf("Failed to create AF_UNIX socket\n"); + error["message"] = "Failed to process request."; result["error"] = error; result["timestamp"] = time(NULL); std::stringstream ss; ss << pretty_print(result); return ss.str(); + } + struct sockaddr_un addr; - } else { - this->ks_token = token; - json result; - result["action"] = "authorize"; - result["TTL"] = ttl; + memset(&addr, '0', sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, perm_mngt_srv_address.c_str(), sizeof(addr.sun_path)-1); + + // connect + int conn_status = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)); + if (conn_status < 0) { + printf("Failed to connect to permission mngt server: %d\n", conn_status); + error["message"] = "Failed to process request."; + result["error"] = error; result["timestamp"] = time(NULL); std::stringstream ss; ss << pretty_print(result); return ss.str(); } -} + printf("Connected to permission mngt server\n"); -string directAccessProcessor::processQuery(string req_json, - class wschannel& channel) { - json root; - string response; - root = json::parse(req_json); - string action = root["action"].as(); + char buffer[PERM_MNGT_BUFFER_SIZE] = {}; - if (action == "authorize") { - string token = root["tokens"].as(); + memset(&buffer, '\0', sizeof(buffer)); - // cout << "vsscommandprocessor::processQuery: authorize query with token = - // " << token << endl; - response = processAuthorize(token, channel); - } else { - string can_channel = root["channel"].as(); + string jsonstr; + root.dump(jsonstr); + char *msg = &jsonstr[0]; - bool hasAccess = accessValidator->checkAccess(channel, can_channel); + if (write(sockfd, msg, strlen(msg)) < 0){ + printf("Failed to send request to permission mngt server\n"); + error["message"] = "Failed to process request."; + result["error"] = error; + result["timestamp"] = time(NULL); - if (!hasAccess) { - json result; - json error; - result["action"] = action; - error["number"] = 403; - error["reason"] = "Forbidden"; - error["message"] = "Not authorized to access resource"; + std::stringstream ss; + ss << pretty_print(result); + return ss.str(); - result["error"] = error; - result["timestamp"] = time(NULL); + }else{ + printf("sent\n"); + int r = read(sockfd, buffer, PERM_MNGT_BUFFER_SIZE); + if (r < 0) { + printf("Failed to read from permission mngt server\n"); + } else if (r == 0) { + printf("Disconnected from permission mngt server \n"); + connection_status = -1; + } else { + printf("permission mngt: %s\n", buffer); + + try{ + //parse response + json perm_json = json::parse(buffer); + + if (action == "create") { + cout << "directAccessProcessor::processQuery: Create action: " << action << endl; + string can_channel = root["channel"].as(); + result = processCreate(perm_json, can_channel); + } else if (action == "delete") { + cout << "directAccessProcessor::processQuery: Delete action: " << action << endl; + string vcan_channel = root["vchannel"].as(); + result = processDelete(vcan_channel); + } else { + cout << "vsscommandprocessor::processQuery: Unknown action: " << action << endl; + error["message"] = "Unknown action"; + result["error"] = error; + result["timestamp"] = time(NULL); + + } + } + catch(...){ + cout << "Exception occured trying to handle permission mngt response "<< endl; + + error["message"] = "Failed to process request."; + result["error"] = error; + result["timestamp"] = time(NULL); + } + + memset(&buffer, '\0', sizeof(buffer)); std::stringstream ss; ss << pretty_print(result); - response = ss.str(); - - } else if (action == "create") { - // cout << "vsscommandprocessor::processQuery: create query for " << - // can_channel << endl; - response = processCreate(channel, can_channel); - } else if (action == "delete") { - // cout << "vsscommandprocessor::processQuery: delete query for " << - // can_channel << endl; - response = processDelete(can_channel); - } else { - // cout << "vsscommandprocessor::processQuery: Unknown action " << action - // << endl; + return ss.str(); } + } - return response; } diff --git a/direct-access-api/src/obeclient.cpp b/direct-access-api/src/obeclient.cpp index 8aeda07..cafff23 100644 --- a/direct-access-api/src/obeclient.cpp +++ b/direct-access-api/src/obeclient.cpp @@ -1,27 +1,19 @@ -/* - * ****************************************************************************** - * Copyright (c) 2019 KocSistem Bilgi ve Iletisim Hizmetleri A.S.. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/org/documents/epl-2.0/index.php - * - * Contributors: - * Initial functionality - Ismail Burak Oksuzoglu, Erdem Ergen, Aslihan Cura (KocSistem Bilgi ve Iletisim Hizmetleri A.S.) - * ***************************************************************************** - */ - #include "obeclient.hpp" #include "common.hpp" int sockfd; const char *obe_server_ip = "127.0.0.1"; //"192.168.3.40"; // -int obe_port = 6789; +int obe_port = 6786; int connection_status; +pthread_mutex_t deque_mutex; +deque obe_deque; + + e_result obe_client_init() { pthread_t obe_thread; + pthread_t obe_queue_thread; + struct sockaddr_in serv_addr; @@ -41,16 +33,31 @@ e_result obe_client_init() { connection_status = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); if (connection_status < 0) { - printf("Failed to connect to server\n"); + printf("Failed to connect to server %s\n", obe_server_ip); return FAILURE; } printf("Connected to server\n"); + + + if (pthread_create(&obe_queue_thread, NULL, parse_obe_queue, (void *)1) != 0) { + perror("obe_queue_thread thread creation failure !"); + return FAILURE; + }else{ + printf("obe_queue_thread created\n"); + } + + if (pthread_create(&obe_thread, NULL, obe_listen, (void *)1) != 0) { - perror("vcan_read thread creation failure !"); + perror("obe_thread thread creation failure !"); return FAILURE; + }else{ + printf("obe_thread created\n"); + } + + return SUCCESS; } @@ -83,6 +90,9 @@ e_result obe_client_connect() { void *obe_listen(void *notused) { char buffer[BUFFER_SIZE] = {}; + char temp_buffer[BUFFER_SIZE] = {}; + + int counter = 1; while (1) { memset(&buffer, '\0', sizeof(buffer)); @@ -103,12 +113,17 @@ void *obe_listen(void *notused) { printf("Disconnected from daa_obe server \n"); connection_status = -1; } else { - printf("daa_obe: %s\n", buffer); + //printf("daa_obe: %s\n", buffer); // handle msg - parse_buffer(buffer); + pthread_mutex_lock(&deque_mutex); + string bufferstr(buffer); + obe_deque.push_back(bufferstr); + pthread_mutex_unlock(&deque_mutex); memset(&buffer, '\0', sizeof(buffer)); + + //printf("i %i\n", counter++); } } } @@ -150,19 +165,20 @@ char *prepare_outgoing_msg(char *daa_msg, struct can_frame *frame) { strcat(daa_msg, "}"); - printf("%s\n", daa_msg); + // printf("outgoing %s\n", daa_msg); return daa_msg; } -void handle_incoming_msg(char *daa_msg) { +void handle_incoming_msg(char *framestr) { + const char *delimiter = "|"; struct can_frame frame; - char *tempstr = strdup(daa_msg); + char *tempstr = strdup(framestr); frame.can_id = strtol(strsep(&tempstr, delimiter), NULL, 16); - frame.can_dlc = strtol(strsep(&tempstr, delimiter), NULL, 16); + frame.can_dlc = 8; frame.data[0] = strtol(strsep(&tempstr, delimiter), NULL, 16); frame.data[1] = strtol(strsep(&tempstr, delimiter), NULL, 16); frame.data[2] = strtol(strsep(&tempstr, delimiter), NULL, 16); @@ -172,55 +188,85 @@ void handle_incoming_msg(char *daa_msg) { frame.data[6] = strtol(strsep(&tempstr, delimiter), NULL, 16); frame.data[7] = strtol(strsep(&tempstr, delimiter), NULL, 16); - printf("write to vcan:\n"); - // check which vcans have read permissions - dbmanager db; - vector vcan_list = - db.select_vcans_from_read_table(frame.can_id & 0x1FFFFFFF); - for (string vcan : vcan_list) { + //printf("handle inc: %i\n", counter++); + // check which vcans have read permissions + // dbmanager db; + // vector vcan_list = + // db.select_vcans_from_read_table(frame.can_id & 0x1FFFFFFF); + //for (string vcan : vcan_list) { // write to vcan; - cout << "write to vcan: " << vcan << endl; - vcanlistener listener(vcan); - listener.vcan_socket_init(); - listener.vcan_write_frame(&frame); + // cout << "write to vcan: " << vcan << endl; + // vcanlistener listener(vcan); + // listener.vcan_socket_init(); + // listener.vcan_write_frame(&frame); + + vcanwriter::vcan_write_frame("vcanh0", &frame); + usleep(1000); + + //} +} + +void handle_incoming_msgs(string daa_msgs) { + + const char *delimiter = ","; + char *framestr; + char *tempstr = strdup(daa_msgs.c_str()); + + while( (framestr = strsep(&tempstr,delimiter)) != NULL ){ + handle_incoming_msg(framestr); } } +void *parse_obe_queue(void *notused){ + + string buffer; + + while(1){ + + if(obe_deque.empty() == 0){ + + pthread_mutex_lock(&deque_mutex); + buffer = obe_deque.front(); + obe_deque.pop_front(); + pthread_mutex_unlock(&deque_mutex); + + printf("b %i\n", buffer.size()); + parse_buffer(buffer); + + } + usleep(100); + } + +} + + +void parse_buffer(string buffer) { -void parse_buffer(char *msg) { - int i = 0; size_t sindex = -1; size_t eindex = -1; - char daa_msg[BUFFER_SIZE]; + // locate msg start-endpoints - while (msg[i] != '\0') { - if (msg[i] == '{') sindex = i + 1; + sindex = buffer.find_first_of("{"); + eindex = buffer.find_first_of("}"); - if (msg[i] == '}') { - eindex = i; - break; - } - i++; - } // check endpoints - if (sindex > 0 && eindex > 2) { - memcpy(daa_msg, &msg[sindex], eindex - sindex); - daa_msg[eindex - 1] = '\0'; + if (sindex >= 0 && eindex > 2) { - handle_incoming_msg(daa_msg); + string daa_msgs = buffer.substr(sindex+1, eindex-sindex-1); - daa_msg[0] = '\0'; + handle_incoming_msgs(daa_msgs); - if (eindex + 1 != strlen(msg)) - parse_buffer(&msg[eindex + 1]); + if (eindex + 1 != buffer.size()) + parse_buffer(buffer.substr(eindex+1)); else { - printf("...\n"); + // printf("...\n"); return; } } else { - printf("...\n"); + // printf("...\n"); return; } } + diff --git a/direct-access-api/src/vcanhandler.cpp b/direct-access-api/src/vcanhandler.cpp index 387de83..698fbd5 100644 --- a/direct-access-api/src/vcanhandler.cpp +++ b/direct-access-api/src/vcanhandler.cpp @@ -1,17 +1,3 @@ -/* - * ****************************************************************************** - * Copyright (c) 2019 KocSistem Bilgi ve Iletisim Hizmetleri A.S.. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/org/documents/epl-2.0/index.php - * - * Contributors: - * Initial functionality - Ismail Burak Oksuzoglu, Erdem Ergen, Aslihan Cura (KocSistem Bilgi ve Iletisim Hizmetleri A.S.) - * ***************************************************************************** - */ - #include "vcanhandler.hpp" vcanhandler::vcanhandler() {} @@ -160,6 +146,8 @@ e_result vcanlistener::vcan_socket_init() { return SUCCESS; } +void vcanlistener::vcan_close(int can_sock) { close(can_sock); } + void vcanlistener::vcan_read(string vcan_name) { dbmanager db; cout << "Starting to listen vcan bus: " << vcan_name << endl; @@ -193,7 +181,7 @@ void vcanlistener::vcan_read(string vcan_name) { printf("%s -> frame sent to daa_obe\n", can_msg); } else { - std::cout << "no write permissions" << endl; + // std::cout << "no write permissions" << endl; } memset(&can_msg, '\0', sizeof(can_msg)); @@ -238,3 +226,69 @@ void vcanhandler::start_all_listeners() { sleep(1); } } + +vcanwriter::vcanwriter() {} +vcanwriter::~vcanwriter() {} + +unordered_map vcanwriter::vcan_conn_map; + +e_result vcanwriter::vcan_socket_init(string vcan_name) { + struct sockaddr_can addr; + struct ifreq ifr; + int can_sock; + const char *ifname = vcan_name.c_str(); + + if ((can_sock = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + perror("Error while opening vcan socket"); + return FAILURE; + } + + strcpy(ifr.ifr_name, ifname); + ioctl(can_sock, SIOCGIFINDEX, &ifr); + + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + + printf("%s at index %d\n", ifname, ifr.ifr_ifindex); + + if (bind(can_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("Error in vcan socket bind"); + return FAILURE; + } + + // add connection map + vcan_conn_map[vcan_name] = can_sock; + + return SUCCESS; +} + +int vcanwriter::get_connection(string vcan_name) { + if (vcan_conn_map.find(vcan_name) == vcan_conn_map.end()) { + printf("No vcan connection found for %s, creating new connection...\n", + vcan_name.c_str()); + if (vcan_socket_init(vcan_name) == FAILURE) return -1; + } + + return vcan_conn_map[vcan_name]; +} + +void vcanwriter::vcan_write_frame(string vcan_name, struct can_frame *frame) { + static int counter = 1; + int can_sock = get_connection(vcan_name); + if (write(can_sock, frame, sizeof(struct can_frame)) < 0) + printf("Failed to write to vcan bus = %s \n", vcan_name.c_str()); + else { + // printf("Frame sent to vcan bus = %s \n", vcan_name.c_str()); + printf("v %i\n", counter++); + } +} + +void vcanwriter::vcan_close(string vcan_name) { + if (vcan_conn_map.find(vcan_name) == vcan_conn_map.end()) { + printf("Socket not found\n"); + } else { + int can_sock = vcan_conn_map[vcan_name]; + close(can_sock); + vcan_conn_map.erase(vcan_name); + } +} diff --git a/direct-access-api/src/wsserver.cpp b/direct-access-api/src/wsserver.cpp index 240a838..4e4f187 100644 --- a/direct-access-api/src/wsserver.cpp +++ b/direct-access-api/src/wsserver.cpp @@ -205,6 +205,7 @@ void *startWSServer(void *arg) { wserver->startServer(""); } * @return */ int main(int argc, char *argv[]) { + cout << "Starting Direct-Access-Api app... [v20190403]" << endl; wsserver server(PORT, false); pthread_t startWSServer_thread; @@ -220,8 +221,10 @@ int main(int argc, char *argv[]) { vcanhandler handler; handler.start_all_listeners(); - // start obe client - obe_client_init(); +// // start obe client +// obe_client_init(); - getchar(); + //getchar(); + while(1) + sleep(1000); } diff --git a/permision-mngt/keycloakconnector.py b/permision-mngt/keycloakconnector.py index ad3df8c..e4eb80d 100644 --- a/permision-mngt/keycloakconnector.py +++ b/permision-mngt/keycloakconnector.py @@ -9,9 +9,13 @@ # Contributors: Robert Bosch GmbH # This file takes care of the communication with keycloak for retrieving the Tokens. +from json import JSONDecodeError + import OpenSSL from OpenSSL.crypto import FILETYPE_PEM from keycloak import KeycloakOpenID +from requests import get +import json class Keycloakconnector: @@ -24,6 +28,8 @@ def __init__(self, serverurl, realm, clientid, secret): client_secret_key=secret, verify=True) config_well_know = self.keycloak_openid.well_know() + self.serverurl = serverurl + self.realm = realm # print(config_well_know) def getToken(self, appID, api): @@ -44,3 +50,43 @@ def getJWTPublickey(self): pubkey = x509.get_pubkey() pubkey = OpenSSL.crypto.dump_publickey(FILETYPE_PEM, pubkey).decode("utf-8") return pubkey + + def getClientRoles(self, clientid, daa_token): + api_call_headers = {'Authorization': 'Bearer ' + daa_token, 'cache-control': "no-cache", + "Content-Type": "application/json", "charset": "utf-8"} + + roles = "" + + try: + # get keycloak client representation (needed to get the value of id_of_client parameter) + client_url = self.serverurl + "admin/realms/" + self.realm + "/clients?clientId=" + clientid + client_rep = get(client_url, headers=api_call_headers) + client_json = json.loads(client_rep.text) + id_of_client = client_json[0]["id"] + print(id_of_client) + + # get client roles + roles_url = self.serverurl + "admin/realms/" + self.realm + "/clients/" + id_of_client + "/roles" + roles_rep = get(roles_url, headers=api_call_headers) + keycloak_roles = json.loads(roles_rep.text) + + if "can_id_read" in keycloak_roles[0]: + can_id_read_array = keycloak_roles[0]["description"].split(",") + can_id_write_array = keycloak_roles[1]["description"].split(",") + else: + can_id_read_array = keycloak_roles[1]["description"].split(",") + can_id_write_array = keycloak_roles[0]["description"].split(",") + + roles = json.loads('{"can_id_read": "", "can_id_write" : ""}') + roles["can_id_read"] = can_id_read_array + roles["can_id_write"] = can_id_write_array + + except JSONDecodeError as error: + print( + "Json decoding error occurred. Input is not in json format or does not contain the required fields") + raise SyntaxError + except Exception as exp: + print("An exception occurred while retrieving client roles {}".format(exp)) + + return roles + diff --git a/permision-mngt/requestHandler.py b/permision-mngt/requestHandler.py index 49256a0..d063471 100644 --- a/permision-mngt/requestHandler.py +++ b/permision-mngt/requestHandler.py @@ -39,9 +39,31 @@ def prepareNegativeResponse(appID, token, pubkey): return json.dumps(resp).encode("utf-8") # Always return a valid JSON as string +def preparePositiveDaaResponse(appID, roles): + resp = json.loads('{"appid": "", "roles" : ""}') + resp["appid"] = appID + resp["roles"] = roles + return json.dumps(resp).encode("utf-8") # Always return a valid JSON as string + + +def prepareNegativeDaaResponse(appID, roles): + resp = json.loads('{"appid": "", "roles" : ""}') + resp["appid"] = appID + resp["roles"] = roles + return json.dumps(resp).encode("utf-8") # Always return a valid JSON as string + + def processRequest(request): + try: req_json = json.loads(request) + + # check if the request is coming from the direct-access-api client + if req_json.get("daaid") is not None and req_json.get("daasecret") is not None: + print('Direct-Access-API request received.') + response = processDaaRequest(req_json) + return response + api = req_json["api"] appID = req_json["appid"] secret = req_json["secret"] @@ -49,6 +71,7 @@ def processRequest(request): except JSONDecodeError as error: print("Json decoding error occurred. The request is not in json format or does not contain the required fields") raise SyntaxError + # check if a valid token is available in the store token = None isvalid = False @@ -81,6 +104,7 @@ def processRequest(request): keycloak = keycloakconnector.Keycloakconnector(auth_url, realm, appID, secret) token = keycloak.getToken(appID, api) pubkey = keycloak.getJWTPublickey() + isvalid = validator.isTokenValid(token, pubkey) if isvalid: print('Token retrieved from keycloak server is valid so now returning to w3c-server ') @@ -96,3 +120,72 @@ def processRequest(request): print("An exception occurred while retrieving token {}".format(exp)) response = prepareNegativeResponse(appID, " ", " ") return response + + +def processDaaRequest(req_json): + + try: + api = req_json["api"] + appID = req_json["appid"] + app_secret = req_json["secret"] + + except JSONDecodeError as error: + print("Json decoding error occurred. The request is not in json format or does not contain the required fields") + raise SyntaxError + + app_isvalid = False + try: + print('Retrieving client app token from the keycloak server = ' + auth_url) + keycloak = keycloakconnector.Keycloakconnector(auth_url, realm, appID, app_secret) + app_token = keycloak.getToken(appID, api) + app_pubkey = keycloak.getJWTPublickey() + audience = "account" + app_isvalid = validator.isTokenValidAud(app_token, app_pubkey, audience) + + if app_isvalid: + print('App token retrieved from keycloak server is valid') + return retrieveClientRoles(req_json) + + else: + print('App token retrieved from keycloak server is invalid') + response = prepareNegativeDaaResponse(appID, app_token) + return response + except Exception as exp: + print("An exception occurred while retrieving token {}".format(exp)) + response = prepareNegativeDaaResponse(appID, " ") + return response + + +def retrieveClientRoles(req_json): + try: + api = req_json["api"] + appID = req_json["appid"] + daaID = req_json["daaid"] + daa_secret = req_json["daasecret"] + + except JSONDecodeError as error: + print("Json decoding error occurred. The request is not in json format or does not contain the required fields") + raise SyntaxError + + try: + print('Retrieving daa token from the keycloak server = ' + auth_url) + keycloak = keycloakconnector.Keycloakconnector(auth_url, realm, daaID, daa_secret) + daa_token = keycloak.getToken(daaID, api) + daa_pubkey = keycloak.getJWTPublickey() + audience = "account" + daa_isvalid = validator.isTokenValidAud(daa_token, daa_pubkey, audience) + + if daa_isvalid: + print('Daa token retrieved from keycloak server is valid, now retrieving client roles ') + roles = keycloak.getClientRoles(appID, daa_token) + response = preparePositiveDaaResponse(appID, roles) + return response + + else: + print('Daa token retrieved from keycloak server is invalid') + response = prepareNegativeDaaResponse(daaID, daa_token) + return response + except Exception as exp: + print("An exception occurred while retrieving token {}".format(exp)) + response = prepareNegativeDaaResponse(daaID, " ") + return response diff --git a/permision-mngt/tokenValidator.py b/permision-mngt/tokenValidator.py index 00ca262..5f11d6b 100644 --- a/permision-mngt/tokenValidator.py +++ b/permision-mngt/tokenValidator.py @@ -24,4 +24,23 @@ def isTokenValid(token, rsa_key): except jwt.ExpiredSignatureError: logging.error("Token has expired") return False + except Exception as exp: + logging.error("Exception occurred when trying to decode jwt token:", exp) + return False + return True + + +def isTokenValidAud(token, rsa_key, audience): + try: + jwt.decode( + token, + rsa_key, + algorithms=["RS256"], audience=audience + ) + except jwt.ExpiredSignatureError: + logging.error("Token has expired") + return False + except Exception as exp: + logging.error("Exception occurred when trying to decode jwt token with audience parameter set:", exp) + return False return True