From 1d055261b4144dbf86b2658437015b15d4dd9bff Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 4 Sep 2022 00:32:56 +0100 Subject: initial --- include/jsoncons/byte_string.hpp | 820 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 820 insertions(+) create mode 100644 include/jsoncons/byte_string.hpp (limited to 'include/jsoncons/byte_string.hpp') diff --git a/include/jsoncons/byte_string.hpp b/include/jsoncons/byte_string.hpp new file mode 100644 index 0000000..159dff9 --- /dev/null +++ b/include/jsoncons/byte_string.hpp @@ -0,0 +1,820 @@ +// Copyright 2013 Daniel Parker +// Distributed under the Boost license, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See https://github.com/danielaparker/jsoncons for latest version + +#ifndef JSONCONS_BYTE_STRING_HPP +#define JSONCONS_BYTE_STRING_HPP + +#include +#include +#include +#include +#include // std::memcmp +#include // std::allocator +#include +#include +#include // std::setw +#include +#include // std::move +#include +#include +#include +#include + +namespace jsoncons { + + // Algorithms + +namespace detail { + + template + typename std::enable_if::value_type,uint8_t>::value,size_t>::type + encode_base64_generic(InputIt first, InputIt last, const char alphabet[65], Container& result) + { + std::size_t count = 0; + unsigned char a3[3]; + unsigned char a4[4]; + unsigned char fill = alphabet[64]; + int i = 0; + int j = 0; + + while (first != last) + { + a3[i++] = *first++; + if (i == 3) + { + a4[0] = (a3[0] & 0xfc) >> 2; + a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); + a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); + a4[3] = a3[2] & 0x3f; + + for (i = 0; i < 4; i++) + { + result.push_back(alphabet[a4[i]]); + ++count; + } + i = 0; + } + } + + if (i > 0) + { + for (j = i; j < 3; ++j) + { + a3[j] = 0; + } + + a4[0] = (a3[0] & 0xfc) >> 2; + a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); + a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); + + for (j = 0; j < i + 1; ++j) + { + result.push_back(alphabet[a4[j]]); + ++count; + } + + if (fill != 0) + { + while (i++ < 3) + { + result.push_back(fill); + ++count; + } + } + } + + return count; + } + + template + typename std::enable_if::value,decode_result>::type + decode_base64_generic(InputIt first, InputIt last, + const uint8_t reverse_alphabet[256], + F f, + Container& result) + { + uint8_t a4[4], a3[3]; + uint8_t i = 0; + uint8_t j = 0; + + while (first != last && *first != '=') + { + if (!f(*first)) + { + return decode_result{first, conv_errc::conversion_failed}; + } + + a4[i++] = *first++; + if (i == 4) + { + for (i = 0; i < 4; ++i) + { + a4[i] = reverse_alphabet[a4[i]]; + } + + a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); + a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); + a3[2] = ((a4[2] & 0x3) << 6) + a4[3]; + + for (i = 0; i < 3; i++) + { + result.push_back(a3[i]); + } + i = 0; + } + } + + if (i > 0) + { + for (j = 0; j < i; ++j) + { + a4[j] = reverse_alphabet[a4[j]]; + } + + a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4); + a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2); + + for (j = 0; j < i - 1; ++j) + { + result.push_back(a3[j]); + } + } + return decode_result{last, conv_errc::success}; + } + +} // namespace detail + + template + typename std::enable_if::value_type,uint8_t>::value,size_t>::type + encode_base16(InputIt first, InputIt last, Container& result) + { + static constexpr char characters[] = "0123456789ABCDEF"; + + for (auto it = first; it != last; ++it) + { + uint8_t c = *it; + result.push_back(characters[c >> 4]); + result.push_back(characters[c & 0xf]); + } + return (last-first)*2; + } + + template + typename std::enable_if::value_type,uint8_t>::value,size_t>::type + encode_base64url(InputIt first, InputIt last, Container& result) + { + static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-_" + "\0"; + return detail::encode_base64_generic(first, last, alphabet, result); + } + + template + typename std::enable_if::value_type,uint8_t>::value,size_t>::type + encode_base64(InputIt first, InputIt last, Container& result) + { + static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/" + "="; + return detail::encode_base64_generic(first, last, alphabet, result); + } + + template + bool is_base64(Char c) + { + return (c >= 0 && c < 128) && (isalnum((int)c) || c == '+' || c == '/'); + } + + template + bool is_base64url(Char c) + { + return (c >= 0 && c < 128) && (isalnum((int)c) || c == '-' || c == '_'); + } + + inline + static bool is_base64url(int c) + { + return isalnum(c) || c == '-' || c == '_'; + } + + // decode + + template + typename std::enable_if::value,decode_result>::type + decode_base64url(InputIt first, InputIt last, Container& result) + { + static constexpr uint8_t reverse_alphabet[256] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 62, 0xff, 0xff, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 63, + 0xff, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + auto retval = jsoncons::detail::decode_base64_generic(first, last, reverse_alphabet, + is_base64url::value_type>, + result); + return retval.ec == conv_errc::success ? retval : decode_result{retval.it, conv_errc::not_base64url}; + } + + template + typename std::enable_if::value,decode_result>::type + decode_base64(InputIt first, InputIt last, Container& result) + { + static constexpr uint8_t reverse_alphabet[256] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 62, 0xff, 0xff, 0xff, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + auto retval = jsoncons::detail::decode_base64_generic(first, last, reverse_alphabet, + is_base64::value_type>, + result); + return retval.ec == conv_errc::success ? retval : decode_result{retval.it, conv_errc::not_base64}; + } + + template + typename std::enable_if::value,decode_result>::type + decode_base16(InputIt first, InputIt last, Container& result) + { + std::size_t len = std::distance(first,last); + if (len & 1) + { + return decode_result{first, conv_errc::not_base16}; + } + + InputIt it = first; + while (it != last) + { + uint8_t val; + auto a = *it++; + if (a >= '0' && a <= '9') + { + val = (a - '0') << 4; + } + else if ((a | 0x20) >= 'a' && (a | 0x20) <= 'f') + { + val = ((a | 0x20) - 'a' + 10) << 4; + } + else + { + return decode_result{first, conv_errc::not_base16}; + } + + auto b = *it++; + if (b >= '0' && b <= '9') + { + val |= (b - '0'); + } + else if ((b | 0x20) >= 'a' && (b | 0x20) <= 'f') + { + val |= ((b | 0x20) - 'a' + 10); + } + else + { + return decode_result{first, conv_errc::not_base16}; + } + + result.push_back(val); + } + return decode_result{last, conv_errc::success}; + } + + struct byte_traits + { + using char_type = uint8_t; + + static constexpr int eof() + { + return std::char_traits::eof(); + } + + static int compare(const char_type* s1, const char_type* s2, std::size_t count) noexcept + { + return std::memcmp(s1,s2,count); + } + }; + + // basic_byte_string + + template + class basic_byte_string; + + // byte_string_view + class byte_string_view + { + const uint8_t* data_; + std::size_t size_; + public: + using traits_type = byte_traits; + + using const_iterator = const uint8_t*; + using iterator = const_iterator; + using size_type = std::size_t; + using value_type = uint8_t; + using reference = uint8_t&; + using const_reference = const uint8_t&; + using difference_type = std::ptrdiff_t; + using pointer = uint8_t*; + using const_pointer = const uint8_t*; + + constexpr byte_string_view() noexcept + : data_(nullptr), size_(0) + { + } + + constexpr byte_string_view(const uint8_t* data, std::size_t length) noexcept + : data_(data), size_(length) + { + } + + template + constexpr explicit byte_string_view(const Container& cont, + typename std::enable_if::value,int>::type = 0) + : data_(reinterpret_cast(cont.data())), size_(cont.size()) + { + } + + template + constexpr byte_string_view(const basic_byte_string& bytes); + + constexpr byte_string_view(const byte_string_view&) noexcept = default; + + JSONCONS_CPP14_CONSTEXPR byte_string_view(byte_string_view&& other) noexcept + : data_(nullptr), size_(0) + { + const_pointer temp_data = data_; + data_ = other.data_; + other.data_ = temp_data; + + size_type temp_size = size_; + size_ = other.size_; + other.size_ = temp_size; + } + + byte_string_view& operator=(const byte_string_view&) = default; + + byte_string_view& operator=(byte_string_view&& other) noexcept + { + std::swap(data_, other.data_); + std::swap(size_, other.size_); + return *this; + } + + constexpr const uint8_t* data() const noexcept + { + return data_; + } + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use size()") + std::size_t length() const + { + return size_; + } + #endif + constexpr size_t size() const noexcept + { + return size_; + } + + // iterator support + constexpr const_iterator begin() const noexcept + { + return data_; + } + constexpr const_iterator end() const noexcept + { + return data_ + size_; + } + constexpr const_iterator cbegin() const noexcept + { + return data_; + } + constexpr const_iterator cend() const noexcept + { + return data_ + size_; + } + + constexpr uint8_t operator[](size_type pos) const + { + return data_[pos]; + } + + JSONCONS_CPP14_CONSTEXPR byte_string_view substr(size_type pos) const + { + if (pos > size_) + { + JSONCONS_THROW(std::out_of_range("pos exceeds size")); + } + std::size_t n = size_ - pos; + return byte_string_view(data_ + pos, n); + } + + byte_string_view substr(size_type pos, size_type n) const + { + if (pos > size_) + { + JSONCONS_THROW(std::out_of_range("pos exceeds size")); + } + if (pos + n > size_) + { + n = size_ - pos; + } + return byte_string_view(data_ + pos, n); + } + + int compare(const byte_string_view& s) const noexcept + { + const int rc = traits_type::compare(data_, s.data(), (std::min)(size_, s.size())); + return rc != 0 ? rc : (size_ == s.size() ? 0 : size_ < s.size() ? -1 : 1); + } + + template + int compare(const basic_byte_string& s) const noexcept + { + const int rc = traits_type::compare(data_, s.data(), (std::min)(size_, s.size())); + return rc != 0 ? rc : (size_ == s.size() ? 0 : size_ < s.size() ? -1 : 1); + } + + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, const byte_string_view& bstr) + { + std::basic_ostringstream ss; + ss.flags(std::ios::hex); + ss.fill('0'); + + bool first = true; + for (auto b : bstr) + { + if (first) + { + first = false; + } + else + { + ss << ','; + } + ss << std::setw(2) << static_cast(b); + } + os << ss.str(); + return os; + } + }; + + // basic_byte_string + template > + class basic_byte_string + { + using byte_allocator_type = typename std::allocator_traits:: template rebind_alloc; + std::vector data_; + public: + using traits_type = byte_traits; + using allocator_type = byte_allocator_type; + + using value_type = typename std::vector::value_type; + using size_type = typename std::vector::size_type; + using difference_type = typename std::vector::difference_type; + using reference = typename std::vector::reference; + using const_reference = typename std::vector::const_reference; + using pointer = typename std::vector::pointer; + using const_pointer = typename std::vector::const_pointer; + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + + basic_byte_string() = default; + + explicit basic_byte_string(const Allocator& alloc) + : data_(alloc) + { + } + + basic_byte_string(std::initializer_list init) + : data_(std::move(init)) + { + } + + basic_byte_string(std::initializer_list init, const Allocator& alloc) + : data_(std::move(init), alloc) + { + } + + explicit basic_byte_string(const byte_string_view& v) + : data_(v.begin(),v.end()) + { + } + + basic_byte_string(const basic_byte_string& v) + : data_(v.data_) + { + } + + basic_byte_string(basic_byte_string&& v) noexcept + : data_(std::move(v.data_)) + { + } + + basic_byte_string(const byte_string_view& v, const Allocator& alloc) + : data_(v.begin(),v.end(),alloc) + { + } + + basic_byte_string(const uint8_t* data, std::size_t length, const Allocator& alloc = Allocator()) + : data_(data, data+length,alloc) + { + } + + Allocator get_allocator() const + { + return data_.get_allocator(); + } + + basic_byte_string& operator=(const basic_byte_string& s) = default; + + basic_byte_string& operator=(basic_byte_string&& other) noexcept + { + data_.swap(other.data_); + return *this; + } + + void reserve(std::size_t new_cap) + { + data_.reserve(new_cap); + } + + void push_back(uint8_t b) + { + data_.push_back(b); + } + + void assign(const uint8_t* s, std::size_t count) + { + data_.clear(); + data_.insert(s, s+count); + } + + void append(const uint8_t* s, std::size_t count) + { + data_.insert(s, s+count); + } + + void clear() + { + data_.clear(); + } + + uint8_t operator[](size_type pos) const + { + return data_[pos]; + } + + // iterator support + iterator begin() noexcept + { + return data_.begin(); + } + iterator end() noexcept + { + return data_.end(); + } + + const_iterator begin() const noexcept + { + return data_.begin(); + } + const_iterator end() const noexcept + { + return data_.end(); + } + + uint8_t* data() + { + return data_.data(); + } + + const uint8_t* data() const + { + return data_.data(); + } + + std::size_t size() const + { + return data_.size(); + } + + #if !defined(JSONCONS_NO_DEPRECATED) + JSONCONS_DEPRECATED_MSG("Instead, use size()") + std::size_t length() const + { + return data_.size(); + } + #endif + + int compare(const byte_string_view& s) const noexcept + { + const int rc = traits_type::compare(data(), s.data(), (std::min)(size(), s.size())); + return rc != 0 ? rc : (size() == s.size() ? 0 : size() < s.size() ? -1 : 1); + } + + int compare(const basic_byte_string& s) const noexcept + { + const int rc = traits_type::compare(data(), s.data(), (std::min)(size(), s.size())); + return rc != 0 ? rc : (size() == s.size() ? 0 : size() < s.size() ? -1 : 1); + } + + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, const basic_byte_string& o) + { + os << byte_string_view(o); + return os; + } + }; + + template + constexpr byte_string_view::byte_string_view(const basic_byte_string& bytes) + : data_(bytes.data()), size_(bytes.size()) + { + } + + // == + inline + bool operator==(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) == 0; + } + template + bool operator==(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) == 0; + } + template + bool operator==(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) == 0; + } + template + bool operator==(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) == 0; + } + + // != + + inline + bool operator!=(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) != 0; + } + template + bool operator!=(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) != 0; + } + template + bool operator!=(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) != 0; + } + template + bool operator!=(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) != 0; + } + + // <= + + inline + bool operator<=(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + template + bool operator<=(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + template + bool operator<=(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) >= 0; + } + template + bool operator<=(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) >= 0; + } + + // < + + inline + bool operator<(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + template + bool operator<(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + template + bool operator<(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) > 0; + } + template + bool operator<(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) > 0; + } + + // >= + + inline + bool operator>=(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + template + bool operator>=(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + template + bool operator>=(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) <= 0; + } + template + bool operator>=(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) <= 0; + } + + // > + + inline + bool operator>(const byte_string_view& lhs, const byte_string_view& rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + template + bool operator>(const byte_string_view& lhs, const basic_byte_string& rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + template + bool operator>(const basic_byte_string& lhs, const byte_string_view& rhs) noexcept + { + return rhs.compare(lhs) < 0; + } + template + bool operator>(const basic_byte_string& lhs, const basic_byte_string& rhs) noexcept + { + return rhs.compare(lhs) < 0; + } + + using byte_string = basic_byte_string>; + + namespace type_traits { + + template + struct is_basic_byte_string + : std::false_type + {}; + + template + struct is_basic_byte_string> + : std::true_type + {}; + + } // namespace type_traits + +} // namespace jsoncons + +#endif -- cgit v1.2.3