From 46339be271bb5efd2bd7d54e5f93a2b1afc21664 Mon Sep 17 00:00:00 2001 From: Andrew Bell Date: Tue, 11 Sep 2018 11:53:46 -0400 Subject: [PATCH 1/7] Add comment on conversion from BOX2D to BOX3D. --- pdal/util/Bounds.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pdal/util/Bounds.cpp b/pdal/util/Bounds.cpp index 92c6c3fd42..52fef417bd 100644 --- a/pdal/util/Bounds.cpp +++ b/pdal/util/Bounds.cpp @@ -166,6 +166,8 @@ Bounds::Bounds(const BOX2D& box) : m_box(box) m_box.maxz = LOWEST; } +// We don't allow implicit conversion from a BOX2D to BOX3D. Use the explicit +// BOX3D ctor that takes a BOX2D if that's what you want. BOX3D Bounds::to3d() const { if (!is3d()) From a629e3c7a4f8b21bf1f95810bbb5e8b8f688ff6b Mon Sep 17 00:00:00 2001 From: Connor Manning Date: Tue, 11 Sep 2018 12:56:00 -0500 Subject: [PATCH 2/7] Add gzip download support to arbiter (#2164) --- vendor/arbiter/CMakeLists.txt | 6 +- vendor/arbiter/arbiter.cpp | 111 +- vendor/arbiter/arbiter.hpp | 6536 +++++++++++++++++---------------- 3 files changed, 3552 insertions(+), 3101 deletions(-) diff --git a/vendor/arbiter/CMakeLists.txt b/vendor/arbiter/CMakeLists.txt index a5e3207c05..d9bf6234fb 100644 --- a/vendor/arbiter/CMakeLists.txt +++ b/vendor/arbiter/CMakeLists.txt @@ -11,6 +11,10 @@ if (CURL_FOUND) add_definitions("-DARBITER_CURL") endif() +if (ZLIB_FOUND) + add_definitions("-DARBITER_ZLIB") +endif() + PDAL_ADD_FREE_LIBRARY(${PDAL_ARBITER_LIB_NAME} STATIC arbiter.cpp) target_include_directories(${PDAL_ARBITER_LIB_NAME} PRIVATE ${PDAL_JSONCPP_INCLUDE_DIR}) @@ -27,7 +31,7 @@ if (UNIX) target_compile_options(${PDAL_ARBITER_LIB_NAME} PRIVATE "-fPIC") else() target_compile_definitions(${PDAL_ARBITER_LIB_NAME} - PUBLIC -DARBITER_DLL_EXPORT -DARBITER_CURL) + PUBLIC -DARBITER_DLL_EXPORT -DARBITER_CURL -DARBITER_ZLIB) endif() set_target_properties(${PDAL_ARBITER_LIB_NAME} PROPERTIES diff --git a/vendor/arbiter/arbiter.cpp b/vendor/arbiter/arbiter.cpp index 01d1f86c3a..32f4259187 100644 --- a/vendor/arbiter/arbiter.cpp +++ b/vendor/arbiter/arbiter.cpp @@ -55,7 +55,9 @@ SOFTWARE. #include #include +#include #include +#include #endif #include @@ -427,15 +429,13 @@ std::unique_ptr Arbiter::getLocalHandle( throw ArbiterError("Temporary endpoint must be local."); } - std::string name(path); - std::replace(name.begin(), name.end(), '/', '-'); - std::replace(name.begin(), name.end(), '\\', '-'); - std::replace(name.begin(), name.end(), ':', '_'); - - tempEndpoint.put(name, getBinary(path)); - + const auto ext(getExtension(path)); + const std::string basename( + crypto::encodeAsHex(crypto::sha256(stripExtension(path))) + + (ext.size() ? "." + ext : "")); + tempEndpoint.put(basename, getBinary(path)); localHandle.reset( - new fs::LocalHandle(tempEndpoint.root() + name, true)); + new fs::LocalHandle(tempEndpoint.root() + basename, true)); } else { @@ -488,6 +488,12 @@ std::string Arbiter::getExtension(const std::string path) else return std::string(); } +std::string Arbiter::stripExtension(const std::string path) +{ + const std::size_t pos(path.find_last_of('.')); + return path.substr(0, pos); +} + } // namespace arbiter #ifdef ARBITER_CUSTOM_NAMESPACE @@ -629,6 +635,9 @@ std::vector Driver::glob(std::string path, bool verbose) const #include #include +#include +#include +#include #endif #ifdef ARBITER_CUSTOM_NAMESPACE @@ -684,6 +693,36 @@ bool Endpoint::isHttpDerived() const return tryGetHttpDriver() != nullptr; } +std::unique_ptr Endpoint::getLocalHandle( + const std::string subpath) const +{ + std::unique_ptr handle; + + if (isRemote()) + { + const std::string tmp(fs::getTempPath()); + const auto ext(Arbiter::getExtension(subpath)); + const std::string basename( + crypto::encodeAsHex(crypto::sha256(Arbiter::stripExtension( + prefixedRoot() + subpath))) + + (ext.size() ? "." + ext : "")); + + const std::string local(tmp + basename); + + drivers::Fs fs; + fs.put(local, getBinary(subpath)); + + handle.reset(new fs::LocalHandle(local, true)); + } + else + { + handle.reset( + new fs::LocalHandle(fs::expandTilde(fullPath(subpath)), false)); + } + + return handle; +} + std::string Endpoint::get(const std::string subpath) const { return m_driver.get(fullPath(subpath)); @@ -1258,17 +1297,21 @@ std::string expandTilde(std::string in) std::string getTempPath() { + std::string tmp; #ifndef ARBITER_WINDOWS - if (const auto t = util::env("TMPDIR")) return *t; - if (const auto t = util::env("TMP")) return *t; - if (const auto t = util::env("TEMP")) return *t; - if (const auto t = util::env("TEMPDIR")) return *t; - return "/tmp"; + if (const auto t = util::env("TMPDIR")) tmp = *t; + else if (const auto t = util::env("TMP")) tmp = *t; + else if (const auto t = util::env("TEMP")) tmp = *t; + else if (const auto t = util::env("TEMPDIR")) tmp = *t; + else tmp = "/tmp"; #else std::vector path(MAX_PATH, '\0'); - if (GetTempPath(MAX_PATH, path.data())) return path.data(); - else throw ArbiterError("Could not find a temp path."); + if (GetTempPath(MAX_PATH, path.data())) tmp.assign(path.data()); #endif + + if (tmp.empty()) throw ArbiterError("Could not find a temp path."); + if (tmp.back() != '/') tmp += '/'; + return tmp; } LocalHandle::LocalHandle(const std::string localPath, const bool isRemote) @@ -2749,24 +2792,24 @@ std::string Google::Auth::sign( EVP_PKEY* key(loadKey(pkey, false)); - EVP_MD_CTX ctx; - EVP_MD_CTX_init(&ctx); - EVP_DigestSignInit(&ctx, nullptr, EVP_sha256(), nullptr, key); + EVP_MD_CTX* ctx(EVP_MD_CTX_new()); + EVP_MD_CTX_init(ctx); + EVP_DigestSignInit(ctx, nullptr, EVP_sha256(), nullptr, key); - if (EVP_DigestSignUpdate(&ctx, data.data(), data.size()) == 1) + if (EVP_DigestSignUpdate(ctx, data.data(), data.size()) == 1) { std::size_t size(0); - if (EVP_DigestSignFinal(&ctx, nullptr, &size) == 1) + if (EVP_DigestSignFinal(ctx, nullptr, &size) == 1) { std::vector v(size, 0); - if (EVP_DigestSignFinal(&ctx, v.data(), &size) == 1) + if (EVP_DigestSignFinal(ctx, v.data(), &size) == 1) { signature.assign(reinterpret_cast(v.data()), size); } } } - EVP_MD_CTX_cleanup(&ctx); + EVP_MD_CTX_free(ctx); if (signature.empty()) throw ArbiterError("Could not sign JWT"); return signature; #else @@ -3192,6 +3235,12 @@ std::vector Dropbox::glob(std::string path, bool verbose) const #include #include #include + + +#ifdef ARBITER_ZLIB +#include +#endif + #endif #ifdef ARBITER_CURL @@ -3492,6 +3541,24 @@ Response Curl::get( // Run the command. const int httpCode(perform()); + + for (auto& h : receivedHeaders) + { + std::string& v(h.second); + while (v.size() && v.front() == ' ') v = v.substr(1); + while (v.size() && v.back() == ' ') v.pop_back(); + } + + if (receivedHeaders["Content-Encoding"] == "gzip") + { +#ifdef ARBITER_ZLIB + std::string s(gzip::decompress(data.data(), data.size())); + data.assign(s.begin(), s.end()); +#else + throw ArbiterError("Cannot decompress zlib"); +#endif + } + return Response(httpCode, data, receivedHeaders); #else throw ArbiterError(fail); diff --git a/vendor/arbiter/arbiter.hpp b/vendor/arbiter/arbiter.hpp index 012877297e..1d98b73b84 100644 --- a/vendor/arbiter/arbiter.hpp +++ b/vendor/arbiter/arbiter.hpp @@ -1,7 +1,7 @@ /// Arbiter amalgamated header (https://github.com/connormanning/arbiter). /// It is intended to be used with #include "arbiter.hpp" -// Git SHA: 7da80808840ddc56990c30ab40c37e5865cf1fea +// Git SHA: e63504fef0e8e4c875b09b98d6ef013204ff21fe // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: LICENSE @@ -49,3645 +49,3842 @@ SOFTWARE. #define ARBITER_EXTERNAL_JSON // ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: arbiter/util/exports.hpp -// ////////////////////////////////////////////////////////////////////// - -#pragma once - -#if defined(_WIN32) || defined(WIN32) || defined(_MSC_VER) -#define ARBITER_WINDOWS -#endif - -#ifndef ARBITER_DLL -#if defined(ARBITER_WINDOWS) -#if defined(ARBITER_DLL_EXPORT) -# define ARBITER_DLL __declspec(dllexport) -#elif defined(PDAL_DLL_IMPORT) -# define ARBITER_DLL __declspec(dllimport) -#else -# define ARBITER_DLL -#endif -#else -# if defined(USE_GCC_VISIBILITY_FLAG) -# define ARBITER_DLL __attribute__ ((visibility("default"))) -# else -# define ARBITER_DLL -# endif -#endif -#endif - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: arbiter/util/exports.hpp +// Beginning of content of file: arbiter/third/xml/rapidxml.hpp // ////////////////////////////////////////////////////////////////////// +#ifndef RAPIDXML_HPP_INCLUDED +#define RAPIDXML_HPP_INCLUDED +/* +======================================================================== +Copyright (c) 2006, 2007 Marcin Kalicinski +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +======================================================================== +*/ -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: arbiter/util/types.hpp -// ////////////////////////////////////////////////////////////////////// - -#pragma once +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation -#include -#include -#include -#include +// If standard library is disabled, user must provide implementations of required functions and typedefs +#if !defined(RAPIDXML_NO_STDLIB) + #include // For std::size_t + #include // For assert + #include // For placement new +#endif -#ifdef ARBITER_CUSTOM_NAMESPACE -namespace ARBITER_CUSTOM_NAMESPACE -{ +// On MSVC, disable "conditional expression is constant" warning (level 4). +// This warning is almost impossible to avoid with certain types of templated code +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) // Conditional expression is constant #endif -namespace arbiter -{ +/////////////////////////////////////////////////////////////////////////// +// RAPIDXML_PARSE_ERROR -/** @brief Exception class for all internally thrown runtime errors. */ -class ArbiterError : public std::runtime_error -{ -public: - ArbiterError(std::string msg) : std::runtime_error(msg) { } -}; +#if defined(RAPIDXML_NO_EXCEPTIONS) -namespace http +#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } + +namespace rapidxml { + //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, + //! this function is called to notify user about the error. + //! It must be defined by the user. + //!

+ //! This function cannot return. If it does, the results are undefined. + //!

+ //! A very simple definition might look like that: + //!
+    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
+    //! {
+    //!     std::cout << "Parse error: " << what << "\n";
+    //!     std::abort();
+    //! }
+    //! 
+ //! \param what Human readable description of the error. + //! \param where Pointer to character data where error was detected. + void parse_error_handler(const char *what, void *where); +} -/** HTTP header fields. */ -using Headers = std::map; +#else -/** HTTP query parameters. */ -using Query = std::map; +#include // For std::exception -/** @cond arbiter_internal */ +#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) -class Response +namespace rapidxml { -public: - Response(int code = 0) - : m_code(code) - , m_data() - { } - - Response(int code, std::vector data) - : m_code(code) - , m_data(data) - { } - Response( - int code, - const std::vector& data, - const Headers& headers) - : m_code(code) - , m_data(data) - , m_headers(headers) - { } + //! Parse error exception. + //! This exception is thrown by the parser when an error occurs. + //! Use what() function to get human-readable error message. + //! Use where() function to get a pointer to position within source text where error was detected. + //!

+ //! If throwing exceptions by the parser is undesirable, + //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. + //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. + //! This function must be defined by the user. + //!

+ //! This class derives from std::exception class. + class parse_error: public std::exception + { - ~Response() { } + public: - bool ok() const { return m_code / 100 == 2; } - bool clientError() const { return m_code / 100 == 4; } - bool serverError() const { return m_code / 100 == 5; } - int code() const { return m_code; } + //! Constructs parse error + parse_error(const char *what, void *where) + : m_what(what) + , m_where(where) + { + } - std::vector data() const { return m_data; } - const Headers& headers() const { return m_headers; } - std::string str() const - { - return std::string(data().data(), data().size()); - } + //! Gets human readable description of error. + //! \return Pointer to null terminated description of the error. + virtual const char *what() const throw() + { + return m_what; + } -private: - int m_code; - std::vector m_data; - Headers m_headers; -}; + //! Gets pointer to character data where error happened. + //! Ch should be the same as char type of xml_document that produced the error. + //! \return Pointer to location within the parsed string where error occured. + template + Ch *where() const + { + return reinterpret_cast(m_where); + } -/** @endcond */ + private: -} // namespace http -} // namespace arbiter + const char *m_what; + void *m_where; -#ifdef ARBITER_CUSTOM_NAMESPACE + }; } -#endif +#endif -// ////////////////////////////////////////////////////////////////////// -// End of content of file: arbiter/util/types.hpp -// ////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// Pool sizes +#ifndef RAPIDXML_STATIC_POOL_SIZE + // Size of static memory block of memory_pool. + // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. + #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) +#endif +#ifndef RAPIDXML_DYNAMIC_POOL_SIZE + // Size of dynamic memory block of memory_pool. + // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. + #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) +#endif +#ifndef RAPIDXML_ALIGNMENT + // Memory allocation alignment. + // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. + // All memory allocations for nodes, attributes and strings will be aligned to this value. + // This must be a power of 2 and at least 1, otherwise memory_pool will not work. + #define RAPIDXML_ALIGNMENT sizeof(void *) +#endif +namespace rapidxml +{ + // Forward declarations + template class xml_node; + template class xml_attribute; + template class xml_document; + //! Enumeration listing all node types produced by the parser. + //! Use xml_node::type() function to query node type. + enum node_type + { + node_document, //!< A document node. Name and value are empty. + node_element, //!< An element node. Name contains element name. Value contains text of first data node. + node_data, //!< A data node. Name is empty. Value contains data text. + node_cdata, //!< A CDATA node. Name is empty. Value contains data text. + node_comment, //!< A comment node. Name is empty. Value contains comment text. + node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. + node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. + node_pi //!< A PI node. Name contains target. Value contains instructions. + }; -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: arbiter/util/curl.hpp -// ////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + // Parsing flags -#pragma once + //! Parse flag instructing the parser to not create data nodes. + //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_data_nodes = 0x1; -#include -#include -#include -#include + //! Parse flag instructing the parser to not use text of first data node as a value of parent element. + //! Can be combined with other flags by use of | operator. + //! Note that child data nodes of element node take precendence over its value when printing. + //! That is, if element has one or more child data nodes and a value, the value will be ignored. + //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. + //!

+ //! See xml_document::parse() function. + const int parse_no_element_values = 0x2; -#ifndef ARBITER_IS_AMALGAMATION + //! Parse flag instructing the parser to not place zero terminators after strings in the source text. + //! By default zero terminators are placed, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_string_terminators = 0x4; -#include -#include + //! Parse flag instructing the parser to not translate entities in the source text. + //! By default entities are translated, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_entity_translation = 0x8; + //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. + //! By default, UTF-8 handling is enabled. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_utf8 = 0x10; -#ifndef ARBITER_EXTERNAL_JSON -#include -#endif + //! Parse flag instructing the parser to create XML declaration node. + //! By default, declaration node is not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_declaration_node = 0x20; -#endif + //! Parse flag instructing the parser to create comments nodes. + //! By default, comment nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_comment_nodes = 0x40; + //! Parse flag instructing the parser to create DOCTYPE node. + //! By default, doctype node is not created. + //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_doctype_node = 0x80; + //! Parse flag instructing the parser to create PI nodes. + //! By default, PI nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_pi_nodes = 0x100; -#ifdef ARBITER_EXTERNAL_JSON -#include -#endif + //! Parse flag instructing the parser to validate closing tag names. + //! If not set, name inside closing tag is irrelevant to the parser. + //! By default, closing tags are not validated. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_validate_closing_tags = 0x200; -#ifdef ARBITER_CURL -#include -#else -typedef void CURL; -#endif + //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. + //! By default, whitespace is not trimmed. + //! This flag does not cause the parser to modify source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_trim_whitespace = 0x400; -struct curl_slist; + //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. + //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. + //! By default, whitespace is not normalized. + //! If this flag is specified, source text will be modified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_normalize_whitespace = 0x800; -#ifdef ARBITER_CUSTOM_NAMESPACE -namespace ARBITER_CUSTOM_NAMESPACE -{ -#endif + // Compound flags -namespace arbiter -{ -namespace http -{ + //! Parse flags which represent default behaviour of the parser. + //! This is always equal to 0, so that all other flags can be simply ored together. + //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. + //! This also means that meaning of each flag is a negation of the default setting. + //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, + //! and using the flag will disable it. + //!

+ //! See xml_document::parse() function. + const int parse_default = 0; -/** @cond arbiter_internal */ + //! A combination of parse flags that forbids any modifications of the source text. + //! This also results in faster parsing. However, note that the following will occur: + //!
    + //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • + //!
  • entities will not be translated
  • + //!
  • whitespace will not be normalized
  • + //!
+ //! See xml_document::parse() function. + const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; -class Pool; + //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. + //!

+ //! See xml_document::parse() function. + const int parse_fastest = parse_non_destructive | parse_no_data_nodes; -class ARBITER_DLL Curl -{ - friend class Pool; + //! A combination of parse flags resulting in largest amount of data being extracted. + //! This usually results in slowest parsing. + //!

+ //! See xml_document::parse() function. + const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; - static constexpr std::size_t defaultHttpTimeout = 5; + /////////////////////////////////////////////////////////////////////// + // Internals -public: - ~Curl(); + //! \cond internal + namespace internal + { - http::Response get( - std::string path, - Headers headers, - Query query, - std::size_t reserve); + // Struct that contains lookup tables for the parser + // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). + template + struct lookup_tables + { + static const unsigned char lookup_whitespace[256]; // Whitespace table + static const unsigned char lookup_node_name[256]; // Node name table + static const unsigned char lookup_text[256]; // Text table + static const unsigned char lookup_text_pure_no_ws[256]; // Text table + static const unsigned char lookup_text_pure_with_ws[256]; // Text table + static const unsigned char lookup_attribute_name[256]; // Attribute name table + static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes + static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes + static const unsigned char lookup_digits[256]; // Digits + static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters + }; - http::Response head(std::string path, Headers headers, Query query); + // Find length of the string + template + inline std::size_t measure(const Ch *p) + { + const Ch *tmp = p; + while (*tmp) + ++tmp; + return tmp - p; + } - http::Response put( - std::string path, - const std::vector& data, - Headers headers, - Query query); + // Compare strings for equality + template + inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) + { + if (size1 != size2) + return false; + if (case_sensitive) + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (*p1 != *p2) + return false; + } + else + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) + return false; + } + return true; + } + } + //! \endcond - http::Response post( - std::string path, - const std::vector& data, - Headers headers, - Query query); + /////////////////////////////////////////////////////////////////////// + // Memory pool -private: - Curl(const Json::Value& json = Json::Value()); + //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. + //! In most cases, you will not need to use this class directly. + //! However, if you need to create nodes manually or modify names/values of nodes, + //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. + //! Not only is this faster than allocating them by using new operator, + //! but also their lifetime will be tied to the lifetime of document, + //! possibly simplyfing memory management. + //!

+ //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. + //! You can also call allocate_string() function to allocate strings. + //! Such strings can then be used as names or values of nodes without worrying about their lifetime. + //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, + //! or when the pool is destroyed. + //!

+ //! It is also possible to create a standalone memory_pool, and use it + //! to allocate nodes, whose lifetime will not be tied to any document. + //!

+ //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. + //! Until static memory is exhausted, no dynamic memory allocations are done. + //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, + //! by using global new[] and delete[] operators. + //! This behaviour can be changed by setting custom allocation routines. + //! Use set_allocator() function to set them. + //!

+ //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. + //! This value defaults to the size of pointer on target architecture. + //!

+ //! To obtain absolutely top performance from the parser, + //! it is important that all nodes are allocated from a single, contiguous block of memory. + //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. + //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT + //! to obtain best wasted memory to performance compromise. + //! To do it, define their values before rapidxml.hpp file is included. + //! \param Ch Character type of created nodes. + template + class memory_pool + { - void init(std::string path, const Headers& headers, const Query& query); + public: - // Returns HTTP status code. - int perform(); + //! \cond internal + typedef void *(alloc_function)(std::size_t); // Type of user-defined function used to allocate memory + typedef void (free_func)(void *); // Type of user-defined function used to free memory + //! \endcond - Curl(const Curl&); - Curl& operator=(const Curl&); + //! Constructs empty pool with default allocator functions. + memory_pool() + : m_alloc_function(0) + , m_free_func(0) + { + init(); + } - CURL* m_curl = nullptr; - curl_slist* m_headers = nullptr; + //! Destroys pool and frees all the memory. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Nodes allocated from the pool are no longer valid. + ~memory_pool() + { + clear(); + } - bool m_verbose = false; - long m_timeout = defaultHttpTimeout; - bool m_followRedirect = true; - bool m_verifyPeer = true; - std::unique_ptr m_caPath; - std::unique_ptr m_caInfo; + //! Allocates a new node from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param type Type of node to create. + //! \param name Name to assign to the node, or 0 to assign no name. + //! \param value Value to assign to the node, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated node. This pointer will never be NULL. + xml_node *allocate_node(node_type type, + const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_node)); + xml_node *node = new(memory) xml_node(type); + if (name) + { + if (name_size > 0) + node->name(name, name_size); + else + node->name(name); + } + if (value) + { + if (value_size > 0) + node->value(value, value_size); + else + node->value(value); + } + return node; + } - std::vector m_data; -}; + //! Allocates a new attribute from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param name Name to assign to the attribute, or 0 to assign no name. + //! \param value Value to assign to the attribute, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated attribute. This pointer will never be NULL. + xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_attribute)); + xml_attribute *attribute = new(memory) xml_attribute; + if (name) + { + if (name_size > 0) + attribute->name(name, name_size); + else + attribute->name(name); + } + if (value) + { + if (value_size > 0) + attribute->value(value, value_size); + else + attribute->value(value); + } + return attribute; + } -/** @endcond */ + //! Allocates a char array of given size from the pool, and optionally copies a given string to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param source String to initialize the allocated memory with, or 0 to not initialize it. + //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. + //! \return Pointer to allocated char array. This pointer will never be NULL. + Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) + { + assert(source || size); // Either source or size (or both) must be specified + if (size == 0) + size = internal::measure(source) + 1; + Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); + if (source) + for (std::size_t i = 0; i < size; ++i) + result[i] = source[i]; + return result; + } -} // namespace http -} // namespace arbiter + //! Clones an xml_node and its hierarchy of child nodes and attributes. + //! Nodes and attributes are allocated from this memory pool. + //! Names and values are not cloned, they are shared between the clone and the source. + //! Result node can be optionally specified as a second parameter, + //! in which case its contents will be replaced with cloned source node. + //! This is useful when you want to clone entire document. + //! \param source Node to clone. + //! \param result Node to put results in, or 0 to automatically allocate result node + //! \return Pointer to cloned node. This pointer will never be NULL. + xml_node *clone_node(const xml_node *source, xml_node *result = 0) + { + // Prepare result node + if (result) + { + result->remove_all_attributes(); + result->remove_all_nodes(); + result->type(source->type()); + } + else + result = allocate_node(source->type()); -#ifdef ARBITER_CUSTOM_NAMESPACE -} -#endif + // Clone name and value + result->name(source->name(), source->name_size()); + result->value(source->value(), source->value_size()); + // Clone child nodes and attributes + for (xml_node *child = source->first_node(); child; child = child->next_sibling()) + result->append_node(clone_node(child)); + for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) + result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); -// ////////////////////////////////////////////////////////////////////// -// End of content of file: arbiter/util/curl.hpp -// ////////////////////////////////////////////////////////////////////// + return result; + } + //! Clears the pool. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Any nodes or strings allocated from the pool will no longer be valid. + void clear() + { + while (m_begin != m_static_memory) + { + char *previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; + if (m_free_func) + m_free_func(m_begin); + else + delete[] m_begin; + m_begin = previous_begin; + } + init(); + } + //! Sets or resets the user-defined memory allocation functions for the pool. + //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. + //! Allocation function must not return invalid pointer on failure. It should either throw, + //! stop the program, or use longjmp() function to pass control to other place of program. + //! If it returns invalid pointer, results are undefined. + //!

+ //! User defined allocation functions must have the following forms: + //!
+ //!
void *allocate(std::size_t size); + //!
void free(void *pointer); + //!

+ //! \param af Allocation function, or 0 to restore default function + //! \param ff Free function, or 0 to restore default function + void set_allocator(alloc_function *af, free_func *ff) + { + assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet + m_alloc_function = af; + m_free_func = ff; + } + private: + struct header + { + char *previous_begin; + }; + void init() + { + m_begin = m_static_memory; + m_ptr = align(m_begin); + m_end = m_static_memory + sizeof(m_static_memory); + } -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: arbiter/util/http.hpp -// ////////////////////////////////////////////////////////////////////// + char *align(char *ptr) + { + std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); + return ptr + alignment; + } -#pragma once + char *allocate_raw(std::size_t size) + { + // Allocate + void *memory; + if (m_alloc_function) // Allocate memory using either user-specified allocation function or global operator new[] + { + memory = m_alloc_function(size); + assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp + } + else + { + memory = new char[size]; +#ifdef RAPIDXML_NO_EXCEPTIONS + if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc + RAPIDXML_PARSE_ERROR("out of memory", 0); +#endif + } + return static_cast(memory); + } -#include -#include -#include -#include -#include -#include -#include -#include + void *allocate_aligned(std::size_t size) + { + // Calculate aligned pointer + char *result = align(m_ptr); -#ifndef ARBITER_IS_AMALGAMATION + // If not enough memory left in current pool, allocate a new pool + if (result + size > m_end) + { + // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) + std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; + if (pool_size < size) + pool_size = size; -#include -#include -#include + // Allocate + std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation + char *raw_memory = allocate_raw(alloc_size); -#ifndef ARBITER_EXTERNAL_JSON -#include -#endif + // Setup new pool in allocated memory + char *pool = align(raw_memory); + header *new_header = reinterpret_cast
(pool); + new_header->previous_begin = m_begin; + m_begin = raw_memory; + m_ptr = pool + sizeof(header); + m_end = raw_memory + alloc_size; -#endif + // Calculate aligned pointer again using new pool + result = align(m_ptr); + } + // Update pool and return aligned pointer + m_ptr = result + size; + return result; + } + char *m_begin; // Start of raw memory making up current pool + char *m_ptr; // First free byte in current pool + char *m_end; // One past last available byte in current pool + char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory + alloc_function *m_alloc_function; // Allocator function, or 0 if default is to be used + free_func *m_free_func; // Free function, or 0 if default is to be used + }; -#ifdef ARBITER_EXTERNAL_JSON -#include -#endif - -#ifdef ARBITER_CUSTOM_NAMESPACE -namespace ARBITER_CUSTOM_NAMESPACE -{ -#endif - -namespace arbiter -{ -namespace http -{ - -/** Perform URI percent-encoding, without encoding characters included in - * @p exclusions. - */ -ARBITER_DLL std::string sanitize(std::string path, std::string exclusions = "/"); + /////////////////////////////////////////////////////////////////////////// + // XML base -/** Build a query string from key-value pairs. If @p query is empty, the - * result is an empty string. Otherwise, the result will start with the - * '?' character. - */ -ARBITER_DLL std::string buildQueryString(const http::Query& query); + //! Base class for xml_node and xml_attribute implementing common functions: + //! name(), name_size(), value(), value_size() and parent(). + //! \param Ch Character type to use + template + class xml_base + { -/** @cond arbiter_internal */ + public: -class ARBITER_DLL Pool; + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction -class ARBITER_DLL Resource -{ -public: - Resource(Pool& pool, Curl& curl, std::size_t id, std::size_t retry); - ~Resource(); + // Construct a base with empty name, value and parent + xml_base() + : m_name(0) + , m_value(0) + , m_parent(0) + { + } - http::Response get( - std::string path, - Headers headers = Headers(), - Query query = Query(), - std::size_t reserve = 0); + /////////////////////////////////////////////////////////////////////////// + // Node data access - http::Response head( - std::string path, - Headers headers = Headers(), - Query query = Query()); + //! Gets name of the node. + //! Interpretation of name depends on type of node. + //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use name_size() function to determine length of the name. + //! \return Name of node, or empty string if node has no name. + Ch *name() const + { + return m_name ? m_name : nullstr(); + } - http::Response put( - std::string path, - const std::vector& data, - Headers headers = Headers(), - Query query = Query()); + //! Gets size of node name, not including terminator character. + //! This function works correctly irrespective of whether name is or is not zero terminated. + //! \return Size of node name, in characters. + std::size_t name_size() const + { + return m_name ? m_name_size : 0; + } - http::Response post( - std::string path, - const std::vector& data, - Headers headers = Headers(), - Query query = Query()); + //! Gets value of node. + //! Interpretation of value depends on type of node. + //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use value_size() function to determine length of the value. + //! \return Value of node, or empty string if node has no value. + Ch *value() const + { + return m_value ? m_value : nullstr(); + } -private: - Pool& m_pool; - Curl& m_curl; - std::size_t m_id; - std::size_t m_retry; + //! Gets size of node value, not including terminator character. + //! This function works correctly irrespective of whether value is or is not zero terminated. + //! \return Size of node value, in characters. + std::size_t value_size() const + { + return m_value ? m_value_size : 0; + } - http::Response exec(std::function f); -}; + /////////////////////////////////////////////////////////////////////////// + // Node modification -class ARBITER_DLL Pool -{ - // Only HttpResource may release. - friend class Resource; + //! Sets name of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of name must be specified separately, because name does not have to be zero terminated. + //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //! \param name Name of node to set. Does not have to be zero terminated. + //! \param size Size of name, in characters. This does not include zero terminator, if one is present. + void name(const Ch *name, std::size_t size) + { + m_name = const_cast(name); + m_name_size = size; + } -public: - Pool( - std::size_t concurrent = 4, - std::size_t retry = 4, - Json::Value json = Json::Value()); - ~Pool(); + //! Sets name of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). + //! \param name Name of node to set. Must be zero terminated. + void name(const Ch *name) + { + this->name(name, internal::measure(name)); + } - Resource acquire(); + //! Sets value of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of value must be specified separately, because it does not have to be zero terminated. + //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //!

+ //! If an element has a child node of type node_data, it will take precedence over element value when printing. + //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. + //! \param value value of node to set. Does not have to be zero terminated. + //! \param size Size of value, in characters. This does not include zero terminator, if one is present. + void value(const Ch *value, std::size_t size) + { + m_value = const_cast(value); + m_value_size = size; + } -private: - void release(std::size_t id); + //! Sets value of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). + //! \param value Vame of node to set. Must be zero terminated. + void value(const Ch *value) + { + this->value(value, internal::measure(value)); + } - std::vector> m_curls; - std::vector m_available; - std::size_t m_retry; + /////////////////////////////////////////////////////////////////////////// + // Related nodes access - std::mutex m_mutex; - std::condition_variable m_cv; -}; + //! Gets node parent. + //! \return Pointer to parent node, or 0 if there is no parent. + xml_node *parent() const + { + return m_parent; + } -/** @endcond */ + protected: -} // namespace http -} // namespace arbiter + // Return empty string + static Ch *nullstr() + { + static Ch zero = Ch('\0'); + return &zero; + } -#ifdef ARBITER_CUSTOM_NAMESPACE -} -#endif + Ch *m_name; // Name of node, or 0 if no name + Ch *m_value; // Value of node, or 0 if no value + std::size_t m_name_size; // Length of node name, or undefined of no name + std::size_t m_value_size; // Length of node value, or undefined if no value + xml_node *m_parent; // Pointer to parent node, or 0 if none + }; -// ////////////////////////////////////////////////////////////////////// -// End of content of file: arbiter/util/http.hpp -// ////////////////////////////////////////////////////////////////////// + //! Class representing attribute node of XML document. + //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). + //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. + //! Thus, this text must persist in memory for the lifetime of attribute. + //! \param Ch Character type to use. + template + class xml_attribute: public xml_base + { + friend class xml_node; + public: + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + //! Constructs an empty attribute with the specified type. + //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. + xml_attribute() + { + } + /////////////////////////////////////////////////////////////////////////// + // Related nodes access -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: arbiter/util/ini.hpp -// ////////////////////////////////////////////////////////////////////// + //! Gets document of which attribute is a child. + //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. + xml_document *document() const + { + if (xml_node *node = this->parent()) + { + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + else + return 0; + } -#pragma once - -#include -#include -#include + //! Gets previous attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_prev_attribute : 0; + } -#ifdef ARBITER_CUSTOM_NAMESPACE -namespace ARBITER_CUSTOM_NAMESPACE -{ -#endif + //! Gets next attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_next_attribute : 0; + } -namespace arbiter -{ -namespace ini -{ + private: -using Section = std::string; -using Key = std::string; -using Val = std::string; -using Contents = std::map>; + xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero + xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero -Contents parse(const std::string& s); + }; -} // namespace ini + /////////////////////////////////////////////////////////////////////////// + // XML node -} // namespace arbiter + //! Class representing a node of XML document. + //! Each node may have associated name and value strings, which are available through name() and value() functions. + //! Interpretation of name and value depends on type of the node. + //! Type of node can be determined by using type() function. + //!

+ //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. + //! Thus, this text must persist in the memory for the lifetime of node. + //! \param Ch Character type to use. + template + class xml_node: public xml_base + { -#ifdef ARBITER_CUSTOM_NAMESPACE -} -#endif + public: + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction -// ////////////////////////////////////////////////////////////////////// -// End of content of file: arbiter/util/ini.hpp -// ////////////////////////////////////////////////////////////////////// + //! Constructs an empty node with the specified type. + //! Consider using memory_pool of appropriate document to allocate nodes manually. + //! \param type Type of node to construct. + xml_node(node_type type) + : m_type(type) + , m_first_node(0) + , m_first_attribute(0) + { + } + /////////////////////////////////////////////////////////////////////////// + // Node data access + //! Gets type of node. + //! \return Type of node. + node_type type() const + { + return m_type; + } + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + //! Gets document of which node is a child. + //! \return Pointer to document that contains this node, or 0 if there is no parent document. + xml_document *document() const + { + xml_node *node = const_cast *>(this); + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + //! Gets first child node, optionally matching node name. + //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_first_node; child; child = child->next_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_first_node; + } -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: arbiter/util/time.hpp -// ////////////////////////////////////////////////////////////////////// + //! Gets last child node, optionally matching node name. + //! Behaviour is undefined if node has no children. + //! Use first_node() to test if node has children. + //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(m_first_node); // Cannot query for last child if node has no children + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_last_node; child; child = child->previous_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_last_node; + } -#pragma once + //! Gets previous sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_prev_sibling; + } -#include -#include -#include + //! Gets next sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_next_sibling; + } -#ifndef ARBITER_IS_AMALGAMATION -#include -#endif + //! Gets first attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute; + } + //! Gets last attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute ? m_last_attribute : 0; + } + /////////////////////////////////////////////////////////////////////////// + // Node modification -#ifdef ARBITER_CUSTOM_NAMESPACE -namespace ARBITER_CUSTOM_NAMESPACE -{ -#endif + //! Sets type of node. + //! \param type Type of node to set. + void type(node_type type) + { + m_type = type; + } -namespace arbiter -{ + /////////////////////////////////////////////////////////////////////////// + // Node manipulation -class ARBITER_DLL Time -{ -public: - static const std::string iso8601; - static const std::string iso8601NoSeparators; - static const std::string dateNoSeparators; + //! Prepends a new child node. + //! The prepended child becomes the first child, and all existing children are moved one position back. + //! \param child Node to prepend. + void prepend_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_next_sibling = m_first_node; + m_first_node->m_prev_sibling = child; + } + else + { + child->m_next_sibling = 0; + m_last_node = child; + } + m_first_node = child; + child->m_parent = this; + child->m_prev_sibling = 0; + } - Time(); - Time(const std::string& s, const std::string& format = "%Y-%m-%dT%H:%M:%SZ"); + //! Appends a new child node. + //! The appended child becomes the last child. + //! \param child Node to append. + void append_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_prev_sibling = m_last_node; + m_last_node->m_next_sibling = child; + } + else + { + child->m_prev_sibling = 0; + m_first_node = child; + } + m_last_node = child; + child->m_parent = this; + child->m_next_sibling = 0; + } - std::string str(const std::string& format = "%Y-%m-%dT%H:%M:%SZ") const; + //! Inserts a new child node at specified place inside the node. + //! All children after and including the specified node are moved one position back. + //! \param where Place where to insert the child, or 0 to insert at the back. + //! \param child Node to insert. + void insert_node(xml_node *where, xml_node *child) + { + assert(!where || where->parent() == this); + assert(child && !child->parent() && child->type() != node_document); + if (where == m_first_node) + prepend_node(child); + else if (where == 0) + append_node(child); + else + { + child->m_prev_sibling = where->m_prev_sibling; + child->m_next_sibling = where; + where->m_prev_sibling->m_next_sibling = child; + where->m_prev_sibling = child; + child->m_parent = this; + } + } - // Return value is in seconds. - int64_t operator-(const Time& other) const; - int64_t asUnix() const; + //! Removes first child node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_first_node() + { + assert(first_node()); + xml_node *child = m_first_node; + m_first_node = child->m_next_sibling; + if (child->m_next_sibling) + child->m_next_sibling->m_prev_sibling = 0; + else + m_last_node = 0; + child->m_parent = 0; + } -private: - std::time_t m_time; -}; + //! Removes last child of the node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_last_node() + { + assert(first_node()); + xml_node *child = m_last_node; + if (child->m_prev_sibling) + { + m_last_node = child->m_prev_sibling; + child->m_prev_sibling->m_next_sibling = 0; + } + else + m_first_node = 0; + child->m_parent = 0; + } -} // namespace arbiter + //! Removes specified child from the node + // \param where Pointer to child to be removed. + void remove_node(xml_node *where) + { + assert(where && where->parent() == this); + assert(first_node()); + if (where == m_first_node) + remove_first_node(); + else if (where == m_last_node) + remove_last_node(); + else + { + where->m_prev_sibling->m_next_sibling = where->m_next_sibling; + where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; + where->m_parent = 0; + } + } -#ifdef ARBITER_CUSTOM_NAMESPACE -} -#endif + //! Removes all child nodes (but not attributes). + void remove_all_nodes() + { + for (xml_node *node = first_node(); node; node = node->m_next_sibling) + node->m_parent = 0; + m_first_node = 0; + } + //! Prepends a new attribute to the node. + //! \param attribute Attribute to prepend. + void prepend_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_next_attribute = m_first_attribute; + m_first_attribute->m_prev_attribute = attribute; + } + else + { + attribute->m_next_attribute = 0; + m_last_attribute = attribute; + } + m_first_attribute = attribute; + attribute->m_parent = this; + attribute->m_prev_attribute = 0; + } -// ////////////////////////////////////////////////////////////////////// -// End of content of file: arbiter/util/time.hpp -// ////////////////////////////////////////////////////////////////////// + //! Appends a new attribute to the node. + //! \param attribute Attribute to append. + void append_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_prev_attribute = m_last_attribute; + m_last_attribute->m_next_attribute = attribute; + } + else + { + attribute->m_prev_attribute = 0; + m_first_attribute = attribute; + } + m_last_attribute = attribute; + attribute->m_parent = this; + attribute->m_next_attribute = 0; + } + //! Inserts a new attribute at specified place inside the node. + //! All attributes after and including the specified attribute are moved one position back. + //! \param where Place where to insert the attribute, or 0 to insert at the back. + //! \param attribute Attribute to insert. + void insert_attribute(xml_attribute *where, xml_attribute *attribute) + { + assert(!where || where->parent() == this); + assert(attribute && !attribute->parent()); + if (where == m_first_attribute) + prepend_attribute(attribute); + else if (where == 0) + append_attribute(attribute); + else + { + attribute->m_prev_attribute = where->m_prev_attribute; + attribute->m_next_attribute = where; + where->m_prev_attribute->m_next_attribute = attribute; + where->m_prev_attribute = attribute; + attribute->m_parent = this; + } + } + //! Removes first attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_first_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_first_attribute; + if (attribute->m_next_attribute) + { + attribute->m_next_attribute->m_prev_attribute = 0; + } + else + m_last_attribute = 0; + attribute->m_parent = 0; + m_first_attribute = attribute->m_next_attribute; + } + //! Removes last attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_last_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_last_attribute; + if (attribute->m_prev_attribute) + { + attribute->m_prev_attribute->m_next_attribute = 0; + m_last_attribute = attribute->m_prev_attribute; + } + else + m_first_attribute = 0; + attribute->m_parent = 0; + } + //! Removes specified attribute from node. + //! \param where Pointer to attribute to be removed. + void remove_attribute(xml_attribute *where) + { + assert(first_attribute() && where->parent() == this); + if (where == m_first_attribute) + remove_first_attribute(); + else if (where == m_last_attribute) + remove_last_attribute(); + else + { + where->m_prev_attribute->m_next_attribute = where->m_next_attribute; + where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; + where->m_parent = 0; + } + } + //! Removes all attributes of node. + void remove_all_attributes() + { + for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) + attribute->m_parent = 0; + m_first_attribute = 0; + } -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: arbiter/driver.hpp -// ////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include -#include -#include + private: -#ifndef ARBITER_IS_AMALGAMATION + /////////////////////////////////////////////////////////////////////////// + // Restrictions -#include + // No copying + xml_node(const xml_node &); + void operator =(const xml_node &); -#endif + /////////////////////////////////////////////////////////////////////////// + // Data members -#ifdef ARBITER_CUSTOM_NAMESPACE -namespace ARBITER_CUSTOM_NAMESPACE -{ -#endif + // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. + // This is required for maximum performance, as it allows the parser to omit initialization of + // unneded/redundant values. + // + // The rules are as follows: + // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively + // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage + // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage -namespace arbiter -{ + node_type m_type; // Type of node; always valid + xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid + xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero + xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid + xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero + xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero -class HttpPool; + }; -/** @brief Base class for interacting with a storage type. - * - * A Driver handles reading, writing, and possibly globbing from a storage - * source. It is intended to be overriden by specialized subclasses for each - * supported storage mechanism. - * - * Derived classes must override Driver::type, - * Driver::put(std::string, const std::vector&) const, and - * Driver::get(std::string, std::vector&) const, - * Driver::getSize(std::string) const - and may optionally - * override Driver::glob if possible. - * - * HTTP-derived classes should override the PUT and GET versions that accept - * http::Headers and http::Query parameters instead. - */ -class ARBITER_DLL Driver -{ -public: - virtual ~Driver() { } + /////////////////////////////////////////////////////////////////////////// + // XML document - /** - * Returns a string identifying this driver type, which should be unique - * among all other drivers. Paths that begin with the substring - * `://` will be routed to this driver. For example, `fs`, `s3`, or - * `http`. - * - * @note Derived classes must override. - */ - virtual std::string type() const = 0; + //! This class represents root of the DOM hierarchy. + //! It is also an xml_node and a memory_pool through public inheritance. + //! Use parse() function to build a DOM tree from a zero-terminated XML text string. + //! parse() function allocates memory for nodes and attributes by using functions of xml_document, + //! which are inherited from memory_pool. + //! To access root node of the document, use the document itself, as if it was an xml_node. + //! \param Ch Character type to use. + template + class xml_document: public xml_node, public memory_pool + { - /** Get string data. */ - std::string get(std::string path) const; + public: - /** Get string data, if available. */ - std::unique_ptr tryGet(std::string path) const; + //! Constructs empty XML document + xml_document() + : xml_node(node_document) + { + } - /** Get binary data. */ - std::vector getBinary(std::string path) const; + //! Parses zero-terminated XML string according to given flags. + //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. + //! The string must persist for the lifetime of the document. + //! In case of error, rapidxml::parse_error exception will be thrown. + //!

+ //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. + //! Make sure that data is zero-terminated. + //!

+ //! Document can be parsed into multiple times. + //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. + //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. + template + void parse(Ch *text) + { + assert(text); - /** Get binary data, if available. */ - std::unique_ptr> tryGetBinary(std::string path) const; + // Remove current contents + this->remove_all_nodes(); + this->remove_all_attributes(); - /** - * Write @p data to the given @p path. - * - * @note Derived classes must override. - * - * @param path Path with the type-specifying prefix information stripped. - */ - virtual void put(std::string path, const std::vector& data) const = 0; + // Parse BOM, if any + parse_bom(text); - /** True for remote paths, otherwise false. If `true`, a fs::LocalHandle - * request will download and write this file to the local filesystem. - */ - virtual bool isRemote() const { return true; } + // Parse children + while (1) + { + // Skip whitespace before node + skip(text); + if (*text == 0) + break; - /** Get the file size in bytes, if available. */ - virtual std::unique_ptr tryGetSize(std::string path) const = 0; + // Parse and append new child + if (*text == Ch('<')) + { + ++text; // Skip '<' + if (xml_node *node = parse_node(text)) + this->append_node(node); + } + else + RAPIDXML_PARSE_ERROR("expected <", text); + } - /** Get the file size in bytes, or throw if it does not exist. */ - std::size_t getSize(std::string path) const; + } - /** Write string data. */ - void put(std::string path, const std::string& data) const; + //! Clears the document by deleting all nodes and clearing the memory pool. + //! All nodes owned by document pool are destroyed. + void clear() + { + this->remove_all_nodes(); + this->remove_all_attributes(); + memory_pool::clear(); + } - /** Copy a file, where @p src and @p dst must both be of this driver - * type. Type-prefixes must be stripped from the input parameters. - */ - virtual void copy(std::string src, std::string dst) const; + private: - /** @brief Resolve a possibly globbed path. - * - * See Arbiter::resolve for details. - */ - std::vector resolve( - std::string path, - bool verbose = false) const; + /////////////////////////////////////////////////////////////////////// + // Internal character utility functions -protected: - /** @brief Resolve a wildcard path. - * - * This operation should return a non-recursive resolution of the files - * matching the given wildcard @p path (no directories). With the exception - * of the filesystem Driver, results should be prefixed with - * `type() + "://"`. - * - * @note The default behavior is to throw ArbiterError, so derived classes - * may optionally override if they can perform this behavior. - * - * @param path A string ending with the character `*`, and with all - * type-specifying prefix information like `http://` or `s3://` stripped. - * - * @param verbose If true, this function may print out minimal information - * to indicate that progress is occurring. - */ - virtual std::vector glob(std::string path, bool verbose) const; + // Detect whitespace character + struct whitespace_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; + } + }; - /** - * @param path Path with the type-specifying prefix information stripped. - * @param[out] data Empty vector in which to write resulting data. - */ - virtual bool get(std::string path, std::vector& data) const = 0; -}; + // Detect node name character + struct node_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; + } + }; -typedef std::map> DriverMap; + // Detect attribute name character + struct attribute_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; + } + }; -} // namespace arbiter + // Detect text character (PCDATA) + struct text_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; + } + }; -#ifdef ARBITER_CUSTOM_NAMESPACE -} -#endif + // Detect text character (PCDATA) that does not require processing + struct text_pure_no_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; + } + }; + // Detect text character (PCDATA) that does not require processing + struct text_pure_with_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; + } + }; -// ////////////////////////////////////////////////////////////////////// -// End of content of file: arbiter/driver.hpp -// ////////////////////////////////////////////////////////////////////// + // Detect attribute value character + template + struct attribute_value_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + // Detect attribute value character + template + struct attribute_value_pure_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + // Insert coded character, using UTF8 or 8-bit ASCII + template + static void insert_coded_character(Ch *&text, unsigned long code) + { + if (Flags & parse_no_utf8) + { + // Insert 8-bit ASCII character + // Todo: possibly verify that code is less than 256 and use replacement char otherwise? + text[0] = static_cast(code); + text += 1; + } + else + { + // Insert UTF8 sequence + if (code < 0x80) // 1 byte sequence + { + text[0] = static_cast(code); + text += 1; + } + else if (code < 0x800) // 2 byte sequence + { + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xC0); + text += 2; + } + else if (code < 0x10000) // 3 byte sequence + { + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xE0); + text += 3; + } + else if (code < 0x110000) // 4 byte sequence + { + text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xF0); + text += 4; + } + else // Invalid, only codes up to 0x10FFFF are allowed in Unicode + { + RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); + } + } + } + // Skip characters until predicate evaluates to true + template + static void skip(Ch *&text) + { + Ch *tmp = text; + while (StopPred::test(*tmp)) + ++tmp; + text = tmp; + } + // Skip characters until predicate evaluates to true while doing the following: + // - replacing XML character entity references with proper characters (' & " < > &#...;) + // - condensing whitespace sequences to single space character + template + static Ch *skip_and_expand_character_refs(Ch *&text) + { + // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip + if (Flags & parse_no_entity_translation && + !(Flags & parse_normalize_whitespace) && + !(Flags & parse_trim_whitespace)) + { + skip(text); + return text; + } + // Use simple skip until first modification is detected + skip(text); -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: arbiter/drivers/fs.hpp -// ////////////////////////////////////////////////////////////////////// + // Use translation skip + Ch *src = text; + Ch *dest = src; + while (StopPred::test(*src)) + { + // If entity translation is enabled + if (!(Flags & parse_no_entity_translation)) + { + // Test if replacement is needed + if (src[0] == Ch('&')) + { + switch (src[1]) + { -#pragma once + // & ' + case Ch('a'): + if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) + { + *dest = Ch('&'); + ++dest; + src += 5; + continue; + } + if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) + { + *dest = Ch('\''); + ++dest; + src += 6; + continue; + } + break; -#ifndef ARBITER_IS_AMALGAMATION -#include + // " + case Ch('q'): + if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) + { + *dest = Ch('"'); + ++dest; + src += 6; + continue; + } + break; -#ifndef ARBITER_EXTERNAL_JSON -#include -#endif + // > + case Ch('g'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('>'); + ++dest; + src += 4; + continue; + } + break; -#endif + // < + case Ch('l'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('<'); + ++dest; + src += 4; + continue; + } + break; + // &#...; - assumes ASCII + case Ch('#'): + if (src[2] == Ch('x')) + { + unsigned long code = 0; + src += 3; // Skip &#x + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 16 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + else + { + unsigned long code = 0; + src += 2; // Skip &# + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 10 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + if (*src == Ch(';')) + ++src; + else + RAPIDXML_PARSE_ERROR("expected ;", src); + continue; -#ifndef ARBITER_IS_AMALGAMATION -#include -#endif + // Something else + default: + // Ignore, just copy '&' verbatim + break; -#ifdef ARBITER_EXTERNAL_JSON -#include -#endif + } + } + } -#ifdef ARBITER_CUSTOM_NAMESPACE -namespace ARBITER_CUSTOM_NAMESPACE -{ -#endif + // If whitespace condensing is enabled + if (Flags & parse_normalize_whitespace) + { + // Test if condensing is needed + if (whitespace_pred::test(*src)) + { + *dest = Ch(' '); ++dest; // Put single space in dest + ++src; // Skip first whitespace char + // Skip remaining whitespace chars + while (whitespace_pred::test(*src)) + ++src; + continue; + } + } -namespace arbiter -{ + // No replacement, only copy character + *dest++ = *src++; -class Arbiter; + } -/** - * \addtogroup fs - * @{ - */ + // Return new end + text = src; + return dest; -/** Filesystem utilities. */ -namespace fs -{ - /** @brief Returns true if created, false if already existed. */ - ARBITER_DLL bool mkdirp(std::string dir); + } - /** @brief Returns true if removed, otherwise false. */ - ARBITER_DLL bool remove(std::string filename); + /////////////////////////////////////////////////////////////////////// + // Internal parsing functions - /** @brief Performs tilde expansion to a fully-qualified path, if possible. - */ - ARBITER_DLL std::string expandTilde(std::string path); - - /** @brief Get temporary path from environment. */ - ARBITER_DLL std::string getTempPath(); - - /** @brief Resolve a possible wildcard path. */ - ARBITER_DLL std::vector glob(std::string path); - - /** @brief A scoped local filehandle for a possibly remote path. - * - * This is an RAII style pseudo-filehandle. It manages the scope of a - * local temporary version of a file, where that file may have been copied - * from a remote storage location. - * - * See Arbiter::getLocalHandle for details about construction. - */ - class ARBITER_DLL LocalHandle - { - friend class arbiter::Arbiter; - - public: - /** @brief Deletes the local path if the data was copied from a remote - * source. - * - * This is a no-op if the path was already local and not copied. - */ - ~LocalHandle(); - - /** @brief Get the path of the locally stored file. - * - * @return A local filesystem absolute path containing the data - * requested in Arbiter::getLocalHandle. - */ - std::string localPath() const { return m_localPath; } - - /** @brief Release the managed local path and return the path from - * LocalHandle::localPath. - * - * After this call, destruction of the LocalHandle will not erase the - * temporary file that may have been created. - */ - std::string release() + // Parse BOM, if any + template + void parse_bom(Ch *&text) { - m_erase = false; - return localPath(); + // UTF-8? + if (static_cast(text[0]) == 0xEF && + static_cast(text[1]) == 0xBB && + static_cast(text[2]) == 0xBF) + { + text += 3; // Skup utf-8 bom + } } - private: - LocalHandle(std::string localPath, bool isRemote); + // Parse XML declaration ( + xml_node *parse_xml_declaration(Ch *&text) + { + // If parsing of declaration is disabled + if (!(Flags & parse_declaration_node)) + { + // Skip until end of declaration + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } - const std::string m_localPath; - bool m_erase; - }; -} -/** @} */ + // Create declaration + xml_node *declaration = this->allocate_node(node_declaration); -namespace drivers -{ + // Skip whitespace before attributes or ?> + skip(text); -/** @brief Local filesystem driver. */ -class ARBITER_DLL Fs : public Driver -{ -public: - Fs() { } + // Parse declaration attributes + parse_node_attributes(text, declaration); - using Driver::get; + // Skip ?> + if (text[0] != Ch('?') || text[1] != Ch('>')) + RAPIDXML_PARSE_ERROR("expected ?>", text); + text += 2; - static std::unique_ptr create(const Json::Value& json); + return declaration; + } - virtual std::string type() const override { return "file"; } + // Parse XML comment (' + return 0; // Do not produce comment node + } - virtual std::unique_ptr tryGetSize( - std::string path) const override; + // Remember value start + Ch *value = text; - virtual void put( - std::string path, - const std::vector& data) const override; + // Skip until end of comment + while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } - virtual std::vector glob( - std::string path, - bool verbose) const override; + // Create comment node + xml_node *comment = this->allocate_node(node_comment); + comment->value(value, text - value); - virtual bool isRemote() const override { return false; } + // Place zero terminator after comment value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); - virtual void copy(std::string src, std::string dst) const override; + text += 3; // Skip '-->' + return comment; + } -protected: - virtual bool get(std::string path, std::vector& data) const override; -}; + // Parse DOCTYPE + template + xml_node *parse_doctype(Ch *&text) + { + // Remember value start + Ch *value = text; -} // namespace drivers + // Skip to > + while (*text != Ch('>')) + { + // Determine character type + switch (*text) + { -} // namespace arbiter + // If '[' encountered, scan for matching ending ']' using naive algorithm with depth + // This works for all W3C test files except for 2 most wicked + case Ch('['): + { + ++text; // Skip '[' + int depth = 1; + while (depth > 0) + { + switch (*text) + { + case Ch('['): ++depth; break; + case Ch(']'): --depth; break; + case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + ++text; + } + break; + } -#ifdef ARBITER_CUSTOM_NAMESPACE -} -#endif + // Error on end of text + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + // Other character, skip it + default: + ++text; -// ////////////////////////////////////////////////////////////////////// -// End of content of file: arbiter/drivers/fs.hpp -// ////////////////////////////////////////////////////////////////////// + } + } + // If DOCTYPE nodes enabled + if (Flags & parse_doctype_node) + { + // Create a new doctype node + xml_node *doctype = this->allocate_node(node_doctype); + doctype->value(value, text - value); + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + text += 1; // skip '>' + return doctype; + } + else + { + text += 1; // skip '>' + return 0; + } + } + // Parse PI + template + xml_node *parse_pi(Ch *&text) + { + // If creation of PI nodes is enabled + if (Flags & parse_pi_nodes) + { + // Create pi node + xml_node *pi = this->allocate_node(node_pi); -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: arbiter/drivers/http.hpp -// ////////////////////////////////////////////////////////////////////// + // Extract PI target name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected PI target", text); + pi->name(name, text - name); -#pragma once + // Skip whitespace between pi target and pi + skip(text); -#include -#include + // Remember start of pi + Ch *value = text; -#ifndef ARBITER_IS_AMALGAMATION -#include -#include -#endif + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } -#ifdef ARBITER_CUSTOM_NAMESPACE -namespace ARBITER_CUSTOM_NAMESPACE -{ -#endif + // Set pi value (verbatim, no entity expansion or whitespace normalization) + pi->value(value, text - value); -namespace arbiter -{ -namespace drivers -{ + // Place zero terminator after name and value + if (!(Flags & parse_no_string_terminators)) + { + pi->name()[pi->name_size()] = Ch('\0'); + pi->value()[pi->value_size()] = Ch('\0'); + } -/** @brief HTTP driver. Intended as both a standalone driver as well as a base - * for derived drivers build atop HTTP. - * - * Derivers should overload the HTTP-specific put/get methods that accept - * headers and query parameters rather than Driver::put and Driver::get. - * - * Internal methods for derivers are provided as protected methods. - */ -class ARBITER_DLL Http : public Driver -{ -public: - Http(http::Pool& pool); - static std::unique_ptr create( - http::Pool& pool, - const Json::Value& json); + text += 2; // Skip '?>' + return pi; + } + else + { + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + } - // Inherited from Driver. - virtual std::string type() const override { return "http"; } + // Parse and append data + // Return character that ends data. + // This is necessary because this character might have been overwritten by a terminating 0 + template + Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) + { + // Backup to contents start if whitespace trimming is disabled + if (!(Flags & parse_trim_whitespace)) + text = contents_start; - /** By default, performs a HEAD request and returns the contents of the - * Content-Length header. - */ - virtual std::unique_ptr tryGetSize( - std::string path) const override; + // Skip until end of data + Ch *value = text, *end; + if (Flags & parse_normalize_whitespace) + end = skip_and_expand_character_refs(text); + else + end = skip_and_expand_character_refs(text); - virtual void put( - std::string path, - const std::vector& data) const final override - { - put(path, data, http::Headers(), http::Query()); - } + // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > + if (Flags & parse_trim_whitespace) + { + if (Flags & parse_normalize_whitespace) + { + // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end + if (*(end - 1) == Ch(' ')) + --end; + } + else + { + // Backup until non-whitespace character is found + while (whitespace_pred::test(*(end - 1))) + --end; + } + } - /* HTTP-specific driver methods follow. Since many drivers (S3, Dropbox, - * etc.) are built atop HTTP, we'll provide HTTP-specific methods for - * derived classes to use in addition to the generic PUT/GET combinations. - * - * Specifically, we'll add POST/HEAD calls, and allow headers and query - * parameters to be passed as well. - */ + // If characters are still left between end and value (this test is only necessary if normalization is enabled) + // Create new data node + if (!(Flags & parse_no_data_nodes)) + { + xml_node *data = this->allocate_node(node_data); + data->value(value, end - value); + node->append_node(data); + } - /** Perform an HTTP GET request. */ - std::string get( - std::string path, - http::Headers headers = http::Headers(), - http::Query query = http::Query()) const; + // Add data to parent node if no data exists yet + if (!(Flags & parse_no_element_values)) + if (*node->value() == Ch('\0')) + node->value(value, end - value); - /** Perform an HTTP GET request. */ - std::unique_ptr tryGet( - std::string path, - http::Headers headers = http::Headers(), - http::Query query = http::Query()) const; + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + Ch ch = *text; + *end = Ch('\0'); + return ch; // Return character that ends data; this is required because zero terminator overwritten it + } - /** Perform an HTTP GET request. */ - std::vector getBinary( - std::string path, - http::Headers headers, - http::Query query) const; + // Return character that ends data + return *text; + } - /** Perform an HTTP GET request. */ - std::unique_ptr> tryGetBinary( - std::string path, - http::Headers headers, - http::Query query) const; + // Parse CDATA + template + xml_node *parse_cdata(Ch *&text) + { + // If CDATA is disabled + if (Flags & parse_no_data_nodes) + { + // Skip until end of cdata + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 3; // Skip ]]> + return 0; // Do not produce CDATA node + } - /** Perform an HTTP PUT request. */ - void put( - std::string path, - const std::string& data, - http::Headers headers, - http::Query query) const; + // Skip until end of cdata + Ch *value = text; + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } - /** HTTP-derived Drivers should override this version of PUT to allow for - * custom headers and query parameters. - */ + // Create new cdata node + xml_node *cdata = this->allocate_node(node_cdata); + cdata->value(value, text - value); - /** Perform an HTTP PUT request. */ - virtual void put( - std::string path, - const std::vector& data, - http::Headers headers, - http::Query query) const; + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); - void post( - std::string path, - const std::string& data, - http::Headers headers, - http::Query query) const; - void post( - std::string path, - const std::vector& data, - http::Headers headers, - http::Query query) const; + text += 3; // Skip ]]> + return cdata; + } - /* These operations are other HTTP-specific calls that derived drivers may - * need for their underlying API use. - */ - http::Response internalGet( - std::string path, - http::Headers headers = http::Headers(), - http::Query query = http::Query(), - std::size_t reserve = 0) const; + // Parse element node + template + xml_node *parse_element(Ch *&text) + { + // Create element node + xml_node *element = this->allocate_node(node_element); - http::Response internalPut( - std::string path, - const std::vector& data, - http::Headers headers = http::Headers(), - http::Query query = http::Query()) const; + // Extract element name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected element name", text); + element->name(name, text - name); - http::Response internalHead( - std::string path, - http::Headers headers = http::Headers(), - http::Query query = http::Query()) const; + // Skip whitespace between element name and attributes or > + skip(text); - http::Response internalPost( - std::string path, - const std::vector& data, - http::Headers headers = http::Headers(), - http::Query query = http::Query()) const; + // Parse attributes, if any + parse_node_attributes(text, element); -protected: - /** HTTP-derived Drivers should override this version of GET to allow for - * custom headers and query parameters. - */ - virtual bool get( - std::string path, - std::vector& data, - http::Headers headers, - http::Query query) const; + // Determine ending type + if (*text == Ch('>')) + { + ++text; + parse_node_contents(text, element); + } + else if (*text == Ch('/')) + { + ++text; + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; + } + else + RAPIDXML_PARSE_ERROR("expected >", text); - http::Pool& m_pool; + // Place zero terminator after name + if (!(Flags & parse_no_string_terminators)) + element->name()[element->name_size()] = Ch('\0'); -private: - virtual bool get( - std::string path, - std::vector& data) const final override - { - return get(path, data, http::Headers(), http::Query()); - } + // Return parsed element + return element; + } - std::string typedPath(const std::string& p) const; -}; + // Determine node type, and parse it + template + xml_node *parse_node(Ch *&text) + { + // Parse proper node type + switch (text[0]) + { -/** @brief HTTPS driver. Identical to the HTTP driver except for its type - * string. - */ -class Https : public Http -{ -public: - Https(http::Pool& pool) : Http(pool) { } + // <... + default: + // Parse and append element node + return parse_element(text); - static std::unique_ptr create( - http::Pool& pool, - const Json::Value& json) - { - return std::unique_ptr(new Https(pool)); - } + // (text); + } + else + { + // Parse PI + return parse_pi(text); + } - virtual std::string type() const override { return "https"; } -}; + // (text); + } + break; + // (text); + } + break; -// ////////////////////////////////////////////////////////////////////// -// End of content of file: arbiter/drivers/http.hpp -// ////////////////////////////////////////////////////////////////////// + // (text); + } + } // switch + // Attempt to skip other, unrecognized node types starting with ')) + { + if (*text == 0) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + ++text; // Skip '>' + return 0; // No node recognized + } + } + // Parse contents of the node - children, data etc. + template + void parse_node_contents(Ch *&text, xml_node *node) + { + // For all children and text + while (1) + { + // Skip whitespace between > and node contents + Ch *contents_start = text; // Store start of node contents before whitespace is skipped + skip(text); + Ch next_char = *text; + // After data nodes, instead of continuing the loop, control jumps here. + // This is because zero termination inside parse_and_append_data() function + // would wreak havoc with the above code. + // Also, skipping whitespace after data nodes is unnecessary. + after_data_node: -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: arbiter/third/xml/rapidxml.hpp -// ////////////////////////////////////////////////////////////////////// + // Determine what comes next: node closing, child node, data node, or 0? + switch (next_char) + { -#ifndef RAPIDXML_HPP_INCLUDED -#define RAPIDXML_HPP_INCLUDED + // Node closing or child node + case Ch('<'): + if (text[1] == Ch('/')) + { + // Node closing + text += 2; // Skip '(text); + if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) + RAPIDXML_PARSE_ERROR("invalid closing tag name", text); + } + else + { + // No validation, just skip name + skip(text); + } + // Skip remaining whitespace after node name + skip(text); + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; // Skip '>' + return; // Node closed, finished parsing contents + } + else + { + // Child node + ++text; // Skip '<' + if (xml_node *child = parse_node(text)) + node->append_node(child); + } + break; -/* -======================================================================== -Copyright (c) 2006, 2007 Marcin Kalicinski + // End of data - error + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: + // Data node + default: + next_char = parse_and_append_data(node, text, contents_start); + goto after_data_node; // Bypass regular processing after data nodes -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + } + } + } -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. -======================================================================== -*/ + // Parse XML attributes of the node + template + void parse_node_attributes(Ch *&text, xml_node *node) + { + // For all attributes + while (attribute_name_pred::test(*text)) + { + // Extract attribute name + Ch *name = text; + ++text; // Skip first character of attribute name + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected attribute name", name); -// Copyright (C) 2006, 2009 Marcin Kalicinski -// Version 1.13 -// Revision $DateTime: 2009/05/13 01:46:17 $ -//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation + // Create new attribute + xml_attribute *attribute = this->allocate_attribute(); + attribute->name(name, text - name); + node->append_attribute(attribute); -// If standard library is disabled, user must provide implementations of required functions and typedefs -#if !defined(RAPIDXML_NO_STDLIB) - #include // For std::size_t - #include // For assert - #include // For placement new -#endif + // Skip whitespace after attribute name + skip(text); -// On MSVC, disable "conditional expression is constant" warning (level 4). -// This warning is almost impossible to avoid with certain types of templated code -#ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable:4127) // Conditional expression is constant -#endif + // Skip = + if (*text != Ch('=')) + RAPIDXML_PARSE_ERROR("expected =", text); + ++text; -/////////////////////////////////////////////////////////////////////////// -// RAPIDXML_PARSE_ERROR + // Add terminating zero after name + if (!(Flags & parse_no_string_terminators)) + attribute->name()[attribute->name_size()] = 0; -#if defined(RAPIDXML_NO_EXCEPTIONS) + // Skip whitespace after = + skip(text); -#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } + // Skip quote and remember if it was ' or " + Ch quote = *text; + if (quote != Ch('\'') && quote != Ch('"')) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; -namespace rapidxml -{ - //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, - //! this function is called to notify user about the error. - //! It must be defined by the user. - //!

- //! This function cannot return. If it does, the results are undefined. - //!

- //! A very simple definition might look like that: - //!
-    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
-    //! {
-    //!     std::cout << "Parse error: " << what << "\n";
-    //!     std::abort();
-    //! }
-    //! 
- //! \param what Human readable description of the error. - //! \param where Pointer to character data where error was detected. - void parse_error_handler(const char *what, void *where); -} + // Extract attribute value and expand char refs in it + Ch *value = text, *end; + const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes + if (quote == Ch('\'')) + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + else + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); -#else + // Set attribute value + attribute->value(value, end - value); -#include // For std::exception + // Make sure that end quote is present + if (*text != quote) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; // Skip quote -#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) + // Add terminating zero after value + if (!(Flags & parse_no_string_terminators)) + attribute->value()[attribute->value_size()] = 0; -namespace rapidxml -{ + // Skip whitespace after attribute value + skip(text); + } + } - //! Parse error exception. - //! This exception is thrown by the parser when an error occurs. - //! Use what() function to get human-readable error message. - //! Use where() function to get a pointer to position within source text where error was detected. - //!

- //! If throwing exceptions by the parser is undesirable, - //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. - //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. - //! This function must be defined by the user. - //!

- //! This class derives from std::exception class. - class parse_error: public std::exception - { + }; - public: + //! \cond internal + namespace internal + { - //! Constructs parse error - parse_error(const char *what, void *where) - : m_what(what) - , m_where(where) + // Whitespace (space \n \r \t) + template + const unsigned char lookup_tables::lookup_whitespace[256] = { - } + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; - //! Gets human readable description of error. - //! \return Pointer to null terminated description of the error. - virtual const char *what() const throw() + // Node name (anything but space \n \r \t / > ? \0) + template + const unsigned char lookup_tables::lookup_node_name[256] = { - return m_what; - } + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; - //! Gets pointer to character data where error happened. - //! Ch should be the same as char type of xml_document that produced the error. - //! \return Pointer to location within the parsed string where error occured. - template - Ch *where() const + // Text (i.e. PCDATA) (anything but < \0) + template + const unsigned char lookup_tables::lookup_text[256] = { - return reinterpret_cast(m_where); - } - - private: - - const char *m_what; - void *m_where; - - }; -} + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 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, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; -#endif + // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled + // (anything but < \0 &) + template + const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; -/////////////////////////////////////////////////////////////////////////// -// Pool sizes + // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled + // (anything but < \0 & space \n \r \t) + template + const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; -#ifndef RAPIDXML_STATIC_POOL_SIZE - // Size of static memory block of memory_pool. - // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. - // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. - #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) -#endif + // Attribute name (anything but space \n \r \t / < > = ? ! \0) + template + const unsigned char lookup_tables::lookup_attribute_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; -#ifndef RAPIDXML_DYNAMIC_POOL_SIZE - // Size of dynamic memory block of memory_pool. - // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. - // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. - #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) -#endif + // Attribute data with single quote (anything but ' \0) + template + const unsigned char lookup_tables::lookup_attribute_data_1[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 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, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; -#ifndef RAPIDXML_ALIGNMENT - // Memory allocation alignment. - // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. - // All memory allocations for nodes, attributes and strings will be aligned to this value. - // This must be a power of 2 and at least 1, otherwise memory_pool will not work. - #define RAPIDXML_ALIGNMENT sizeof(void *) -#endif + // Attribute data with single quote that does not require processing (anything but ' \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; -namespace rapidxml -{ - // Forward declarations - template class xml_node; - template class xml_attribute; - template class xml_document; + // Attribute data with double quote (anything but " \0) + template + const unsigned char lookup_tables::lookup_attribute_data_2[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; - //! Enumeration listing all node types produced by the parser. - //! Use xml_node::type() function to query node type. - enum node_type - { - node_document, //!< A document node. Name and value are empty. - node_element, //!< An element node. Name contains element name. Value contains text of first data node. - node_data, //!< A data node. Name is empty. Value contains data text. - node_cdata, //!< A CDATA node. Name is empty. Value contains data text. - node_comment, //!< A comment node. Name is empty. Value contains comment text. - node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. - node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. - node_pi //!< A PI node. Name contains target. Value contains instructions. - }; + // Attribute data with double quote that does not require processing (anything but " \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; - /////////////////////////////////////////////////////////////////////// - // Parsing flags + // Digits (dec and hex, 255 denotes end of numeric character reference) + template + const unsigned char lookup_tables::lookup_digits[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F + }; - //! Parse flag instructing the parser to not create data nodes. - //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_no_data_nodes = 0x1; + // Upper case conversion + template + const unsigned char lookup_tables::lookup_upcase[256] = + { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F + }; + } + //! \endcond - //! Parse flag instructing the parser to not use text of first data node as a value of parent element. - //! Can be combined with other flags by use of | operator. - //! Note that child data nodes of element node take precendence over its value when printing. - //! That is, if element has one or more child data nodes and a value, the value will be ignored. - //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. - //!

- //! See xml_document::parse() function. - const int parse_no_element_values = 0x2; +} - //! Parse flag instructing the parser to not place zero terminators after strings in the source text. - //! By default zero terminators are placed, modifying source text. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_no_string_terminators = 0x4; +// Undefine internal macros +#undef RAPIDXML_PARSE_ERROR - //! Parse flag instructing the parser to not translate entities in the source text. - //! By default entities are translated, modifying source text. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_no_entity_translation = 0x8; +// On MSVC, restore warnings state +#ifdef _MSC_VER + #pragma warning(pop) +#endif - //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. - //! By default, UTF-8 handling is enabled. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_no_utf8 = 0x10; +#endif - //! Parse flag instructing the parser to create XML declaration node. - //! By default, declaration node is not created. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_declaration_node = 0x20; - //! Parse flag instructing the parser to create comments nodes. - //! By default, comment nodes are not created. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_comment_nodes = 0x40; +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/third/xml/rapidxml.hpp +// ////////////////////////////////////////////////////////////////////// - //! Parse flag instructing the parser to create DOCTYPE node. - //! By default, doctype node is not created. - //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_doctype_node = 0x80; - //! Parse flag instructing the parser to create PI nodes. - //! By default, PI nodes are not created. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_pi_nodes = 0x100; - //! Parse flag instructing the parser to validate closing tag names. - //! If not set, name inside closing tag is irrelevant to the parser. - //! By default, closing tags are not validated. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_validate_closing_tags = 0x200; - //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. - //! By default, whitespace is not trimmed. - //! This flag does not cause the parser to modify source text. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_trim_whitespace = 0x400; - //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. - //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. - //! By default, whitespace is not normalized. - //! If this flag is specified, source text will be modified. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_normalize_whitespace = 0x800; - // Compound flags +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/third/xml/xml.hpp +// ////////////////////////////////////////////////////////////////////// - //! Parse flags which represent default behaviour of the parser. - //! This is always equal to 0, so that all other flags can be simply ored together. - //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. - //! This also means that meaning of each flag is a negation of the default setting. - //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, - //! and using the flag will disable it. - //!

- //! See xml_document::parse() function. - const int parse_default = 0; +#ifndef ARBITER_IS_AMALGAMATION - //! A combination of parse flags that forbids any modifications of the source text. - //! This also results in faster parsing. However, note that the following will occur: - //!
    - //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • - //!
  • entities will not be translated
  • - //!
  • whitespace will not be normalized
  • - //!
- //! See xml_document::parse() function. - const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; +#ifndef ARBITER_EXTERNAL_XML +#include +#endif - //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. - //!

- //! See xml_document::parse() function. - const int parse_fastest = parse_non_destructive | parse_no_data_nodes; +#endif - //! A combination of parse flags resulting in largest amount of data being extracted. - //! This usually results in slowest parsing. - //!

- //! See xml_document::parse() function. - const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; +#ifdef ARBITER_EXTERNAL_XML +#include +#endif - /////////////////////////////////////////////////////////////////////// - // Internals +namespace Xml = rapidxml; - //! \cond internal - namespace internal + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/third/xml/xml.hpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/third/gzip/config.hpp +// ////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifdef ARBITER_ZLIB + +#ifndef ZLIB_CONST +#define ZLIB_CONST +#endif + +#endif + + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/third/gzip/config.hpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/third/gzip/utils.hpp +// ////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifdef ARBITER_ZLIB + +#include +#include + +namespace gzip { + +// These live in gzip.hpp because it doesnt need to use deps. +// Otherwise, they would need to live in impl files if these methods used +// zlib structures or functions like inflate/deflate) +inline bool is_compressed(const char* data, std::size_t size) +{ + return size > 2 && + ( + // zlib + ( + static_cast(data[0]) == 0x78 && + (static_cast(data[1]) == 0x9C || + static_cast(data[1]) == 0x01 || + static_cast(data[1]) == 0xDA || + static_cast(data[1]) == 0x5E)) || + // gzip + (static_cast(data[0]) == 0x1F && static_cast(data[1]) == 0x8B)); +} +} // namespace gzip + +#endif + + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/third/gzip/utils.hpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/third/gzip/version.hpp +// ////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifdef ARBITER_ZLIB + +/// The major version number +#define GZIP_VERSION_MAJOR 1 + +/// The minor version number +#define GZIP_VERSION_MINOR 0 + +/// The patch number +#define GZIP_VERSION_PATCH 0 + +/// The complete version number +#define GZIP_VERSION_CODE (GZIP_VERSION_MAJOR * 10000 + GZIP_VERSION_MINOR * 100 + GZIP_VERSION_PATCH) + +/// Version number as string +#define GZIP_VERSION_STRING "1.0.0" + +#endif + + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/third/gzip/version.hpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/third/gzip/compress.hpp +// ////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifdef ARBITER_ZLIB + +#ifndef ARBITER_IS_AMALGAMATION +#include "config.hpp" +#endif + +// zlib +#include + +// std +#include +#include +#include + +namespace gzip { + +class Compressor +{ + std::size_t max_; + int level_; + + public: + Compressor(int level = Z_DEFAULT_COMPRESSION, + std::size_t max_bytes = 2000000000) // by default refuse operation if uncompressed data is > 2GB + : max_(max_bytes), + level_(level) { + } - // Struct that contains lookup tables for the parser - // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). - template - struct lookup_tables + template + void compress(InputType& output, + const char* data, + std::size_t size) const + { + +#ifdef DEBUG + // Verify if size input will fit into unsigned int, type used for zlib's avail_in + if (size > std::numeric_limits::max()) { - static const unsigned char lookup_whitespace[256]; // Whitespace table - static const unsigned char lookup_node_name[256]; // Node name table - static const unsigned char lookup_text[256]; // Text table - static const unsigned char lookup_text_pure_no_ws[256]; // Text table - static const unsigned char lookup_text_pure_with_ws[256]; // Text table - static const unsigned char lookup_attribute_name[256]; // Attribute name table - static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote - static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote - static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes - static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes - static const unsigned char lookup_digits[256]; // Digits - static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters - }; + throw std::runtime_error("size arg is too large to fit into unsigned int type"); + } +#endif + if (size > max_) + { + throw std::runtime_error("size may use more memory than intended when decompressing"); + } - // Find length of the string - template - inline std::size_t measure(const Ch *p) + z_stream deflate_s; + deflate_s.zalloc = Z_NULL; + deflate_s.zfree = Z_NULL; + deflate_s.opaque = Z_NULL; + deflate_s.avail_in = 0; + deflate_s.next_in = Z_NULL; + + // The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). + // It should be in the range 8..15 for this version of the library. + // Larger values of this parameter result in better compression at the expense of memory usage. + // This range of values also changes the decoding type: + // -8 to -15 for raw deflate + // 8 to 15 for zlib + // (8 to 15) + 16 for gzip + // (8 to 15) + 32 to automatically detect gzip/zlib header (decompression/inflate only) + constexpr int window_bits = 15 + 16; // gzip with windowbits of 15 + + constexpr int mem_level = 8; + // The memory requirements for deflate are (in bytes): + // (1 << (window_bits+2)) + (1 << (mem_level+9)) + // with a default value of 8 for mem_level and our window_bits of 15 + // this is 128Kb + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" + if (deflateInit2(&deflate_s, level_, Z_DEFLATED, window_bits, mem_level, Z_DEFAULT_STRATEGY) != Z_OK) { - const Ch *tmp = p; - while (*tmp) - ++tmp; - return tmp - p; + throw std::runtime_error("deflate init failed"); } +#pragma GCC diagnostic pop - // Compare strings for equality - template - inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) + deflate_s.next_in = reinterpret_cast(data); + deflate_s.avail_in = static_cast(size); + + std::size_t size_compressed = 0; + do { - if (size1 != size2) - return false; - if (case_sensitive) - { - for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) - if (*p1 != *p2) - return false; - } - else + size_t increase = size / 2 + 1024; + if (output.size() < (size_compressed + increase)) { - for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) - if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) - return false; + output.resize(size_compressed + increase); } - return true; - } + // There is no way we see that "increase" would not fit in an unsigned int, + // hence we use static cast here to avoid -Wshorten-64-to-32 error + deflate_s.avail_out = static_cast(increase); + deflate_s.next_out = reinterpret_cast((&output[0] + size_compressed)); + // From http://www.zlib.net/zlib_how.html + // "deflate() has a return value that can indicate errors, yet we do not check it here. + // Why not? Well, it turns out that deflate() can do no wrong here." + // Basically only possible error is from deflateInit not working properly + deflate(&deflate_s, Z_FINISH); + size_compressed += (increase - deflate_s.avail_out); + } while (deflate_s.avail_out == 0); + + deflateEnd(&deflate_s); + output.resize(size_compressed); } - //! \endcond +}; - /////////////////////////////////////////////////////////////////////// - // Memory pool +inline std::string compress(const char* data, + std::size_t size, + int level = Z_DEFAULT_COMPRESSION) +{ + Compressor comp(level); + std::string output; + comp.compress(output, data, size); + return output; +} - //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. - //! In most cases, you will not need to use this class directly. - //! However, if you need to create nodes manually or modify names/values of nodes, - //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. - //! Not only is this faster than allocating them by using new operator, - //! but also their lifetime will be tied to the lifetime of document, - //! possibly simplyfing memory management. - //!

- //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. - //! You can also call allocate_string() function to allocate strings. - //! Such strings can then be used as names or values of nodes without worrying about their lifetime. - //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, - //! or when the pool is destroyed. - //!

- //! It is also possible to create a standalone memory_pool, and use it - //! to allocate nodes, whose lifetime will not be tied to any document. - //!

- //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. - //! Until static memory is exhausted, no dynamic memory allocations are done. - //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, - //! by using global new[] and delete[] operators. - //! This behaviour can be changed by setting custom allocation routines. - //! Use set_allocator() function to set them. - //!

- //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. - //! This value defaults to the size of pointer on target architecture. - //!

- //! To obtain absolutely top performance from the parser, - //! it is important that all nodes are allocated from a single, contiguous block of memory. - //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. - //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT - //! to obtain best wasted memory to performance compromise. - //! To do it, define their values before rapidxml.hpp file is included. - //! \param Ch Character type of created nodes. - template - class memory_pool - { +} // namespace gzip - public: +#endif // ARBITER_ZLIB - //! \cond internal - typedef void *(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory - typedef void (free_func)(void *); // Type of user-defined function used to free memory - //! \endcond - //! Constructs empty pool with default allocator functions. - memory_pool() - : m_alloc_func(0) - , m_free_func(0) +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/third/gzip/compress.hpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/third/gzip/decompress.hpp +// ////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifdef ARBITER_ZLIB + +#ifndef ARBITER_IS_AMALGAMATION +#include "config.hpp" +#endif + +// zlib +#include + +// std +#include +#include +#include +#include + +namespace gzip { + +class Decompressor +{ + std::size_t max_; + + public: + Decompressor(std::size_t max_bytes = 1000000000) // by default refuse operation if compressed data is > 1GB + : max_(max_bytes) + { + } + + template + void decompress(OutputType& output, + const char* data, + std::size_t size) const + { + z_stream inflate_s; + + inflate_s.zalloc = Z_NULL; + inflate_s.zfree = Z_NULL; + inflate_s.opaque = Z_NULL; + inflate_s.avail_in = 0; + inflate_s.next_in = Z_NULL; + + // The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). + // It should be in the range 8..15 for this version of the library. + // Larger values of this parameter result in better compression at the expense of memory usage. + // This range of values also changes the decoding type: + // -8 to -15 for raw deflate + // 8 to 15 for zlib + // (8 to 15) + 16 for gzip + // (8 to 15) + 32 to automatically detect gzip/zlib header + constexpr int window_bits = 15 + 32; // auto with windowbits of 15 + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" + if (inflateInit2(&inflate_s, window_bits) != Z_OK) { - init(); + throw std::runtime_error("inflate init failed"); } +#pragma GCC diagnostic pop + inflate_s.next_in = reinterpret_cast(data); - //! Destroys pool and frees all the memory. - //! This causes memory occupied by nodes allocated by the pool to be freed. - //! Nodes allocated from the pool are no longer valid. - ~memory_pool() +#ifdef DEBUG + // Verify if size (long type) input will fit into unsigned int, type used for zlib's avail_in + std::uint64_t size_64 = size * 2; + if (size_64 > std::numeric_limits::max()) { - clear(); + inflateEnd(&inflate_s); + throw std::runtime_error("size arg is too large to fit into unsigned int type x2"); } - - //! Allocates a new node from the pool, and optionally assigns name and value to it. - //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. - //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function - //! will call rapidxml::parse_error_handler() function. - //! \param type Type of node to create. - //! \param name Name to assign to the node, or 0 to assign no name. - //! \param value Value to assign to the node, or 0 to assign no value. - //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. - //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. - //! \return Pointer to allocated node. This pointer will never be NULL. - xml_node *allocate_node(node_type type, - const Ch *name = 0, const Ch *value = 0, - std::size_t name_size = 0, std::size_t value_size = 0) +#endif + if (size > max_ || (size * 2) > max_) { - void *memory = allocate_aligned(sizeof(xml_node)); - xml_node *node = new(memory) xml_node(type); - if (name) - { - if (name_size > 0) - node->name(name, name_size); - else - node->name(name); - } - if (value) - { - if (value_size > 0) - node->value(value, value_size); - else - node->value(value); - } - return node; + inflateEnd(&inflate_s); + throw std::runtime_error("size may use more memory than intended when decompressing"); } - - //! Allocates a new attribute from the pool, and optionally assigns name and value to it. - //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. - //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function - //! will call rapidxml::parse_error_handler() function. - //! \param name Name to assign to the attribute, or 0 to assign no name. - //! \param value Value to assign to the attribute, or 0 to assign no value. - //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. - //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. - //! \return Pointer to allocated attribute. This pointer will never be NULL. - xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, - std::size_t name_size = 0, std::size_t value_size = 0) + inflate_s.avail_in = static_cast(size); + std::size_t size_uncompressed = 0; + do { - void *memory = allocate_aligned(sizeof(xml_attribute)); - xml_attribute *attribute = new(memory) xml_attribute; - if (name) + std::size_t resize_to = size_uncompressed + 2 * size; + if (resize_to > max_) { - if (name_size > 0) - attribute->name(name, name_size); - else - attribute->name(name); + inflateEnd(&inflate_s); + throw std::runtime_error("size of output string will use more memory then intended when decompressing"); } - if (value) + output.resize(resize_to); + inflate_s.avail_out = static_cast(2 * size); + inflate_s.next_out = reinterpret_cast(&output[0] + size_uncompressed); + int ret = inflate(&inflate_s, Z_FINISH); + if (ret != Z_STREAM_END && ret != Z_OK && ret != Z_BUF_ERROR) { - if (value_size > 0) - attribute->value(value, value_size); - else - attribute->value(value); + std::string error_msg = inflate_s.msg; + inflateEnd(&inflate_s); + throw std::runtime_error(error_msg); } - return attribute; - } - //! Allocates a char array of given size from the pool, and optionally copies a given string to it. - //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. - //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function - //! will call rapidxml::parse_error_handler() function. - //! \param source String to initialize the allocated memory with, or 0 to not initialize it. - //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. - //! \return Pointer to allocated char array. This pointer will never be NULL. - Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) - { - assert(source || size); // Either source or size (or both) must be specified - if (size == 0) - size = internal::measure(source) + 1; - Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); - if (source) - for (std::size_t i = 0; i < size; ++i) - result[i] = source[i]; - return result; - } + size_uncompressed += (2 * size - inflate_s.avail_out); + } while (inflate_s.avail_out == 0); + inflateEnd(&inflate_s); + output.resize(size_uncompressed); + } +}; - //! Clones an xml_node and its hierarchy of child nodes and attributes. - //! Nodes and attributes are allocated from this memory pool. - //! Names and values are not cloned, they are shared between the clone and the source. - //! Result node can be optionally specified as a second parameter, - //! in which case its contents will be replaced with cloned source node. - //! This is useful when you want to clone entire document. - //! \param source Node to clone. - //! \param result Node to put results in, or 0 to automatically allocate result node - //! \return Pointer to cloned node. This pointer will never be NULL. - xml_node *clone_node(const xml_node *source, xml_node *result = 0) - { - // Prepare result node - if (result) - { - result->remove_all_attributes(); - result->remove_all_nodes(); - result->type(source->type()); - } - else - result = allocate_node(source->type()); +inline std::string decompress(const char* data, std::size_t size) +{ + Decompressor decomp; + std::string output; + decomp.decompress(output, data, size); + return output; +} - // Clone name and value - result->name(source->name(), source->name_size()); - result->value(source->value(), source->value_size()); +} // namespace gzip - // Clone child nodes and attributes - for (xml_node *child = source->first_node(); child; child = child->next_sibling()) - result->append_node(clone_node(child)); - for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) - result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); +#endif // ARBITER_ZLIB - return result; - } - //! Clears the pool. - //! This causes memory occupied by nodes allocated by the pool to be freed. - //! Any nodes or strings allocated from the pool will no longer be valid. - void clear() - { - while (m_begin != m_static_memory) - { - char *previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; - if (m_free_func) - m_free_func(m_begin); - else - delete[] m_begin; - m_begin = previous_begin; - } - init(); - } +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/third/gzip/decompress.hpp +// ////////////////////////////////////////////////////////////////////// - //! Sets or resets the user-defined memory allocation functions for the pool. - //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. - //! Allocation function must not return invalid pointer on failure. It should either throw, - //! stop the program, or use longjmp() function to pass control to other place of program. - //! If it returns invalid pointer, results are undefined. - //!

- //! User defined allocation functions must have the following forms: - //!
- //!
void *allocate(std::size_t size); - //!
void free(void *pointer); - //!

- //! \param af Allocation function, or 0 to restore default function - //! \param ff Free function, or 0 to restore default function - void set_allocator(alloc_func *af, free_func *ff) - { - assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet - m_alloc_func = af; - m_free_func = ff; - } - private: - struct header - { - char *previous_begin; - }; - void init() - { - m_begin = m_static_memory; - m_ptr = align(m_begin); - m_end = m_static_memory + sizeof(m_static_memory); - } - char *align(char *ptr) - { - std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); - return ptr + alignment; - } - char *allocate_raw(std::size_t size) - { - // Allocate - void *memory; - if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] - { - memory = m_alloc_func(size); - assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp - } - else - { - memory = new char[size]; -#ifdef RAPIDXML_NO_EXCEPTIONS - if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc - RAPIDXML_PARSE_ERROR("out of memory", 0); -#endif - } - return static_cast(memory); - } +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/util/exports.hpp +// ////////////////////////////////////////////////////////////////////// - void *allocate_aligned(std::size_t size) - { - // Calculate aligned pointer - char *result = align(m_ptr); +#pragma once - // If not enough memory left in current pool, allocate a new pool - if (result + size > m_end) - { - // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) - std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; - if (pool_size < size) - pool_size = size; +#if defined(_WIN32) || defined(WIN32) || defined(_MSC_VER) +#define ARBITER_WINDOWS +#endif - // Allocate - std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation - char *raw_memory = allocate_raw(alloc_size); +#ifndef ARBITER_DLL +#if defined(ARBITER_WINDOWS) +#if defined(ARBITER_DLL_EXPORT) +# define ARBITER_DLL __declspec(dllexport) +#elif defined(PDAL_DLL_IMPORT) +# define ARBITER_DLL __declspec(dllimport) +#else +# define ARBITER_DLL +#endif +#else +# if defined(USE_GCC_VISIBILITY_FLAG) +# define ARBITER_DLL __attribute__ ((visibility("default"))) +# else +# define ARBITER_DLL +# endif +#endif +#endif - // Setup new pool in allocated memory - char *pool = align(raw_memory); - header *new_header = reinterpret_cast
(pool); - new_header->previous_begin = m_begin; - m_begin = raw_memory; - m_ptr = pool + sizeof(header); - m_end = raw_memory + alloc_size; +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/util/exports.hpp +// ////////////////////////////////////////////////////////////////////// - // Calculate aligned pointer again using new pool - result = align(m_ptr); - } - // Update pool and return aligned pointer - m_ptr = result + size; - return result; - } - char *m_begin; // Start of raw memory making up current pool - char *m_ptr; // First free byte in current pool - char *m_end; // One past last available byte in current pool - char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory - alloc_func *m_alloc_func; // Allocator function, or 0 if default is to be used - free_func *m_free_func; // Free function, or 0 if default is to be used - }; - /////////////////////////////////////////////////////////////////////////// - // XML base - //! Base class for xml_node and xml_attribute implementing common functions: - //! name(), name_size(), value(), value_size() and parent(). - //! \param Ch Character type to use - template - class xml_base - { - public: +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/util/types.hpp +// ////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - // Construction & destruction +#pragma once - // Construct a base with empty name, value and parent - xml_base() - : m_name(0) - , m_value(0) - , m_parent(0) - { - } +#include +#include +#include +#include - /////////////////////////////////////////////////////////////////////////// - // Node data access +#ifdef ARBITER_CUSTOM_NAMESPACE +namespace ARBITER_CUSTOM_NAMESPACE +{ +#endif - //! Gets name of the node. - //! Interpretation of name depends on type of node. - //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. - //!

- //! Use name_size() function to determine length of the name. - //! \return Name of node, or empty string if node has no name. - Ch *name() const - { - return m_name ? m_name : nullstr(); - } +namespace arbiter +{ - //! Gets size of node name, not including terminator character. - //! This function works correctly irrespective of whether name is or is not zero terminated. - //! \return Size of node name, in characters. - std::size_t name_size() const - { - return m_name ? m_name_size : 0; - } +/** @brief Exception class for all internally thrown runtime errors. */ +class ArbiterError : public std::runtime_error +{ +public: + ArbiterError(std::string msg) : std::runtime_error(msg) { } +}; - //! Gets value of node. - //! Interpretation of value depends on type of node. - //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. - //!

- //! Use value_size() function to determine length of the value. - //! \return Value of node, or empty string if node has no value. - Ch *value() const - { - return m_value ? m_value : nullstr(); - } +namespace http +{ - //! Gets size of node value, not including terminator character. - //! This function works correctly irrespective of whether value is or is not zero terminated. - //! \return Size of node value, in characters. - std::size_t value_size() const - { - return m_value ? m_value_size : 0; - } +/** HTTP header fields. */ +using Headers = std::map; - /////////////////////////////////////////////////////////////////////////// - // Node modification +/** HTTP query parameters. */ +using Query = std::map; - //! Sets name of node to a non zero-terminated string. - //! See \ref ownership_of_strings. - //!

- //! Note that node does not own its name or value, it only stores a pointer to it. - //! It will not delete or otherwise free the pointer on destruction. - //! It is reponsibility of the user to properly manage lifetime of the string. - //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - - //! on destruction of the document the string will be automatically freed. - //!

- //! Size of name must be specified separately, because name does not have to be zero terminated. - //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). - //! \param name Name of node to set. Does not have to be zero terminated. - //! \param size Size of name, in characters. This does not include zero terminator, if one is present. - void name(const Ch *name, std::size_t size) - { - m_name = const_cast(name); - m_name_size = size; - } +/** @cond arbiter_internal */ - //! Sets name of node to a zero-terminated string. - //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). - //! \param name Name of node to set. Must be zero terminated. - void name(const Ch *name) - { - this->name(name, internal::measure(name)); - } +class Response +{ +public: + Response(int code = 0) + : m_code(code) + , m_data() + { } - //! Sets value of node to a non zero-terminated string. - //! See \ref ownership_of_strings. - //!

- //! Note that node does not own its name or value, it only stores a pointer to it. - //! It will not delete or otherwise free the pointer on destruction. - //! It is reponsibility of the user to properly manage lifetime of the string. - //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - - //! on destruction of the document the string will be automatically freed. - //!

- //! Size of value must be specified separately, because it does not have to be zero terminated. - //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). - //!

- //! If an element has a child node of type node_data, it will take precedence over element value when printing. - //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. - //! \param value value of node to set. Does not have to be zero terminated. - //! \param size Size of value, in characters. This does not include zero terminator, if one is present. - void value(const Ch *value, std::size_t size) - { - m_value = const_cast(value); - m_value_size = size; - } + Response(int code, std::vector data) + : m_code(code) + , m_data(data) + { } - //! Sets value of node to a zero-terminated string. - //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). - //! \param value Vame of node to set. Must be zero terminated. - void value(const Ch *value) - { - this->value(value, internal::measure(value)); - } + Response( + int code, + const std::vector& data, + const Headers& headers) + : m_code(code) + , m_data(data) + , m_headers(headers) + { } - /////////////////////////////////////////////////////////////////////////// - // Related nodes access + ~Response() { } - //! Gets node parent. - //! \return Pointer to parent node, or 0 if there is no parent. - xml_node *parent() const - { - return m_parent; - } + bool ok() const { return m_code / 100 == 2; } + bool clientError() const { return m_code / 100 == 4; } + bool serverError() const { return m_code / 100 == 5; } + int code() const { return m_code; } - protected: + std::vector data() const { return m_data; } + const Headers& headers() const { return m_headers; } + std::string str() const + { + return std::string(data().data(), data().size()); + } - // Return empty string - static Ch *nullstr() - { - static Ch zero = Ch('\0'); - return &zero; - } +private: + int m_code; + std::vector m_data; + Headers m_headers; +}; - Ch *m_name; // Name of node, or 0 if no name - Ch *m_value; // Value of node, or 0 if no value - std::size_t m_name_size; // Length of node name, or undefined of no name - std::size_t m_value_size; // Length of node value, or undefined if no value - xml_node *m_parent; // Pointer to parent node, or 0 if none +/** @endcond */ - }; +} // namespace http +} // namespace arbiter - //! Class representing attribute node of XML document. - //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). - //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. - //! Thus, this text must persist in memory for the lifetime of attribute. - //! \param Ch Character type to use. - template - class xml_attribute: public xml_base - { +#ifdef ARBITER_CUSTOM_NAMESPACE +} +#endif - friend class xml_node; - public: +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/util/types.hpp +// ////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - // Construction & destruction - //! Constructs an empty attribute with the specified type. - //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. - xml_attribute() - { - } - /////////////////////////////////////////////////////////////////////////// - // Related nodes access - //! Gets document of which attribute is a child. - //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. - xml_document *document() const - { - if (xml_node *node = this->parent()) - { - while (node->parent()) - node = node->parent(); - return node->type() == node_document ? static_cast *>(node) : 0; - } - else - return 0; - } - //! Gets previous attribute, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - return attribute; - return 0; - } - else - return this->m_parent ? m_prev_attribute : 0; - } - //! Gets next attribute, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - return attribute; - return 0; - } - else - return this->m_parent ? m_next_attribute : 0; - } +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/util/curl.hpp +// ////////////////////////////////////////////////////////////////////// - private: +#pragma once - xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero - xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero +#include +#include +#include +#include - }; +#ifndef ARBITER_IS_AMALGAMATION - /////////////////////////////////////////////////////////////////////////// - // XML node +#include +#include - //! Class representing a node of XML document. - //! Each node may have associated name and value strings, which are available through name() and value() functions. - //! Interpretation of name and value depends on type of the node. - //! Type of node can be determined by using type() function. - //!

- //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. - //! Thus, this text must persist in the memory for the lifetime of node. - //! \param Ch Character type to use. - template - class xml_node: public xml_base - { - - public: - /////////////////////////////////////////////////////////////////////////// - // Construction & destruction +#ifndef ARBITER_EXTERNAL_JSON +#include +#endif - //! Constructs an empty node with the specified type. - //! Consider using memory_pool of appropriate document to allocate nodes manually. - //! \param type Type of node to construct. - xml_node(node_type type) - : m_type(type) - , m_first_node(0) - , m_first_attribute(0) - { - } +#endif - /////////////////////////////////////////////////////////////////////////// - // Node data access - //! Gets type of node. - //! \return Type of node. - node_type type() const - { - return m_type; - } - /////////////////////////////////////////////////////////////////////////// - // Related nodes access +#ifdef ARBITER_EXTERNAL_JSON +#include +#endif - //! Gets document of which node is a child. - //! \return Pointer to document that contains this node, or 0 if there is no parent document. - xml_document *document() const - { - xml_node *node = const_cast *>(this); - while (node->parent()) - node = node->parent(); - return node->type() == node_document ? static_cast *>(node) : 0; - } +#ifdef ARBITER_CURL +#include +#else +typedef void CURL; +#endif - //! Gets first child node, optionally matching node name. - //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found child, or 0 if not found. - xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_node *child = m_first_node; child; child = child->next_sibling()) - if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) - return child; - return 0; - } - else - return m_first_node; - } +struct curl_slist; - //! Gets last child node, optionally matching node name. - //! Behaviour is undefined if node has no children. - //! Use first_node() to test if node has children. - //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found child, or 0 if not found. - xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - assert(m_first_node); // Cannot query for last child if node has no children - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_node *child = m_last_node; child; child = child->previous_sibling()) - if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) - return child; - return 0; - } - else - return m_last_node; - } +#ifdef ARBITER_CUSTOM_NAMESPACE +namespace ARBITER_CUSTOM_NAMESPACE +{ +#endif - //! Gets previous sibling node, optionally matching node name. - //! Behaviour is undefined if node has no parent. - //! Use parent() to test if node has a parent. - //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found sibling, or 0 if not found. - xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - assert(this->m_parent); // Cannot query for siblings if node has no parent - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) - if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) - return sibling; - return 0; - } - else - return m_prev_sibling; - } +namespace arbiter +{ +namespace http +{ - //! Gets next sibling node, optionally matching node name. - //! Behaviour is undefined if node has no parent. - //! Use parent() to test if node has a parent. - //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found sibling, or 0 if not found. - xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - assert(this->m_parent); // Cannot query for siblings if node has no parent - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) - if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) - return sibling; - return 0; - } - else - return m_next_sibling; - } +/** @cond arbiter_internal */ - //! Gets first attribute of node, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - return attribute; - return 0; - } - else - return m_first_attribute; - } +class Pool; - //! Gets last attribute of node, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - return attribute; - return 0; - } - else - return m_first_attribute ? m_last_attribute : 0; - } +class ARBITER_DLL Curl +{ + friend class Pool; - /////////////////////////////////////////////////////////////////////////// - // Node modification + static constexpr std::size_t defaultHttpTimeout = 5; - //! Sets type of node. - //! \param type Type of node to set. - void type(node_type type) - { - m_type = type; - } +public: + ~Curl(); - /////////////////////////////////////////////////////////////////////////// - // Node manipulation + http::Response get( + std::string path, + Headers headers, + Query query, + std::size_t reserve); - //! Prepends a new child node. - //! The prepended child becomes the first child, and all existing children are moved one position back. - //! \param child Node to prepend. - void prepend_node(xml_node *child) - { - assert(child && !child->parent() && child->type() != node_document); - if (first_node()) - { - child->m_next_sibling = m_first_node; - m_first_node->m_prev_sibling = child; - } - else - { - child->m_next_sibling = 0; - m_last_node = child; - } - m_first_node = child; - child->m_parent = this; - child->m_prev_sibling = 0; - } + http::Response head(std::string path, Headers headers, Query query); - //! Appends a new child node. - //! The appended child becomes the last child. - //! \param child Node to append. - void append_node(xml_node *child) - { - assert(child && !child->parent() && child->type() != node_document); - if (first_node()) - { - child->m_prev_sibling = m_last_node; - m_last_node->m_next_sibling = child; - } - else - { - child->m_prev_sibling = 0; - m_first_node = child; - } - m_last_node = child; - child->m_parent = this; - child->m_next_sibling = 0; - } + http::Response put( + std::string path, + const std::vector& data, + Headers headers, + Query query); - //! Inserts a new child node at specified place inside the node. - //! All children after and including the specified node are moved one position back. - //! \param where Place where to insert the child, or 0 to insert at the back. - //! \param child Node to insert. - void insert_node(xml_node *where, xml_node *child) - { - assert(!where || where->parent() == this); - assert(child && !child->parent() && child->type() != node_document); - if (where == m_first_node) - prepend_node(child); - else if (where == 0) - append_node(child); - else - { - child->m_prev_sibling = where->m_prev_sibling; - child->m_next_sibling = where; - where->m_prev_sibling->m_next_sibling = child; - where->m_prev_sibling = child; - child->m_parent = this; - } - } + http::Response post( + std::string path, + const std::vector& data, + Headers headers, + Query query); - //! Removes first child node. - //! If node has no children, behaviour is undefined. - //! Use first_node() to test if node has children. - void remove_first_node() - { - assert(first_node()); - xml_node *child = m_first_node; - m_first_node = child->m_next_sibling; - if (child->m_next_sibling) - child->m_next_sibling->m_prev_sibling = 0; - else - m_last_node = 0; - child->m_parent = 0; - } +private: + Curl(const Json::Value& json = Json::Value()); - //! Removes last child of the node. - //! If node has no children, behaviour is undefined. - //! Use first_node() to test if node has children. - void remove_last_node() - { - assert(first_node()); - xml_node *child = m_last_node; - if (child->m_prev_sibling) - { - m_last_node = child->m_prev_sibling; - child->m_prev_sibling->m_next_sibling = 0; - } - else - m_first_node = 0; - child->m_parent = 0; - } + void init(std::string path, const Headers& headers, const Query& query); - //! Removes specified child from the node - // \param where Pointer to child to be removed. - void remove_node(xml_node *where) - { - assert(where && where->parent() == this); - assert(first_node()); - if (where == m_first_node) - remove_first_node(); - else if (where == m_last_node) - remove_last_node(); - else - { - where->m_prev_sibling->m_next_sibling = where->m_next_sibling; - where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; - where->m_parent = 0; - } - } + // Returns HTTP status code. + int perform(); - //! Removes all child nodes (but not attributes). - void remove_all_nodes() - { - for (xml_node *node = first_node(); node; node = node->m_next_sibling) - node->m_parent = 0; - m_first_node = 0; - } + Curl(const Curl&); + Curl& operator=(const Curl&); - //! Prepends a new attribute to the node. - //! \param attribute Attribute to prepend. - void prepend_attribute(xml_attribute *attribute) - { - assert(attribute && !attribute->parent()); - if (first_attribute()) - { - attribute->m_next_attribute = m_first_attribute; - m_first_attribute->m_prev_attribute = attribute; - } - else - { - attribute->m_next_attribute = 0; - m_last_attribute = attribute; - } - m_first_attribute = attribute; - attribute->m_parent = this; - attribute->m_prev_attribute = 0; - } + CURL* m_curl = nullptr; + curl_slist* m_headers = nullptr; - //! Appends a new attribute to the node. - //! \param attribute Attribute to append. - void append_attribute(xml_attribute *attribute) - { - assert(attribute && !attribute->parent()); - if (first_attribute()) - { - attribute->m_prev_attribute = m_last_attribute; - m_last_attribute->m_next_attribute = attribute; - } - else - { - attribute->m_prev_attribute = 0; - m_first_attribute = attribute; - } - m_last_attribute = attribute; - attribute->m_parent = this; - attribute->m_next_attribute = 0; - } + bool m_verbose = false; + long m_timeout = defaultHttpTimeout; + bool m_followRedirect = true; + bool m_verifyPeer = true; + std::unique_ptr m_caPath; + std::unique_ptr m_caInfo; - //! Inserts a new attribute at specified place inside the node. - //! All attributes after and including the specified attribute are moved one position back. - //! \param where Place where to insert the attribute, or 0 to insert at the back. - //! \param attribute Attribute to insert. - void insert_attribute(xml_attribute *where, xml_attribute *attribute) - { - assert(!where || where->parent() == this); - assert(attribute && !attribute->parent()); - if (where == m_first_attribute) - prepend_attribute(attribute); - else if (where == 0) - append_attribute(attribute); - else - { - attribute->m_prev_attribute = where->m_prev_attribute; - attribute->m_next_attribute = where; - where->m_prev_attribute->m_next_attribute = attribute; - where->m_prev_attribute = attribute; - attribute->m_parent = this; - } - } + std::vector m_data; +}; - //! Removes first attribute of the node. - //! If node has no attributes, behaviour is undefined. - //! Use first_attribute() to test if node has attributes. - void remove_first_attribute() - { - assert(first_attribute()); - xml_attribute *attribute = m_first_attribute; - if (attribute->m_next_attribute) - { - attribute->m_next_attribute->m_prev_attribute = 0; - } - else - m_last_attribute = 0; - attribute->m_parent = 0; - m_first_attribute = attribute->m_next_attribute; - } +/** @endcond */ - //! Removes last attribute of the node. - //! If node has no attributes, behaviour is undefined. - //! Use first_attribute() to test if node has attributes. - void remove_last_attribute() - { - assert(first_attribute()); - xml_attribute *attribute = m_last_attribute; - if (attribute->m_prev_attribute) - { - attribute->m_prev_attribute->m_next_attribute = 0; - m_last_attribute = attribute->m_prev_attribute; - } - else - m_first_attribute = 0; - attribute->m_parent = 0; - } +} // namespace http +} // namespace arbiter - //! Removes specified attribute from node. - //! \param where Pointer to attribute to be removed. - void remove_attribute(xml_attribute *where) - { - assert(first_attribute() && where->parent() == this); - if (where == m_first_attribute) - remove_first_attribute(); - else if (where == m_last_attribute) - remove_last_attribute(); - else - { - where->m_prev_attribute->m_next_attribute = where->m_next_attribute; - where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; - where->m_parent = 0; - } - } +#ifdef ARBITER_CUSTOM_NAMESPACE +} +#endif - //! Removes all attributes of node. - void remove_all_attributes() - { - for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) - attribute->m_parent = 0; - m_first_attribute = 0; - } - private: +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/util/curl.hpp +// ////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - // Restrictions - // No copying - xml_node(const xml_node &); - void operator =(const xml_node &); - /////////////////////////////////////////////////////////////////////////// - // Data members - // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. - // This is required for maximum performance, as it allows the parser to omit initialization of - // unneded/redundant values. - // - // The rules are as follows: - // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively - // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage - // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage - node_type m_type; // Type of node; always valid - xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid - xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero - xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid - xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero - xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero - xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero - }; +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/util/http.hpp +// ////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - // XML document +#pragma once - //! This class represents root of the DOM hierarchy. - //! It is also an xml_node and a memory_pool through public inheritance. - //! Use parse() function to build a DOM tree from a zero-terminated XML text string. - //! parse() function allocates memory for nodes and attributes by using functions of xml_document, - //! which are inherited from memory_pool. - //! To access root node of the document, use the document itself, as if it was an xml_node. - //! \param Ch Character type to use. - template - class xml_document: public xml_node, public memory_pool - { - - public: +#include +#include +#include +#include +#include +#include +#include +#include - //! Constructs empty XML document - xml_document() - : xml_node(node_document) - { - } +#ifndef ARBITER_IS_AMALGAMATION - //! Parses zero-terminated XML string according to given flags. - //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. - //! The string must persist for the lifetime of the document. - //! In case of error, rapidxml::parse_error exception will be thrown. - //!

- //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. - //! Make sure that data is zero-terminated. - //!

- //! Document can be parsed into multiple times. - //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. - //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. - template - void parse(Ch *text) - { - assert(text); +#include +#include +#include - // Remove current contents - this->remove_all_nodes(); - this->remove_all_attributes(); +#ifndef ARBITER_EXTERNAL_JSON +#include +#endif - // Parse BOM, if any - parse_bom(text); +#endif - // Parse children - while (1) - { - // Skip whitespace before node - skip(text); - if (*text == 0) - break; - // Parse and append new child - if (*text == Ch('<')) - { - ++text; // Skip '<' - if (xml_node *node = parse_node(text)) - this->append_node(node); - } - else - RAPIDXML_PARSE_ERROR("expected <", text); - } - } +#ifdef ARBITER_EXTERNAL_JSON +#include +#endif - //! Clears the document by deleting all nodes and clearing the memory pool. - //! All nodes owned by document pool are destroyed. - void clear() - { - this->remove_all_nodes(); - this->remove_all_attributes(); - memory_pool::clear(); - } +#ifdef ARBITER_CUSTOM_NAMESPACE +namespace ARBITER_CUSTOM_NAMESPACE +{ +#endif - private: +namespace arbiter +{ +namespace http +{ - /////////////////////////////////////////////////////////////////////// - // Internal character utility functions +/** Perform URI percent-encoding, without encoding characters included in + * @p exclusions. + */ +ARBITER_DLL std::string sanitize(std::string path, std::string exclusions = "/"); - // Detect whitespace character - struct whitespace_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; - } - }; +/** Build a query string from key-value pairs. If @p query is empty, the + * result is an empty string. Otherwise, the result will start with the + * '?' character. + */ +ARBITER_DLL std::string buildQueryString(const http::Query& query); - // Detect node name character - struct node_name_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; - } - }; +/** @cond arbiter_internal */ - // Detect attribute name character - struct attribute_name_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; - } - }; +class ARBITER_DLL Pool; - // Detect text character (PCDATA) - struct text_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; - } - }; +class ARBITER_DLL Resource +{ +public: + Resource(Pool& pool, Curl& curl, std::size_t id, std::size_t retry); + ~Resource(); - // Detect text character (PCDATA) that does not require processing - struct text_pure_no_ws_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; - } - }; + http::Response get( + std::string path, + Headers headers = Headers(), + Query query = Query(), + std::size_t reserve = 0); - // Detect text character (PCDATA) that does not require processing - struct text_pure_with_ws_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; - } - }; + http::Response head( + std::string path, + Headers headers = Headers(), + Query query = Query()); - // Detect attribute value character - template - struct attribute_value_pred - { - static unsigned char test(Ch ch) - { - if (Quote == Ch('\'')) - return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; - if (Quote == Ch('\"')) - return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; - return 0; // Should never be executed, to avoid warnings on Comeau - } - }; + http::Response put( + std::string path, + const std::vector& data, + Headers headers = Headers(), + Query query = Query()); - // Detect attribute value character - template - struct attribute_value_pure_pred - { - static unsigned char test(Ch ch) - { - if (Quote == Ch('\'')) - return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; - if (Quote == Ch('\"')) - return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; - return 0; // Should never be executed, to avoid warnings on Comeau - } - }; + http::Response post( + std::string path, + const std::vector& data, + Headers headers = Headers(), + Query query = Query()); - // Insert coded character, using UTF8 or 8-bit ASCII - template - static void insert_coded_character(Ch *&text, unsigned long code) - { - if (Flags & parse_no_utf8) - { - // Insert 8-bit ASCII character - // Todo: possibly verify that code is less than 256 and use replacement char otherwise? - text[0] = static_cast(code); - text += 1; - } - else - { - // Insert UTF8 sequence - if (code < 0x80) // 1 byte sequence - { - text[0] = static_cast(code); - text += 1; - } - else if (code < 0x800) // 2 byte sequence - { - text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; - text[0] = static_cast(code | 0xC0); - text += 2; - } - else if (code < 0x10000) // 3 byte sequence - { - text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; - text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; - text[0] = static_cast(code | 0xE0); - text += 3; - } - else if (code < 0x110000) // 4 byte sequence - { - text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; - text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; - text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; - text[0] = static_cast(code | 0xF0); - text += 4; - } - else // Invalid, only codes up to 0x10FFFF are allowed in Unicode - { - RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); - } - } - } +private: + Pool& m_pool; + Curl& m_curl; + std::size_t m_id; + std::size_t m_retry; - // Skip characters until predicate evaluates to true - template - static void skip(Ch *&text) - { - Ch *tmp = text; - while (StopPred::test(*tmp)) - ++tmp; - text = tmp; - } + http::Response exec(std::function f); +}; - // Skip characters until predicate evaluates to true while doing the following: - // - replacing XML character entity references with proper characters (' & " < > &#...;) - // - condensing whitespace sequences to single space character - template - static Ch *skip_and_expand_character_refs(Ch *&text) - { - // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip - if (Flags & parse_no_entity_translation && - !(Flags & parse_normalize_whitespace) && - !(Flags & parse_trim_whitespace)) - { - skip(text); - return text; - } +class ARBITER_DLL Pool +{ + // Only HttpResource may release. + friend class Resource; - // Use simple skip until first modification is detected - skip(text); +public: + Pool( + std::size_t concurrent = 4, + std::size_t retry = 4, + Json::Value json = Json::Value()); + ~Pool(); - // Use translation skip - Ch *src = text; - Ch *dest = src; - while (StopPred::test(*src)) - { - // If entity translation is enabled - if (!(Flags & parse_no_entity_translation)) - { - // Test if replacement is needed - if (src[0] == Ch('&')) - { - switch (src[1]) - { + Resource acquire(); - // & ' - case Ch('a'): - if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) - { - *dest = Ch('&'); - ++dest; - src += 5; - continue; - } - if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) - { - *dest = Ch('\''); - ++dest; - src += 6; - continue; - } - break; +private: + void release(std::size_t id); - // " - case Ch('q'): - if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) - { - *dest = Ch('"'); - ++dest; - src += 6; - continue; - } - break; + std::vector> m_curls; + std::vector m_available; + std::size_t m_retry; - // > - case Ch('g'): - if (src[2] == Ch('t') && src[3] == Ch(';')) - { - *dest = Ch('>'); - ++dest; - src += 4; - continue; - } - break; + std::mutex m_mutex; + std::condition_variable m_cv; +}; - // < - case Ch('l'): - if (src[2] == Ch('t') && src[3] == Ch(';')) - { - *dest = Ch('<'); - ++dest; - src += 4; - continue; - } - break; +/** @endcond */ - // &#...; - assumes ASCII - case Ch('#'): - if (src[2] == Ch('x')) - { - unsigned long code = 0; - src += 3; // Skip &#x - while (1) - { - unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; - if (digit == 0xFF) - break; - code = code * 16 + digit; - ++src; - } - insert_coded_character(dest, code); // Put character in output - } - else - { - unsigned long code = 0; - src += 2; // Skip &# - while (1) - { - unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; - if (digit == 0xFF) - break; - code = code * 10 + digit; - ++src; - } - insert_coded_character(dest, code); // Put character in output - } - if (*src == Ch(';')) - ++src; - else - RAPIDXML_PARSE_ERROR("expected ;", src); - continue; +} // namespace http +} // namespace arbiter - // Something else - default: - // Ignore, just copy '&' verbatim - break; +#ifdef ARBITER_CUSTOM_NAMESPACE +} +#endif - } - } - } - // If whitespace condensing is enabled - if (Flags & parse_normalize_whitespace) - { - // Test if condensing is needed - if (whitespace_pred::test(*src)) - { - *dest = Ch(' '); ++dest; // Put single space in dest - ++src; // Skip first whitespace char - // Skip remaining whitespace chars - while (whitespace_pred::test(*src)) - ++src; - continue; - } - } +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/util/http.hpp +// ////////////////////////////////////////////////////////////////////// - // No replacement, only copy character - *dest++ = *src++; - } - // Return new end - text = src; - return dest; - } - /////////////////////////////////////////////////////////////////////// - // Internal parsing functions - // Parse BOM, if any - template - void parse_bom(Ch *&text) - { - // UTF-8? - if (static_cast(text[0]) == 0xEF && - static_cast(text[1]) == 0xBB && - static_cast(text[2]) == 0xBF) - { - text += 3; // Skup utf-8 bom - } - } +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/util/ini.hpp +// ////////////////////////////////////////////////////////////////////// - // Parse XML declaration ( - xml_node *parse_xml_declaration(Ch *&text) - { - // If parsing of declaration is disabled - if (!(Flags & parse_declaration_node)) - { - // Skip until end of declaration - while (text[0] != Ch('?') || text[1] != Ch('>')) - { - if (!text[0]) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } - text += 2; // Skip '?>' - return 0; - } +#pragma once - // Create declaration - xml_node *declaration = this->allocate_node(node_declaration); +#include +#include +#include - // Skip whitespace before attributes or ?> - skip(text); +#ifdef ARBITER_CUSTOM_NAMESPACE +namespace ARBITER_CUSTOM_NAMESPACE +{ +#endif - // Parse declaration attributes - parse_node_attributes(text, declaration); +namespace arbiter +{ +namespace ini +{ - // Skip ?> - if (text[0] != Ch('?') || text[1] != Ch('>')) - RAPIDXML_PARSE_ERROR("expected ?>", text); - text += 2; +using Section = std::string; +using Key = std::string; +using Val = std::string; +using Contents = std::map>; - return declaration; - } +Contents parse(const std::string& s); - // Parse XML comment (' - return 0; // Do not produce comment node - } +} // namespace ini - // Remember value start - Ch *value = text; +} // namespace arbiter - // Skip until end of comment - while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) - { - if (!text[0]) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } +#ifdef ARBITER_CUSTOM_NAMESPACE +} +#endif - // Create comment node - xml_node *comment = this->allocate_node(node_comment); - comment->value(value, text - value); - // Place zero terminator after comment value - if (!(Flags & parse_no_string_terminators)) - *text = Ch('\0'); +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/util/ini.hpp +// ////////////////////////////////////////////////////////////////////// - text += 3; // Skip '-->' - return comment; - } - // Parse DOCTYPE - template - xml_node *parse_doctype(Ch *&text) - { - // Remember value start - Ch *value = text; - // Skip to > - while (*text != Ch('>')) - { - // Determine character type - switch (*text) - { - // If '[' encountered, scan for matching ending ']' using naive algorithm with depth - // This works for all W3C test files except for 2 most wicked - case Ch('['): - { - ++text; // Skip '[' - int depth = 1; - while (depth > 0) - { - switch (*text) - { - case Ch('['): ++depth; break; - case Ch(']'): --depth; break; - case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); - } - ++text; - } - break; - } - // Error on end of text - case Ch('\0'): - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - // Other character, skip it - default: - ++text; +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/util/time.hpp +// ////////////////////////////////////////////////////////////////////// - } - } +#pragma once - // If DOCTYPE nodes enabled - if (Flags & parse_doctype_node) - { - // Create a new doctype node - xml_node *doctype = this->allocate_node(node_doctype); - doctype->value(value, text - value); +#include +#include +#include - // Place zero terminator after value - if (!(Flags & parse_no_string_terminators)) - *text = Ch('\0'); +#ifndef ARBITER_IS_AMALGAMATION +#include +#endif - text += 1; // skip '>' - return doctype; - } - else - { - text += 1; // skip '>' - return 0; - } - } - // Parse PI - template - xml_node *parse_pi(Ch *&text) - { - // If creation of PI nodes is enabled - if (Flags & parse_pi_nodes) - { - // Create pi node - xml_node *pi = this->allocate_node(node_pi); +#ifdef ARBITER_CUSTOM_NAMESPACE +namespace ARBITER_CUSTOM_NAMESPACE +{ +#endif - // Extract PI target name - Ch *name = text; - skip(text); - if (text == name) - RAPIDXML_PARSE_ERROR("expected PI target", text); - pi->name(name, text - name); +namespace arbiter +{ - // Skip whitespace between pi target and pi - skip(text); +class ARBITER_DLL Time +{ +public: + static const std::string iso8601; + static const std::string iso8601NoSeparators; + static const std::string dateNoSeparators; - // Remember start of pi - Ch *value = text; + Time(); + Time(const std::string& s, const std::string& format = "%Y-%m-%dT%H:%M:%SZ"); - // Skip to '?>' - while (text[0] != Ch('?') || text[1] != Ch('>')) - { - if (*text == Ch('\0')) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } + std::string str(const std::string& format = "%Y-%m-%dT%H:%M:%SZ") const; - // Set pi value (verbatim, no entity expansion or whitespace normalization) - pi->value(value, text - value); + // Return value is in seconds. + int64_t operator-(const Time& other) const; + int64_t asUnix() const; - // Place zero terminator after name and value - if (!(Flags & parse_no_string_terminators)) - { - pi->name()[pi->name_size()] = Ch('\0'); - pi->value()[pi->value_size()] = Ch('\0'); - } +private: + std::time_t m_time; +}; - text += 2; // Skip '?>' - return pi; - } - else - { - // Skip to '?>' - while (text[0] != Ch('?') || text[1] != Ch('>')) - { - if (*text == Ch('\0')) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } - text += 2; // Skip '?>' - return 0; - } - } +} // namespace arbiter - // Parse and append data - // Return character that ends data. - // This is necessary because this character might have been overwritten by a terminating 0 - template - Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) - { - // Backup to contents start if whitespace trimming is disabled - if (!(Flags & parse_trim_whitespace)) - text = contents_start; +#ifdef ARBITER_CUSTOM_NAMESPACE +} +#endif - // Skip until end of data - Ch *value = text, *end; - if (Flags & parse_normalize_whitespace) - end = skip_and_expand_character_refs(text); - else - end = skip_and_expand_character_refs(text); - // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > - if (Flags & parse_trim_whitespace) - { - if (Flags & parse_normalize_whitespace) - { - // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end - if (*(end - 1) == Ch(' ')) - --end; - } - else - { - // Backup until non-whitespace character is found - while (whitespace_pred::test(*(end - 1))) - --end; - } - } +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/util/time.hpp +// ////////////////////////////////////////////////////////////////////// - // If characters are still left between end and value (this test is only necessary if normalization is enabled) - // Create new data node - if (!(Flags & parse_no_data_nodes)) - { - xml_node *data = this->allocate_node(node_data); - data->value(value, end - value); - node->append_node(data); - } - // Add data to parent node if no data exists yet - if (!(Flags & parse_no_element_values)) - if (*node->value() == Ch('\0')) - node->value(value, end - value); - // Place zero terminator after value - if (!(Flags & parse_no_string_terminators)) - { - Ch ch = *text; - *end = Ch('\0'); - return ch; // Return character that ends data; this is required because zero terminator overwritten it - } - // Return character that ends data - return *text; - } - // Parse CDATA - template - xml_node *parse_cdata(Ch *&text) - { - // If CDATA is disabled - if (Flags & parse_no_data_nodes) - { - // Skip until end of cdata - while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) - { - if (!text[0]) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } - text += 3; // Skip ]]> - return 0; // Do not produce CDATA node - } - // Skip until end of cdata - Ch *value = text; - while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) - { - if (!text[0]) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/driver.hpp +// ////////////////////////////////////////////////////////////////////// - // Create new cdata node - xml_node *cdata = this->allocate_node(node_cdata); - cdata->value(value, text - value); +#pragma once - // Place zero terminator after value - if (!(Flags & parse_no_string_terminators)) - *text = Ch('\0'); +#include +#include +#include +#include +#include - text += 3; // Skip ]]> - return cdata; - } +#ifndef ARBITER_IS_AMALGAMATION - // Parse element node - template - xml_node *parse_element(Ch *&text) - { - // Create element node - xml_node *element = this->allocate_node(node_element); +#include - // Extract element name - Ch *name = text; - skip(text); - if (text == name) - RAPIDXML_PARSE_ERROR("expected element name", text); - element->name(name, text - name); +#endif - // Skip whitespace between element name and attributes or > - skip(text); +#ifdef ARBITER_CUSTOM_NAMESPACE +namespace ARBITER_CUSTOM_NAMESPACE +{ +#endif - // Parse attributes, if any - parse_node_attributes(text, element); +namespace arbiter +{ - // Determine ending type - if (*text == Ch('>')) - { - ++text; - parse_node_contents(text, element); - } - else if (*text == Ch('/')) - { - ++text; - if (*text != Ch('>')) - RAPIDXML_PARSE_ERROR("expected >", text); - ++text; - } - else - RAPIDXML_PARSE_ERROR("expected >", text); +class HttpPool; - // Place zero terminator after name - if (!(Flags & parse_no_string_terminators)) - element->name()[element->name_size()] = Ch('\0'); +/** @brief Base class for interacting with a storage type. + * + * A Driver handles reading, writing, and possibly globbing from a storage + * source. It is intended to be overriden by specialized subclasses for each + * supported storage mechanism. + * + * Derived classes must override Driver::type, + * Driver::put(std::string, const std::vector&) const, and + * Driver::get(std::string, std::vector&) const, + * Driver::getSize(std::string) const - and may optionally + * override Driver::glob if possible. + * + * HTTP-derived classes should override the PUT and GET versions that accept + * http::Headers and http::Query parameters instead. + */ +class ARBITER_DLL Driver +{ +public: + virtual ~Driver() { } - // Return parsed element - return element; - } + /** + * Returns a string identifying this driver type, which should be unique + * among all other drivers. Paths that begin with the substring + * `://` will be routed to this driver. For example, `fs`, `s3`, or + * `http`. + * + * @note Derived classes must override. + */ + virtual std::string type() const = 0; - // Determine node type, and parse it - template - xml_node *parse_node(Ch *&text) - { - // Parse proper node type - switch (text[0]) - { + /** Get string data. */ + std::string get(std::string path) const; - // <... - default: - // Parse and append element node - return parse_element(text); + /** Get string data, if available. */ + std::unique_ptr tryGet(std::string path) const; - // (text); - } - else - { - // Parse PI - return parse_pi(text); - } + /** Get binary data. */ + std::vector getBinary(std::string path) const; - // > tryGetBinary(std::string path) const; - // Parse proper subset of & data) const = 0; - // (text); - } - break; + /** True for remote paths, otherwise false. If `true`, a fs::LocalHandle + * request will download and write this file to the local filesystem. + */ + virtual bool isRemote() const { return true; } - // (text); - } - break; + /** Get the file size in bytes, if available. */ + virtual std::unique_ptr tryGetSize(std::string path) const = 0; - // (text); - } + /** Get the file size in bytes, or throw if it does not exist. */ + std::size_t getSize(std::string path) const; - } // switch + /** Write string data. */ + void put(std::string path, const std::string& data) const; - // Attempt to skip other, unrecognized node types starting with ')) - { - if (*text == 0) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } - ++text; // Skip '>' - return 0; // No node recognized + /** Copy a file, where @p src and @p dst must both be of this driver + * type. Type-prefixes must be stripped from the input parameters. + */ + virtual void copy(std::string src, std::string dst) const; - } - } + /** @brief Resolve a possibly globbed path. + * + * See Arbiter::resolve for details. + */ + std::vector resolve( + std::string path, + bool verbose = false) const; - // Parse contents of the node - children, data etc. - template - void parse_node_contents(Ch *&text, xml_node *node) - { - // For all children and text - while (1) - { - // Skip whitespace between > and node contents - Ch *contents_start = text; // Store start of node contents before whitespace is skipped - skip(text); - Ch next_char = *text; +protected: + /** @brief Resolve a wildcard path. + * + * This operation should return a non-recursive resolution of the files + * matching the given wildcard @p path (no directories). With the exception + * of the filesystem Driver, results should be prefixed with + * `type() + "://"`. + * + * @note The default behavior is to throw ArbiterError, so derived classes + * may optionally override if they can perform this behavior. + * + * @param path A string ending with the character `*`, and with all + * type-specifying prefix information like `http://` or `s3://` stripped. + * + * @param verbose If true, this function may print out minimal information + * to indicate that progress is occurring. + */ + virtual std::vector glob(std::string path, bool verbose) const; - // After data nodes, instead of continuing the loop, control jumps here. - // This is because zero termination inside parse_and_append_data() function - // would wreak havoc with the above code. - // Also, skipping whitespace after data nodes is unnecessary. - after_data_node: + /** + * @param path Path with the type-specifying prefix information stripped. + * @param[out] data Empty vector in which to write resulting data. + */ + virtual bool get(std::string path, std::vector& data) const = 0; +}; - // Determine what comes next: node closing, child node, data node, or 0? - switch (next_char) - { +typedef std::map> DriverMap; - // Node closing or child node - case Ch('<'): - if (text[1] == Ch('/')) - { - // Node closing - text += 2; // Skip '(text); - if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) - RAPIDXML_PARSE_ERROR("invalid closing tag name", text); - } - else - { - // No validation, just skip name - skip(text); - } - // Skip remaining whitespace after node name - skip(text); - if (*text != Ch('>')) - RAPIDXML_PARSE_ERROR("expected >", text); - ++text; // Skip '>' - return; // Node closed, finished parsing contents - } - else - { - // Child node - ++text; // Skip '<' - if (xml_node *child = parse_node(text)) - node->append_node(child); - } - break; +} // namespace arbiter - // End of data - error - case Ch('\0'): - RAPIDXML_PARSE_ERROR("unexpected end of data", text); +#ifdef ARBITER_CUSTOM_NAMESPACE +} +#endif - // Data node - default: - next_char = parse_and_append_data(node, text, contents_start); - goto after_data_node; // Bypass regular processing after data nodes - } - } - } +// ////////////////////////////////////////////////////////////////////// +// End of content of file: arbiter/driver.hpp +// ////////////////////////////////////////////////////////////////////// - // Parse XML attributes of the node - template - void parse_node_attributes(Ch *&text, xml_node *node) - { - // For all attributes - while (attribute_name_pred::test(*text)) - { - // Extract attribute name - Ch *name = text; - ++text; // Skip first character of attribute name - skip(text); - if (text == name) - RAPIDXML_PARSE_ERROR("expected attribute name", name); - // Create new attribute - xml_attribute *attribute = this->allocate_attribute(); - attribute->name(name, text - name); - node->append_attribute(attribute); - // Skip whitespace after attribute name - skip(text); - // Skip = - if (*text != Ch('=')) - RAPIDXML_PARSE_ERROR("expected =", text); - ++text; - // Add terminating zero after name - if (!(Flags & parse_no_string_terminators)) - attribute->name()[attribute->name_size()] = 0; - // Skip whitespace after = - skip(text); +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: arbiter/drivers/fs.hpp +// ////////////////////////////////////////////////////////////////////// - // Skip quote and remember if it was ' or " - Ch quote = *text; - if (quote != Ch('\'') && quote != Ch('"')) - RAPIDXML_PARSE_ERROR("expected ' or \"", text); - ++text; +#pragma once - // Extract attribute value and expand char refs in it - Ch *value = text, *end; - const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes - if (quote == Ch('\'')) - end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); - else - end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); +#ifndef ARBITER_IS_AMALGAMATION +#include - // Set attribute value - attribute->value(value, end - value); +#ifndef ARBITER_EXTERNAL_JSON +#include +#endif - // Make sure that end quote is present - if (*text != quote) - RAPIDXML_PARSE_ERROR("expected ' or \"", text); - ++text; // Skip quote +#endif - // Add terminating zero after value - if (!(Flags & parse_no_string_terminators)) - attribute->value()[attribute->value_size()] = 0; - // Skip whitespace after attribute value - skip(text); - } - } +#ifndef ARBITER_IS_AMALGAMATION +#include +#endif - }; +#ifdef ARBITER_EXTERNAL_JSON +#include +#endif - //! \cond internal - namespace internal - { +#ifdef ARBITER_CUSTOM_NAMESPACE +namespace ARBITER_CUSTOM_NAMESPACE +{ +#endif - // Whitespace (space \n \r \t) - template - const unsigned char lookup_tables::lookup_whitespace[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F - }; +namespace arbiter +{ - // Node name (anything but space \n \r \t / > ? \0) - template - const unsigned char lookup_tables::lookup_node_name[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; +class Arbiter; +class Endpoint; - // Text (i.e. PCDATA) (anything but < \0) - template - const unsigned char lookup_tables::lookup_text[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 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, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; +/** + * \addtogroup fs + * @{ + */ - // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled - // (anything but < \0 &) - template - const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; +/** Filesystem utilities. */ +namespace fs +{ + /** @brief Returns true if created, false if already existed. */ + ARBITER_DLL bool mkdirp(std::string dir); - // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled - // (anything but < \0 & space \n \r \t) - template - const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; + /** @brief Returns true if removed, otherwise false. */ + ARBITER_DLL bool remove(std::string filename); - // Attribute name (anything but space \n \r \t / < > = ? ! \0) - template - const unsigned char lookup_tables::lookup_attribute_name[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; + /** @brief Performs tilde expansion to a fully-qualified path, if possible. + */ + ARBITER_DLL std::string expandTilde(std::string path); - // Attribute data with single quote (anything but ' \0) - template - const unsigned char lookup_tables::lookup_attribute_data_1[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 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, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; + /** @brief Get temporary path from environment. */ + ARBITER_DLL std::string getTempPath(); - // Attribute data with single quote that does not require processing (anything but ' \0 &) - template - const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; + /** @brief Resolve a possible wildcard path. */ + ARBITER_DLL std::vector glob(std::string path); - // Attribute data with double quote (anything but " \0) - template - const unsigned char lookup_tables::lookup_attribute_data_2[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; + /** @brief A scoped local filehandle for a possibly remote path. + * + * This is an RAII style pseudo-filehandle. It manages the scope of a + * local temporary version of a file, where that file may have been copied + * from a remote storage location. + * + * See Arbiter::getLocalHandle for details about construction. + */ + class ARBITER_DLL LocalHandle + { + friend class arbiter::Arbiter; + friend class arbiter::Endpoint; - // Attribute data with double quote that does not require processing (anything but " \0 &) - template - const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; + public: + /** @brief Deletes the local path if the data was copied from a remote + * source. + * + * This is a no-op if the path was already local and not copied. + */ + ~LocalHandle(); - // Digits (dec and hex, 255 denotes end of numeric character reference) - template - const unsigned char lookup_tables::lookup_digits[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 - 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 - 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F - }; + /** @brief Get the path of the locally stored file. + * + * @return A local filesystem absolute path containing the data + * requested in Arbiter::getLocalHandle. + */ + std::string localPath() const { return m_localPath; } - // Upper case conversion - template - const unsigned char lookup_tables::lookup_upcase[256] = - { - // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 - 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 - 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 - 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 - 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A - 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B - 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C - 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D - 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E - 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F - }; - } - //! \endcond + /** @brief Release the managed local path and return the path from + * LocalHandle::localPath. + * + * After this call, destruction of the LocalHandle will not erase the + * temporary file that may have been created. + */ + std::string release() + { + m_erase = false; + return localPath(); + } + + private: + LocalHandle(std::string localPath, bool isRemote); + const std::string m_localPath; + bool m_erase; + }; } +/** @} */ -// Undefine internal macros -#undef RAPIDXML_PARSE_ERROR +namespace drivers +{ -// On MSVC, restore warnings state -#ifdef _MSC_VER - #pragma warning(pop) -#endif +/** @brief Local filesystem driver. */ +class ARBITER_DLL Fs : public Driver +{ +public: + Fs() { } + + using Driver::get; + + static std::unique_ptr create(const Json::Value& json); + + virtual std::string type() const override { return "file"; } + + virtual std::unique_ptr tryGetSize( + std::string path) const override; + + virtual void put( + std::string path, + const std::vector& data) const override; + + virtual std::vector glob( + std::string path, + bool verbose) const override; + + virtual bool isRemote() const override { return false; } + virtual void copy(std::string src, std::string dst) const override; + +protected: + virtual bool get(std::string path, std::vector& data) const override; +}; + +} // namespace drivers + +} // namespace arbiter + +#ifdef ARBITER_CUSTOM_NAMESPACE +} #endif // ////////////////////////////////////////////////////////////////////// -// End of content of file: arbiter/third/xml/rapidxml.hpp +// End of content of file: arbiter/drivers/fs.hpp // ////////////////////////////////////////////////////////////////////// @@ -3696,26 +3893,199 @@ namespace rapidxml // ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: arbiter/third/xml/xml.hpp +// Beginning of content of file: arbiter/drivers/http.hpp // ////////////////////////////////////////////////////////////////////// -#ifndef ARBITER_IS_AMALGAMATION +#pragma once -#ifndef ARBITER_EXTERNAL_XML -#include -#endif +#include +#include +#ifndef ARBITER_IS_AMALGAMATION +#include +#include #endif -#ifdef ARBITER_EXTERNAL_XML -#include +#ifdef ARBITER_CUSTOM_NAMESPACE +namespace ARBITER_CUSTOM_NAMESPACE +{ #endif -namespace Xml = rapidxml; +namespace arbiter +{ +namespace drivers +{ + +/** @brief HTTP driver. Intended as both a standalone driver as well as a base + * for derived drivers build atop HTTP. + * + * Derivers should overload the HTTP-specific put/get methods that accept + * headers and query parameters rather than Driver::put and Driver::get. + * + * Internal methods for derivers are provided as protected methods. + */ +class ARBITER_DLL Http : public Driver +{ +public: + Http(http::Pool& pool); + static std::unique_ptr create( + http::Pool& pool, + const Json::Value& json); + + // Inherited from Driver. + virtual std::string type() const override { return "http"; } + + /** By default, performs a HEAD request and returns the contents of the + * Content-Length header. + */ + virtual std::unique_ptr tryGetSize( + std::string path) const override; + + virtual void put( + std::string path, + const std::vector& data) const final override + { + put(path, data, http::Headers(), http::Query()); + } + + /* HTTP-specific driver methods follow. Since many drivers (S3, Dropbox, + * etc.) are built atop HTTP, we'll provide HTTP-specific methods for + * derived classes to use in addition to the generic PUT/GET combinations. + * + * Specifically, we'll add POST/HEAD calls, and allow headers and query + * parameters to be passed as well. + */ + + /** Perform an HTTP GET request. */ + std::string get( + std::string path, + http::Headers headers = http::Headers(), + http::Query query = http::Query()) const; + + /** Perform an HTTP GET request. */ + std::unique_ptr tryGet( + std::string path, + http::Headers headers = http::Headers(), + http::Query query = http::Query()) const; + + /** Perform an HTTP GET request. */ + std::vector getBinary( + std::string path, + http::Headers headers, + http::Query query) const; + + /** Perform an HTTP GET request. */ + std::unique_ptr> tryGetBinary( + std::string path, + http::Headers headers, + http::Query query) const; + + /** Perform an HTTP PUT request. */ + void put( + std::string path, + const std::string& data, + http::Headers headers, + http::Query query) const; + + /** HTTP-derived Drivers should override this version of PUT to allow for + * custom headers and query parameters. + */ + + /** Perform an HTTP PUT request. */ + virtual void put( + std::string path, + const std::vector& data, + http::Headers headers, + http::Query query) const; + + void post( + std::string path, + const std::string& data, + http::Headers headers, + http::Query query) const; + void post( + std::string path, + const std::vector& data, + http::Headers headers, + http::Query query) const; + + /* These operations are other HTTP-specific calls that derived drivers may + * need for their underlying API use. + */ + http::Response internalGet( + std::string path, + http::Headers headers = http::Headers(), + http::Query query = http::Query(), + std::size_t reserve = 0) const; + + http::Response internalPut( + std::string path, + const std::vector& data, + http::Headers headers = http::Headers(), + http::Query query = http::Query()) const; + + http::Response internalHead( + std::string path, + http::Headers headers = http::Headers(), + http::Query query = http::Query()) const; + + http::Response internalPost( + std::string path, + const std::vector& data, + http::Headers headers = http::Headers(), + http::Query query = http::Query()) const; + +protected: + /** HTTP-derived Drivers should override this version of GET to allow for + * custom headers and query parameters. + */ + virtual bool get( + std::string path, + std::vector& data, + http::Headers headers, + http::Query query) const; + + http::Pool& m_pool; + +private: + virtual bool get( + std::string path, + std::vector& data) const final override + { + return get(path, data, http::Headers(), http::Query()); + } + + std::string typedPath(const std::string& p) const; +}; + +/** @brief HTTPS driver. Identical to the HTTP driver except for its type + * string. + */ +class Https : public Http +{ +public: + Https(http::Pool& pool) : Http(pool) { } + + static std::unique_ptr create( + http::Pool& pool, + const Json::Value& json) + { + return std::unique_ptr(new Https(pool)); + } + + virtual std::string type() const override { return "https"; } +}; + +} // namespace drivers +} // namespace arbiter + +#ifdef ARBITER_CUSTOM_NAMESPACE +} +#endif // ////////////////////////////////////////////////////////////////////// -// End of content of file: arbiter/third/xml/xml.hpp +// End of content of file: arbiter/drivers/http.hpp // ////////////////////////////////////////////////////////////////////// @@ -3855,7 +4225,9 @@ namespace crypto ARBITER_DLL std::vector sha256(const std::vector& data); ARBITER_DLL std::string sha256(const std::string& data); -ARBITER_DLL std::string hmacSha256(const std::string& key, const std::string& data); +ARBITER_DLL std::string hmacSha256( + const std::string& key, + const std::string& data); } // namespace crypto } // namespace arbiter @@ -4688,6 +5060,7 @@ class Test : public Fs #ifndef ARBITER_IS_AMALGAMATION +#include #include #include @@ -4747,6 +5120,9 @@ class ARBITER_DLL Endpoint /** See Arbiter::isHttpDerived. */ bool isHttpDerived() const; + /** See Arbiter::getLocalHandle. */ + std::unique_ptr getLocalHandle(std::string subpath) const; + /** Passthrough to Driver::get. */ std::string get(std::string subpath) const; @@ -5186,6 +5562,10 @@ class ARBITER_DLL Arbiter * string if there are no '.' characters. */ static std::string getExtension(std::string path); + /** Strip the characters following (and including) the final instance of + * '.' if one exists, otherwise return the full path. */ + static std::string stripExtension(std::string path); + /** Fetch the common HTTP pool, which may be useful when dynamically * constructing adding a Driver via Arbiter::addDriver. */ From 6e8bcd7a7e977f8bb2a5d311d745d19e55beb093 Mon Sep 17 00:00:00 2001 From: Andrew Bell Date: Wed, 12 Sep 2018 14:26:04 -0400 Subject: [PATCH 3/7] Set spatialreference for all readers. (#2165) Close #2159 --- io/LasReader.cpp | 1 - io/TextReader.cpp | 1 - pdal/Reader.cpp | 1 + plugins/oci/io/OciReader.cpp | 1 - plugins/sqlite/io/SQLiteReader.cpp | 8 ++--- test/unit/apps/pcpipelineTestJSON.cpp | 52 +++++++++++++++++++++++++-- 6 files changed, 52 insertions(+), 12 deletions(-) diff --git a/io/LasReader.cpp b/io/LasReader.cpp index fa5a9cd01e..22d8da52f7 100644 --- a/io/LasReader.cpp +++ b/io/LasReader.cpp @@ -79,7 +79,6 @@ LasReader::~LasReader() void LasReader::addArgs(ProgramArgs& args) { - addSpatialReferenceArg(args); args.add("extra_dims", "Dimensions to assign to extra byte data", m_extraDimSpec); args.add("compression", "Decompressor to use", m_compression, "EITHER"); diff --git a/io/TextReader.cpp b/io/TextReader.cpp index e61008c7b4..5715a80374 100644 --- a/io/TextReader.cpp +++ b/io/TextReader.cpp @@ -156,7 +156,6 @@ void TextReader::initialize(PointTableRef table) void TextReader::addArgs(ProgramArgs& args) { - addSpatialReferenceArg(args); m_separatorArg = &(args.add("separator", "Separator character that " "overrides special character found in header line", m_separator, ' ')); args.add("header", "Use this string as the header line.", m_header); diff --git a/pdal/Reader.cpp b/pdal/Reader.cpp index ac1720a3b5..e37fb08293 100644 --- a/pdal/Reader.cpp +++ b/pdal/Reader.cpp @@ -43,6 +43,7 @@ void Reader::readerAddArgs(ProgramArgs& args) m_filenameArg = &args.add("filename", "Name of file to read", m_filename); m_countArg = &args.add("count", "Maximum number of points read", m_count, (std::numeric_limits::max)()); + addSpatialReferenceArg(args); } } // namespace pdal diff --git a/plugins/oci/io/OciReader.cpp b/plugins/oci/io/OciReader.cpp index 4886ce0f96..6dc0384008 100644 --- a/plugins/oci/io/OciReader.cpp +++ b/plugins/oci/io/OciReader.cpp @@ -63,7 +63,6 @@ void OciReader::addArgs(ProgramArgs& args) args.add("connection", "Connection string", m_connSpec); args.add("populate_pointsourceid", "Set point source ID", m_updatePointSourceId); - addSpatialReferenceArg(args); } diff --git a/plugins/sqlite/io/SQLiteReader.cpp b/plugins/sqlite/io/SQLiteReader.cpp index 386a207fe9..ca731fc7c0 100644 --- a/plugins/sqlite/io/SQLiteReader.cpp +++ b/plugins/sqlite/io/SQLiteReader.cpp @@ -79,10 +79,8 @@ void SQLiteReader::initialize() std::string(e.what())); } - if (m_spatialRef.empty()) - m_spatialRef = fetchSpatialReference(m_query); - setSpatialReference(m_spatialRef); - + if (getSpatialReference().empty()) + setSpatialReference(fetchSpatialReference(m_query)); m_patch = PatchPtr(new Patch()); } @@ -123,8 +121,6 @@ SQLiteReader::fetchSpatialReference(std::string const& query) const void SQLiteReader::addArgs(ProgramArgs& args) { - args.add("spatialreference", "Spatial reference to apply to points if " - "one doesn't exist", m_spatialRef); args.add("query", "SELECT statement that returns point cloud", m_query); args.add("connection", "Database connection string", m_connection); args.add("module", "Spatialite module name", m_modulename); diff --git a/test/unit/apps/pcpipelineTestJSON.cpp b/test/unit/apps/pcpipelineTestJSON.cpp index 9e16bfad94..d4a9619984 100644 --- a/test/unit/apps/pcpipelineTestJSON.cpp +++ b/test/unit/apps/pcpipelineTestJSON.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include "Support.hpp" #include @@ -307,7 +308,7 @@ TEST(json, tags) EXPECT_EQ(totalInputs, 3U); } -TEST(json, issue1417) +TEST(json, issue_1417) { std::string options = "--readers.las.filename=" + Support::datapath("las/utm15.las"); @@ -315,7 +316,7 @@ TEST(json, issue1417) } // Make sure we handle repeated options properly -TEST(json, issue1941) +TEST(json, issue_1941) { PipelineManager manager; std::string file; @@ -338,7 +339,6 @@ TEST(json, issue1941) EXPECT_THROW(manager2.readPipeline(file), pdal_error); } - // Test that stage options passed via --stage..