Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
+1765
View File
File diff suppressed because it is too large Load Diff
+715
View File
@@ -0,0 +1,715 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_BASIC_PARSER_HPP
#define BOOST_JSON_BASIC_PARSER_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/detail/except.hpp>
#include <boost/json/error.hpp>
#include <boost/json/kind.hpp>
#include <boost/json/parse_options.hpp>
#include <boost/json/detail/stack.hpp>
#include <boost/json/detail/stream.hpp>
#include <boost/json/detail/utf8.hpp>
#include <boost/json/detail/sbo_buffer.hpp>
namespace boost {
namespace json {
/** An incremental SAX parser for serialized JSON.
This implements a SAX-style parser, invoking a
caller-supplied handler with each parsing event.
To use, first declare a variable of type
`basic_parser<T>` where `T` meets the handler
requirements specified below. Then call
@ref write_some one or more times with the input,
setting `more = false` on the final buffer.
The parsing events are realized through member
function calls on the handler, which exists
as a data member of the parser.
\n
The parser may dynamically allocate intermediate
storage as needed to accommodate the nesting level
of the input JSON. On subsequent invocations, the
parser can cheaply re-use this memory, improving
performance. This storage is freed when the
parser is destroyed
@par Usage
To get the declaration and function definitions
for this class it is necessary to include this
file instead:
@code
#include <boost/json/basic_parser_impl.hpp>
@endcode
Users who wish to parse JSON into the DOM container
@ref value will not use this class directly; instead
they will create an instance of @ref parser or
@ref stream_parser and use that instead. Alternatively,
they may call the function @ref parse. This class is
designed for users who wish to perform custom actions
instead of building a @ref value. For example, to
produce a DOM from an external library.
\n
@note
By default, only conforming JSON using UTF-8
encoding is accepted. However, select non-compliant
syntax can be allowed by construction using a
@ref parse_options set to desired values.
@par Handler
The handler provided must be implemented as an
object of class type which defines each of the
required event member functions below. The event
functions return a `bool` where `true` indicates
success, and `false` indicates failure. If the
member function returns `false`, it must set
the error code to a suitable value. This error
code will be returned by the write function to
the caller.
\n
Handlers are required to declare the maximum
limits on various elements. If these limits
are exceeded during parsing, then parsing
fails with an error.
\n
The following declaration meets the parser's
handler requirements:
@code
struct handler
{
/// The maximum number of elements allowed in an array
static constexpr std::size_t max_array_size = -1;
/// The maximum number of elements allowed in an object
static constexpr std::size_t max_object_size = -1;
/// The maximum number of characters allowed in a string
static constexpr std::size_t max_string_size = -1;
/// The maximum number of characters allowed in a key
static constexpr std::size_t max_key_size = -1;
/// Called once when the JSON parsing begins.
///
/// @return `true` on success.
/// @param ec Set to the error, if any occurred.
///
bool on_document_begin( error_code& ec );
/// Called when the JSON parsing is done.
///
/// @return `true` on success.
/// @param ec Set to the error, if any occurred.
///
bool on_document_end( error_code& ec );
/// Called when the beginning of an array is encountered.
///
/// @return `true` on success.
/// @param ec Set to the error, if any occurred.
///
bool on_array_begin( error_code& ec );
/// Called when the end of the current array is encountered.
///
/// @return `true` on success.
/// @param n The number of elements in the array.
/// @param ec Set to the error, if any occurred.
///
bool on_array_end( std::size_t n, error_code& ec );
/// Called when the beginning of an object is encountered.
///
/// @return `true` on success.
/// @param ec Set to the error, if any occurred.
///
bool on_object_begin( error_code& ec );
/// Called when the end of the current object is encountered.
///
/// @return `true` on success.
/// @param n The number of elements in the object.
/// @param ec Set to the error, if any occurred.
///
bool on_object_end( std::size_t n, error_code& ec );
/// Called with characters corresponding to part of the current string.
///
/// @return `true` on success.
/// @param s The partial characters
/// @param n The total size of the string thus far
/// @param ec Set to the error, if any occurred.
///
bool on_string_part( string_view s, std::size_t n, error_code& ec );
/// Called with the last characters corresponding to the current string.
///
/// @return `true` on success.
/// @param s The remaining characters
/// @param n The total size of the string
/// @param ec Set to the error, if any occurred.
///
bool on_string( string_view s, std::size_t n, error_code& ec );
/// Called with characters corresponding to part of the current key.
///
/// @return `true` on success.
/// @param s The partial characters
/// @param n The total size of the key thus far
/// @param ec Set to the error, if any occurred.
///
bool on_key_part( string_view s, std::size_t n, error_code& ec );
/// Called with the last characters corresponding to the current key.
///
/// @return `true` on success.
/// @param s The remaining characters
/// @param n The total size of the key
/// @param ec Set to the error, if any occurred.
///
bool on_key( string_view s, std::size_t n, error_code& ec );
/// Called with the characters corresponding to part of the current number.
///
/// @return `true` on success.
/// @param s The partial characters
/// @param ec Set to the error, if any occurred.
///
bool on_number_part( string_view s, error_code& ec );
/// Called when a signed integer is parsed.
///
/// @return `true` on success.
/// @param i The value
/// @param s The remaining characters
/// @param ec Set to the error, if any occurred.
///
bool on_int64( int64_t i, string_view s, error_code& ec );
/// Called when an unsigend integer is parsed.
///
/// @return `true` on success.
/// @param u The value
/// @param s The remaining characters
/// @param ec Set to the error, if any occurred.
///
bool on_uint64( uint64_t u, string_view s, error_code& ec );
/// Called when a double is parsed.
///
/// @return `true` on success.
/// @param d The value
/// @param s The remaining characters
/// @param ec Set to the error, if any occurred.
///
bool on_double( double d, string_view s, error_code& ec );
/// Called when a boolean is parsed.
///
/// @return `true` on success.
/// @param b The value
/// @param s The remaining characters
/// @param ec Set to the error, if any occurred.
///
bool on_bool( bool b, error_code& ec );
/// Called when a null is parsed.
///
/// @return `true` on success.
/// @param ec Set to the error, if any occurred.
///
bool on_null( error_code& ec );
/// Called with characters corresponding to part of the current comment.
///
/// @return `true` on success.
/// @param s The partial characters.
/// @param ec Set to the error, if any occurred.
///
bool on_comment_part( string_view s, error_code& ec );
/// Called with the last characters corresponding to the current comment.
///
/// @return `true` on success.
/// @param s The remaining characters
/// @param ec Set to the error, if any occurred.
///
bool on_comment( string_view s, error_code& ec );
};
@endcode
@see
@ref parse,
@ref stream_parser,
[Validating parser example](../../doc/html/json/examples.html#json.examples.validate).
@headerfile <boost/json/basic_parser.hpp>
*/
template<class Handler>
class basic_parser
{
enum class state : char
{
doc1, doc3,
com1, com2, com3, com4,
lit1,
str1, str2, str3, str4,
str5, str6, str7, str8,
sur1, sur2, sur3,
sur4, sur5, sur6,
obj1, obj2, obj3, obj4,
obj5, obj6, obj7, obj8,
obj9, obj10, obj11,
arr1, arr2, arr3,
arr4, arr5, arr6,
num1, num2, num3, num4,
num5, num6, num7, num8,
exp1, exp2, exp3,
val1, val2, val3
};
struct number
{
uint64_t mant;
int bias;
int exp;
bool frac;
bool neg;
};
template< bool StackEmpty_, char First_ >
struct parse_number_helper;
// optimization: must come first
Handler h_;
number num_;
error_code ec_;
detail::stack st_;
detail::utf8_sequence seq_;
unsigned u1_;
unsigned u2_;
bool more_; // false for final buffer
bool done_ = false; // true on complete parse
bool clean_ = true; // write_some exited cleanly
const char* end_;
detail::sbo_buffer<16 + 16 + 1 + 1> num_buf_;
parse_options opt_;
// how many levels deeper the parser can go
std::size_t depth_ = opt_.max_depth;
unsigned char cur_lit_ = 0;
unsigned char lit_offset_ = 0;
inline void reserve();
inline const char* sentinel();
inline bool incomplete(
const detail::const_stream_wrapper& cs);
#ifdef __INTEL_COMPILER
#pragma warning push
#pragma warning disable 2196
#endif
BOOST_NOINLINE
inline
const char*
suspend_or_fail(state st);
BOOST_NOINLINE
inline
const char*
suspend_or_fail(
state st,
std::size_t n);
BOOST_NOINLINE
inline
const char*
fail(const char* p) noexcept;
BOOST_NOINLINE
inline
const char*
fail(
const char* p,
error ev,
source_location const* loc) noexcept;
BOOST_NOINLINE
inline
const char*
maybe_suspend(
const char* p,
state st);
BOOST_NOINLINE
inline
const char*
maybe_suspend(
const char* p,
state st,
std::size_t n);
BOOST_NOINLINE
inline
const char*
maybe_suspend(
const char* p,
state st,
const number& num);
BOOST_NOINLINE
inline
const char*
suspend(
const char* p,
state st);
BOOST_NOINLINE
inline
const char*
suspend(
const char* p,
state st,
const number& num);
#ifdef __INTEL_COMPILER
#pragma warning pop
#endif
template<bool StackEmpty_/*, bool Terminal_*/>
const char* parse_comment(const char* p,
std::integral_constant<bool, StackEmpty_> stack_empty,
/*std::integral_constant<bool, Terminal_>*/ bool terminal);
template<bool StackEmpty_>
const char* parse_document(const char* p,
std::integral_constant<bool, StackEmpty_> stack_empty);
template<bool StackEmpty_, bool AllowComments_/*,
bool AllowTrailing_, bool AllowBadUTF8_*/>
const char* parse_value(const char* p,
std::integral_constant<bool, StackEmpty_> stack_empty,
std::integral_constant<bool, AllowComments_> allow_comments,
/*std::integral_constant<bool, AllowTrailing_>*/ bool allow_trailing,
/*std::integral_constant<bool, AllowBadUTF8_>*/ bool allow_bad_utf8);
template<bool AllowComments_/*,
bool AllowTrailing_, bool AllowBadUTF8_*/>
const char* resume_value(const char* p,
std::integral_constant<bool, AllowComments_> allow_comments,
/*std::integral_constant<bool, AllowTrailing_>*/ bool allow_trailing,
/*std::integral_constant<bool, AllowBadUTF8_>*/ bool allow_bad_utf8);
template<bool StackEmpty_, bool AllowComments_/*,
bool AllowTrailing_, bool AllowBadUTF8_*/>
const char* parse_object(const char* p,
std::integral_constant<bool, StackEmpty_> stack_empty,
std::integral_constant<bool, AllowComments_> allow_comments,
/*std::integral_constant<bool, AllowTrailing_>*/ bool allow_trailing,
/*std::integral_constant<bool, AllowBadUTF8_>*/ bool allow_bad_utf8);
template<bool StackEmpty_, bool AllowComments_/*,
bool AllowTrailing_, bool AllowBadUTF8_*/>
const char* parse_array(const char* p,
std::integral_constant<bool, StackEmpty_> stack_empty,
std::integral_constant<bool, AllowComments_> allow_comments,
/*std::integral_constant<bool, AllowTrailing_>*/ bool allow_trailing,
/*std::integral_constant<bool, AllowBadUTF8_>*/ bool allow_bad_utf8);
template<int Literal>
const char* parse_literal(const char* p,
std::integral_constant<int, Literal> literal);
template<bool StackEmpty_, bool IsKey_/*,
bool AllowBadUTF8_*/>
const char* parse_string(const char* p,
std::integral_constant<bool, StackEmpty_> stack_empty,
std::integral_constant<bool, IsKey_> is_key,
/*std::integral_constant<bool, AllowBadUTF8_>*/ bool allow_bad_utf8);
template<bool StackEmpty_, char First_, number_precision Numbers_>
const char* parse_number(const char* p,
std::integral_constant<bool, StackEmpty_> stack_empty,
std::integral_constant<char, First_> first,
std::integral_constant<number_precision, Numbers_> numbers);
template<bool StackEmpty_, bool IsKey_/*,
bool AllowBadUTF8_*/>
const char* parse_unescaped(const char* p,
std::integral_constant<bool, StackEmpty_> stack_empty,
std::integral_constant<bool, IsKey_> is_key,
/*std::integral_constant<bool, AllowBadUTF8_>*/ bool allow_bad_utf8);
template<bool StackEmpty_/*, bool IsKey_,
bool AllowBadUTF8_*/>
const char* parse_escaped(
const char* p,
std::size_t total,
std::integral_constant<bool, StackEmpty_> stack_empty,
/*std::integral_constant<bool, IsKey_>*/ bool is_key,
/*std::integral_constant<bool, AllowBadUTF8_>*/ bool allow_bad_utf8);
// intentionally private
std::size_t
depth() const noexcept
{
return opt_.max_depth - depth_;
}
public:
/// Copy constructor (deleted)
basic_parser(
basic_parser const&) = delete;
/// Copy assignment (deleted)
basic_parser& operator=(
basic_parser const&) = delete;
/** Destructor.
All dynamically allocated internal memory is freed.
@par Effects
@code
this->handler().~Handler()
@endcode
@par Complexity
Same as `~Handler()`.
@par Exception Safety
Same as `~Handler()`.
*/
~basic_parser() = default;
/** Constructor.
This function constructs the parser with
the specified options, with any additional
arguments forwarded to the handler's constructor.
@par Complexity
Same as `Handler( std::forward< Args >( args )... )`.
@par Exception Safety
Same as `Handler( std::forward< Args >( args )... )`.
@param opt Configuration settings for the parser.
If this structure is default constructed, the
parser will accept only standard JSON.
@param args Optional additional arguments
forwarded to the handler's constructor.
@see parse_options
*/
template<class... Args>
explicit
basic_parser(
parse_options const& opt,
Args&&... args);
/** Return a reference to the handler.
This function provides access to the constructed
instance of the handler owned by the parser.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
Handler&
handler() noexcept
{
return h_;
}
/** Return a reference to the handler.
This function provides access to the constructed
instance of the handler owned by the parser.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
Handler const&
handler() const noexcept
{
return h_;
}
/** Return the last error.
This returns the last error code which
was generated in the most recent call
to @ref write_some.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
error_code
last_error() const noexcept
{
return ec_;
}
/** Return true if a complete JSON has been parsed.
This function returns `true` when all of these
conditions are met:
@li A complete serialized JSON has been
presented to the parser, and
@li No error or exception has occurred since the
parser was constructed, or since the last call
to @ref reset,
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
bool
done() const noexcept
{
return done_;
}
/** Reset the state, to parse a new document.
This function discards the current parsing
state, to prepare for parsing a new document.
Dynamically allocated temporary memory used
by the implementation is not deallocated.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
void
reset() noexcept;
/** Indicate a parsing failure.
This changes the state of the parser to indicate
that the parse has failed. A parser implementation
can use this to fail the parser if needed due to
external inputs.
@note
If `!ec`, the stored error code is unspecified.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param ec The error code to set. If the code does
not indicate failure, an implementation-defined
error code that indicates failure will be stored
instead.
*/
void
fail(error_code ec) noexcept;
/** Parse some of an input string as JSON, incrementally.
This function parses the JSON in the specified
buffer, calling the handler to emit each SAX
parsing event. The parse proceeds from the
current state, which is at the beginning of a
new JSON or in the middle of the current JSON
if any characters were already parsed.
\n
The characters in the buffer are processed
starting from the beginning, until one of the
following conditions is met:
@li All of the characters in the buffer
have been parsed, or
@li Some of the characters in the buffer
have been parsed and the JSON is complete, or
@li A parsing error occurs.
The supplied buffer does not need to contain the
entire JSON. Subsequent calls can provide more
serialized data, allowing JSON to be processed
incrementally. The end of the serialized JSON
can be indicated by passing `more = false`.
@par Complexity
Linear in `size`.
@par Exception Safety
Basic guarantee.
Calls to the handler may throw.
Upon error or exception, subsequent calls will
fail until @ref reset is called to parse a new JSON.
@return The number of characters successfully
parsed, which may be smaller than `size`.
@param more `true` if there are possibly more
buffers in the current JSON, otherwise `false`.
@param data A pointer to a buffer of `size`
characters to parse.
@param size The number of characters pointed to
by `data`.
@param ec Set to the error, if any occurred.
*/
/** @{ */
std::size_t
write_some(
bool more,
char const* data,
std::size_t size,
error_code& ec);
std::size_t
write_some(
bool more,
char const* data,
std::size_t size,
std::error_code& ec);
/** @} */
};
} // namespace json
} // namespace boost
#endif
File diff suppressed because it is too large Load Diff
+400
View File
@@ -0,0 +1,400 @@
//
// Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_CONVERSION_HPP
#define BOOST_JSON_CONVERSION_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/fwd.hpp>
#include <type_traits>
namespace boost {
namespace json {
namespace detail {
template< class Ctx, class T, class Dir >
struct supported_context;
} // namespace detail
/** Customization point tag.
This tag type is used by the function
@ref value_from to select overloads
of `tag_invoke`.
@note This type is empty; it has no members.
@see @ref value_from, @ref value_to, @ref value_to_tag,
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf">
tag_invoke: A general pattern for supporting customisable functions</a>
*/
struct value_from_tag { };
/** Customization point tag type.
This tag type is used by the function
@ref value_to to select overloads
of `tag_invoke`.
@note This type is empty; it has no members.
@see @ref value_from, @ref value_from_tag, @ref value_to,
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf">
tag_invoke: A general pattern for supporting customisable functions</a>
*/
template<class T>
struct value_to_tag { };
/** Customization point tag type.
This tag type is used by the function
@ref try_value_to to select overloads
of `tag_invoke`.
@note This type is empty; it has no members.
@see @ref value_to, @ref value_to_tag
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf">
tag_invoke: A general pattern for supporting customisable functions</a>
*/
template<class T>
struct try_value_to_tag { };
/** Determine if `T` can be treated like a string during conversions.
Provides the member constant `value` that is equal to `true`, if `T` is
convertible to @ref string_view. Otherwise, `value` is equal to `false`.
<br>
Users can specialize the trait for their own types if they don't want them
to be treated like strings. For example:
@code
namespace boost {
namespace json {
template <>
struct is_string_like<your::string> : std::false_type
{ };
} // namespace boost
} // namespace json
@endcode
@par Types satisfying the trait
@ref string,
@ref string_view,
<a href="https://en.cppreference.com/w/cpp/string/basic_string"><tt>std::string</tt></a>,
<a href="https://en.cppreference.com/w/cpp/string/basic_string_view"><tt>std::string_view</tt></a>.
@see @ref value_from, @ref value_to
*/
template<class T>
struct is_string_like;
/** Determine if `T` can be treated like a sequence during conversions.
Given `t`, a glvalue of type `T`, if
@li given `It`, the type denoted by `decltype(std::begin(t))`,
<tt>std::iterator_traits<It>::iterator_category</tt> is well-formed and
denotes a type; and
@li `decltype(std::end(t))` also denotes the type `It`;
then the trait provides the member constant `value` that is equal to
`true`. Otherwise, `value` is equal to `false`.<br>
Users can specialize the trait for their own types if they don't want them
to be treated like sequences. For example:
@code
namespace boost {
namespace json {
template <>
struct is_sequence_like<your::container> : std::false_type
{ };
} // namespace boost
} // namespace json
@endcode
@par Types satisfying the trait
Any <a href="https://en.cppreference.com/w/cpp/named_req/SequenceContainer"><em>SequenceContainer</em></a>,
array types.
@see @ref value_from, @ref value_to
*/
template<class T>
struct is_sequence_like;
/** Determine if `T` can be treated like a 1-to-1 mapping during
conversions.
Given `t`, a glvalue of type `T`, if
@li <tt>is_sequence_like<T>::value</tt> is `true`; and
@li given type `It` denoting `decltype(std::begin(t))`, and types `K`
and `M`, <tt>std::iterator_traits<It>::value_type</tt> denotes
`std::pair<K, M>`; and
@li <tt>std::is_string_like<K>::value</tt> is `true`; and
@li given `v`, a glvalue of type `V`, and `E`, the type denoted by
`decltype(t.emplace(v))`,
<tt>std::is_tuple_like<E>::value</tt> is `true`;
then the trait provides the member constant `value`
that is equal to `true`. Otherwise, `value` is equal to `false`.<br>
Users can specialize the trait for their own types if they don't want them
to be treated like mappings. For example:
@code
namespace boost {
namespace json {
template <>
struct is_map_like<your::map> : std::false_type
{ };
} // namespace boost
} // namespace json
@endcode
@note
The restriction for `t.emplace()` return type ensures that the container
does not accept duplicate keys.
@par Types satisfying the trait
<a href="https://en.cppreference.com/w/cpp/container/map"><tt>std::map</tt></a>,
<a href="https://en.cppreference.com/w/cpp/container/unordered_map"><tt>std::unordered_map</tt></a>.
@see @ref value_from, @ref value_to
*/
template<class T>
struct is_map_like;
/** Determine if `T` can be treated like a tuple during conversions.
Provides the member constant `value` that is equal to `true`, if
<tt>std::tuple_size<T>::value</tt> is a positive number. Otherwise, `value`
is equal to `false`.<br>
Users can specialize the trait for their own types if they don't want them
to be treated like tuples. For example:
@code
namespace boost {
namespace json {
template <>
struct is_tuple_like<your::tuple> : std::false_type
{ };
} // namespace boost
} // namespace json
@endcode
@par Types satisfying the trait
<a href="https://en.cppreference.com/w/cpp/utility/tuple"><tt>std::tuple</tt></a>,
<a href="https://en.cppreference.com/w/cpp/utility/pair"><tt>std::pair</tt></a>.
@see @ref value_from, @ref value_to
*/
template<class T>
struct is_tuple_like;
/** Determine if `T` can be treated like null during conversions.
Primary template instantiations provide the member constant `value` that is
equal to `false`. Users can specialize the trait for their own types if
they **do** want them to be treated as nulls. For example:
@code
namespace boost {
namespace json {
template <>
struct is_null_like<your::null_type> : std::true_type
{ };
} // namespace boost
} // namespace json
@endcode
@par Types satisfying the trait
<a href="https://en.cppreference.com/w/cpp/types/nullptr_t"><tt>std::nullptr_t</tt></a>.
@see @ref value_from, @ref value_to
*/
template<class T>
struct is_null_like
: std::false_type
{ };
/** Determine if `T` should be treated as a described class
Described classes are serialised as objects with an element for each
described public data member. A described class should not have described
bases or non-public members.<br>
Or more formally, given `L`, a class template
of the form `template<class...> struct L {};`, if
@li <tt>boost::describe::has_members<T, boost::describe::mod_public>::value</tt> is `true`; and
@li `boost::describe::describe_members<T, boost::describe::mod_private | boost::describe::mod_protected>` denotes `L<>`; and
@li `boost::describe::describe_bases<T, boost::describe::mod_any_access>` denotes `L<>`; and
@li <tt>std::is_union<T>::value</tt> is `false`;
then the trait provides the member constant `value`
that is equal to `true`. Otherwise, `value` is equal to `false`.<br>
Users can specialize the trait for their own types if they don't want them
to be treated as described classes. For example:
@code
namespace boost {
namespace json {
template <>
struct is_described_class<your::described_class> : std::false_type
{ };
} // namespace boost
} // namespace json
@endcode
Users can also specialize the trait for their own types _with_ described
bases to enable this conversion implementation. In this case the class will
be serialized in a flattened way, that is members of bases will be
serialized as direct elements of the object, and no nested objects will be
created for bases.
@see <a href="https://www.boost.org/doc/libs/develop/libs/describe/doc/html/describe.html">Boost.Describe</a>.
*/
template<class T>
struct is_described_class;
/** Determine if `T` should be treated as a described enum
Described enums are serialised as strings when their value equals to a
described enumerator, and as integers otherwise. The reverse operation
does not convert numbers to enums values, though, and instead produces
an error.<br>
If <tt>boost::describe::has_describe_enumerators<T>::value</tt> is `true`,
then the trait provides the member constant `value`
that is equal to `true`. Otherwise, `value` is equal to `false`.<br>
Users can specialize the trait for their own enums if they don't want them
to be treated as described enums. For example:
@code
namespace boost {
namespace json {
template <>
struct is_described_enum<your::described_enum> : std::false_type
{ };
} // namespace boost
} // namespace json
@endcode
@see <a href="https://www.boost.org/doc/libs/develop/libs/describe/doc/html/describe.html">Boost.Describe</a>.
*/
template<class T>
struct is_described_enum;
/** Determine if `T` should be treated as a variant
Variants are serialised the same way their active alternative is
serialised. The opposite conversion selects the first alternative for which
conversion succeeds.<br>
Given `t`, a glvalue of type ` const T`, if
<tt>t.valueless_by_exception()</tt> is well-formed, then the trait provides
the member constant `value` that is equal to `true`. Otherwise, `value` is
equal to `false`.<br>
Users can specialize the trait for their own types if they don't want them
to be treated as variants. For example:
@code
namespace boost {
namespace json {
template <>
struct is_variant_like<your::variant> : std::false_type
{ };
} // namespace boost
} // namespace json
@endcode
*/
template<class T>
struct is_variant_like;
/** Determine if `T` should be treated as an optional
Optionals are serialised as `null` if empty, or as the stored type
otherwise.<br>
Given `t`, a glvalue of type `T`, if
@li <tt>decltype( t.value() )</tt> is well-formed and isn't a void type; and
@li <tt>t.reset()</tt> is well-formed;
then the trait provides the member constant `value`
that is equal to `true`. Otherwise, `value` is equal to `false`.<br>
Users can specialize the trait for their own types if they don't want them
to be treated as optionals. For example:
@code
namespace boost {
namespace json {
template <>
struct is_optional_like<your::optional> : std::false_type
{ };
} // namespace boost
} // namespace json
@endcode
*/
template<class T>
struct is_optional_like;
} // namespace json
} // namespace boost
#include <boost/json/impl/conversion.hpp>
#endif // BOOST_JSON_CONVERSION_HPP
+77
View File
@@ -0,0 +1,77 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_ARRAY_HPP
#define BOOST_JSON_DETAIL_ARRAY_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/storage_ptr.hpp>
#include <cstddef>
namespace boost {
namespace json {
class value;
namespace detail {
class unchecked_array
{
value* data_;
std::size_t size_;
storage_ptr const& sp_;
public:
inline
~unchecked_array();
unchecked_array(
value* data,
std::size_t size,
storage_ptr const& sp) noexcept
: data_(data)
, size_(size)
, sp_(sp)
{
}
unchecked_array(
unchecked_array&& other) noexcept
: data_(other.data_)
, size_(other.size_)
, sp_(other.sp_)
{
other.data_ = nullptr;
}
storage_ptr const&
storage() const noexcept
{
return sp_;
}
std::size_t
size() const noexcept
{
return size_;
}
inline
void
relocate(value* dest) noexcept;
};
} // detail
} // namespace json
} // namespace boost
// includes are at the bottom of <boost/json/value.hpp>
#endif
+146
View File
@@ -0,0 +1,146 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_BUFFER_HPP
#define BOOST_JSON_DETAIL_BUFFER_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/string_view.hpp>
#include <cstring>
namespace boost {
namespace json {
namespace detail {
// A simple string-like temporary static buffer
template<std::size_t N>
class buffer
{
public:
using size_type = std::size_t;
buffer() = default;
bool
empty() const noexcept
{
return size_ == 0;
}
string_view
get() const noexcept
{
return {buf_, size_};
}
operator string_view() const noexcept
{
return get();
}
char const*
data() const noexcept
{
return buf_;
}
size_type
size() const noexcept
{
return size_;
}
size_type
capacity() const noexcept
{
return N - size_;
}
size_type
max_size() const noexcept
{
return N;
}
void
clear() noexcept
{
size_ = 0;
}
void
push_back(char ch) noexcept
{
BOOST_ASSERT(capacity() > 0);
buf_[size_++] = ch;
}
// append an unescaped string
void
append(
char const* s,
size_type n)
{
BOOST_ASSERT(n <= N - size_);
std::memcpy(buf_ + size_, s, n);
size_ += n;
}
// append valid 32-bit code point as utf8
void
append_utf8(
unsigned long cp) noexcept
{
auto dest = buf_ + size_;
if(cp < 0x80)
{
BOOST_ASSERT(size_ <= N - 1);
dest[0] = static_cast<char>(cp);
size_ += 1;
return;
}
if(cp < 0x800)
{
BOOST_ASSERT(size_ <= N - 2);
dest[0] = static_cast<char>( (cp >> 6) | 0xc0);
dest[1] = static_cast<char>( (cp & 0x3f) | 0x80);
size_ += 2;
return;
}
if(cp < 0x10000)
{
BOOST_ASSERT(size_ <= N - 3);
dest[0] = static_cast<char>( (cp >> 12) | 0xe0);
dest[1] = static_cast<char>(((cp >> 6) & 0x3f) | 0x80);
dest[2] = static_cast<char>( (cp & 0x3f) | 0x80);
size_ += 3;
return;
}
{
BOOST_ASSERT(size_ <= N - 4);
dest[0] = static_cast<char>( (cp >> 18) | 0xf0);
dest[1] = static_cast<char>(((cp >> 12) & 0x3f) | 0x80);
dest[2] = static_cast<char>(((cp >> 6) & 0x3f) | 0x80);
dest[3] = static_cast<char>( (cp & 0x3f) | 0x80);
size_ += 4;
}
}
private:
char buf_[N];
size_type size_ = 0;
};
} // detail
} // namespace json
} // namespace boost
#endif
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_JSON_DETAIL_CHARCONV_CHARS_FORMAT_HPP
#define BOOST_JSON_DETAIL_CHARCONV_CHARS_FORMAT_HPP
namespace boost { namespace json { namespace detail { namespace charconv {
// Floating-point format for primitive numerical conversion
// chars_format is a bitmask type (16.3.3.3.3)
enum class chars_format : unsigned
{
scientific = 1 << 0,
fixed = 1 << 1,
hex = 1 << 2,
general = fixed | scientific
};
}}}} // Namespaces
#endif // BOOST_JSON_DETAIL_CHARCONV_CHARS_FORMAT_HPP
+201
View File
@@ -0,0 +1,201 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_COMPUTE_FLOAT64_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_COMPUTE_FLOAT64_HPP
#include <boost/json/detail/charconv/detail/config.hpp>
#include <boost/json/detail/charconv/detail/significand_tables.hpp>
#include <boost/json/detail/charconv/detail/emulated128.hpp>
#include <boost/core/bit.hpp>
#include <cstdint>
#include <cfloat>
#include <cstring>
#include <cmath>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail {
static constexpr double powers_of_ten[] = {
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22
};
// Attempts to compute i * 10^(power) exactly; and if "negative" is true, negate the result.
//
// This function will only work in some cases, when it does not work, success is
// set to false. This should work *most of the time* (like 99% of the time).
// We assume that power is in the [-325, 308] interval.
inline double compute_float64(std::int64_t power, std::uint64_t i, bool negative, bool& success) noexcept
{
static constexpr auto smallest_power = -325;
static constexpr auto largest_power = 308;
// We start with a fast path
// It was described in Clinger WD.
// How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
if (0 <= power && power <= 22 && i <= UINT64_C(9007199254740991))
#else
if (-22 <= power && power <= 22 && i <= UINT64_C(9007199254740991))
#endif
{
// The general idea is as follows.
// If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then
// 1) Both s and p can be represented exactly as 64-bit floating-point
// values
// (binary64).
// 2) Because s and p can be represented exactly as floating-point values,
// then s * p
// and s / p will produce correctly rounded values.
auto d = static_cast<double>(i);
if (power < 0)
{
d = d / powers_of_ten[-power];
}
else
{
d = d * powers_of_ten[power];
}
if (negative)
{
d = -d;
}
success = true;
return d;
}
// When 22 < power && power < 22 + 16, we could
// hope for another, secondary fast path. It was
// described by David M. Gay in "Correctly rounded
// binary-decimal and decimal-binary conversions." (1990)
// If you need to compute i * 10^(22 + x) for x < 16,
// first compute i * 10^x, if you know that result is exact
// (e.g., when i * 10^x < 2^53),
// then you can still proceed and do (i * 10^x) * 10^22.
// Is this worth your time?
// You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53)
// for this second fast path to work.
// If you have 22 < power *and* power < 22 + 16, and then you
// optimistically compute "i * 10^(x-22)", there is still a chance that you
// have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of
// this optimization maybe less common than we would like. Source:
// http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
// also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html
if (i == 0 || power < smallest_power)
{
return negative ? -0.0 : 0.0;
}
else if (power > largest_power)
{
return negative ? -HUGE_VAL : HUGE_VAL;
}
const std::uint64_t factor_significand = significand_64[power - smallest_power];
const std::int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63;
int leading_zeros = boost::core::countl_zero(i);
i <<= static_cast<std::uint64_t>(leading_zeros);
uint128 product = umul128(i, factor_significand);
std::uint64_t low = product.low;
std::uint64_t high = product.high;
// We know that upper has at most one leading zero because
// both i and factor_mantissa have a leading one. This means
// that the result is at least as large as ((1<<63)*(1<<63))/(1<<64).
//
// As long as the first 9 bits of "upper" are not "1", then we
// know that we have an exact computed value for the leading
// 55 bits because any imprecision would play out as a +1, in the worst case.
// Having 55 bits is necessary because we need 53 bits for the mantissa,
// but we have to have one rounding bit and, we can waste a bit if the most
// significant bit of the product is zero.
//
// We expect this next branch to be rarely taken (say 1% of the time).
// When (upper & 0x1FF) == 0x1FF, it can be common for
// lower + i < lower to be true (proba. much higher than 1%).
if (BOOST_UNLIKELY((high & 0x1FF) == 0x1FF) && (low + i < low))
{
const std::uint64_t factor_significand_low = significand_128[power - smallest_power];
product = umul128(i, factor_significand_low);
//const std::uint64_t product_low = product.low;
const std::uint64_t product_middle2 = product.high;
const std::uint64_t product_middle1 = low;
std::uint64_t product_high = high;
const std::uint64_t product_middle = product_middle1 + product_middle2;
if (product_middle < product_middle1)
{
product_high++;
}
// Commented out because possibly unneeded
// See: https://arxiv.org/pdf/2212.06644.pdf
/*
// we want to check whether mantissa *i + i would affect our result
// This does happen, e.g. with 7.3177701707893310e+15
if (((product_middle + 1 == 0) && ((product_high & 0x1FF) == 0x1FF) && (product_low + i < product_low)))
{
success = false;
return 0;
}
*/
low = product_middle;
high = product_high;
}
// The final significand should be 53 bits with a leading 1
// We shift it so that it occupies 54 bits with a leading 1
const std::uint64_t upper_bit = high >> 63;
std::uint64_t significand = high >> (upper_bit + 9);
leading_zeros += static_cast<int>(1 ^ upper_bit);
// If we have lots of trailing zeros we may fall between two values
if (BOOST_UNLIKELY((low == 0) && ((high & 0x1FF) == 0) && ((significand & 3) == 1)))
{
// if significand & 1 == 1 we might need to round up
success = false;
return 0;
}
significand += significand & 1;
significand >>= 1;
// Here the significand < (1<<53), unless there is an overflow
if (significand >= (UINT64_C(1) << 53))
{
significand = (UINT64_C(1) << 52);
leading_zeros--;
}
significand &= ~(UINT64_C(1) << 52);
const std::uint64_t real_exponent = exponent - leading_zeros;
// We have to check that real_exponent is in range, otherwise fail
if (BOOST_UNLIKELY((real_exponent < 1) || (real_exponent > 2046)))
{
success = false;
return 0;
}
significand |= real_exponent << 52;
significand |= ((static_cast<std::uint64_t>(negative) << 63));
double d;
std::memcpy(&d, &significand, sizeof(d));
success = true;
return d;
}
}}}}} // Namespaces
#endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_COMPUTE_FLOAT64_HPP
+59
View File
@@ -0,0 +1,59 @@
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_CONFIG_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_CONFIG_HPP
#include <boost/config.hpp>
#include <type_traits>
#include <cfloat>
// Use 128 bit integers and supress warnings for using extensions
#if defined(BOOST_HAS_INT128)
# define BOOST_JSON_INT128_MAX (boost::int128_type)(((boost::uint128_type) 1 << 127) - 1)
# define BOOST_JSON_UINT128_MAX ((2 * (boost::uint128_type) BOOST_JSON_INT128_MAX) + 1)
#endif
#ifndef BOOST_NO_CXX14_CONSTEXPR
# define BOOST_JSON_CXX14_CONSTEXPR BOOST_CXX14_CONSTEXPR
# define BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE BOOST_CXX14_CONSTEXPR
#else
# define BOOST_JSON_CXX14_CONSTEXPR inline
# define BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
#endif
#if defined(__GNUC__) && __GNUC__ == 5
# define BOOST_JSON_GCC5_CONSTEXPR inline
#else
# define BOOST_JSON_GCC5_CONSTEXPR BOOST_JSON_CXX14_CONSTEXPR
#endif
// Inclue intrinsics if available
#if defined(BOOST_MSVC)
# include <intrin.h>
# if defined(_WIN64)
# define BOOST_JSON_HAS_MSVC_64BIT_INTRINSICS
# else
# define BOOST_JSON_HAS_MSVC_32BIT_INTRINSICS
# endif
#endif
// Suppress additional buffer overrun check.
// I have no idea why MSVC thinks some functions here are vulnerable to the buffer overrun
// attacks. No, they aren't.
#if defined(__GNUC__) || defined(__clang__)
#define BOOST_JSON_SAFEBUFFERS
#elif defined(_MSC_VER)
#define BOOST_JSON_SAFEBUFFERS __declspec(safebuffers)
#else
#define BOOST_JSON_SAFEBUFFERS
#endif
#if defined(__has_builtin)
#define BOOST_JSON_HAS_BUILTIN(x) __has_builtin(x)
#else
#define BOOST_JSON_HAS_BUILTIN(x) false
#endif
#endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_CONFIG_HPP
+191
View File
@@ -0,0 +1,191 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
// If the architecture (e.g. ARM) does not have __int128 we need to emulate it
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_EMULATED128_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_EMULATED128_HPP
#include <boost/json/detail/charconv/detail/config.hpp>
#include <cstdint>
#include <cassert>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail {
// Compilers might support built-in 128-bit integer types. However, it seems that
// emulating them with a pair of 64-bit integers actually produces a better code,
// so we avoid using those built-ins. That said, they are still useful for
// implementing 64-bit x 64-bit -> 128-bit multiplication.
struct uint128
{
std::uint64_t high;
std::uint64_t low;
uint128& operator+=(std::uint64_t n) & noexcept
{
#if BOOST_JSON_HAS_BUILTIN(__builtin_addcll)
unsigned long long carry;
low = __builtin_addcll(low, n, 0, &carry);
high = __builtin_addcll(high, 0, carry, &carry);
#elif BOOST_JSON_HAS_BUILTIN(__builtin_ia32_addcarryx_u64)
unsigned long long result;
auto carry = __builtin_ia32_addcarryx_u64(0, low, n, &result);
low = result;
__builtin_ia32_addcarryx_u64(carry, high, 0, &result);
high = result;
#elif defined(BOOST_MSVC) && defined(_M_X64)
auto carry = _addcarry_u64(0, low, n, &low);
_addcarry_u64(carry, high, 0, &high);
#else
auto sum = low + n;
high += (sum < low ? 1 : 0);
low = sum;
#endif
return *this;
}
};
static inline std::uint64_t umul64(std::uint32_t x, std::uint32_t y) noexcept
{
#if defined(BOOST_JSON_HAS_MSVC_32BIT_INTRINSICS) && !defined(_M_ARM)
return __emulu(x, y);
#else
return x * static_cast<std::uint64_t>(y);
#endif
}
// Get 128-bit result of multiplication of two 64-bit unsigned integers.
BOOST_JSON_SAFEBUFFERS inline uint128 umul128(std::uint64_t x, std::uint64_t y) noexcept
{
#if defined(BOOST_HAS_INT128)
auto result = static_cast<boost::uint128_type>(x) * static_cast<boost::uint128_type>(y);
return {static_cast<std::uint64_t>(result >> 64), static_cast<std::uint64_t>(result)};
#elif defined(BOOST_JSON_HAS_MSVC_64BIT_INTRINSICS) && !defined(_M_ARM64)
std::uint64_t high;
std::uint64_t low = _umul128(x, y, &high);
return {high, low};
// https://developer.arm.com/documentation/dui0802/a/A64-General-Instructions/UMULH
#elif defined(_M_ARM64) && !defined(__MINGW32__)
std::uint64_t high = __umulh(x, y);
std::uint64_t low = x * y;
return {high, low};
#else
auto a = static_cast<std::uint32_t>(x >> 32);
auto b = static_cast<std::uint32_t>(x);
auto c = static_cast<std::uint32_t>(y >> 32);
auto d = static_cast<std::uint32_t>(y);
auto ac = umul64(a, c);
auto bc = umul64(b, c);
auto ad = umul64(a, d);
auto bd = umul64(b, d);
auto intermediate = (bd >> 32) + static_cast<std::uint32_t>(ad) + static_cast<std::uint32_t>(bc);
return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
(intermediate << 32) + static_cast<std::uint32_t>(bd)};
#endif
}
BOOST_JSON_SAFEBUFFERS inline std::uint64_t umul128_upper64(std::uint64_t x, std::uint64_t y) noexcept
{
#if defined(BOOST_HAS_INT128)
auto result = static_cast<boost::uint128_type>(x) * static_cast<boost::uint128_type>(y);
return static_cast<std::uint64_t>(result >> 64);
#elif defined(BOOST_JSON_HAS_MSVC_64BIT_INTRINSICS)
return __umulh(x, y);
#else
auto a = static_cast<std::uint32_t>(x >> 32);
auto b = static_cast<std::uint32_t>(x);
auto c = static_cast<std::uint32_t>(y >> 32);
auto d = static_cast<std::uint32_t>(y);
auto ac = umul64(a, c);
auto bc = umul64(b, c);
auto ad = umul64(a, d);
auto bd = umul64(b, d);
auto intermediate = (bd >> 32) + static_cast<std::uint32_t>(ad) + static_cast<std::uint32_t>(bc);
return ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32);
#endif
}
// Get upper 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit
// unsigned integer.
BOOST_JSON_SAFEBUFFERS inline uint128 umul192_upper128(std::uint64_t x, uint128 y) noexcept
{
auto r = umul128(x, y.high);
r += umul128_upper64(x, y.low);
return r;
}
// Get upper 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit
// unsigned integer.
inline std::uint64_t umul96_upper64(std::uint32_t x, std::uint64_t y) noexcept
{
#if defined(BOOST_HAS_INT128) || defined(BOOST_JSON_HAS_MSVC_64BIT_INTRINSICS)
return umul128_upper64(static_cast<std::uint64_t>(x) << 32, y);
#else
auto yh = static_cast<std::uint32_t>(y >> 32);
auto yl = static_cast<std::uint32_t>(y);
auto xyh = umul64(x, yh);
auto xyl = umul64(x, yl);
return xyh + (xyl >> 32);
#endif
}
// Get lower 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit
// unsigned integer.
BOOST_JSON_SAFEBUFFERS inline uint128 umul192_lower128(std::uint64_t x, uint128 y) noexcept
{
auto high = x * y.high;
auto highlow = umul128(x, y.low);
return {high + highlow.high, highlow.low};
}
// Get lower 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit
// unsigned integer.
inline std::uint64_t umul96_lower64(std::uint32_t x, std::uint64_t y) noexcept
{
return x * y;
}
}}}}} // Namespaces
#endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_EMULATED128_HPP
@@ -0,0 +1,283 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// Derivative of: https://github.com/fastfloat/fast_float
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_ASCII_NUMBER_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_ASCII_NUMBER_HPP
#include <boost/json/detail/charconv/detail/fast_float/float_common.hpp>
#include <cctype>
#include <cstdint>
#include <cstring>
#include <iterator>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
// Next function can be micro-optimized, but compilers are entirely
// able to optimize it well.
template <typename UC>
BOOST_FORCEINLINE constexpr bool is_integer(UC c) noexcept {
return !(c > UC('9') || c < UC('0'));
}
BOOST_FORCEINLINE constexpr uint64_t byteswap(uint64_t val) {
return (val & 0xFF00000000000000) >> 56
| (val & 0x00FF000000000000) >> 40
| (val & 0x0000FF0000000000) >> 24
| (val & 0x000000FF00000000) >> 8
| (val & 0x00000000FF000000) << 8
| (val & 0x0000000000FF0000) << 24
| (val & 0x000000000000FF00) << 40
| (val & 0x00000000000000FF) << 56;
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
uint64_t read_u64(const char *chars) {
if (cpp20_and_in_constexpr()) {
uint64_t val = 0;
for(int i = 0; i < 8; ++i) {
val |= uint64_t(*chars) << (i*8);
++chars;
}
return val;
}
uint64_t val;
::memcpy(&val, chars, sizeof(uint64_t));
#ifdef BOOST_JSON_BIG_ENDIAN
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#endif
return val;
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
void write_u64(uint8_t *chars, uint64_t val) {
if (cpp20_and_in_constexpr()) {
for(int i = 0; i < 8; ++i) {
*chars = uint8_t(val);
val >>= 8;
++chars;
}
return;
}
#ifdef BOOST_JSON_BIG_ENDIAN
// Need to read as-if the number was in little-endian order.
val = byteswap(val);
#endif
::memcpy(chars, &val, sizeof(uint64_t));
}
// credit @aqrit
BOOST_FORCEINLINE BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
uint32_t parse_eight_digits_unrolled(uint64_t val) {
constexpr uint64_t mask = 0x000000FF000000FF;
constexpr uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
constexpr uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
val -= 0x3030303030303030;
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
return uint32_t(val);
}
BOOST_FORCEINLINE constexpr
uint32_t parse_eight_digits_unrolled(const char16_t *) noexcept {
return 0;
}
BOOST_FORCEINLINE constexpr
uint32_t parse_eight_digits_unrolled(const char32_t *) noexcept {
return 0;
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
return parse_eight_digits_unrolled(read_u64(chars));
}
// credit @aqrit
BOOST_FORCEINLINE constexpr bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & 0x8080808080808080));
}
BOOST_FORCEINLINE constexpr
bool is_made_of_eight_digits_fast(const char16_t *) noexcept {
return false;
}
BOOST_FORCEINLINE constexpr
bool is_made_of_eight_digits_fast(const char32_t *) noexcept {
return false;
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
bool is_made_of_eight_digits_fast(const char *chars) noexcept {
return is_made_of_eight_digits_fast(read_u64(chars));
}
template <typename UC>
struct parsed_number_string_t {
int64_t exponent{0};
uint64_t mantissa{0};
UC const * lastmatch{nullptr};
bool negative{false};
bool valid{false};
bool too_many_digits{false};
// contains the range of the significant digits
span<const UC> integer{}; // non-nullable
span<const UC> fraction{}; // nullable
};
using byte_span = span<char>;
using parsed_number_string = parsed_number_string_t<char>;
// Assuming that you use no more than 19 digits, this will
// parse an ASCII string.
template <typename UC>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, parse_options_t<UC> options) noexcept {
chars_format const fmt = options.format;
UC const decimal_point = options.decimal_point;
parsed_number_string_t<UC> answer;
answer.valid = false;
answer.too_many_digits = false;
answer.negative = (*p == UC('-'));
if (*p == UC('-')) // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
{
++p;
if (p == pend) {
return answer;
}
if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
return answer;
}
}
UC const * const start_digits = p;
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
while ((p != pend) && is_integer(*p)) {
// a multiplication by 10 is cheaper than an arbitrary integer
// multiplication
i = 10 * i +
uint64_t(*p - UC('0')); // might overflow, we will handle the overflow later
++p;
}
UC const * const end_of_integer_part = p;
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
answer.integer = span<const UC>(start_digits, size_t(digit_count));
int64_t exponent = 0;
if ((p != pend) && (*p == decimal_point)) {
++p;
UC const * before = p;
// can occur at most twice without overflowing, but let it occur more, since
// for integers with many digits, digit parsing is the primary bottleneck.
if (std::is_same<UC,char>::value) {
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
p += 8;
}
}
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - UC('0'));
++p;
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
}
exponent = before - p;
answer.fraction = span<const UC>(before, size_t(p - before));
digit_count -= exponent;
}
// we must have encountered at least one integer!
if (digit_count == 0) {
return answer;
}
int64_t exp_number = 0; // explicit exponential part
if (((unsigned)fmt & (unsigned)chars_format::scientific) && (p != pend) && ((UC('e') == *p) || (UC('E') == *p))) {
UC const * location_of_e = p;
++p;
bool neg_exp = false;
if ((p != pend) && (UC('-') == *p)) {
neg_exp = true;
++p;
} else if ((p != pend) && (UC('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
++p;
}
if ((p == pend) || !is_integer(*p)) {
if(!((unsigned)fmt & (unsigned)chars_format::fixed)) {
// We are in error.
return answer;
}
// Otherwise, we will be ignoring the 'e'.
p = location_of_e;
} else {
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - UC('0'));
if (exp_number < 0x10000000) {
exp_number = 10 * exp_number + digit;
}
++p;
}
if(neg_exp) { exp_number = - exp_number; }
exponent += exp_number;
}
} else {
// If it scientific and not fixed, we have to bail out.
if(((unsigned)fmt & (unsigned)chars_format::scientific) && !((unsigned)fmt & (unsigned)chars_format::fixed))
{
return answer;
}
}
answer.lastmatch = p;
answer.valid = true;
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon.
//
// We can deal with up to 19 digits.
if (digit_count > 19) { // this is uncommon
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
// We need to be mindful of the case where we only have zeroes...
// E.g., 0.000000000...000.
UC const * start = start_digits;
while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
if(*start == UC('0')) { digit_count --; }
start++;
}
if (digit_count > 19) {
answer.too_many_digits = true;
// Let us start again, this time, avoiding overflows.
// We don't need to check if is_integer, since we use the
// pre-tokenized spans from above.
i = 0;
p = answer.integer.ptr;
UC const * int_end = p + answer.integer.len();
constexpr uint64_t minimal_nineteen_digit_integer{1000000000000000000};
while((i < minimal_nineteen_digit_integer) && (p != int_end)) {
i = i * 10 + uint64_t(*p - UC('0'));
++p;
}
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
exponent = end_of_integer_part - p + exp_number;
} else { // We have a value with a fractional component.
p = answer.fraction.ptr;
UC const * frac_end = p + answer.fraction.len();
while((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
i = i * 10 + uint64_t(*p - UC('0'));
++p;
}
exponent = answer.fraction.ptr - p + exp_number;
}
// We have now corrected both exponent and i, to a truncated value
}
}
answer.exponent = exponent;
answer.mantissa = i;
return answer;
}
}}}}}} // namespace s
#endif
@@ -0,0 +1,623 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// Derivative of: https://github.com/fastfloat/fast_float
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_BIGINT_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_BIGINT_HPP
#include <boost/json/detail/charconv/detail/fast_float/float_common.hpp>
#include <algorithm>
#include <cstdint>
#include <climits>
#include <cstring>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
// the limb width: we want efficient multiplication of double the bits in
// limb, or for 64-bit limbs, at least 64-bit multiplication where we can
// extract the high and low parts efficiently. this is every 64-bit
// architecture except for sparc, which emulates 128-bit multiplication.
// we might have platforms where `CHAR_BIT` is not 8, so let's avoid
// doing `8 * sizeof(limb)`.
#if defined(BOOST_JSON_FASTFLOAT_64BIT) && !defined(__sparc)
#define BOOST_JSON_FASTFLOAT_64BIT_LIMB 1
typedef uint64_t limb;
constexpr size_t limb_bits = 64;
#else
#define BOOST_JSON_FASTFLOAT_32BIT_LIMB
typedef uint32_t limb;
constexpr size_t limb_bits = 32;
#endif
typedef span<limb> limb_span;
// number of bits in a bigint. this needs to be at least the number
// of bits required to store the largest bigint, which is
// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
// ~3600 bits, so we round to 4000.
constexpr size_t bigint_bits = 4000;
constexpr size_t bigint_limbs = bigint_bits / limb_bits;
// vector-like type that is allocated on the stack. the entire
// buffer is pre-allocated, and only the length changes.
template <uint16_t size>
struct stackvec {
limb data[size];
// we never need more than 150 limbs
uint16_t length{0};
stackvec() = default;
stackvec(const stackvec &) = delete;
stackvec &operator=(const stackvec &) = delete;
stackvec(stackvec &&) = delete;
stackvec &operator=(stackvec &&other) = delete;
// create stack vector from existing limb span.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) {
try_extend(s);
}
BOOST_JSON_CXX14_CONSTEXPR limb& operator[](size_t index) noexcept {
BOOST_ASSERT(index < length);
return data[index];
}
BOOST_JSON_CXX14_CONSTEXPR const limb& operator[](size_t index) const noexcept {
BOOST_ASSERT(index < length);
return data[index];
}
// index from the end of the container
BOOST_JSON_CXX14_CONSTEXPR const limb& rindex(size_t index) const noexcept {
BOOST_ASSERT(index < length);
size_t rindex = length - index - 1;
return data[rindex];
}
// set the length, without bounds checking.
BOOST_JSON_CXX14_CONSTEXPR void set_len(size_t len) noexcept {
length = uint16_t(len);
}
constexpr size_t len() const noexcept {
return length;
}
constexpr bool is_empty() const noexcept {
return length == 0;
}
constexpr size_t capacity() const noexcept {
return size;
}
// append item to vector, without bounds checking
BOOST_JSON_CXX14_CONSTEXPR void push_unchecked(limb value) noexcept {
data[length] = value;
length++;
}
// append item to vector, returning if item was added
BOOST_JSON_CXX14_CONSTEXPR bool try_push(limb value) noexcept {
if (len() < capacity()) {
push_unchecked(value);
return true;
} else {
return false;
}
}
// add items to the vector, from a span, without bounds checking
BOOST_JSON_FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
limb* ptr = data + length;
std::copy_n(s.ptr, s.len(), ptr);
set_len(len() + s.len());
}
// try to add items to the vector, returning if items were added
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
if (len() + s.len() <= capacity()) {
extend_unchecked(s);
return true;
} else {
return false;
}
}
// resize the vector, without bounds checking
// if the new size is longer than the vector, assign value to each
// appended item.
BOOST_JSON_FASTFLOAT_CONSTEXPR20
void resize_unchecked(size_t new_len, limb value) noexcept {
if (new_len > len()) {
size_t count = new_len - len();
limb* first = data + len();
limb* last = first + count;
::std::fill(first, last, value);
set_len(new_len);
} else {
set_len(new_len);
}
}
// try to resize the vector, returning if the vector was resized.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
if (new_len > capacity()) {
return false;
} else {
resize_unchecked(new_len, value);
return true;
}
}
// check if any limbs are non-zero after the given index.
// this needs to be done in reverse order, since the index
// is relative to the most significant limbs.
BOOST_JSON_CXX14_CONSTEXPR bool nonzero(size_t index) const noexcept {
while (index < len()) {
if (rindex(index) != 0) {
return true;
}
index++;
}
return false;
}
// normalize the big integer, so most-significant zero limbs are removed.
BOOST_JSON_CXX14_CONSTEXPR void normalize() noexcept {
while (len() > 0 && rindex(0) == 0) {
length--;
}
}
};
BOOST_FORCEINLINE BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
uint64_t empty_hi64(bool& truncated) noexcept {
truncated = false;
return 0;
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept {
truncated = false;
int shl = leading_zeroes(r0);
return r0 << shl;
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
int shl = leading_zeroes(r0);
if (shl == 0) {
truncated = r1 != 0;
return r0;
} else {
int shr = 64 - shl;
truncated = (r1 << shl) != 0;
return (r0 << shl) | (r1 >> shr);
}
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept {
return uint64_hi64(r0, truncated);
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept {
uint64_t x0 = r0;
uint64_t x1 = r1;
return uint64_hi64((x0 << 32) | x1, truncated);
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept {
uint64_t x0 = r0;
uint64_t x1 = r1;
uint64_t x2 = r2;
return uint64_hi64(x0, (x1 << 32) | x2, truncated);
}
// add two small integers, checking for overflow.
// we want an efficient operation. for msvc, where
// we don't have built-in intrinsics, this is still
// pretty fast.
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
limb scalar_add(limb x, limb y, bool& overflow) noexcept {
limb z;
// gcc and clang
#if defined(__has_builtin)
#if __has_builtin(__builtin_add_overflow)
if (!cpp20_and_in_constexpr()) {
overflow = __builtin_add_overflow(x, y, &z);
return z;
}
#endif
#endif
// generic, this still optimizes correctly on MSVC.
z = x + y;
overflow = z < x;
return z;
}
// multiply two small integers, getting both the high and low bits.
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
limb scalar_mul(limb x, limb y, limb& carry) noexcept {
#ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
#if defined(__SIZEOF_INT128__)
// GCC and clang both define it as an extension.
__uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
carry = limb(z >> limb_bits);
return limb(z);
#else
// fallback, no native 128-bit integer multiplication with carry.
// on msvc, this optimizes identically, somehow.
value128 z = full_multiplication(x, y);
bool overflow;
z.low = scalar_add(z.low, carry, overflow);
z.high += uint64_t(overflow); // cannot overflow
carry = z.high;
return z.low;
#endif
#else
uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
carry = limb(z >> limb_bits);
return limb(z);
#endif
}
// add scalar value to bigint starting from offset.
// used in grade school multiplication
template <uint16_t size>
inline BOOST_JSON_FASTFLOAT_CONSTEXPR20
bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
size_t index = start;
limb carry = y;
bool overflow;
while (carry != 0 && index < vec.len()) {
vec[index] = scalar_add(vec[index], carry, overflow);
carry = limb(overflow);
index += 1;
}
if (carry != 0) {
BOOST_JSON_FASTFLOAT_TRY(vec.try_push(carry));
}
return true;
}
// add scalar value to bigint.
template <uint16_t size>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
bool small_add(stackvec<size>& vec, limb y) noexcept {
return small_add_from(vec, y, 0);
}
// multiply bigint by scalar value.
template <uint16_t size>
inline BOOST_JSON_FASTFLOAT_CONSTEXPR20
bool small_mul(stackvec<size>& vec, limb y) noexcept {
limb carry = 0;
for (size_t index = 0; index < vec.len(); index++) {
vec[index] = scalar_mul(vec[index], y, carry);
}
if (carry != 0) {
BOOST_JSON_FASTFLOAT_TRY(vec.try_push(carry));
}
return true;
}
// add bigint to bigint starting from index.
// used in grade school multiplication
template <uint16_t size>
BOOST_JSON_FASTFLOAT_CONSTEXPR20
bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
// the effective x buffer is from `xstart..x.len()`, so exit early
// if we can't get that current range.
if (x.len() < start || y.len() > x.len() - start) {
BOOST_JSON_FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
}
bool carry = false;
for (size_t index = 0; index < y.len(); index++) {
limb xi = x[index + start];
limb yi = y[index];
bool c1 = false;
bool c2 = false;
xi = scalar_add(xi, yi, c1);
if (carry) {
xi = scalar_add(xi, 1, c2);
}
x[index + start] = xi;
carry = c1 | c2;
}
// handle overflow
if (carry) {
BOOST_JSON_FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
}
return true;
}
// add bigint to bigint.
template <uint16_t size>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
bool large_add_from(stackvec<size>& x, limb_span y) noexcept {
return large_add_from(x, y, 0);
}
// grade-school multiplication algorithm
template <uint16_t size>
BOOST_JSON_FASTFLOAT_CONSTEXPR20
bool long_mul(stackvec<size>& x, limb_span y) noexcept {
limb_span xs = limb_span(x.data, x.len());
stackvec<size> z(xs);
limb_span zs = limb_span(z.data, z.len());
if (y.len() != 0) {
limb y0 = y[0];
BOOST_JSON_FASTFLOAT_TRY(small_mul(x, y0));
for (size_t index = 1; index < y.len(); index++) {
limb yi = y[index];
stackvec<size> zi;
if (yi != 0) {
// re-use the same buffer throughout
zi.set_len(0);
BOOST_JSON_FASTFLOAT_TRY(zi.try_extend(zs));
BOOST_JSON_FASTFLOAT_TRY(small_mul(zi, yi));
limb_span zis = limb_span(zi.data, zi.len());
BOOST_JSON_FASTFLOAT_TRY(large_add_from(x, zis, index));
}
}
}
x.normalize();
return true;
}
// grade-school multiplication algorithm
template <uint16_t size>
BOOST_JSON_FASTFLOAT_CONSTEXPR20
bool large_mul(stackvec<size>& x, limb_span y) noexcept {
if (y.len() == 1) {
BOOST_JSON_FASTFLOAT_TRY(small_mul(x, y[0]));
} else {
BOOST_JSON_FASTFLOAT_TRY(long_mul(x, y));
}
return true;
}
template <typename = void>
struct pow5_tables {
static constexpr uint32_t large_step = 135;
static constexpr uint64_t small_power_of_5[] = {
1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL,
1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL,
6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL,
3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL,
2384185791015625UL, 11920928955078125UL, 59604644775390625UL,
298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL,
};
#ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
constexpr static limb large_power_of_5[] = {
1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
10482974169319127550UL, 198276706040285095UL};
#else
constexpr static limb large_power_of_5[] = {
4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
#endif
};
template <typename T>
constexpr uint32_t pow5_tables<T>::large_step;
template <typename T>
constexpr uint64_t pow5_tables<T>::small_power_of_5[];
template <typename T>
constexpr limb pow5_tables<T>::large_power_of_5[];
// big integer type. implements a small subset of big integer
// arithmetic, using simple algorithms since asymptotically
// faster algorithms are slower for a small number of limbs.
// all operations assume the big-integer is normalized.
struct bigint : pow5_tables<> {
// storage of the limbs, in little-endian order.
stackvec<bigint_limbs> vec;
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bigint(): vec() {}
bigint(const bigint &) = delete;
bigint &operator=(const bigint &) = delete;
bigint(bigint &&) = delete;
bigint &operator=(bigint &&other) = delete;
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bigint(uint64_t value): vec() {
#ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
vec.push_unchecked(value);
#else
vec.push_unchecked(uint32_t(value));
vec.push_unchecked(uint32_t(value >> 32));
#endif
vec.normalize();
}
// get the high 64 bits from the vector, and if bits were truncated.
// this is to get the significant digits for the float.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool& truncated) const noexcept {
#ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
if (vec.len() == 0) {
return empty_hi64(truncated);
} else if (vec.len() == 1) {
return uint64_hi64(vec.rindex(0), truncated);
} else {
uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
truncated |= vec.nonzero(2);
return result;
}
#else
if (vec.len() == 0) {
return empty_hi64(truncated);
} else if (vec.len() == 1) {
return uint32_hi64(vec.rindex(0), truncated);
} else if (vec.len() == 2) {
return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
} else {
uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
truncated |= vec.nonzero(3);
return result;
}
#endif
}
// compare two big integers, returning the large value.
// assumes both are normalized. if the return value is
// negative, other is larger, if the return value is
// positive, this is larger, otherwise they are equal.
// the limbs are stored in little-endian order, so we
// must compare the limbs in ever order.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 int compare(const bigint& other) const noexcept {
if (vec.len() > other.vec.len()) {
return 1;
} else if (vec.len() < other.vec.len()) {
return -1;
} else {
for (size_t index = vec.len(); index > 0; index--) {
limb xi = vec[index - 1];
limb yi = other.vec[index - 1];
if (xi > yi) {
return 1;
} else if (xi < yi) {
return -1;
}
}
return 0;
}
}
// shift left each limb n bits, carrying over to the new limb
// returns true if we were able to shift all the digits.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept {
// Internally, for each item, we shift left by n, and add the previous
// right shifted limb-bits.
// For example, we transform (for u8) shifted left 2, to:
// b10100100 b01000010
// b10 b10010001 b00001000
BOOST_ASSERT(n != 0);
BOOST_ASSERT(n < sizeof(limb) * 8);
size_t shl = n;
size_t shr = limb_bits - shl;
limb prev = 0;
for (size_t index = 0; index < vec.len(); index++) {
limb xi = vec[index];
vec[index] = (xi << shl) | (prev >> shr);
prev = xi;
}
limb carry = prev >> shr;
if (carry != 0) {
return vec.try_push(carry);
}
return true;
}
// move the limbs left by `n` limbs.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept {
BOOST_ASSERT(n != 0);
if (n + vec.len() > vec.capacity()) {
return false;
} else if (!vec.is_empty()) {
// move limbs
limb* dst = vec.data + n;
const limb* src = vec.data;
std::copy_backward(src, src + vec.len(), dst + vec.len());
// fill in empty limbs
limb* first = vec.data;
limb* last = first + n;
::std::fill(first, last, 0);
vec.set_len(n + vec.len());
return true;
} else {
return true;
}
}
// move the limbs left by `n` bits.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
size_t rem = n % limb_bits;
size_t div = n / limb_bits;
if (rem != 0) {
BOOST_JSON_FASTFLOAT_TRY(shl_bits(rem));
}
if (div != 0) {
BOOST_JSON_FASTFLOAT_TRY(shl_limbs(div));
}
return true;
}
// get the number of leading zeros in the bigint.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept {
if (vec.is_empty()) {
return 0;
} else {
#ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
return leading_zeroes(vec.rindex(0));
#else
// no use defining a specialized leading_zeroes for a 32-bit type.
uint64_t r0 = vec.rindex(0);
return leading_zeroes(r0 << 32);
#endif
}
}
// get the number of bits in the bigint.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept {
int lz = ctlz();
return int(limb_bits * vec.len()) - lz;
}
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept {
return small_mul(vec, y);
}
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept {
return small_add(vec, y);
}
// multiply as if by 2 raised to a power.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept {
return shl(exp);
}
// multiply as if by 5 raised to a power.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
// multiply by a power of 5
constexpr size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
limb_span large = limb_span(large_power_of_5, large_length);
while (exp >= large_step) {
BOOST_JSON_FASTFLOAT_TRY(large_mul(vec, large));
exp -= large_step;
}
#ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
constexpr uint32_t small_step = 27;
constexpr limb max_native = 7450580596923828125UL;
#else
constexpr uint32_t small_step = 13;
constexpr limb max_native = 1220703125U;
#endif
while (exp >= small_step) {
BOOST_JSON_FASTFLOAT_TRY(small_mul(vec, max_native));
exp -= small_step;
}
if (exp != 0) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
// This is similar to https://github.com/llvm/llvm-project/issues/47746,
// except the workaround described there don't work here
BOOST_JSON_FASTFLOAT_TRY(
small_mul(vec, limb(((void)small_power_of_5[0], small_power_of_5[exp])))
);
}
return true;
}
// multiply as if by 10 raised to a power.
BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept {
BOOST_JSON_FASTFLOAT_TRY(pow5(exp));
return pow2(exp);
}
};
}}}}}} // namespace fast_float
#endif
@@ -0,0 +1,34 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// Derivative of: https://github.com/fastfloat/fast_float
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_CONSTEXPR_FEATURE_DETECT_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_CONSTEXPR_FEATURE_DETECT_HPP
#ifdef __has_include
#if __has_include(<version>)
#include <version>
#endif
#endif
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
# define BOOST_JSON_HAS_BIT_CAST
#endif
#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L
# define BOOST_JSON_HAS_IS_CONSTANT_EVALUATED
#endif
// Testing for relevant C++20 constexpr library features
#if defined(BOOST_JSON_HAS_IS_CONSTANT_EVALUATED) \
&& defined(BOOST_JSON_HAS_BIT_CAST) \
&& __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
#define BOOST_JSON_FASTFLOAT_CONSTEXPR20 constexpr
#else
#define BOOST_JSON_FASTFLOAT_CONSTEXPR20
#endif
#endif // BOOST_JSON_DETAIL_CHARCONV_FASTFLOAT_CONSTEXPR_FEATURE_DETECT_HPP
@@ -0,0 +1,196 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// Derivative of: https://github.com/fastfloat/fast_float
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_DECIMAL_TO_BINARY_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_DECIMAL_TO_BINARY_HPP
#include <boost/json/detail/charconv/detail/fast_float/float_common.hpp>
#include <boost/json/detail/charconv/detail/fast_float/fast_table.hpp>
#include <cfloat>
#include <cinttypes>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <cstring>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating
// the result, with the "high" part corresponding to the most significant bits and the
// low part corresponding to the least significant bits.
//
template <int bit_precision>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
value128 compute_product_approximation(int64_t q, uint64_t w) {
const int index = 2 * int(q - powers::smallest_power_of_five);
// For small values of q, e.g., q in [0,27], the answer is always exact because
// The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]);
// gives the exact answer.
value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]);
static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]");
constexpr uint64_t precision_mask = (bit_precision < 64) ?
(uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
: uint64_t(0xFFFFFFFFFFFFFFFF);
if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower)
// regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed.
value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) {
firstproduct.high++;
}
}
return firstproduct;
}
namespace detail {
/**
* For q in (0,350), we have that
* f = (((152170 + 65536) * q ) >> 16);
* is equal to
* floor(p) + q
* where
* p = log(5**q)/log(2) = q * log(5)/log(2)
*
* For negative values of q in (-400,0), we have that
* f = (((152170 + 65536) * q ) >> 16);
* is equal to
* -ceil(p) + q
* where
* p = log(5**-q)/log(2) = -q * log(5)/log(2)
*/
constexpr BOOST_FORCEINLINE int32_t power(int32_t q) noexcept {
return (((152170 + 65536) * q) >> 16) + 63;
}
} // namespace detail
// create an adjusted mantissa, biased by the invalid power2
// for significant digits already multiplied by 10 ** q.
template <typename binary>
BOOST_FORCEINLINE BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
int hilz = int(w >> 63) ^ 1;
adjusted_mantissa answer;
answer.mantissa = w << hilz;
int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias);
return answer;
}
// w * 10 ** q, without rounding the representation up.
// the power2 in the exponent will be adjusted by invalid_am_bias.
template <typename binary>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
int lz = leading_zeroes(w);
w <<= lz;
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
return compute_error_scaled<binary>(q, product.high, lz);
}
// w * 10 ** q
// The returned value should be a valid ieee64 number that simply need to be packed.
// However, in some very rare cases, the computation will fail. In such cases, we
// return an adjusted_mantissa with a negative power of 2: the caller should recompute
// in such cases.
template <typename binary>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
adjusted_mantissa answer;
if ((w == 0) || (q < binary::smallest_power_of_ten())) {
answer.power2 = 0;
answer.mantissa = 0;
// result should be zero
return answer;
}
if (q > binary::largest_power_of_ten()) {
// we want to get infinity:
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
return answer;
}
// At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five].
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(w);
w <<= lz;
// The required precision is binary::mantissa_explicit_bits() + 3 because
// 1. We need the implicit bit
// 2. We need an extra bit for rounding purposes
// 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift)
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
// The computed 'product' is always sufficient.
// Mathematical proof:
// Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to appear)
// See script/mushtak_lemire.py
// The "compute_product_approximation" function can be slightly slower than a branchless approach:
// value128 product = compute_product(q, w);
// but in practice, we can win big with the compute_product_approximation if its additional branch
// is easily predicted. Which is best is data specific.
int upperbit = int(product.high >> 63);
answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent());
if (answer.power2 <= 0) { // we have a subnormal?
// Here have that answer.power2 <= 0 so -answer.power2 >= 0
if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
answer.power2 = 0;
answer.mantissa = 0;
// result should be zero
return answer;
}
// next line is safe because -answer.power2 + 1 < 64
answer.mantissa >>= -answer.power2 + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1;
return answer;
}
// usually, we round *up*, but if we fall right in between and and we have an
// even basis, we need to round down
// We are only concerned with the cases where 5**q fits in single 64-bit word.
if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) &&
((answer.mantissa & 3) == 1) ) { // we may fall between two floats!
// To be in-between two floats we need that in doing
// answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
// ... we dropped out only zeroes. But if this happened, then we can go back!!!
if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) {
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1;
if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) {
answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits());
answer.power2++; // undo previous addition
}
answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits());
if (answer.power2 >= binary::infinite_power()) { // infinity
answer.power2 = binary::infinite_power();
answer.mantissa = 0;
}
return answer;
}
}}}}}} // namespace fast_float
#endif
@@ -0,0 +1,442 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// Derivative of: https://github.com/fastfloat/fast_float
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_DIGIT_COMPARISON_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_DIGIT_COMPARISON_HPP
#include <boost/json/detail/charconv/detail/fast_float/float_common.hpp>
#include <boost/json/detail/charconv/detail/fast_float/bigint.hpp>
#include <boost/json/detail/charconv/detail/fast_float/ascii_number.hpp>
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iterator>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
// 1e0 to 1e19
constexpr static uint64_t powers_of_ten_uint64[] = {
1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL,
1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL,
100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL,
1000000000000000000UL, 10000000000000000000UL};
// calculate the exponent, in scientific notation, of the number.
// this algorithm is not even close to optimized, but it has no practical
// effect on performance: in order to have a faster algorithm, we'd need
// to slow down performance for faster algorithms, and this is still fast.
template <typename UC>
BOOST_FORCEINLINE BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
int32_t scientific_exponent(parsed_number_string_t<UC> & num) noexcept {
uint64_t mantissa = num.mantissa;
int32_t exponent = int32_t(num.exponent);
while (mantissa >= 10000) {
mantissa /= 10000;
exponent += 4;
}
while (mantissa >= 100) {
mantissa /= 100;
exponent += 2;
}
while (mantissa >= 10) {
mantissa /= 10;
exponent += 1;
}
return exponent;
}
// this converts a native floating-point number to an extended-precision float.
template <typename T>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
adjusted_mantissa to_extended(T value) noexcept {
using equiv_uint = typename binary_format<T>::equiv_uint;
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
adjusted_mantissa am;
int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
equiv_uint bits;
#ifdef BOOST_JSON_HAS_BIT_CAST
bits = std::bit_cast<equiv_uint>(value);
#else
::memcpy(&bits, &value, sizeof(T));
#endif
if ((bits & exponent_mask) == 0) {
// denormal
am.power2 = 1 - bias;
am.mantissa = bits & mantissa_mask;
} else {
// normal
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
}
return am;
}
// get the extended precision value of the halfway point between b and b+u.
// we are given a native float that represents b, so we need to adjust it
// halfway between b and b+u.
template <typename T>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
adjusted_mantissa to_extended_halfway(T value) noexcept {
adjusted_mantissa am = to_extended(value);
am.mantissa <<= 1;
am.mantissa += 1;
am.power2 -= 1;
return am;
}
// round an extended-precision float to the nearest machine float.
template <typename T, typename callback>
BOOST_FORCEINLINE BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
void round(adjusted_mantissa& am, callback cb) noexcept {
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
if (-am.power2 >= mantissa_shift) {
// have a denormal float
int32_t shift = -am.power2 + 1;
cb(am, std::min<int32_t>(shift, 64));
// check for round-up: if rounding-nearest carried us to the hidden bit.
am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1;
return;
}
// have a normal float, use the default shift.
cb(am, mantissa_shift);
// check for carry
if (am.mantissa >= (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
am.power2++;
}
// check for infinite: we could have carried to an infinite power
am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
if (am.power2 >= binary_format<T>::infinite_power()) {
am.power2 = binary_format<T>::infinite_power();
am.mantissa = 0;
}
}
template <typename callback>
BOOST_FORCEINLINE BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
const uint64_t mask
= (shift == 64)
? UINT64_MAX
: (uint64_t(1) << shift) - 1;
const uint64_t halfway
= (shift == 0)
? 0
: uint64_t(1) << (shift - 1);
uint64_t truncated_bits = am.mantissa & mask;
bool is_above = truncated_bits > halfway;
bool is_halfway = truncated_bits == halfway;
// shift digits into position
if (shift == 64) {
am.mantissa = 0;
} else {
am.mantissa >>= shift;
}
am.power2 += shift;
bool is_odd = (am.mantissa & 1) == 1;
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
}
BOOST_FORCEINLINE BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
if (shift == 64) {
am.mantissa = 0;
} else {
am.mantissa >>= shift;
}
am.power2 += shift;
}
template <typename UC>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
void skip_zeros(UC const * & first, UC const * last) noexcept {
uint64_t val;
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) {
break;
}
first += int_cmp_len<UC>();
}
while (first != last) {
if (*first != UC('0')) {
break;
}
first++;
}
}
// determine if any non-zero digits were truncated.
// all characters must be valid digits.
template <typename UC>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
bool is_truncated(UC const * first, UC const * last) noexcept {
// do 8-bit optimizations, can just compare to 8 literal 0s.
uint64_t val;
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) {
return true;
}
first += int_cmp_len<UC>();
}
while (first != last) {
if (*first != UC('0')) {
return true;
}
++first;
}
return false;
}
template <typename UC>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
bool is_truncated(span<const UC> s) noexcept {
return is_truncated(s.ptr, s.ptr + s.len());
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
void parse_eight_digits(const char16_t*& , limb& , size_t& , size_t& ) noexcept {
// currently unused
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
void parse_eight_digits(const char32_t*& , limb& , size_t& , size_t& ) noexcept {
// currently unused
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
counter += 8;
count += 8;
}
template <typename UC>
BOOST_FORCEINLINE BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
void parse_one_digit(UC const *& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 10 + limb(*p - UC('0'));
p++;
counter++;
count++;
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
void add_native(bigint& big, limb power, limb value) noexcept {
big.mul(power);
big.add(value);
}
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
void round_up_bigint(bigint& big, size_t& count) noexcept {
// need to round-up the digits, but need to avoid rounding
// ....9999 to ...10000, which could cause a false halfway point.
add_native(big, 10, 1);
count++;
}
// parse the significant digits into a big integer
template <typename UC>
inline BOOST_JSON_FASTFLOAT_CONSTEXPR20
void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, size_t max_digits, size_t& digits) noexcept {
// try to minimize the number of big integer and scalar multiplication.
// therefore, try to parse 8 digits at a time, and multiply by the largest
// scalar value (9 or 19 digits) for each step.
size_t counter = 0;
digits = 0;
limb value = 0;
#ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
constexpr size_t step = 19;
#else
constexpr size_t step = 9;
#endif
// process all integer digits.
UC const * p = num.integer.ptr;
UC const * pend = p + num.integer.len();
skip_zeros(p, pend);
// process all digits, in increments of step per loop
while (p != pend) {
if (std::is_same<UC,char>::value) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits);
}
}
while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits);
}
if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value);
bool truncated = is_truncated(p, pend);
if (num.fraction.ptr != nullptr) {
truncated |= is_truncated(num.fraction);
}
if (truncated) {
round_up_bigint(result, digits);
}
return;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
value = 0;
}
}
// add our fraction digits, if they're available.
if (num.fraction.ptr != nullptr) {
p = num.fraction.ptr;
pend = p + num.fraction.len();
if (digits == 0) {
skip_zeros(p, pend);
}
// process all digits, in increments of step per loop
while (p != pend) {
if (std::is_same<UC,char>::value) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
parse_eight_digits(p, value, counter, digits);
}
}
while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits);
}
if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value);
bool truncated = is_truncated(p, pend);
if (truncated) {
round_up_bigint(result, digits);
}
return;
} else {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
counter = 0;
value = 0;
}
}
}
if (counter != 0) {
add_native(result, limb(powers_of_ten_uint64[counter]), value);
}
}
template <typename T>
inline BOOST_JSON_FASTFLOAT_CONSTEXPR20
adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept {
bigmant.pow10(uint32_t(exponent));
adjusted_mantissa answer;
bool truncated;
answer.mantissa = bigmant.hi64(truncated);
int bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
answer.power2 = bigmant.bit_length() - 64 + bias;
round<T>(answer, [truncated](adjusted_mantissa& a, int32_t shift) {
round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
return is_above || (is_halfway && truncated) || (is_odd && is_halfway);
});
});
return answer;
}
// the scaling here is quite simple: we have, for the real digits `m * 10^e`,
// and for the theoretical digits `n * 2^f`. Since `e` is always negative,
// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`.
// we then need to scale by `2^(f- e)`, and then the two significant digits
// are of the same magnitude.
template <typename T>
inline BOOST_JSON_FASTFLOAT_CONSTEXPR20
adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
bigint& real_digits = bigmant;
int32_t real_exp = exponent;
// get the value of `b`, rounded down, and get a bigint representation of b+h
adjusted_mantissa am_b = am;
// gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type.
round<T>(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); });
T b;
to_float(false, am_b, b);
adjusted_mantissa theor = to_extended_halfway(b);
bigint theor_digits(theor.mantissa);
int32_t theor_exp = theor.power2;
// scale real digits and theor digits to be same power.
int32_t pow2_exp = theor_exp - real_exp;
uint32_t pow5_exp = uint32_t(-real_exp);
if (pow5_exp != 0) {
theor_digits.pow5(pow5_exp);
}
if (pow2_exp > 0) {
theor_digits.pow2(uint32_t(pow2_exp));
} else if (pow2_exp < 0) {
real_digits.pow2(uint32_t(-pow2_exp));
}
// compare digits, and use it to director rounding
int ord = real_digits.compare(theor_digits);
adjusted_mantissa answer = am;
round<T>(answer, [ord](adjusted_mantissa& a, int32_t shift) {
round_nearest_tie_even(a, shift, [ord](bool is_odd, bool, bool) -> bool {
if (ord > 0) {
return true;
} else if (ord < 0) {
return false;
} else {
return is_odd;
}
});
});
return answer;
}
// parse the significant digits as a big integer to unambiguously round
// the significant digits. here, we are trying to determine how to round
// an extended float representation close to `b+h`, halfway between `b`
// (the float rounded-down) and `b+u`, the next positive float. this
// algorithm is always correct, and uses one of two approaches. when
// the exponent is positive relative to the significant digits (such as
// 1234), we create a big-integer representation, get the high 64-bits,
// determine if any lower bits are truncated, and use that to direct
// rounding. in case of a negative exponent relative to the significant
// digits (such as 1.2345), we create a theoretical representation of
// `b` as a big-integer type, scaled to the same binary exponent as
// the actual digits. we then compare the big integer representations
// of both, and use that to direct rounding.
template <typename T, typename UC>
inline BOOST_JSON_FASTFLOAT_CONSTEXPR20
adjusted_mantissa digit_comp(parsed_number_string_t<UC>& num, adjusted_mantissa am) noexcept {
// remove the invalid exponent bias
am.power2 -= invalid_am_bias;
int32_t sci_exp = scientific_exponent(num);
size_t max_digits = binary_format<T>::max_digits();
size_t digits = 0;
bigint bigmant;
parse_mantissa(bigmant, num, max_digits, digits);
// can't underflow, since digits is at most max_digits.
int32_t exponent = sci_exp + 1 - int32_t(digits);
if (exponent >= 0) {
return positive_digit_comp<T>(bigmant, exponent);
} else {
return negative_digit_comp<T>(bigmant, am, exponent);
}
}
}}}}}} // namespace fast_float
#endif
@@ -0,0 +1,48 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// Derivative of: https://github.com/fastfloat/fast_float
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_FAST_FLOAT_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_FAST_FLOAT_HPP
#include <boost/json/detail/charconv/detail/fast_float/float_common.hpp>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
/**
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
* a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale.
* The resulting floating-point value is the closest floating-point values (using either float or double),
* using the "round to even" convention for values that would otherwise fall right in-between two values.
* That is, we provide exact parsing according to the IEEE standard.
*
* Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
* parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
* `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
*
* The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
*
* Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
* the type `fast_float::chars_format`. It is a bitset value: we check whether
* `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
* to determine whether we allow the fixed point and scientific notation respectively.
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
*/
template<typename T, typename UC = char>
BOOST_JSON_FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
T &value, chars_format fmt = chars_format::general) noexcept;
/**
* Like from_chars, but accepts an `options` argument to govern number parsing.
*/
template<typename T, typename UC = char>
BOOST_JSON_FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
T &value, parse_options_t<UC> options) noexcept;
}}}}}} // namespace fast_float
#include <boost/json/detail/charconv/detail/fast_float/parse_number.hpp>
#endif // BOOST_JSON_DETAIL_CHARCONV_FASTFLOAT_FAST_FLOAT_H
@@ -0,0 +1,708 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// Derivative of: https://github.com/fastfloat/fast_float
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_FAST_TABLE_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_FAST_TABLE_HPP
#include <boost/json/detail/charconv/detail/fast_float/float_common.hpp>
#include <cstdint>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
/**
* When mapping numbers from decimal to binary,
* we go from w * 10^q to m * 2^p but we have
* 10^q = 5^q * 2^q, so effectively
* we are trying to match
* w * 2^q * 5^q to m * 2^p. Thus the powers of two
* are not a concern since they can be represented
* exactly using the binary notation, only the powers of five
* affect the binary significand.
*/
/**
* The smallest non-zero float (binary64) is 2^-1074.
* We take as input numbers of the form w x 10^q where w < 2^64.
* We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076.
* However, we have that
* (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074.
* Thus it is possible for a number of the form w * 10^-342 where
* w is a 64-bit value to be a non-zero floating-point number.
*********
* Any number of form w * 10^309 where w>= 1 is going to be
* infinite in binary64 so we never need to worry about powers
* of 5 greater than 308.
*/
template <class unused = void>
struct powers_template {
constexpr static int smallest_power_of_five = binary_format<double>::smallest_power_of_ten();
constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten();
constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1);
// Powers of five from 5^-342 all the way to 5^308 rounded toward one.
constexpr static uint64_t power_of_five_128[number_of_entries] = {
0xeef453d6923bd65a,0x113faa2906a13b3f,
0x9558b4661b6565f8,0x4ac7ca59a424c507,
0xbaaee17fa23ebf76,0x5d79bcf00d2df649,
0xe95a99df8ace6f53,0xf4d82c2c107973dc,
0x91d8a02bb6c10594,0x79071b9b8a4be869,
0xb64ec836a47146f9,0x9748e2826cdee284,
0xe3e27a444d8d98b7,0xfd1b1b2308169b25,
0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7,
0xb208ef855c969f4f,0xbdbd2d335e51a935,
0xde8b2b66b3bc4723,0xad2c788035e61382,
0x8b16fb203055ac76,0x4c3bcb5021afcc31,
0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d,
0xd953e8624b85dd78,0xd71d6dad34a2af0d,
0x87d4713d6f33aa6b,0x8672648c40e5ad68,
0xa9c98d8ccb009506,0x680efdaf511f18c2,
0xd43bf0effdc0ba48,0x212bd1b2566def2,
0x84a57695fe98746d,0x14bb630f7604b57,
0xa5ced43b7e3e9188,0x419ea3bd35385e2d,
0xcf42894a5dce35ea,0x52064cac828675b9,
0x818995ce7aa0e1b2,0x7343efebd1940993,
0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8,
0xca66fa129f9b60a6,0xd41a26e077774ef6,
0xfd00b897478238d0,0x8920b098955522b4,
0x9e20735e8cb16382,0x55b46e5f5d5535b0,
0xc5a890362fddbc62,0xeb2189f734aa831d,
0xf712b443bbd52b7b,0xa5e9ec7501d523e4,
0x9a6bb0aa55653b2d,0x47b233c92125366e,
0xc1069cd4eabe89f8,0x999ec0bb696e840a,
0xf148440a256e2c76,0xc00670ea43ca250d,
0x96cd2a865764dbca,0x380406926a5e5728,
0xbc807527ed3e12bc,0xc605083704f5ecf2,
0xeba09271e88d976b,0xf7864a44c633682e,
0x93445b8731587ea3,0x7ab3ee6afbe0211d,
0xb8157268fdae9e4c,0x5960ea05bad82964,
0xe61acf033d1a45df,0x6fb92487298e33bd,
0x8fd0c16206306bab,0xa5d3b6d479f8e056,
0xb3c4f1ba87bc8696,0x8f48a4899877186c,
0xe0b62e2929aba83c,0x331acdabfe94de87,
0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14,
0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9,
0xdb71e91432b1a24a,0xc9e82cd9f69d6150,
0x892731ac9faf056e,0xbe311c083a225cd2,
0xab70fe17c79ac6ca,0x6dbd630a48aaf406,
0xd64d3d9db981787d,0x92cbbccdad5b108,
0x85f0468293f0eb4e,0x25bbf56008c58ea5,
0xa76c582338ed2621,0xaf2af2b80af6f24e,
0xd1476e2c07286faa,0x1af5af660db4aee1,
0x82cca4db847945ca,0x50d98d9fc890ed4d,
0xa37fce126597973c,0xe50ff107bab528a0,
0xcc5fc196fefd7d0c,0x1e53ed49a96272c8,
0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a,
0x9faacf3df73609b1,0x77b191618c54e9ac,
0xc795830d75038c1d,0xd59df5b9ef6a2417,
0xf97ae3d0d2446f25,0x4b0573286b44ad1d,
0x9becce62836ac577,0x4ee367f9430aec32,
0xc2e801fb244576d5,0x229c41f793cda73f,
0xf3a20279ed56d48a,0x6b43527578c1110f,
0x9845418c345644d6,0x830a13896b78aaa9,
0xbe5691ef416bd60c,0x23cc986bc656d553,
0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8,
0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9,
0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53,
0xe858ad248f5c22c9,0xd1b3400f8f9cff68,
0x91376c36d99995be,0x23100809b9c21fa1,
0xb58547448ffffb2d,0xabd40a0c2832a78a,
0xe2e69915b3fff9f9,0x16c90c8f323f516c,
0x8dd01fad907ffc3b,0xae3da7d97f6792e3,
0xb1442798f49ffb4a,0x99cd11cfdf41779c,
0xdd95317f31c7fa1d,0x40405643d711d583,
0x8a7d3eef7f1cfc52,0x482835ea666b2572,
0xad1c8eab5ee43b66,0xda3243650005eecf,
0xd863b256369d4a40,0x90bed43e40076a82,
0x873e4f75e2224e68,0x5a7744a6e804a291,
0xa90de3535aaae202,0x711515d0a205cb36,
0xd3515c2831559a83,0xd5a5b44ca873e03,
0x8412d9991ed58091,0xe858790afe9486c2,
0xa5178fff668ae0b6,0x626e974dbe39a872,
0xce5d73ff402d98e3,0xfb0a3d212dc8128f,
0x80fa687f881c7f8e,0x7ce66634bc9d0b99,
0xa139029f6a239f72,0x1c1fffc1ebc44e80,
0xc987434744ac874e,0xa327ffb266b56220,
0xfbe9141915d7a922,0x4bf1ff9f0062baa8,
0x9d71ac8fada6c9b5,0x6f773fc3603db4a9,
0xc4ce17b399107c22,0xcb550fb4384d21d3,
0xf6019da07f549b2b,0x7e2a53a146606a48,
0x99c102844f94e0fb,0x2eda7444cbfc426d,
0xc0314325637a1939,0xfa911155fefb5308,
0xf03d93eebc589f88,0x793555ab7eba27ca,
0x96267c7535b763b5,0x4bc1558b2f3458de,
0xbbb01b9283253ca2,0x9eb1aaedfb016f16,
0xea9c227723ee8bcb,0x465e15a979c1cadc,
0x92a1958a7675175f,0xbfacd89ec191ec9,
0xb749faed14125d36,0xcef980ec671f667b,
0xe51c79a85916f484,0x82b7e12780e7401a,
0x8f31cc0937ae58d2,0xd1b2ecb8b0908810,
0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15,
0xdfbdcece67006ac9,0x67a791e093e1d49a,
0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0,
0xaecc49914078536d,0x58fae9f773886e18,
0xda7f5bf590966848,0xaf39a475506a899e,
0x888f99797a5e012d,0x6d8406c952429603,
0xaab37fd7d8f58178,0xc8e5087ba6d33b83,
0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64,
0x855c3be0a17fcd26,0x5cf2eea09a55067f,
0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e,
0xd0601d8efc57b08b,0xf13b94daf124da26,
0x823c12795db6ce57,0x76c53d08d6b70858,
0xa2cb1717b52481ed,0x54768c4b0c64ca6e,
0xcb7ddcdda26da268,0xa9942f5dcf7dfd09,
0xfe5d54150b090b02,0xd3f93b35435d7c4c,
0x9efa548d26e5a6e1,0xc47bc5014a1a6daf,
0xc6b8e9b0709f109a,0x359ab6419ca1091b,
0xf867241c8cc6d4c0,0xc30163d203c94b62,
0x9b407691d7fc44f8,0x79e0de63425dcf1d,
0xc21094364dfb5636,0x985915fc12f542e4,
0xf294b943e17a2bc4,0x3e6f5b7b17b2939d,
0x979cf3ca6cec5b5a,0xa705992ceecf9c42,
0xbd8430bd08277231,0x50c6ff782a838353,
0xece53cec4a314ebd,0xa4f8bf5635246428,
0x940f4613ae5ed136,0x871b7795e136be99,
0xb913179899f68584,0x28e2557b59846e3f,
0xe757dd7ec07426e5,0x331aeada2fe589cf,
0x9096ea6f3848984f,0x3ff0d2c85def7621,
0xb4bca50b065abe63,0xfed077a756b53a9,
0xe1ebce4dc7f16dfb,0xd3e8495912c62894,
0x8d3360f09cf6e4bd,0x64712dd7abbbd95c,
0xb080392cc4349dec,0xbd8d794d96aacfb3,
0xdca04777f541c567,0xecf0d7a0fc5583a0,
0x89e42caaf9491b60,0xf41686c49db57244,
0xac5d37d5b79b6239,0x311c2875c522ced5,
0xd77485cb25823ac7,0x7d633293366b828b,
0x86a8d39ef77164bc,0xae5dff9c02033197,
0xa8530886b54dbdeb,0xd9f57f830283fdfc,
0xd267caa862a12d66,0xd072df63c324fd7b,
0x8380dea93da4bc60,0x4247cb9e59f71e6d,
0xa46116538d0deb78,0x52d9be85f074e608,
0xcd795be870516656,0x67902e276c921f8b,
0x806bd9714632dff6,0xba1cd8a3db53b6,
0xa086cfcd97bf97f3,0x80e8a40eccd228a4,
0xc8a883c0fdaf7df0,0x6122cd128006b2cd,
0xfad2a4b13d1b5d6c,0x796b805720085f81,
0x9cc3a6eec6311a63,0xcbe3303674053bb0,
0xc3f490aa77bd60fc,0xbedbfc4411068a9c,
0xf4f1b4d515acb93b,0xee92fb5515482d44,
0x991711052d8bf3c5,0x751bdd152d4d1c4a,
0xbf5cd54678eef0b6,0xd262d45a78a0635d,
0xef340a98172aace4,0x86fb897116c87c34,
0x9580869f0e7aac0e,0xd45d35e6ae3d4da0,
0xbae0a846d2195712,0x8974836059cca109,
0xe998d258869facd7,0x2bd1a438703fc94b,
0x91ff83775423cc06,0x7b6306a34627ddcf,
0xb67f6455292cbf08,0x1a3bc84c17b1d542,
0xe41f3d6a7377eeca,0x20caba5f1d9e4a93,
0x8e938662882af53e,0x547eb47b7282ee9c,
0xb23867fb2a35b28d,0xe99e619a4f23aa43,
0xdec681f9f4c31f31,0x6405fa00e2ec94d4,
0x8b3c113c38f9f37e,0xde83bc408dd3dd04,
0xae0b158b4738705e,0x9624ab50b148d445,
0xd98ddaee19068c76,0x3badd624dd9b0957,
0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6,
0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c,
0xd47487cc8470652b,0x7647c3200069671f,
0x84c8d4dfd2c63f3b,0x29ecd9f40041e073,
0xa5fb0a17c777cf09,0xf468107100525890,
0xcf79cc9db955c2cc,0x7182148d4066eeb4,
0x81ac1fe293d599bf,0xc6f14cd848405530,
0xa21727db38cb002f,0xb8ada00e5a506a7c,
0xca9cf1d206fdc03b,0xa6d90811f0e4851c,
0xfd442e4688bd304a,0x908f4a166d1da663,
0x9e4a9cec15763e2e,0x9a598e4e043287fe,
0xc5dd44271ad3cdba,0x40eff1e1853f29fd,
0xf7549530e188c128,0xd12bee59e68ef47c,
0x9a94dd3e8cf578b9,0x82bb74f8301958ce,
0xc13a148e3032d6e7,0xe36a52363c1faf01,
0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1,
0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9,
0xbcb2b812db11a5de,0x7415d448f6b6f0e7,
0xebdf661791d60f56,0x111b495b3464ad21,
0x936b9fcebb25c995,0xcab10dd900beec34,
0xb84687c269ef3bfb,0x3d5d514f40eea742,
0xe65829b3046b0afa,0xcb4a5a3112a5112,
0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab,
0xb3f4e093db73a093,0x59ed216765690f56,
0xe0f218b8d25088b8,0x306869c13ec3532c,
0x8c974f7383725573,0x1e414218c73a13fb,
0xafbd2350644eeacf,0xe5d1929ef90898fa,
0xdbac6c247d62a583,0xdf45f746b74abf39,
0x894bc396ce5da772,0x6b8bba8c328eb783,
0xab9eb47c81f5114f,0x66ea92f3f326564,
0xd686619ba27255a2,0xc80a537b0efefebd,
0x8613fd0145877585,0xbd06742ce95f5f36,
0xa798fc4196e952e7,0x2c48113823b73704,
0xd17f3b51fca3a7a0,0xf75a15862ca504c5,
0x82ef85133de648c4,0x9a984d73dbe722fb,
0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba,
0xcc963fee10b7d1b3,0x318df905079926a8,
0xffbbcfe994e5c61f,0xfdf17746497f7052,
0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633,
0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0,
0xf9bd690a1b68637b,0x3dfdce7aa3c673b0,
0x9c1661a651213e2d,0x6bea10ca65c084e,
0xc31bfa0fe5698db8,0x486e494fcff30a62,
0xf3e2f893dec3f126,0x5a89dba3c3efccfa,
0x986ddb5c6b3a76b7,0xf89629465a75e01c,
0xbe89523386091465,0xf6bbb397f1135823,
0xee2ba6c0678b597f,0x746aa07ded582e2c,
0x94db483840b717ef,0xa8c2a44eb4571cdc,
0xba121a4650e4ddeb,0x92f34d62616ce413,
0xe896a0d7e51e1566,0x77b020baf9c81d17,
0x915e2486ef32cd60,0xace1474dc1d122e,
0xb5b5ada8aaff80b8,0xd819992132456ba,
0xe3231912d5bf60e6,0x10e1fff697ed6c69,
0x8df5efabc5979c8f,0xca8d3ffa1ef463c1,
0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2,
0xddd0467c64bce4a0,0xac7cb3f6d05ddbde,
0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b,
0xad4ab7112eb3929d,0x86c16c98d2c953c6,
0xd89d64d57a607744,0xe871c7bf077ba8b7,
0x87625f056c7c4a8b,0x11471cd764ad4972,
0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf,
0xd389b47879823479,0x4aff1d108d4ec2c3,
0x843610cb4bf160cb,0xcedf722a585139ba,
0xa54394fe1eedb8fe,0xc2974eb4ee658828,
0xce947a3da6a9273e,0x733d226229feea32,
0x811ccc668829b887,0x806357d5a3f525f,
0xa163ff802a3426a8,0xca07c2dcb0cf26f7,
0xc9bcff6034c13052,0xfc89b393dd02f0b5,
0xfc2c3f3841f17c67,0xbbac2078d443ace2,
0x9d9ba7832936edc0,0xd54b944b84aa4c0d,
0xc5029163f384a931,0xa9e795e65d4df11,
0xf64335bcf065d37d,0x4d4617b5ff4a16d5,
0x99ea0196163fa42e,0x504bced1bf8e4e45,
0xc06481fb9bcf8d39,0xe45ec2862f71e1d6,
0xf07da27a82c37088,0x5d767327bb4e5a4c,
0x964e858c91ba2655,0x3a6a07f8d510f86f,
0xbbe226efb628afea,0x890489f70a55368b,
0xeadab0aba3b2dbe5,0x2b45ac74ccea842e,
0x92c8ae6b464fc96f,0x3b0b8bc90012929d,
0xb77ada0617e3bbcb,0x9ce6ebb40173744,
0xe55990879ddcaabd,0xcc420a6a101d0515,
0x8f57fa54c2a9eab6,0x9fa946824a12232d,
0xb32df8e9f3546564,0x47939822dc96abf9,
0xdff9772470297ebd,0x59787e2b93bc56f7,
0x8bfbea76c619ef36,0x57eb4edb3c55b65a,
0xaefae51477a06b03,0xede622920b6b23f1,
0xdab99e59958885c4,0xe95fab368e45eced,
0x88b402f7fd75539b,0x11dbcb0218ebb414,
0xaae103b5fcd2a881,0xd652bdc29f26a119,
0xd59944a37c0752a2,0x4be76d3346f0495f,
0x857fcae62d8493a5,0x6f70a4400c562ddb,
0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952,
0xd097ad07a71f26b2,0x7e2000a41346a7a7,
0x825ecc24c873782f,0x8ed400668c0c28c8,
0xa2f67f2dfa90563b,0x728900802f0f32fa,
0xcbb41ef979346bca,0x4f2b40a03ad2ffb9,
0xfea126b7d78186bc,0xe2f610c84987bfa8,
0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9,
0xc6ede63fa05d3143,0x91503d1c79720dbb,
0xf8a95fcf88747d94,0x75a44c6397ce912a,
0x9b69dbe1b548ce7c,0xc986afbe3ee11aba,
0xc24452da229b021b,0xfbe85badce996168,
0xf2d56790ab41c2a2,0xfae27299423fb9c3,
0x97c560ba6b0919a5,0xdccd879fc967d41a,
0xbdb6b8e905cb600f,0x5400e987bbc1c920,
0xed246723473e3813,0x290123e9aab23b68,
0x9436c0760c86e30b,0xf9a0b6720aaf6521,
0xb94470938fa89bce,0xf808e40e8d5b3e69,
0xe7958cb87392c2c2,0xb60b1d1230b20e04,
0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2,
0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3,
0xe2280b6c20dd5232,0x25c6da63c38de1b0,
0x8d590723948a535f,0x579c487e5a38ad0e,
0xb0af48ec79ace837,0x2d835a9df0c6d851,
0xdcdb1b2798182244,0xf8e431456cf88e65,
0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff,
0xac8b2d36eed2dac5,0xe272467e3d222f3f,
0xd7adf884aa879177,0x5b0ed81dcc6abb0f,
0x86ccbb52ea94baea,0x98e947129fc2b4e9,
0xa87fea27a539e9a5,0x3f2398d747b36224,
0xd29fe4b18e88640e,0x8eec7f0d19a03aad,
0x83a3eeeef9153e89,0x1953cf68300424ac,
0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7,
0xcdb02555653131b6,0x3792f412cb06794d,
0x808e17555f3ebf11,0xe2bbd88bbee40bd0,
0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4,
0xc8de047564d20a8b,0xf245825a5a445275,
0xfb158592be068d2e,0xeed6e2f0f0d56712,
0x9ced737bb6c4183d,0x55464dd69685606b,
0xc428d05aa4751e4c,0xaa97e14c3c26b886,
0xf53304714d9265df,0xd53dd99f4b3066a8,
0x993fe2c6d07b7fab,0xe546a8038efe4029,
0xbf8fdb78849a5f96,0xde98520472bdd033,
0xef73d256a5c0f77c,0x963e66858f6d4440,
0x95a8637627989aad,0xdde7001379a44aa8,
0xbb127c53b17ec159,0x5560c018580d5d52,
0xe9d71b689dde71af,0xaab8f01e6e10b4a6,
0x9226712162ab070d,0xcab3961304ca70e8,
0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22,
0xe45c10c42a2b3b05,0x8cb89a7db77c506a,
0x8eb98a7a9a5b04e3,0x77f3608e92adb242,
0xb267ed1940f1c61c,0x55f038b237591ed3,
0xdf01e85f912e37a3,0x6b6c46dec52f6688,
0x8b61313bbabce2c6,0x2323ac4b3b3da015,
0xae397d8aa96c1b77,0xabec975e0a0d081a,
0xd9c7dced53c72255,0x96e7bd358c904a21,
0x881cea14545c7575,0x7e50d64177da2e54,
0xaa242499697392d2,0xdde50bd1d5d0b9e9,
0xd4ad2dbfc3d07787,0x955e4ec64b44e864,
0x84ec3c97da624ab4,0xbd5af13bef0b113e,
0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e,
0xcfb11ead453994ba,0x67de18eda5814af2,
0x81ceb32c4b43fcf4,0x80eacf948770ced7,
0xa2425ff75e14fc31,0xa1258379a94d028d,
0xcad2f7f5359a3b3e,0x96ee45813a04330,
0xfd87b5f28300ca0d,0x8bca9d6e188853fc,
0x9e74d1b791e07e48,0x775ea264cf55347e,
0xc612062576589dda,0x95364afe032a819e,
0xf79687aed3eec551,0x3a83ddbd83f52205,
0x9abe14cd44753b52,0xc4926a9672793543,
0xc16d9a0095928a27,0x75b7053c0f178294,
0xf1c90080baf72cb1,0x5324c68b12dd6339,
0x971da05074da7bee,0xd3f6fc16ebca5e04,
0xbce5086492111aea,0x88f4bb1ca6bcf585,
0xec1e4a7db69561a5,0x2b31e9e3d06c32e6,
0x9392ee8e921d5d07,0x3aff322e62439fd0,
0xb877aa3236a4b449,0x9befeb9fad487c3,
0xe69594bec44de15b,0x4c2ebe687989a9b4,
0x901d7cf73ab0acd9,0xf9d37014bf60a11,
0xb424dc35095cd80f,0x538484c19ef38c95,
0xe12e13424bb40e13,0x2865a5f206b06fba,
0x8cbccc096f5088cb,0xf93f87b7442e45d4,
0xafebff0bcb24aafe,0xf78f69a51539d749,
0xdbe6fecebdedd5be,0xb573440e5a884d1c,
0x89705f4136b4a597,0x31680a88f8953031,
0xabcc77118461cefc,0xfdc20d2b36ba7c3e,
0xd6bf94d5e57a42bc,0x3d32907604691b4d,
0x8637bd05af6c69b5,0xa63f9a49c2c1b110,
0xa7c5ac471b478423,0xfcf80dc33721d54,
0xd1b71758e219652b,0xd3c36113404ea4a9,
0x83126e978d4fdf3b,0x645a1cac083126ea,
0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4,
0xcccccccccccccccc,0xcccccccccccccccd,
0x8000000000000000,0x0,
0xa000000000000000,0x0,
0xc800000000000000,0x0,
0xfa00000000000000,0x0,
0x9c40000000000000,0x0,
0xc350000000000000,0x0,
0xf424000000000000,0x0,
0x9896800000000000,0x0,
0xbebc200000000000,0x0,
0xee6b280000000000,0x0,
0x9502f90000000000,0x0,
0xba43b74000000000,0x0,
0xe8d4a51000000000,0x0,
0x9184e72a00000000,0x0,
0xb5e620f480000000,0x0,
0xe35fa931a0000000,0x0,
0x8e1bc9bf04000000,0x0,
0xb1a2bc2ec5000000,0x0,
0xde0b6b3a76400000,0x0,
0x8ac7230489e80000,0x0,
0xad78ebc5ac620000,0x0,
0xd8d726b7177a8000,0x0,
0x878678326eac9000,0x0,
0xa968163f0a57b400,0x0,
0xd3c21bcecceda100,0x0,
0x84595161401484a0,0x0,
0xa56fa5b99019a5c8,0x0,
0xcecb8f27f4200f3a,0x0,
0x813f3978f8940984,0x4000000000000000,
0xa18f07d736b90be5,0x5000000000000000,
0xc9f2c9cd04674ede,0xa400000000000000,
0xfc6f7c4045812296,0x4d00000000000000,
0x9dc5ada82b70b59d,0xf020000000000000,
0xc5371912364ce305,0x6c28000000000000,
0xf684df56c3e01bc6,0xc732000000000000,
0x9a130b963a6c115c,0x3c7f400000000000,
0xc097ce7bc90715b3,0x4b9f100000000000,
0xf0bdc21abb48db20,0x1e86d40000000000,
0x96769950b50d88f4,0x1314448000000000,
0xbc143fa4e250eb31,0x17d955a000000000,
0xeb194f8e1ae525fd,0x5dcfab0800000000,
0x92efd1b8d0cf37be,0x5aa1cae500000000,
0xb7abc627050305ad,0xf14a3d9e40000000,
0xe596b7b0c643c719,0x6d9ccd05d0000000,
0x8f7e32ce7bea5c6f,0xe4820023a2000000,
0xb35dbf821ae4f38b,0xdda2802c8a800000,
0xe0352f62a19e306e,0xd50b2037ad200000,
0x8c213d9da502de45,0x4526f422cc340000,
0xaf298d050e4395d6,0x9670b12b7f410000,
0xdaf3f04651d47b4c,0x3c0cdd765f114000,
0x88d8762bf324cd0f,0xa5880a69fb6ac800,
0xab0e93b6efee0053,0x8eea0d047a457a00,
0xd5d238a4abe98068,0x72a4904598d6d880,
0x85a36366eb71f041,0x47a6da2b7f864750,
0xa70c3c40a64e6c51,0x999090b65f67d924,
0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d,
0x82818f1281ed449f,0xbff8f10e7a8921a4,
0xa321f2d7226895c7,0xaff72d52192b6a0d,
0xcbea6f8ceb02bb39,0x9bf4f8a69f764490,
0xfee50b7025c36a08,0x2f236d04753d5b4,
0x9f4f2726179a2245,0x1d762422c946590,
0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5,
0xf8ebad2b84e0d58b,0xd2e0898765a7deb2,
0x9b934c3b330c8577,0x63cc55f49f88eb2f,
0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb,
0xf316271c7fc3908a,0x8bef464e3945ef7a,
0x97edd871cfda3a56,0x97758bf0e3cbb5ac,
0xbde94e8e43d0c8ec,0x3d52eeed1cbea317,
0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd,
0x945e455f24fb1cf8,0x8fe8caa93e74ef6a,
0xb975d6b6ee39e436,0xb3e2fd538e122b44,
0xe7d34c64a9c85d44,0x60dbbca87196b616,
0x90e40fbeea1d3a4a,0xbc8955e946fe31cd,
0xb51d13aea4a488dd,0x6babab6398bdbe41,
0xe264589a4dcdab14,0xc696963c7eed2dd1,
0x8d7eb76070a08aec,0xfc1e1de5cf543ca2,
0xb0de65388cc8ada8,0x3b25a55f43294bcb,
0xdd15fe86affad912,0x49ef0eb713f39ebe,
0x8a2dbf142dfcc7ab,0x6e3569326c784337,
0xacb92ed9397bf996,0x49c2c37f07965404,
0xd7e77a8f87daf7fb,0xdc33745ec97be906,
0x86f0ac99b4e8dafd,0x69a028bb3ded71a3,
0xa8acd7c0222311bc,0xc40832ea0d68ce0c,
0xd2d80db02aabd62b,0xf50a3fa490c30190,
0x83c7088e1aab65db,0x792667c6da79e0fa,
0xa4b8cab1a1563f52,0x577001b891185938,
0xcde6fd5e09abcf26,0xed4c0226b55e6f86,
0x80b05e5ac60b6178,0x544f8158315b05b4,
0xa0dc75f1778e39d6,0x696361ae3db1c721,
0xc913936dd571c84c,0x3bc3a19cd1e38e9,
0xfb5878494ace3a5f,0x4ab48a04065c723,
0x9d174b2dcec0e47b,0x62eb0d64283f9c76,
0xc45d1df942711d9a,0x3ba5d0bd324f8394,
0xf5746577930d6500,0xca8f44ec7ee36479,
0x9968bf6abbe85f20,0x7e998b13cf4e1ecb,
0xbfc2ef456ae276e8,0x9e3fedd8c321a67e,
0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e,
0x95d04aee3b80ece5,0xbba1f1d158724a12,
0xbb445da9ca61281f,0x2a8a6e45ae8edc97,
0xea1575143cf97226,0xf52d09d71a3293bd,
0x924d692ca61be758,0x593c2626705f9c56,
0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c,
0xe498f455c38b997a,0xb6dfb9c0f956447,
0x8edf98b59a373fec,0x4724bd4189bd5eac,
0xb2977ee300c50fe7,0x58edec91ec2cb657,
0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed,
0x8b865b215899f46c,0xbd79e0d20082ee74,
0xae67f1e9aec07187,0xecd8590680a3aa11,
0xda01ee641a708de9,0xe80e6f4820cc9495,
0x884134fe908658b2,0x3109058d147fdcdd,
0xaa51823e34a7eede,0xbd4b46f0599fd415,
0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a,
0x850fadc09923329e,0x3e2cf6bc604ddb0,
0xa6539930bf6bff45,0x84db8346b786151c,
0xcfe87f7cef46ff16,0xe612641865679a63,
0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e,
0xa26da3999aef7749,0xe3be5e330f38f09d,
0xcb090c8001ab551c,0x5cadf5bfd3072cc5,
0xfdcb4fa002162a63,0x73d9732fc7c8f7f6,
0x9e9f11c4014dda7e,0x2867e7fddcdd9afa,
0xc646d63501a1511d,0xb281e1fd541501b8,
0xf7d88bc24209a565,0x1f225a7ca91a4226,
0x9ae757596946075f,0x3375788de9b06958,
0xc1a12d2fc3978937,0x52d6b1641c83ae,
0xf209787bb47d6b84,0xc0678c5dbd23a49a,
0x9745eb4d50ce6332,0xf840b7ba963646e0,
0xbd176620a501fbff,0xb650e5a93bc3d898,
0xec5d3fa8ce427aff,0xa3e51f138ab4cebe,
0x93ba47c980e98cdf,0xc66f336c36b10137,
0xb8a8d9bbe123f017,0xb80b0047445d4184,
0xe6d3102ad96cec1d,0xa60dc059157491e5,
0x9043ea1ac7e41392,0x87c89837ad68db2f,
0xb454e4a179dd1877,0x29babe4598c311fb,
0xe16a1dc9d8545e94,0xf4296dd6fef3d67a,
0x8ce2529e2734bb1d,0x1899e4a65f58660c,
0xb01ae745b101e9e4,0x5ec05dcff72e7f8f,
0xdc21a1171d42645d,0x76707543f4fa1f73,
0x899504ae72497eba,0x6a06494a791c53a8,
0xabfa45da0edbde69,0x487db9d17636892,
0xd6f8d7509292d603,0x45a9d2845d3c42b6,
0x865b86925b9bc5c2,0xb8a2392ba45a9b2,
0xa7f26836f282b732,0x8e6cac7768d7141e,
0xd1ef0244af2364ff,0x3207d795430cd926,
0x8335616aed761f1f,0x7f44e6bd49e807b8,
0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6,
0xcd036837130890a1,0x36dba887c37a8c0f,
0x802221226be55a64,0xc2494954da2c9789,
0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c,
0xc83553c5c8965d3d,0x6f92829494e5acc7,
0xfa42a8b73abbf48c,0xcb772339ba1f17f9,
0x9c69a97284b578d7,0xff2a760414536efb,
0xc38413cf25e2d70d,0xfef5138519684aba,
0xf46518c2ef5b8cd1,0x7eb258665fc25d69,
0x98bf2f79d5993802,0xef2f773ffbd97a61,
0xbeeefb584aff8603,0xaafb550ffacfd8fa,
0xeeaaba2e5dbf6784,0x95ba2a53f983cf38,
0x952ab45cfa97a0b2,0xdd945a747bf26183,
0xba756174393d88df,0x94f971119aeef9e4,
0xe912b9d1478ceb17,0x7a37cd5601aab85d,
0x91abb422ccb812ee,0xac62e055c10ab33a,
0xb616a12b7fe617aa,0x577b986b314d6009,
0xe39c49765fdf9d94,0xed5a7e85fda0b80b,
0x8e41ade9fbebc27d,0x14588f13be847307,
0xb1d219647ae6b31c,0x596eb2d8ae258fc8,
0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb,
0x8aec23d680043bee,0x25de7bb9480d5854,
0xada72ccc20054ae9,0xaf561aa79a10ae6a,
0xd910f7ff28069da4,0x1b2ba1518094da04,
0x87aa9aff79042286,0x90fb44d2f05d0842,
0xa99541bf57452b28,0x353a1607ac744a53,
0xd3fa922f2d1675f2,0x42889b8997915ce8,
0x847c9b5d7c2e09b7,0x69956135febada11,
0xa59bc234db398c25,0x43fab9837e699095,
0xcf02b2c21207ef2e,0x94f967e45e03f4bb,
0x8161afb94b44f57d,0x1d1be0eebac278f5,
0xa1ba1ba79e1632dc,0x6462d92a69731732,
0xca28a291859bbf93,0x7d7b8f7503cfdcfe,
0xfcb2cb35e702af78,0x5cda735244c3d43e,
0x9defbf01b061adab,0x3a0888136afa64a7,
0xc56baec21c7a1916,0x88aaa1845b8fdd0,
0xf6c69a72a3989f5b,0x8aad549e57273d45,
0x9a3c2087a63f6399,0x36ac54e2f678864b,
0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd,
0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5,
0x969eb7c47859e743,0x9f644ae5a4b1b325,
0xbc4665b596706114,0x873d5d9f0dde1fee,
0xeb57ff22fc0c7959,0xa90cb506d155a7ea,
0x9316ff75dd87cbd8,0x9a7f12442d588f2,
0xb7dcbf5354e9bece,0xc11ed6d538aeb2f,
0xe5d3ef282a242e81,0x8f1668c8a86da5fa,
0x8fa475791a569d10,0xf96e017d694487bc,
0xb38d92d760ec4455,0x37c981dcc395a9ac,
0xe070f78d3927556a,0x85bbe253f47b1417,
0x8c469ab843b89562,0x93956d7478ccec8e,
0xaf58416654a6babb,0x387ac8d1970027b2,
0xdb2e51bfe9d0696a,0x6997b05fcc0319e,
0x88fcf317f22241e2,0x441fece3bdf81f03,
0xab3c2fddeeaad25a,0xd527e81cad7626c3,
0xd60b3bd56a5586f1,0x8a71e223d8d3b074,
0x85c7056562757456,0xf6872d5667844e49,
0xa738c6bebb12d16c,0xb428f8ac016561db,
0xd106f86e69d785c7,0xe13336d701beba52,
0x82a45b450226b39c,0xecc0024661173473,
0xa34d721642b06084,0x27f002d7f95d0190,
0xcc20ce9bd35c78a5,0x31ec038df7b441f4,
0xff290242c83396ce,0x7e67047175a15271,
0x9f79a169bd203e41,0xf0062c6e984d386,
0xc75809c42c684dd1,0x52c07b78a3e60868,
0xf92e0c3537826145,0xa7709a56ccdf8a82,
0x9bbcc7a142b17ccb,0x88a66076400bb691,
0xc2abf989935ddbfe,0x6acff893d00ea435,
0xf356f7ebf83552fe,0x583f6b8c4124d43,
0x98165af37b2153de,0xc3727a337a8b704a,
0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c,
0xeda2ee1c7064130c,0x1162def06f79df73,
0x9485d4d1c63e8be7,0x8addcb5645ac2ba8,
0xb9a74a0637ce2ee1,0x6d953e2bd7173692,
0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437,
0x910ab1d4db9914a0,0x1d9c9892400a22a2,
0xb54d5e4a127f59c8,0x2503beb6d00cab4b,
0xe2a0b5dc971f303a,0x2e44ae64840fd61d,
0x8da471a9de737e24,0x5ceaecfed289e5d2,
0xb10d8e1456105dad,0x7425a83e872c5f47,
0xdd50f1996b947518,0xd12f124e28f77719,
0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f,
0xace73cbfdc0bfb7b,0x636cc64d1001550b,
0xd8210befd30efa5a,0x3c47f7e05401aa4e,
0x8714a775e3e95c78,0x65acfaec34810a71,
0xa8d9d1535ce3b396,0x7f1839a741a14d0d,
0xd31045a8341ca07c,0x1ede48111209a050,
0x83ea2b892091e44d,0x934aed0aab460432,
0xa4e4b66b68b65d60,0xf81da84d5617853f,
0xce1de40642e3f4b9,0x36251260ab9d668e,
0x80d2ae83e9ce78f3,0xc1d72b7c6b426019,
0xa1075a24e4421730,0xb24cf65b8612f81f,
0xc94930ae1d529cfc,0xdee033f26797b627,
0xfb9b7cd9a4a7443c,0x169840ef017da3b1,
0x9d412e0806e88aa5,0x8e1f289560ee864e,
0xc491798a08a2ad4e,0xf1a6f2bab92a27e2,
0xf5b5d7ec8acb58a2,0xae10af696774b1db,
0x9991a6f3d6bf1765,0xacca6da1e0a8ef29,
0xbff610b0cc6edd3f,0x17fd090a58d32af3,
0xeff394dcff8a948e,0xddfc4b4cef07f5b0,
0x95f83d0a1fb69cd9,0x4abdaf101564f98e,
0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1,
0xea53df5fd18d5513,0x84c86189216dc5ed,
0x92746b9be2f8552c,0x32fd3cf5b4e49bb4,
0xb7118682dbb66a77,0x3fbc8c33221dc2a1,
0xe4d5e82392a40515,0xfabaf3feaa5334a,
0x8f05b1163ba6832d,0x29cb4d87f2a7400e,
0xb2c71d5bca9023f8,0x743e20e9ef511012,
0xdf78e4b2bd342cf6,0x914da9246b255416,
0x8bab8eefb6409c1a,0x1ad089b6c2f7548e,
0xae9672aba3d0c320,0xa184ac2473b529b1,
0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e,
0x8865899617fb1871,0x7e2fa67c7a658892,
0xaa7eebfb9df9de8d,0xddbb901b98feeab7,
0xd51ea6fa85785631,0x552a74227f3ea565,
0x8533285c936b35de,0xd53a88958f87275f,
0xa67ff273b8460356,0x8a892abaf368f137,
0xd01fef10a657842c,0x2d2b7569b0432d85,
0x8213f56a67f6b29b,0x9c3b29620e29fc73,
0xa298f2c501f45f42,0x8349f3ba91b47b8f,
0xcb3f2f7642717713,0x241c70a936219a73,
0xfe0efb53d30dd4d7,0xed238cd383aa0110,
0x9ec95d1463e8a506,0xf4363804324a40aa,
0xc67bb4597ce2ce48,0xb143c6053edcd0d5,
0xf81aa16fdc1b81da,0xdd94b7868e94050a,
0x9b10a4e5e9913128,0xca7cf2b4191c8326,
0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0,
0xf24a01a73cf2dccf,0xbc633b39673c8cec,
0x976e41088617ca01,0xd5be0503e085d813,
0xbd49d14aa79dbc82,0x4b2d8644d8a74e18,
0xec9c459d51852ba2,0xddf8e7d60ed1219e,
0x93e1ab8252f33b45,0xcabb90e5c942b503,
0xb8da1662e7b00a17,0x3d6a751f3b936243,
0xe7109bfba19c0c9d,0xcc512670a783ad4,
0x906a617d450187e2,0x27fb2b80668b24c5,
0xb484f9dc9641e9da,0xb1f9f660802dedf6,
0xe1a63853bbd26451,0x5e7873f8a0396973,
0x8d07e33455637eb2,0xdb0b487b6423e1e8,
0xb049dc016abc5e5f,0x91ce1a9a3d2cda62,
0xdc5c5301c56b75f7,0x7641a140cc7810fb,
0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d,
0xac2820d9623bf429,0x546345fa9fbdcd44,
0xd732290fbacaf133,0xa97c177947ad4095,
0x867f59a9d4bed6c0,0x49ed8eabcccc485d,
0xa81f301449ee8c70,0x5c68f256bfff5a74,
0xd226fc195c6a2f8c,0x73832eec6fff3111,
0x83585d8fd9c25db7,0xc831fd53c5ff7eab,
0xa42e74f3d032f525,0xba3e7ca8b77f5e55,
0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb,
0x80444b5e7aa7cf85,0x7980d163cf5b81b3,
0xa0555e361951c366,0xd7e105bcc332621f,
0xc86ab5c39fa63440,0x8dd9472bf3fefaa7,
0xfa856334878fc150,0xb14f98f6f0feb951,
0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3,
0xc3b8358109e84f07,0xa862f80ec4700c8,
0xf4a642e14c6262c8,0xcd27bb612758c0fa,
0x98e7e9cccfbd7dbd,0x8038d51cb897789c,
0xbf21e44003acdd2c,0xe0470a63e6bd56c3,
0xeeea5d5004981478,0x1858ccfce06cac74,
0x95527a5202df0ccb,0xf37801e0c43ebc8,
0xbaa718e68396cffd,0xd30560258f54e6ba,
0xe950df20247c83fd,0x47c6b82ef32a2069,
0x91d28b7416cdd27e,0x4cdc331d57fa5441,
0xb6472e511c81471d,0xe0133fe4adf8e952,
0xe3d8f9e563a198e5,0x58180fddd97723a6,
0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,};
};
template <class unused>
constexpr uint64_t powers_template<unused>::power_of_five_128[number_of_entries];
using powers = powers_template<>;
}}}}}} // namespace fast_float
#endif
@@ -0,0 +1,562 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// Derivative of: https://github.com/fastfloat/fast_float
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_FLOAT_COMMON_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_FLOAT_COMMON_HPP
#include <boost/json/detail/charconv/detail/fast_float/constexpr_feature_detect.hpp>
#include <boost/json/detail/charconv/detail/from_chars_result.hpp>
#include <boost/json/detail/charconv/detail/config.hpp>
#include <boost/json/detail/charconv/chars_format.hpp>
#include <cfloat>
#include <cstdint>
#include <cassert>
#include <cstring>
#include <type_traits>
#include <system_error>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
template <typename UC>
struct parse_options_t {
constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
UC dot = UC('.'))
: format(fmt), decimal_point(dot) {}
/** Which number formats are accepted */
chars_format format;
/** The character used as decimal point */
UC decimal_point;
};
using parse_options = parse_options_t<char>;
}}}}}}
#ifdef BOOST_JSON_HAS_BIT_CAST
#include <bit>
#endif
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
|| defined(__MINGW64__) \
|| defined(__s390x__) \
|| (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) )
#define BOOST_JSON_FASTFLOAT_64BIT 1
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \
|| defined(__arm__) || defined(_M_ARM) || defined(__ppc__) \
|| defined(__MINGW32__) || defined(__EMSCRIPTEN__))
#define BOOST_JSON_FASTFLOAT_32BIT 1
#else
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
// We can never tell the register width, but the SIZE_MAX is a good approximation.
// UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability.
#if SIZE_MAX == 0xffff
#error Unknown platform (16-bit, unsupported)
#elif SIZE_MAX == 0xffffffff
#define BOOST_JSON_FASTFLOAT_32BIT 1
#elif SIZE_MAX == 0xffffffffffffffff
#define BOOST_JSON_FASTFLOAT_64BIT 1
#else
#error Unknown platform (not 32-bit, not 64-bit?)
#endif
#endif
#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__))
#include <intrin.h>
#endif
#if defined(_MSC_VER) && !defined(__clang__)
#define BOOST_JSON_FASTFLOAT_VISUAL_STUDIO 1
#endif
// rust style `try!()` macro, or `?` operator
#define BOOST_JSON_FASTFLOAT_TRY(x) { if (!(x)) return false; }
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
BOOST_FORCEINLINE constexpr bool cpp20_and_in_constexpr() {
#ifdef BOOST_JSON_HAS_IS_CONSTANT_EVALUATED
return std::is_constant_evaluated();
#else
return false;
#endif
}
// Compares two ASCII strings in a case insensitive manner.
template <typename UC>
inline BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE bool
fastfloat_strncasecmp(UC const * input1, UC const * input2, size_t length) {
char running_diff{0};
for (size_t i = 0; i < length; ++i) {
running_diff |= (char(input1[i]) ^ char(input2[i]));
}
return (running_diff == 0) || (running_diff == 32);
}
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
// a pointer and a length to a contiguous block of memory
template <typename T>
struct span {
const T* ptr;
size_t length;
constexpr span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
constexpr span() : ptr(nullptr), length(0) {}
constexpr size_t len() const noexcept {
return length;
}
BOOST_JSON_CXX14_CONSTEXPR const T& operator[](size_t index) const noexcept {
BOOST_ASSERT(index < length);
return ptr[index];
}
};
struct value128 {
uint64_t low;
uint64_t high;
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
constexpr value128() : low(0), high(0) {}
};
/* Helper C++11 constexpr generic implementation of leading_zeroes */
BOOST_FORCEINLINE constexpr
int leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
return (
((input_num & uint64_t(0xffffffff00000000)) && (input_num >>= 32, last_bit |= 32)),
((input_num & uint64_t( 0xffff0000)) && (input_num >>= 16, last_bit |= 16)),
((input_num & uint64_t( 0xff00)) && (input_num >>= 8, last_bit |= 8)),
((input_num & uint64_t( 0xf0)) && (input_num >>= 4, last_bit |= 4)),
((input_num & uint64_t( 0xc)) && (input_num >>= 2, last_bit |= 2)),
((input_num & uint64_t( 0x2)) && (input_num >>= 1, last_bit |= 1)),
63 - last_bit
);
}
/* result might be undefined when input_num is zero */
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
int leading_zeroes(uint64_t input_num) {
assert(input_num > 0);
if (cpp20_and_in_constexpr()) {
return leading_zeroes_generic(input_num);
}
#ifdef BOOST_JSON_FASTFLOAT_VISUAL_STUDIO
#if defined(_M_X64) || defined(_M_ARM64)
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
_BitScanReverse64(&leading_zero, input_num);
return (int)(63 - leading_zero);
#else
return leading_zeroes_generic(input_num);
#endif
#else
return __builtin_clzll(input_num);
#endif
}
// slow emulation routine for 32-bit
BOOST_FORCEINLINE constexpr uint64_t emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
BOOST_FORCEINLINE BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#ifdef BOOST_JSON_FASTFLOAT_32BIT
// slow emulation routine for 32-bit
#if !defined(__MINGW64__)
BOOST_FORCEINLINE BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
return umul128_generic(ab, cd, hi);
}
#endif // !__MINGW64__
#endif // BOOST_JSON_FASTFLOAT_32BIT
// compute 64-bit a*b
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
value128 full_multiplication(uint64_t a, uint64_t b) {
if (cpp20_and_in_constexpr()) {
value128 answer;
answer.low = umul128_generic(a, b, &answer.high);
return answer;
}
value128 answer;
#if defined(_M_ARM64) && !defined(__MINGW32__)
// ARM64 has native support for 64-bit multiplications, no need to emulate
// But MinGW on ARM64 doesn't have native support for 64-bit multiplications
answer.high = __umulh(a, b);
answer.low = a * b;
#elif defined(BOOST_JSON_FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__))
unsigned long long high;
answer.low = _umul128(a, b, &high); // _umul128 not available on ARM64
answer.high = static_cast<uint64_t>(high);
#elif defined(BOOST_JSON_FASTFLOAT_64BIT)
__uint128_t r = ((__uint128_t)a) * b;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#else
answer.low = umul128_generic(a, b, &answer.high);
#endif
return answer;
}
struct adjusted_mantissa {
uint64_t mantissa{0};
int32_t power2{0}; // a negative value indicates an invalid result
adjusted_mantissa() = default;
constexpr bool operator==(const adjusted_mantissa &o) const {
return mantissa == o.mantissa && power2 == o.power2;
}
constexpr bool operator!=(const adjusted_mantissa &o) const {
return mantissa != o.mantissa || power2 != o.power2;
}
};
// Bias so we can get the real exponent with an invalid adjusted_mantissa.
constexpr static int32_t invalid_am_bias = -0x8000;
// used for binary_format_lookup_tables<T>::max_mantissa
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
template <typename T, typename U = void>
struct binary_format_lookup_tables;
template <typename T> struct binary_format : binary_format_lookup_tables<T> {
using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
static inline constexpr int mantissa_explicit_bits();
static inline constexpr int minimum_exponent();
static inline constexpr int infinite_power();
static inline constexpr int sign_index();
static inline constexpr int min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
static inline constexpr int max_exponent_fast_path();
static inline constexpr int max_exponent_round_to_even();
static inline constexpr int min_exponent_round_to_even();
static inline constexpr uint64_t max_mantissa_fast_path(int64_t power);
static inline constexpr uint64_t max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
static inline constexpr int largest_power_of_ten();
static inline constexpr int smallest_power_of_ten();
static inline constexpr T exact_power_of_ten(int64_t power);
static inline constexpr size_t max_digits();
static inline constexpr equiv_uint exponent_mask();
static inline constexpr equiv_uint mantissa_mask();
static inline constexpr equiv_uint hidden_bit_mask();
};
template <typename U>
struct binary_format_lookup_tables<double, U> {
static constexpr double powers_of_ten[] = {
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
// Largest integer value v so that (5**index * v) <= 1<<53.
// 0x10000000000000 == 1 << 53
static constexpr std::uint64_t max_mantissa[] = {
UINT64_C(0x10000000000000),
UINT64_C(0x10000000000000) / UINT64_C(5),
UINT64_C(0x10000000000000) / (UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (UINT64_C(5) * UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555),
UINT64_C(0x10000000000000) / (constant_55555 * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * UINT64_C(5) * UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * constant_55555),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * constant_55555 * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * constant_55555 * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * constant_55555 * UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * constant_55555 * UINT64_C(5) * UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * constant_55555 * constant_55555),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x10000000000000) / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * UINT64_C(5) * UINT64_C(5) * UINT64_C(5) * UINT64_C(5))};
};
template <typename U>
constexpr double binary_format_lookup_tables<double, U>::powers_of_ten[];
template <typename U>
constexpr uint64_t binary_format_lookup_tables<double, U>::max_mantissa[];
template <typename U>
struct binary_format_lookup_tables<float, U> {
static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// Largest integer value v so that (5**index * v) <= 1<<24.
// 0x1000000 == 1<<24
static constexpr uint64_t max_mantissa[] = {
UINT64_C(0x1000000),
UINT64_C(0x1000000) / UINT64_C(5),
UINT64_C(0x1000000) / (UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x1000000) / (UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x1000000) / (UINT64_C(5) * UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x1000000) / (constant_55555),
UINT64_C(0x1000000) / (constant_55555 * UINT64_C(5)),
UINT64_C(0x1000000) / (constant_55555 * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x1000000) / (constant_55555 * UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x1000000) / (constant_55555 * UINT64_C(5) * UINT64_C(5) * UINT64_C(5) * UINT64_C(5)),
UINT64_C(0x1000000) / (constant_55555 * constant_55555),
UINT64_C(0x1000000) / (constant_55555 * constant_55555 * UINT64_C(5))};
};
template <typename U>
constexpr float binary_format_lookup_tables<float, U>::powers_of_ten[];
template <typename U>
constexpr uint64_t binary_format_lookup_tables<float, U>::max_mantissa[];
template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -22;
#endif
}
template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -10;
#endif
}
template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() {
return 52;
}
template <> inline constexpr int binary_format<float>::mantissa_explicit_bits() {
return 23;
}
template <> inline constexpr int binary_format<double>::max_exponent_round_to_even() {
return 23;
}
template <> inline constexpr int binary_format<float>::max_exponent_round_to_even() {
return 10;
}
template <> inline constexpr int binary_format<double>::min_exponent_round_to_even() {
return -4;
}
template <> inline constexpr int binary_format<float>::min_exponent_round_to_even() {
return -17;
}
template <> inline constexpr int binary_format<double>::minimum_exponent() {
return -1023;
}
template <> inline constexpr int binary_format<float>::minimum_exponent() {
return -127;
}
template <> inline constexpr int binary_format<double>::infinite_power() {
return 0x7FF;
}
template <> inline constexpr int binary_format<float>::infinite_power() {
return 0xFF;
}
template <> inline constexpr int binary_format<double>::sign_index() { return 63; }
template <> inline constexpr int binary_format<float>::sign_index() { return 31; }
template <> inline constexpr int binary_format<double>::max_exponent_fast_path() {
return 22;
}
template <> inline constexpr int binary_format<float>::max_exponent_fast_path() {
return 10;
}
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that
// power >= 0 && power <= 22
//
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power];
}
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that
// power >= 0 && power <= 10
//
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power];
}
template <>
inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power];
}
template <>
inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power];
}
template <>
inline constexpr int binary_format<double>::largest_power_of_ten() {
return 308;
}
template <>
inline constexpr int binary_format<float>::largest_power_of_ten() {
return 38;
}
template <>
inline constexpr int binary_format<double>::smallest_power_of_ten() {
return -342;
}
template <>
inline constexpr int binary_format<float>::smallest_power_of_ten() {
return -65;
}
template <> inline constexpr size_t binary_format<double>::max_digits() {
return 769;
}
template <> inline constexpr size_t binary_format<float>::max_digits() {
return 114;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::exponent_mask() {
return 0x7F800000;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::exponent_mask() {
return 0x7FF0000000000000;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::mantissa_mask() {
return 0x007FFFFF;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::mantissa_mask() {
return 0x000FFFFFFFFFFFFF;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::hidden_bit_mask() {
return 0x00800000;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::hidden_bit_mask() {
return 0x0010000000000000;
}
template<typename T>
BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
void to_float(bool negative, adjusted_mantissa am, T &value) {
using uint = typename binary_format<T>::equiv_uint;
uint word = (uint)am.mantissa;
word |= uint(am.power2) << binary_format<T>::mantissa_explicit_bits();
word |= uint(negative) << binary_format<T>::sign_index();
#ifdef BOOST_JSON_HAS_BIT_CAST
value = std::bit_cast<T>(word);
#else
::memcpy(&value, &word, sizeof(T));
#endif
}
template<typename UC>
static constexpr uint64_t int_cmp_zeros()
{
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), "Unsupported character size");
return (sizeof(UC) == 1) ? 0x3030303030303030 : (sizeof(UC) == 2) ? (uint64_t(UC('0')) << 48 | uint64_t(UC('0')) << 32 | uint64_t(UC('0')) << 16 | UC('0')) : (uint64_t(UC('0')) << 32 | UC('0'));
}
template<typename UC>
static constexpr int int_cmp_len()
{
return sizeof(uint64_t) / sizeof(UC);
}
template<typename UC>
static constexpr UC const * str_const_nan()
{
return nullptr;
}
template<>
constexpr char const * str_const_nan<char>()
{
return "nan";
}
template<>
constexpr wchar_t const * str_const_nan<wchar_t>()
{
return L"nan";
}
template<>
constexpr char16_t const * str_const_nan<char16_t>()
{
return u"nan";
}
template<>
constexpr char32_t const * str_const_nan<char32_t>()
{
return U"nan";
}
template<typename UC>
static constexpr UC const * str_const_inf()
{
return nullptr;
}
template<>
constexpr char const * str_const_inf<char>()
{
return "infinity";
}
template<>
constexpr wchar_t const * str_const_inf<wchar_t>()
{
return L"infinity";
}
template<>
constexpr char16_t const * str_const_inf<char16_t>()
{
return u"infinity";
}
template<>
constexpr char32_t const * str_const_inf<char32_t>()
{
return U"infinity";
}
}}}}}} // namespaces
#endif
@@ -0,0 +1,237 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// Derivative of: https://github.com/fastfloat/fast_float
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_PARSE_NUMBER_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_PARSE_NUMBER_HPP
#include <boost/json/detail/charconv/detail/fast_float/ascii_number.hpp>
#include <boost/json/detail/charconv/detail/fast_float/decimal_to_binary.hpp>
#include <boost/json/detail/charconv/detail/fast_float/digit_comparison.hpp>
#include <boost/json/detail/charconv/detail/fast_float/float_common.hpp>
#include <cmath>
#include <cstring>
#include <limits>
#include <system_error>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
namespace detail {
/**
* Special case +inf, -inf, nan, infinity, -infinity.
* The case comparisons could be made much faster given that we know that the
* strings a null-free and fixed.
**/
#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
template <typename T, typename UC>
from_chars_result_t<UC> BOOST_JSON_CXX14_CONSTEXPR
parse_infnan(UC const * first, UC const * last, T &value) noexcept {
from_chars_result_t<UC> answer{};
answer.ptr = first;
answer.ec = std::errc(); // be optimistic
bool minusSign = false;
if (*first == UC('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
minusSign = true;
++first;
}
if (last - first >= 3) {
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
answer.ptr = (first += 3);
value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
// Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
if(first != last && *first == UC('(')) {
for(UC const * ptr = first + 1; ptr != last; ++ptr) {
if (*ptr == UC(')')) {
answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
break;
}
else if(!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
break; // forbidden char, not nan(n-char-seq-opt)
}
}
return answer;
}
if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
answer.ptr = first + 8;
} else {
answer.ptr = first + 3;
}
value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
return answer;
}
}
answer.ec = std::errc::invalid_argument;
return answer;
}
#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
/**
* Returns true if the floating-pointing rounding mode is to 'nearest'.
* It is the default on most system. This function is meant to be inexpensive.
* Credit : @mwalcott3
*/
BOOST_FORCEINLINE bool rounds_to_nearest() noexcept {
// https://lemire.me/blog/2020/06/26/gcc-not-nearest/
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return false;
#endif
// See
// A fast function to check your floating-point rounding mode
// https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
//
// This function is meant to be equivalent to :
// prior: #include <cfenv>
// return fegetround() == FE_TONEAREST;
// However, it is expected to be much faster than the fegetround()
// function call.
//
// The volatile keywoard prevents the compiler from computing the function
// at compile-time.
// There might be other ways to prevent compile-time optimizations (e.g., asm).
// The value does not need to be std::numeric_limits<float>::min(), any small
// value so that 1 + x should round to 1 would do (after accounting for excess
// precision, as in 387 instructions).
static volatile float fmin = (std::numeric_limits<float>::min)();
float fmini = fmin; // we copy it so that it gets loaded at most once.
//
// Explanation:
// Only when fegetround() == FE_TONEAREST do we have that
// fmin + 1.0f == 1.0f - fmin.
//
// FE_UPWARD:
// fmin + 1.0f > 1
// 1.0f - fmin == 1
//
// FE_DOWNWARD or FE_TOWARDZERO:
// fmin + 1.0f == 1
// 1.0f - fmin < 1
//
// Note: This may fail to be accurate if fast-math has been
// enabled, as rounding conventions may not apply.
#ifdef BOOST_JSON_FASTFLOAT_VISUAL_STUDIO
# pragma warning(push)
// todo: is there a VS warning?
// see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
#elif defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wfloat-equal"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
return (fmini + 1.0f == 1.0f - fmini);
#ifdef BOOST_JSON_FASTFLOAT_VISUAL_STUDIO
# pragma warning(pop)
#elif defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
} // namespace detail
template<typename T, typename UC>
BOOST_JSON_FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
return from_chars_advanced(first, last, value, parse_options_t<UC>{fmt});
}
template<typename T, typename UC>
BOOST_JSON_FASTFLOAT_CONSTEXPR20
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
T &value, parse_options_t<UC> options) noexcept {
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
static_assert (std::is_same<UC, char>::value ||
std::is_same<UC, wchar_t>::value ||
std::is_same<UC, char16_t>::value ||
std::is_same<UC, char32_t>::value , "only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer;
if (first == last) {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
return answer;
}
parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
if (!pns.valid) {
return detail::parse_infnan(first, last, value);
}
answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch;
// The implementation of the Clinger's fast path is convoluted because
// we want round-to-nearest in all cases, irrespective of the rounding mode
// selected on the thread.
// We proceed optimistically, assuming that detail::rounds_to_nearest() returns
// true.
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
// Unfortunately, the conventional Clinger's fast path is only possible
// when the system rounds to the nearest float.
//
// We expect the next branch to almost always be selected.
// We could check it first (before the previous branch), but
// there might be performance advantages at having the check
// be last.
if(!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
// We have that fegetround() == FE_TONEAREST.
// Next is Clinger's fast path.
if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
value = T(pns.mantissa);
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
if (pns.negative) { value = -value; }
return answer;
}
} else {
// We do not have that fegetround() == FE_TONEAREST.
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal
if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
#if defined(__clang__)
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
if(pns.mantissa == 0) {
value = pns.negative ? -0. : 0.;
return answer;
}
#endif
value = T(pns.mantissa) * binary_format<T>::exact_power_of_ten(pns.exponent);
if (pns.negative) { value = -value; }
return answer;
}
}
}
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
if(pns.too_many_digits && am.power2 >= 0) {
if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
}
}
// If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
// then we need to go the long way around again. This is very uncommon.
if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
to_float(pns.negative, am, value);
// Test for over/underflow.
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format<T>::infinite_power()) {
answer.ec = std::errc::result_out_of_range;
}
return answer;
}
}}}}}} // namespace fast_float
#endif
@@ -0,0 +1,172 @@
// Copyright 2022 Peter Dimov
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_FLOAT_IMPL_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_FLOAT_IMPL_HPP
#include <boost/json/detail/charconv/detail/config.hpp>
#include <boost/json/detail/charconv/detail/from_chars_result.hpp>
#include <boost/json/detail/charconv/detail/parser.hpp>
#include <boost/json/detail/charconv/detail/compute_float64.hpp>
#include <boost/json/detail/charconv/chars_format.hpp>
#include <system_error>
#include <cstdlib>
#include <cmath>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail {
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable: 4244) // Implict converion when BOOST_IF_CONSTEXPR expands to if
#elif defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
template <typename T>
from_chars_result from_chars_strtod_impl(const char* first, const char* last, T& value, char* buffer) noexcept
{
// For strto(f/d)
// Floating point value corresponding to the contents of str on success.
// If the converted value falls out of range of corresponding return type, range error occurs and HUGE_VAL, HUGE_VALF or HUGE_VALL is returned.
// If no conversion can be performed, 0 is returned and *str_end is set to str.
std::memcpy(buffer, first, static_cast<std::size_t>(last - first));
buffer[last - first] = '\0';
char* str_end;
T return_value {};
BOOST_IF_CONSTEXPR (std::is_same<T, float>::value)
{
return_value = std::strtof(buffer, &str_end);
if (return_value == HUGE_VALF)
{
return {last, std::errc::result_out_of_range};
}
}
else BOOST_IF_CONSTEXPR (std::is_same<T, double>::value)
{
return_value = std::strtod(buffer, &str_end);
if (return_value == HUGE_VAL)
{
return {last, std::errc::result_out_of_range};
}
}
else
{
return_value = std::strtold(buffer, &str_end);
if (return_value == HUGE_VALL)
{
return {last, std::errc::result_out_of_range};
}
}
// Since this is a fallback routine we are safe to check for 0
if (return_value == 0 && str_end == last)
{
return {first, std::errc::result_out_of_range};
}
value = return_value;
return {first + (str_end - buffer), std::errc()};
}
template <typename T>
inline from_chars_result from_chars_strtod(const char* first, const char* last, T& value) noexcept
{
if (last - first < 1024)
{
char buffer[1024];
return from_chars_strtod_impl(first, last, value, buffer);
}
// If the string to be parsed does not fit into the 1024 byte static buffer than we have to allocate a buffer.
// malloc is used here because it does not throw on allocation failure.
char* buffer = static_cast<char*>(std::malloc(last - first + 1));
if (buffer == nullptr)
{
return {first, std::errc::not_enough_memory};
}
auto r = from_chars_strtod_impl(first, last, value, buffer);
std::free(buffer);
return r;
}
template <typename T>
from_chars_result from_chars_float_impl(const char* first, const char* last, T& value, chars_format fmt) noexcept
{
bool sign {};
std::uint64_t significand {};
std::int64_t exponent {};
auto r = charconv::detail::parser(first, last, sign, significand, exponent, fmt);
if (r.ec != std::errc())
{
return r;
}
else if (significand == 0)
{
value = sign ? static_cast<T>(-0.0L) : static_cast<T>(0.0L);
return r;
}
else if (exponent == -1)
{
// A full length significand e.g. -1985444280612224 with a power of -1 sometimes
// fails in compute_float64 but is trivial to calculate
// Found investigating GitHub issue #47
value = (sign ? -static_cast<T>(significand) : static_cast<T>(significand)) / 10;
}
bool success {};
T return_val {};
return_val = compute_float64(exponent, significand, sign, success);
if (!success)
{
if (significand == 1 && exponent == 0)
{
value = 1;
r.ptr = last;
r.ec = std::errc();
}
else
{
if (return_val == HUGE_VAL || return_val == -HUGE_VAL)
{
value = return_val;
r.ec = std::errc::result_out_of_range;
}
else if (exponent < -342)
{
value = sign ? -0.0 : 0.0;
r.ec = std::errc::result_out_of_range;
}
else
{
r = from_chars_strtod(first, r.ptr, value);
}
}
}
else
{
value = return_val;
}
return r;
}
#ifdef BOOST_MSVC
# pragma warning(pop)
#elif defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
}}}}} // Namespace boost::charconv::detail
#endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_FLOAT_IMPL_HPP
@@ -0,0 +1,252 @@
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP
#include <boost/json/detail/charconv/detail/config.hpp>
#include <boost/json/detail/charconv/detail/from_chars_result.hpp>
#include <boost/config.hpp>
#include <system_error>
#include <type_traits>
#include <limits>
#include <cstdlib>
#include <cerrno>
#include <cstddef>
#include <cstdint>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail {
static constexpr unsigned char uchar_values[] =
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
static_assert(sizeof(uchar_values) == 256, "uchar_values should represent all 256 values of unsigned char");
// Convert characters for 0-9, A-Z, a-z to 0-35. Anything else is 255
constexpr unsigned char digit_from_char(char val) noexcept
{
return uchar_values[static_cast<unsigned char>(val)];
}
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned
# pragma warning(disable: 4189) // 'is_negative': local variable is initialized but not referenced
#elif defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wconstant-conversion"
#elif defined(__GNUC__) && (__GNUC__ < 7)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Woverflow"
#elif defined(__GNUC__) && (__GNUC__ >= 7)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
template <typename Integer, typename Unsigned_Integer>
BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* first, const char* last, Integer& value, int base) noexcept
{
Unsigned_Integer result = 0;
Unsigned_Integer overflow_value = 0;
Unsigned_Integer max_digit = 0;
// Check pre-conditions
if (!((first <= last) && (base >= 2 && base <= 36)))
{
return {first, std::errc::invalid_argument};
}
Unsigned_Integer unsigned_base = static_cast<Unsigned_Integer>(base);
// Strip sign if the type is signed
// Negative sign will be appended at the end of parsing
BOOST_ATTRIBUTE_UNUSED bool is_negative = false;
auto next = first;
#ifdef BOOST_HAS_INT128
BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::int128_type>::value || std::is_signed<Integer>::value)
#else
BOOST_IF_CONSTEXPR (std::is_signed<Integer>::value)
#endif
{
if (next != last)
{
if (*next == '-')
{
is_negative = true;
++next;
}
else if (*next == '+')
{
return {next, std::errc::invalid_argument};
}
}
#ifdef BOOST_HAS_INT128
BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::int128_type>::value)
{
overflow_value = BOOST_JSON_INT128_MAX;
max_digit = BOOST_JSON_INT128_MAX;
}
else
#endif
{
overflow_value = (std::numeric_limits<Integer>::max)();
max_digit = (std::numeric_limits<Integer>::max)();
}
if (is_negative)
{
++overflow_value;
++max_digit;
}
}
else
{
if (next != last && (*next == '-' || *next == '+'))
{
return {first, std::errc::invalid_argument};
}
#ifdef BOOST_HAS_INT128
BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::uint128_type>::value)
{
overflow_value = BOOST_JSON_UINT128_MAX;
max_digit = BOOST_JSON_UINT128_MAX;
}
else
#endif
{
overflow_value = (std::numeric_limits<Unsigned_Integer>::max)();
max_digit = (std::numeric_limits<Unsigned_Integer>::max)();
}
}
#ifdef BOOST_HAS_INT128
BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::int128_type>::value)
{
overflow_value /= unsigned_base;
max_digit %= unsigned_base;
overflow_value *= 2; // Overflow value would cause INT128_MIN in non-base10 to fail
}
else
#endif
{
overflow_value /= unsigned_base;
max_digit %= unsigned_base;
}
// If the only character was a sign abort now
if (next == last)
{
return {first, std::errc::invalid_argument};
}
bool overflowed = false;
std::ptrdiff_t nc = last - next;
constexpr std::ptrdiff_t nd = std::numeric_limits<Integer>::digits10;
{
std::ptrdiff_t i = 0;
for( ; i < nd && i < nc; ++i )
{
// overflow is not possible in the first nd characters
const unsigned char current_digit = digit_from_char(*next);
if (current_digit >= unsigned_base)
{
break;
}
result = static_cast<Unsigned_Integer>(result * unsigned_base + current_digit);
++next;
}
for( ; i < nc; ++i )
{
const unsigned char current_digit = digit_from_char(*next);
if (current_digit >= unsigned_base)
{
break;
}
if (result < overflow_value || (result == overflow_value && current_digit <= max_digit))
{
result = static_cast<Unsigned_Integer>(result * unsigned_base + current_digit);
}
else
{
// Required to keep updating the value of next, but the result is garbage
overflowed = true;
}
++next;
}
}
// Return the parsed value, adding the sign back if applicable
// If we have overflowed then we do not return the result
if (overflowed)
{
return {next, std::errc::result_out_of_range};
}
value = static_cast<Integer>(result);
#ifdef BOOST_HAS_INT128
BOOST_IF_CONSTEXPR (std::is_same<Integer, boost::int128_type>::value || std::is_signed<Integer>::value)
#else
BOOST_IF_CONSTEXPR (std::is_signed<Integer>::value)
#endif
{
if (is_negative)
{
value = -(static_cast<Unsigned_Integer>(value));
}
}
return {next, std::errc()};
}
#ifdef BOOST_MSVC
# pragma warning(pop)
#elif defined(__clang__) && defined(__APPLE__)
# pragma clang diagnostic pop
#elif defined(__GNUC__) && (__GNUC__ < 7 || __GNUC__ >= 9)
# pragma GCC diagnostic pop
#endif
// Only from_chars for integer types is constexpr (as of C++23)
template <typename Integer>
BOOST_JSON_GCC5_CONSTEXPR from_chars_result from_chars(const char* first, const char* last, Integer& value, int base = 10) noexcept
{
using Unsigned_Integer = typename std::make_unsigned<Integer>::type;
return detail::from_chars_integer_impl<Integer, Unsigned_Integer>(first, last, value, base);
}
}}}}} // Namespaces
#endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_INTEGER_IMPL_HPP
@@ -0,0 +1,39 @@
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_RESULT_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_RESULT_HPP
#include <system_error>
namespace boost { namespace json { namespace detail { namespace charconv {
// 22.13.3, Primitive numerical input conversion
template <typename UC>
struct from_chars_result_t
{
const UC* ptr;
// Values:
// 0 = no error
// EINVAL = invalid_argument
// ERANGE = result_out_of_range
std::errc ec;
friend constexpr bool operator==(const from_chars_result_t<UC>& lhs, const from_chars_result_t<UC>& rhs) noexcept
{
return lhs.ptr == rhs.ptr && lhs.ec == rhs.ec;
}
friend constexpr bool operator!=(const from_chars_result_t<UC>& lhs, const from_chars_result_t<UC>& rhs) noexcept
{
return !(lhs == rhs);
}
};
using from_chars_result = from_chars_result_t<char>;
}}}} // Namespaces
#endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_FROM_CHARS_RESULT_HPP
@@ -0,0 +1,240 @@
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_INTEGER_SEARCH_TREES_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_INTEGER_SEARCH_TREES_HPP
// https://stackoverflow.com/questions/1489830/efficient-way-to-determine-number-of-digits-in-an-integer?page=1&tab=scoredesc#tab-top
// https://graphics.stanford.edu/~seander/bithacks.html
#include <boost/json/detail/charconv/detail/config.hpp>
#include <limits>
#include <array>
#include <cstdint>
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail {
// Generic solution
template <typename T>
BOOST_JSON_CXX14_CONSTEXPR int num_digits(T x) noexcept
{
int digits = 0;
while (x)
{
x /= 10;
++digits;
}
return digits;
}
template <>
BOOST_JSON_CXX14_CONSTEXPR int num_digits(std::uint32_t x) noexcept
{
if (x >= UINT32_C(10000))
{
if (x >= UINT32_C(10000000))
{
if (x >= UINT32_C(100000000))
{
if (x >= UINT32_C(1000000000))
{
return 10;
}
return 9;
}
return 8;
}
else if (x >= UINT32_C(100000))
{
if (x >= UINT32_C(1000000))
{
return 7;
}
return 6;
}
return 5;
}
else if (x >= UINT32_C(100))
{
if (x >= UINT32_C(1000))
{
return 4;
}
return 3;
}
else if (x >= UINT32_C(10))
{
return 2;
}
return 1;
}
template <>
BOOST_JSON_CXX14_CONSTEXPR int num_digits(std::uint64_t x) noexcept
{
if (x >= UINT64_C(10000000000))
{
if (x >= UINT64_C(100000000000000))
{
if (x >= UINT64_C(10000000000000000))
{
if (x >= UINT64_C(100000000000000000))
{
if (x >= UINT64_C(1000000000000000000))
{
if (x >= UINT64_C(10000000000000000000))
{
return 20;
}
return 19;
}
return 18;
}
return 17;
}
else if (x >= UINT64_C(1000000000000000))
{
return 16;
}
return 15;
}
if (x >= UINT64_C(1000000000000))
{
if (x >= UINT64_C(10000000000000))
{
return 14;
}
return 13;
}
if (x >= UINT64_C(100000000000))
{
return 12;
}
return 11;
}
else if (x >= UINT64_C(100000))
{
if (x >= UINT64_C(10000000))
{
if (x >= UINT64_C(100000000))
{
if (x >= UINT64_C(1000000000))
{
return 10;
}
return 9;
}
return 8;
}
if (x >= UINT64_C(1000000))
{
return 7;
}
return 6;
}
if (x >= UINT64_C(100))
{
if (x >= UINT64_C(1000))
{
if (x >= UINT64_C(10000))
{
return 5;
}
return 4;
}
return 3;
}
if (x >= UINT64_C(10))
{
return 2;
}
return 1;
}
#ifdef BOOST_HAS_INT128
static constexpr std::array<std::uint64_t, 20> powers_of_10 =
{{
UINT64_C(1), UINT64_C(10), UINT64_C(100), UINT64_C(1000), UINT64_C(10000), UINT64_C(100000), UINT64_C(1000000),
UINT64_C(10000000), UINT64_C(100000000), UINT64_C(1000000000), UINT64_C(10000000000), UINT64_C(100000000000),
UINT64_C(1000000000000), UINT64_C(10000000000000), UINT64_C(100000000000000), UINT64_C(1000000000000000),
UINT64_C(10000000000000000), UINT64_C(100000000000000000), UINT64_C(1000000000000000000), UINT64_C(10000000000000000000)
}};
// Assume that if someone is using 128 bit ints they are favoring the top end of the range
// Max value is 340,282,366,920,938,463,463,374,607,431,768,211,455 (39 digits)
BOOST_JSON_CXX14_CONSTEXPR int num_digits(boost::uint128_type x) noexcept
{
// There is not literal for boost::uint128_type so we need to calculate them using the max value of the
// std::uint64_t powers of 10
constexpr boost::uint128_type digits_39 = static_cast<boost::uint128_type>(UINT64_C(10000000000000000000)) *
static_cast<boost::uint128_type>(UINT64_C(10000000000000000000));
constexpr boost::uint128_type digits_38 = digits_39 / 10;
constexpr boost::uint128_type digits_37 = digits_38 / 10;
constexpr boost::uint128_type digits_36 = digits_37 / 10;
constexpr boost::uint128_type digits_35 = digits_36 / 10;
constexpr boost::uint128_type digits_34 = digits_35 / 10;
constexpr boost::uint128_type digits_33 = digits_34 / 10;
constexpr boost::uint128_type digits_32 = digits_33 / 10;
constexpr boost::uint128_type digits_31 = digits_32 / 10;
constexpr boost::uint128_type digits_30 = digits_31 / 10;
constexpr boost::uint128_type digits_29 = digits_30 / 10;
constexpr boost::uint128_type digits_28 = digits_29 / 10;
constexpr boost::uint128_type digits_27 = digits_28 / 10;
constexpr boost::uint128_type digits_26 = digits_27 / 10;
constexpr boost::uint128_type digits_25 = digits_26 / 10;
constexpr boost::uint128_type digits_24 = digits_25 / 10;
constexpr boost::uint128_type digits_23 = digits_24 / 10;
constexpr boost::uint128_type digits_22 = digits_23 / 10;
constexpr boost::uint128_type digits_21 = digits_22 / 10;
return (x >= digits_39) ? 39 :
(x >= digits_38) ? 38 :
(x >= digits_37) ? 37 :
(x >= digits_36) ? 36 :
(x >= digits_35) ? 35 :
(x >= digits_34) ? 34 :
(x >= digits_33) ? 33 :
(x >= digits_32) ? 32 :
(x >= digits_31) ? 31 :
(x >= digits_30) ? 30 :
(x >= digits_29) ? 29 :
(x >= digits_28) ? 28 :
(x >= digits_27) ? 27 :
(x >= digits_26) ? 26 :
(x >= digits_25) ? 25 :
(x >= digits_24) ? 24 :
(x >= digits_23) ? 23 :
(x >= digits_22) ? 22 :
(x >= digits_21) ? 21 :
(x >= powers_of_10[19]) ? 20 :
(x >= powers_of_10[18]) ? 19 :
(x >= powers_of_10[17]) ? 18 :
(x >= powers_of_10[16]) ? 17 :
(x >= powers_of_10[15]) ? 16 :
(x >= powers_of_10[14]) ? 15 :
(x >= powers_of_10[13]) ? 14 :
(x >= powers_of_10[12]) ? 13 :
(x >= powers_of_10[11]) ? 12 :
(x >= powers_of_10[10]) ? 11 :
(x >= powers_of_10[9]) ? 10 :
(x >= powers_of_10[8]) ? 9 :
(x >= powers_of_10[7]) ? 8 :
(x >= powers_of_10[6]) ? 7 :
(x >= powers_of_10[5]) ? 6 :
(x >= powers_of_10[4]) ? 5 :
(x >= powers_of_10[3]) ? 4 :
(x >= powers_of_10[2]) ? 3 :
(x >= powers_of_10[1]) ? 2 :
(x >= powers_of_10[0]) ? 1 : 0;
}
#endif
}}}}} // Namespace boost::json::detail::charconv::detail
#endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_INTEGER_SEARCH_TREES_HPP
+378
View File
@@ -0,0 +1,378 @@
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_PARSER_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_PARSER_HPP
#include <boost/json/detail/charconv/detail/config.hpp>
#include <boost/json/detail/charconv/detail/from_chars_result.hpp>
#include <boost/json/detail/charconv/detail/from_chars_integer_impl.hpp>
#include <boost/json/detail/charconv/detail/integer_search_trees.hpp>
#include <boost/json/detail/charconv/limits.hpp>
#include <boost/json/detail/charconv/chars_format.hpp>
#include <system_error>
#include <type_traits>
#include <limits>
#include <cerrno>
#include <cstdint>
#include <cstring>
#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail {
inline bool is_integer_char(char c) noexcept
{
return (c >= '0') && (c <= '9');
}
inline bool is_hex_char(char c) noexcept
{
return is_integer_char(c) || (((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')));
}
inline bool is_delimiter(char c, chars_format fmt) noexcept
{
if (fmt != chars_format::hex)
{
return !is_integer_char(c) && c != 'e' && c != 'E';
}
return !is_hex_char(c) && c != 'p' && c != 'P';
}
template <typename Unsigned_Integer, typename Integer>
inline from_chars_result parser(const char* first, const char* last, bool& sign, Unsigned_Integer& significand, Integer& exponent, chars_format fmt = chars_format::general) noexcept
{
if (first > last)
{
return {first, std::errc::invalid_argument};
}
auto next = first;
bool all_zeros = true;
// First extract the sign
if (*next == '-')
{
sign = true;
++next;
}
else if (*next == '+')
{
return {next, std::errc::invalid_argument};
}
else
{
sign = false;
}
// Ignore leading zeros (e.g. 00005 or -002.3e+5)
while (*next == '0' && next != last)
{
++next;
}
// If the number is 0 we can abort now
char exp_char;
char capital_exp_char;
if (fmt != chars_format::hex)
{
exp_char = 'e';
capital_exp_char = 'E';
}
else
{
exp_char = 'p';
capital_exp_char = 'P';
}
if (next == last || *next == exp_char || *next == -capital_exp_char)
{
significand = 0;
exponent = 0;
return {next, std::errc()};
}
// Next we get the significand
constexpr std::size_t significand_buffer_size = limits<Unsigned_Integer>::max_chars10 - 1; // Base 10 or 16
char significand_buffer[significand_buffer_size] {};
std::size_t i = 0;
std::size_t dot_position = 0;
Integer extra_zeros = 0;
Integer leading_zero_powers = 0;
const auto char_validation_func = (fmt != charconv::chars_format::hex) ? is_integer_char : is_hex_char;
const int base = (fmt != charconv::chars_format::hex) ? 10 : 16;
while (char_validation_func(*next) && next != last && i < significand_buffer_size)
{
all_zeros = false;
significand_buffer[i] = *next;
++next;
++i;
}
bool fractional = false;
if (next == last)
{
// if fmt is chars_format::scientific the e is required
if (fmt == chars_format::scientific)
{
return {first, std::errc::invalid_argument};
}
exponent = 0;
std::size_t offset = i;
from_chars_result r = from_chars(significand_buffer, significand_buffer + offset, significand, base);
switch (r.ec)
{
case std::errc::invalid_argument:
return {first, std::errc::invalid_argument};
case std::errc::result_out_of_range:
return {next, std::errc::result_out_of_range};
default:
return {next, std::errc()};
}
}
else if (*next == '.')
{
++next;
fractional = true;
dot_position = i;
// Process the fractional part if we have it
//
// if fmt is chars_format::scientific the e is required
// if fmt is chars_format::fixed and not scientific the e is disallowed
// if fmt is chars_format::general (which is scientific and fixed) the e is optional
// If we have the value 0.00001 we can continue to chop zeros and adjust the exponent
// so that we get the useful parts of the fraction
if (all_zeros)
{
while (*next == '0' && next != last)
{
++next;
--leading_zero_powers;
}
if (next == last)
{
return {last, std::errc()};
}
}
while (char_validation_func(*next) && next != last && i < significand_buffer_size)
{
significand_buffer[i] = *next;
++next;
++i;
}
}
if (i == significand_buffer_size)
{
// We can not process any more significant figures into the significand so skip to the end
// or the exponent part and capture the additional orders of magnitude for the exponent
bool found_dot = false;
while ((char_validation_func(*next) || *next == '.') && next != last)
{
++next;
if (!fractional && !found_dot)
{
++extra_zeros;
}
if (*next == '.')
{
found_dot = true;
}
}
}
if (next == last || is_delimiter(*next, fmt))
{
if (fmt == chars_format::scientific)
{
return {first, std::errc::invalid_argument};
}
if (dot_position != 0 || fractional)
{
exponent = static_cast<Integer>(dot_position) - i + extra_zeros + leading_zero_powers;
}
else
{
exponent = extra_zeros + leading_zero_powers;
}
std::size_t offset = i;
from_chars_result r = from_chars(significand_buffer, significand_buffer + offset, significand, base);
switch (r.ec)
{
case std::errc::invalid_argument:
return {first, std::errc::invalid_argument};
case std::errc::result_out_of_range:
return {next, std::errc::result_out_of_range};
default:
return {next, std::errc()};
}
}
else if (*next == exp_char || *next == capital_exp_char)
{
// Would be a number without a significand e.g. e+03
if (next == first)
{
return {next, std::errc::invalid_argument};
}
++next;
if (fmt == chars_format::fixed)
{
return {first, std::errc::invalid_argument};
}
exponent = i - 1;
std::size_t offset = i;
bool round = false;
// If more digits are present than representable in the significand of the target type
// we set the maximum
if (offset > significand_buffer_size)
{
offset = significand_buffer_size - 1;
i = significand_buffer_size;
if (significand_buffer[offset] == '5' ||
significand_buffer[offset] == '6' ||
significand_buffer[offset] == '7' ||
significand_buffer[offset] == '8' ||
significand_buffer[offset] == '9')
{
round = true;
}
}
// If the significand is 0 from chars will return std::errc::invalid_argument because there is nothing in the buffer,
// but it is a valid value. We need to continue parsing to get the correct value of ptr even
// though we know we could bail now.
//
// See GitHub issue #29: https://github.com/cppalliance/charconv/issues/29
if (offset != 0)
{
from_chars_result r = from_chars(significand_buffer, significand_buffer + offset, significand, base);
switch (r.ec)
{
case std::errc::invalid_argument:
return {first, std::errc::invalid_argument};
case std::errc::result_out_of_range:
return {next, std::errc::result_out_of_range};
default:
break;
}
if (round)
{
significand += 1;
}
}
}
else
{
return {first, std::errc::invalid_argument};
}
// Finally we get the exponent
constexpr std::size_t exponent_buffer_size = 6; // Float128 min exp is 16382
char exponent_buffer[exponent_buffer_size] {};
Integer significand_digits = i;
i = 0;
// Get the sign first
if (*next == '-')
{
exponent_buffer[i] = *next;
++next;
++i;
}
else if (*next == '+')
{
++next;
}
// Next strip any leading zeros
while (*next == '0')
{
++next;
}
// Process the significant values
while (is_integer_char(*next) && next != last && i < exponent_buffer_size)
{
exponent_buffer[i] = *next;
++next;
++i;
}
// If the exponent can't fit in the buffer the number is not representable
if (next != last && i == exponent_buffer_size)
{
return {next, std::errc::result_out_of_range};
}
// If the exponent was e+00 or e-00
if (i == 0 || (i == 1 && exponent_buffer[0] == '-'))
{
if (fractional)
{
exponent = static_cast<Integer>(dot_position) - significand_digits;
}
else
{
exponent = extra_zeros;
}
return {next, std::errc()};
}
const auto r = from_chars(exponent_buffer, exponent_buffer + i, exponent);
exponent += leading_zero_powers;
switch (r.ec)
{
case std::errc::invalid_argument:
return {first, std::errc::invalid_argument};
case std::errc::result_out_of_range:
return {next, std::errc::result_out_of_range};
default:
if (fractional)
{
// Need to take the offset from 1.xxx because compute_floatXXX assumes the significand is an integer
// so the exponent is off by the number of digits in the significand - 1
if (fmt == chars_format::hex)
{
// In hex the number of digits parsed is possibly less than the number of digits in base10
exponent -= num_digits(significand) - dot_position;
}
else
{
exponent -= significand_digits - dot_position;
}
}
else
{
exponent += extra_zeros;
}
return {next, std::errc()};
}
}
}}}}} // Namespaces
#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
#endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_PARSER_HPP
@@ -0,0 +1,667 @@
// Copyright 2020-2023 Daniel Lemire
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_SIGNIFICAND_TABLES_HPP
#define BOOST_JSON_DETAIL_CHARCONV_DETAIL_SIGNIFICAND_TABLES_HPP
#include <cstdint>
// The significand of a floating point number is often referred to as the mantissa.
// Using the term mantissa is discouraged by IEEE 1516
namespace boost { namespace json { namespace detail { namespace charconv { namespace detail {
// The significands of powers of ten from -308 to 308, extended out to sixty four
// bits. The array contains the powers of ten approximated
// as a 64-bit significand. It goes from 10^BOOST_CHARCONV_FASTFLOAT_SMALLEST_POWER to
// 10^BOOST_CHARCONV_FASTFLOAT_LARGEST_POWER (inclusively).
// The significand is truncated, and never rounded up.
// Uses about 5KB.
static constexpr std::uint64_t significand_64[] = {
0xa5ced43b7e3e9188, 0xcf42894a5dce35ea,
0x818995ce7aa0e1b2, 0xa1ebfb4219491a1f,
0xca66fa129f9b60a6, 0xfd00b897478238d0,
0x9e20735e8cb16382, 0xc5a890362fddbc62,
0xf712b443bbd52b7b, 0x9a6bb0aa55653b2d,
0xc1069cd4eabe89f8, 0xf148440a256e2c76,
0x96cd2a865764dbca, 0xbc807527ed3e12bc,
0xeba09271e88d976b, 0x93445b8731587ea3,
0xb8157268fdae9e4c, 0xe61acf033d1a45df,
0x8fd0c16206306bab, 0xb3c4f1ba87bc8696,
0xe0b62e2929aba83c, 0x8c71dcd9ba0b4925,
0xaf8e5410288e1b6f, 0xdb71e91432b1a24a,
0x892731ac9faf056e, 0xab70fe17c79ac6ca,
0xd64d3d9db981787d, 0x85f0468293f0eb4e,
0xa76c582338ed2621, 0xd1476e2c07286faa,
0x82cca4db847945ca, 0xa37fce126597973c,
0xcc5fc196fefd7d0c, 0xff77b1fcbebcdc4f,
0x9faacf3df73609b1, 0xc795830d75038c1d,
0xf97ae3d0d2446f25, 0x9becce62836ac577,
0xc2e801fb244576d5, 0xf3a20279ed56d48a,
0x9845418c345644d6, 0xbe5691ef416bd60c,
0xedec366b11c6cb8f, 0x94b3a202eb1c3f39,
0xb9e08a83a5e34f07, 0xe858ad248f5c22c9,
0x91376c36d99995be, 0xb58547448ffffb2d,
0xe2e69915b3fff9f9, 0x8dd01fad907ffc3b,
0xb1442798f49ffb4a, 0xdd95317f31c7fa1d,
0x8a7d3eef7f1cfc52, 0xad1c8eab5ee43b66,
0xd863b256369d4a40, 0x873e4f75e2224e68,
0xa90de3535aaae202, 0xd3515c2831559a83,
0x8412d9991ed58091, 0xa5178fff668ae0b6,
0xce5d73ff402d98e3, 0x80fa687f881c7f8e,
0xa139029f6a239f72, 0xc987434744ac874e,
0xfbe9141915d7a922, 0x9d71ac8fada6c9b5,
0xc4ce17b399107c22, 0xf6019da07f549b2b,
0x99c102844f94e0fb, 0xc0314325637a1939,
0xf03d93eebc589f88, 0x96267c7535b763b5,
0xbbb01b9283253ca2, 0xea9c227723ee8bcb,
0x92a1958a7675175f, 0xb749faed14125d36,
0xe51c79a85916f484, 0x8f31cc0937ae58d2,
0xb2fe3f0b8599ef07, 0xdfbdcece67006ac9,
0x8bd6a141006042bd, 0xaecc49914078536d,
0xda7f5bf590966848, 0x888f99797a5e012d,
0xaab37fd7d8f58178, 0xd5605fcdcf32e1d6,
0x855c3be0a17fcd26, 0xa6b34ad8c9dfc06f,
0xd0601d8efc57b08b, 0x823c12795db6ce57,
0xa2cb1717b52481ed, 0xcb7ddcdda26da268,
0xfe5d54150b090b02, 0x9efa548d26e5a6e1,
0xc6b8e9b0709f109a, 0xf867241c8cc6d4c0,
0x9b407691d7fc44f8, 0xc21094364dfb5636,
0xf294b943e17a2bc4, 0x979cf3ca6cec5b5a,
0xbd8430bd08277231, 0xece53cec4a314ebd,
0x940f4613ae5ed136, 0xb913179899f68584,
0xe757dd7ec07426e5, 0x9096ea6f3848984f,
0xb4bca50b065abe63, 0xe1ebce4dc7f16dfb,
0x8d3360f09cf6e4bd, 0xb080392cc4349dec,
0xdca04777f541c567, 0x89e42caaf9491b60,
0xac5d37d5b79b6239, 0xd77485cb25823ac7,
0x86a8d39ef77164bc, 0xa8530886b54dbdeb,
0xd267caa862a12d66, 0x8380dea93da4bc60,
0xa46116538d0deb78, 0xcd795be870516656,
0x806bd9714632dff6, 0xa086cfcd97bf97f3,
0xc8a883c0fdaf7df0, 0xfad2a4b13d1b5d6c,
0x9cc3a6eec6311a63, 0xc3f490aa77bd60fc,
0xf4f1b4d515acb93b, 0x991711052d8bf3c5,
0xbf5cd54678eef0b6, 0xef340a98172aace4,
0x9580869f0e7aac0e, 0xbae0a846d2195712,
0xe998d258869facd7, 0x91ff83775423cc06,
0xb67f6455292cbf08, 0xe41f3d6a7377eeca,
0x8e938662882af53e, 0xb23867fb2a35b28d,
0xdec681f9f4c31f31, 0x8b3c113c38f9f37e,
0xae0b158b4738705e, 0xd98ddaee19068c76,
0x87f8a8d4cfa417c9, 0xa9f6d30a038d1dbc,
0xd47487cc8470652b, 0x84c8d4dfd2c63f3b,
0xa5fb0a17c777cf09, 0xcf79cc9db955c2cc,
0x81ac1fe293d599bf, 0xa21727db38cb002f,
0xca9cf1d206fdc03b, 0xfd442e4688bd304a,
0x9e4a9cec15763e2e, 0xc5dd44271ad3cdba,
0xf7549530e188c128, 0x9a94dd3e8cf578b9,
0xc13a148e3032d6e7, 0xf18899b1bc3f8ca1,
0x96f5600f15a7b7e5, 0xbcb2b812db11a5de,
0xebdf661791d60f56, 0x936b9fcebb25c995,
0xb84687c269ef3bfb, 0xe65829b3046b0afa,
0x8ff71a0fe2c2e6dc, 0xb3f4e093db73a093,
0xe0f218b8d25088b8, 0x8c974f7383725573,
0xafbd2350644eeacf, 0xdbac6c247d62a583,
0x894bc396ce5da772, 0xab9eb47c81f5114f,
0xd686619ba27255a2, 0x8613fd0145877585,
0xa798fc4196e952e7, 0xd17f3b51fca3a7a0,
0x82ef85133de648c4, 0xa3ab66580d5fdaf5,
0xcc963fee10b7d1b3, 0xffbbcfe994e5c61f,
0x9fd561f1fd0f9bd3, 0xc7caba6e7c5382c8,
0xf9bd690a1b68637b, 0x9c1661a651213e2d,
0xc31bfa0fe5698db8, 0xf3e2f893dec3f126,
0x986ddb5c6b3a76b7, 0xbe89523386091465,
0xee2ba6c0678b597f, 0x94db483840b717ef,
0xba121a4650e4ddeb, 0xe896a0d7e51e1566,
0x915e2486ef32cd60, 0xb5b5ada8aaff80b8,
0xe3231912d5bf60e6, 0x8df5efabc5979c8f,
0xb1736b96b6fd83b3, 0xddd0467c64bce4a0,
0x8aa22c0dbef60ee4, 0xad4ab7112eb3929d,
0xd89d64d57a607744, 0x87625f056c7c4a8b,
0xa93af6c6c79b5d2d, 0xd389b47879823479,
0x843610cb4bf160cb, 0xa54394fe1eedb8fe,
0xce947a3da6a9273e, 0x811ccc668829b887,
0xa163ff802a3426a8, 0xc9bcff6034c13052,
0xfc2c3f3841f17c67, 0x9d9ba7832936edc0,
0xc5029163f384a931, 0xf64335bcf065d37d,
0x99ea0196163fa42e, 0xc06481fb9bcf8d39,
0xf07da27a82c37088, 0x964e858c91ba2655,
0xbbe226efb628afea, 0xeadab0aba3b2dbe5,
0x92c8ae6b464fc96f, 0xb77ada0617e3bbcb,
0xe55990879ddcaabd, 0x8f57fa54c2a9eab6,
0xb32df8e9f3546564, 0xdff9772470297ebd,
0x8bfbea76c619ef36, 0xaefae51477a06b03,
0xdab99e59958885c4, 0x88b402f7fd75539b,
0xaae103b5fcd2a881, 0xd59944a37c0752a2,
0x857fcae62d8493a5, 0xa6dfbd9fb8e5b88e,
0xd097ad07a71f26b2, 0x825ecc24c873782f,
0xa2f67f2dfa90563b, 0xcbb41ef979346bca,
0xfea126b7d78186bc, 0x9f24b832e6b0f436,
0xc6ede63fa05d3143, 0xf8a95fcf88747d94,
0x9b69dbe1b548ce7c, 0xc24452da229b021b,
0xf2d56790ab41c2a2, 0x97c560ba6b0919a5,
0xbdb6b8e905cb600f, 0xed246723473e3813,
0x9436c0760c86e30b, 0xb94470938fa89bce,
0xe7958cb87392c2c2, 0x90bd77f3483bb9b9,
0xb4ecd5f01a4aa828, 0xe2280b6c20dd5232,
0x8d590723948a535f, 0xb0af48ec79ace837,
0xdcdb1b2798182244, 0x8a08f0f8bf0f156b,
0xac8b2d36eed2dac5, 0xd7adf884aa879177,
0x86ccbb52ea94baea, 0xa87fea27a539e9a5,
0xd29fe4b18e88640e, 0x83a3eeeef9153e89,
0xa48ceaaab75a8e2b, 0xcdb02555653131b6,
0x808e17555f3ebf11, 0xa0b19d2ab70e6ed6,
0xc8de047564d20a8b, 0xfb158592be068d2e,
0x9ced737bb6c4183d, 0xc428d05aa4751e4c,
0xf53304714d9265df, 0x993fe2c6d07b7fab,
0xbf8fdb78849a5f96, 0xef73d256a5c0f77c,
0x95a8637627989aad, 0xbb127c53b17ec159,
0xe9d71b689dde71af, 0x9226712162ab070d,
0xb6b00d69bb55c8d1, 0xe45c10c42a2b3b05,
0x8eb98a7a9a5b04e3, 0xb267ed1940f1c61c,
0xdf01e85f912e37a3, 0x8b61313bbabce2c6,
0xae397d8aa96c1b77, 0xd9c7dced53c72255,
0x881cea14545c7575, 0xaa242499697392d2,
0xd4ad2dbfc3d07787, 0x84ec3c97da624ab4,
0xa6274bbdd0fadd61, 0xcfb11ead453994ba,
0x81ceb32c4b43fcf4, 0xa2425ff75e14fc31,
0xcad2f7f5359a3b3e, 0xfd87b5f28300ca0d,
0x9e74d1b791e07e48, 0xc612062576589dda,
0xf79687aed3eec551, 0x9abe14cd44753b52,
0xc16d9a0095928a27, 0xf1c90080baf72cb1,
0x971da05074da7bee, 0xbce5086492111aea,
0xec1e4a7db69561a5, 0x9392ee8e921d5d07,
0xb877aa3236a4b449, 0xe69594bec44de15b,
0x901d7cf73ab0acd9, 0xb424dc35095cd80f,
0xe12e13424bb40e13, 0x8cbccc096f5088cb,
0xafebff0bcb24aafe, 0xdbe6fecebdedd5be,
0x89705f4136b4a597, 0xabcc77118461cefc,
0xd6bf94d5e57a42bc, 0x8637bd05af6c69b5,
0xa7c5ac471b478423, 0xd1b71758e219652b,
0x83126e978d4fdf3b, 0xa3d70a3d70a3d70a,
0xcccccccccccccccc, 0x8000000000000000,
0xa000000000000000, 0xc800000000000000,
0xfa00000000000000, 0x9c40000000000000,
0xc350000000000000, 0xf424000000000000,
0x9896800000000000, 0xbebc200000000000,
0xee6b280000000000, 0x9502f90000000000,
0xba43b74000000000, 0xe8d4a51000000000,
0x9184e72a00000000, 0xb5e620f480000000,
0xe35fa931a0000000, 0x8e1bc9bf04000000,
0xb1a2bc2ec5000000, 0xde0b6b3a76400000,
0x8ac7230489e80000, 0xad78ebc5ac620000,
0xd8d726b7177a8000, 0x878678326eac9000,
0xa968163f0a57b400, 0xd3c21bcecceda100,
0x84595161401484a0, 0xa56fa5b99019a5c8,
0xcecb8f27f4200f3a, 0x813f3978f8940984,
0xa18f07d736b90be5, 0xc9f2c9cd04674ede,
0xfc6f7c4045812296, 0x9dc5ada82b70b59d,
0xc5371912364ce305, 0xf684df56c3e01bc6,
0x9a130b963a6c115c, 0xc097ce7bc90715b3,
0xf0bdc21abb48db20, 0x96769950b50d88f4,
0xbc143fa4e250eb31, 0xeb194f8e1ae525fd,
0x92efd1b8d0cf37be, 0xb7abc627050305ad,
0xe596b7b0c643c719, 0x8f7e32ce7bea5c6f,
0xb35dbf821ae4f38b, 0xe0352f62a19e306e,
0x8c213d9da502de45, 0xaf298d050e4395d6,
0xdaf3f04651d47b4c, 0x88d8762bf324cd0f,
0xab0e93b6efee0053, 0xd5d238a4abe98068,
0x85a36366eb71f041, 0xa70c3c40a64e6c51,
0xd0cf4b50cfe20765, 0x82818f1281ed449f,
0xa321f2d7226895c7, 0xcbea6f8ceb02bb39,
0xfee50b7025c36a08, 0x9f4f2726179a2245,
0xc722f0ef9d80aad6, 0xf8ebad2b84e0d58b,
0x9b934c3b330c8577, 0xc2781f49ffcfa6d5,
0xf316271c7fc3908a, 0x97edd871cfda3a56,
0xbde94e8e43d0c8ec, 0xed63a231d4c4fb27,
0x945e455f24fb1cf8, 0xb975d6b6ee39e436,
0xe7d34c64a9c85d44, 0x90e40fbeea1d3a4a,
0xb51d13aea4a488dd, 0xe264589a4dcdab14,
0x8d7eb76070a08aec, 0xb0de65388cc8ada8,
0xdd15fe86affad912, 0x8a2dbf142dfcc7ab,
0xacb92ed9397bf996, 0xd7e77a8f87daf7fb,
0x86f0ac99b4e8dafd, 0xa8acd7c0222311bc,
0xd2d80db02aabd62b, 0x83c7088e1aab65db,
0xa4b8cab1a1563f52, 0xcde6fd5e09abcf26,
0x80b05e5ac60b6178, 0xa0dc75f1778e39d6,
0xc913936dd571c84c, 0xfb5878494ace3a5f,
0x9d174b2dcec0e47b, 0xc45d1df942711d9a,
0xf5746577930d6500, 0x9968bf6abbe85f20,
0xbfc2ef456ae276e8, 0xefb3ab16c59b14a2,
0x95d04aee3b80ece5, 0xbb445da9ca61281f,
0xea1575143cf97226, 0x924d692ca61be758,
0xb6e0c377cfa2e12e, 0xe498f455c38b997a,
0x8edf98b59a373fec, 0xb2977ee300c50fe7,
0xdf3d5e9bc0f653e1, 0x8b865b215899f46c,
0xae67f1e9aec07187, 0xda01ee641a708de9,
0x884134fe908658b2, 0xaa51823e34a7eede,
0xd4e5e2cdc1d1ea96, 0x850fadc09923329e,
0xa6539930bf6bff45, 0xcfe87f7cef46ff16,
0x81f14fae158c5f6e, 0xa26da3999aef7749,
0xcb090c8001ab551c, 0xfdcb4fa002162a63,
0x9e9f11c4014dda7e, 0xc646d63501a1511d,
0xf7d88bc24209a565, 0x9ae757596946075f,
0xc1a12d2fc3978937, 0xf209787bb47d6b84,
0x9745eb4d50ce6332, 0xbd176620a501fbff,
0xec5d3fa8ce427aff, 0x93ba47c980e98cdf,
0xb8a8d9bbe123f017, 0xe6d3102ad96cec1d,
0x9043ea1ac7e41392, 0xb454e4a179dd1877,
0xe16a1dc9d8545e94, 0x8ce2529e2734bb1d,
0xb01ae745b101e9e4, 0xdc21a1171d42645d,
0x899504ae72497eba, 0xabfa45da0edbde69,
0xd6f8d7509292d603, 0x865b86925b9bc5c2,
0xa7f26836f282b732, 0xd1ef0244af2364ff,
0x8335616aed761f1f, 0xa402b9c5a8d3a6e7,
0xcd036837130890a1, 0x802221226be55a64,
0xa02aa96b06deb0fd, 0xc83553c5c8965d3d,
0xfa42a8b73abbf48c, 0x9c69a97284b578d7,
0xc38413cf25e2d70d, 0xf46518c2ef5b8cd1,
0x98bf2f79d5993802, 0xbeeefb584aff8603,
0xeeaaba2e5dbf6784, 0x952ab45cfa97a0b2,
0xba756174393d88df, 0xe912b9d1478ceb17,
0x91abb422ccb812ee, 0xb616a12b7fe617aa,
0xe39c49765fdf9d94, 0x8e41ade9fbebc27d,
0xb1d219647ae6b31c, 0xde469fbd99a05fe3,
0x8aec23d680043bee, 0xada72ccc20054ae9,
0xd910f7ff28069da4, 0x87aa9aff79042286,
0xa99541bf57452b28, 0xd3fa922f2d1675f2,
0x847c9b5d7c2e09b7, 0xa59bc234db398c25,
0xcf02b2c21207ef2e, 0x8161afb94b44f57d,
0xa1ba1ba79e1632dc, 0xca28a291859bbf93,
0xfcb2cb35e702af78, 0x9defbf01b061adab,
0xc56baec21c7a1916, 0xf6c69a72a3989f5b,
0x9a3c2087a63f6399, 0xc0cb28a98fcf3c7f,
0xf0fdf2d3f3c30b9f, 0x969eb7c47859e743,
0xbc4665b596706114, 0xeb57ff22fc0c7959,
0x9316ff75dd87cbd8, 0xb7dcbf5354e9bece,
0xe5d3ef282a242e81, 0x8fa475791a569d10,
0xb38d92d760ec4455, 0xe070f78d3927556a,
0x8c469ab843b89562, 0xaf58416654a6babb,
0xdb2e51bfe9d0696a, 0x88fcf317f22241e2,
0xab3c2fddeeaad25a, 0xd60b3bd56a5586f1,
0x85c7056562757456, 0xa738c6bebb12d16c,
0xd106f86e69d785c7, 0x82a45b450226b39c,
0xa34d721642b06084, 0xcc20ce9bd35c78a5,
0xff290242c83396ce, 0x9f79a169bd203e41,
0xc75809c42c684dd1, 0xf92e0c3537826145,
0x9bbcc7a142b17ccb, 0xc2abf989935ddbfe,
0xf356f7ebf83552fe, 0x98165af37b2153de,
0xbe1bf1b059e9a8d6, 0xeda2ee1c7064130c,
0x9485d4d1c63e8be7, 0xb9a74a0637ce2ee1,
0xe8111c87c5c1ba99, 0x910ab1d4db9914a0,
0xb54d5e4a127f59c8, 0xe2a0b5dc971f303a,
0x8da471a9de737e24, 0xb10d8e1456105dad,
0xdd50f1996b947518, 0x8a5296ffe33cc92f,
0xace73cbfdc0bfb7b, 0xd8210befd30efa5a,
0x8714a775e3e95c78, 0xa8d9d1535ce3b396,
0xd31045a8341ca07c, 0x83ea2b892091e44d,
0xa4e4b66b68b65d60, 0xce1de40642e3f4b9,
0x80d2ae83e9ce78f3, 0xa1075a24e4421730,
0xc94930ae1d529cfc, 0xfb9b7cd9a4a7443c,
0x9d412e0806e88aa5, 0xc491798a08a2ad4e,
0xf5b5d7ec8acb58a2, 0x9991a6f3d6bf1765,
0xbff610b0cc6edd3f, 0xeff394dcff8a948e,
0x95f83d0a1fb69cd9, 0xbb764c4ca7a4440f,
0xea53df5fd18d5513, 0x92746b9be2f8552c,
0xb7118682dbb66a77, 0xe4d5e82392a40515,
0x8f05b1163ba6832d, 0xb2c71d5bca9023f8,
0xdf78e4b2bd342cf6, 0x8bab8eefb6409c1a,
0xae9672aba3d0c320, 0xda3c0f568cc4f3e8,
0x8865899617fb1871, 0xaa7eebfb9df9de8d,
0xd51ea6fa85785631, 0x8533285c936b35de,
0xa67ff273b8460356, 0xd01fef10a657842c,
0x8213f56a67f6b29b, 0xa298f2c501f45f42,
0xcb3f2f7642717713, 0xfe0efb53d30dd4d7,
0x9ec95d1463e8a506, 0xc67bb4597ce2ce48,
0xf81aa16fdc1b81da, 0x9b10a4e5e9913128,
0xc1d4ce1f63f57d72, 0xf24a01a73cf2dccf,
0x976e41088617ca01, 0xbd49d14aa79dbc82,
0xec9c459d51852ba2, 0x93e1ab8252f33b45,
0xb8da1662e7b00a17, 0xe7109bfba19c0c9d,
0x906a617d450187e2, 0xb484f9dc9641e9da,
0xe1a63853bbd26451, 0x8d07e33455637eb2,
0xb049dc016abc5e5f, 0xdc5c5301c56b75f7,
0x89b9b3e11b6329ba, 0xac2820d9623bf429,
0xd732290fbacaf133, 0x867f59a9d4bed6c0,
0xa81f301449ee8c70, 0xd226fc195c6a2f8c,
0x83585d8fd9c25db7, 0xa42e74f3d032f525,
0xcd3a1230c43fb26f, 0x80444b5e7aa7cf85,
0xa0555e361951c366, 0xc86ab5c39fa63440,
0xfa856334878fc150, 0x9c935e00d4b9d8d2,
0xc3b8358109e84f07, 0xf4a642e14c6262c8,
0x98e7e9cccfbd7dbd, 0xbf21e44003acdd2c,
0xeeea5d5004981478, 0x95527a5202df0ccb,
0xbaa718e68396cffd, 0xe950df20247c83fd,
0x91d28b7416cdd27e, 0xb6472e511c81471d,
0xe3d8f9e563a198e5, 0x8e679c2f5e44ff8f
};
// A complement to significand_64
// complete to a 128-bit significand.
// Uses about 5KB but is rarely accessed.
static constexpr std::uint64_t significand_128[] = {
0x419ea3bd35385e2d, 0x52064cac828675b9,
0x7343efebd1940993, 0x1014ebe6c5f90bf8,
0xd41a26e077774ef6, 0x8920b098955522b4,
0x55b46e5f5d5535b0, 0xeb2189f734aa831d,
0xa5e9ec7501d523e4, 0x47b233c92125366e,
0x999ec0bb696e840a, 0xc00670ea43ca250d,
0x380406926a5e5728, 0xc605083704f5ecf2,
0xf7864a44c633682e, 0x7ab3ee6afbe0211d,
0x5960ea05bad82964, 0x6fb92487298e33bd,
0xa5d3b6d479f8e056, 0x8f48a4899877186c,
0x331acdabfe94de87, 0x9ff0c08b7f1d0b14,
0x7ecf0ae5ee44dd9, 0xc9e82cd9f69d6150,
0xbe311c083a225cd2, 0x6dbd630a48aaf406,
0x92cbbccdad5b108, 0x25bbf56008c58ea5,
0xaf2af2b80af6f24e, 0x1af5af660db4aee1,
0x50d98d9fc890ed4d, 0xe50ff107bab528a0,
0x1e53ed49a96272c8, 0x25e8e89c13bb0f7a,
0x77b191618c54e9ac, 0xd59df5b9ef6a2417,
0x4b0573286b44ad1d, 0x4ee367f9430aec32,
0x229c41f793cda73f, 0x6b43527578c1110f,
0x830a13896b78aaa9, 0x23cc986bc656d553,
0x2cbfbe86b7ec8aa8, 0x7bf7d71432f3d6a9,
0xdaf5ccd93fb0cc53, 0xd1b3400f8f9cff68,
0x23100809b9c21fa1, 0xabd40a0c2832a78a,
0x16c90c8f323f516c, 0xae3da7d97f6792e3,
0x99cd11cfdf41779c, 0x40405643d711d583,
0x482835ea666b2572, 0xda3243650005eecf,
0x90bed43e40076a82, 0x5a7744a6e804a291,
0x711515d0a205cb36, 0xd5a5b44ca873e03,
0xe858790afe9486c2, 0x626e974dbe39a872,
0xfb0a3d212dc8128f, 0x7ce66634bc9d0b99,
0x1c1fffc1ebc44e80, 0xa327ffb266b56220,
0x4bf1ff9f0062baa8, 0x6f773fc3603db4a9,
0xcb550fb4384d21d3, 0x7e2a53a146606a48,
0x2eda7444cbfc426d, 0xfa911155fefb5308,
0x793555ab7eba27ca, 0x4bc1558b2f3458de,
0x9eb1aaedfb016f16, 0x465e15a979c1cadc,
0xbfacd89ec191ec9, 0xcef980ec671f667b,
0x82b7e12780e7401a, 0xd1b2ecb8b0908810,
0x861fa7e6dcb4aa15, 0x67a791e093e1d49a,
0xe0c8bb2c5c6d24e0, 0x58fae9f773886e18,
0xaf39a475506a899e, 0x6d8406c952429603,
0xc8e5087ba6d33b83, 0xfb1e4a9a90880a64,
0x5cf2eea09a55067f, 0xf42faa48c0ea481e,
0xf13b94daf124da26, 0x76c53d08d6b70858,
0x54768c4b0c64ca6e, 0xa9942f5dcf7dfd09,
0xd3f93b35435d7c4c, 0xc47bc5014a1a6daf,
0x359ab6419ca1091b, 0xc30163d203c94b62,
0x79e0de63425dcf1d, 0x985915fc12f542e4,
0x3e6f5b7b17b2939d, 0xa705992ceecf9c42,
0x50c6ff782a838353, 0xa4f8bf5635246428,
0x871b7795e136be99, 0x28e2557b59846e3f,
0x331aeada2fe589cf, 0x3ff0d2c85def7621,
0xfed077a756b53a9, 0xd3e8495912c62894,
0x64712dd7abbbd95c, 0xbd8d794d96aacfb3,
0xecf0d7a0fc5583a0, 0xf41686c49db57244,
0x311c2875c522ced5, 0x7d633293366b828b,
0xae5dff9c02033197, 0xd9f57f830283fdfc,
0xd072df63c324fd7b, 0x4247cb9e59f71e6d,
0x52d9be85f074e608, 0x67902e276c921f8b,
0xba1cd8a3db53b6, 0x80e8a40eccd228a4,
0x6122cd128006b2cd, 0x796b805720085f81,
0xcbe3303674053bb0, 0xbedbfc4411068a9c,
0xee92fb5515482d44, 0x751bdd152d4d1c4a,
0xd262d45a78a0635d, 0x86fb897116c87c34,
0xd45d35e6ae3d4da0, 0x8974836059cca109,
0x2bd1a438703fc94b, 0x7b6306a34627ddcf,
0x1a3bc84c17b1d542, 0x20caba5f1d9e4a93,
0x547eb47b7282ee9c, 0xe99e619a4f23aa43,
0x6405fa00e2ec94d4, 0xde83bc408dd3dd04,
0x9624ab50b148d445, 0x3badd624dd9b0957,
0xe54ca5d70a80e5d6, 0x5e9fcf4ccd211f4c,
0x7647c3200069671f, 0x29ecd9f40041e073,
0xf468107100525890, 0x7182148d4066eeb4,
0xc6f14cd848405530, 0xb8ada00e5a506a7c,
0xa6d90811f0e4851c, 0x908f4a166d1da663,
0x9a598e4e043287fe, 0x40eff1e1853f29fd,
0xd12bee59e68ef47c, 0x82bb74f8301958ce,
0xe36a52363c1faf01, 0xdc44e6c3cb279ac1,
0x29ab103a5ef8c0b9, 0x7415d448f6b6f0e7,
0x111b495b3464ad21, 0xcab10dd900beec34,
0x3d5d514f40eea742, 0xcb4a5a3112a5112,
0x47f0e785eaba72ab, 0x59ed216765690f56,
0x306869c13ec3532c, 0x1e414218c73a13fb,
0xe5d1929ef90898fa, 0xdf45f746b74abf39,
0x6b8bba8c328eb783, 0x66ea92f3f326564,
0xc80a537b0efefebd, 0xbd06742ce95f5f36,
0x2c48113823b73704, 0xf75a15862ca504c5,
0x9a984d73dbe722fb, 0xc13e60d0d2e0ebba,
0x318df905079926a8, 0xfdf17746497f7052,
0xfeb6ea8bedefa633, 0xfe64a52ee96b8fc0,
0x3dfdce7aa3c673b0, 0x6bea10ca65c084e,
0x486e494fcff30a62, 0x5a89dba3c3efccfa,
0xf89629465a75e01c, 0xf6bbb397f1135823,
0x746aa07ded582e2c, 0xa8c2a44eb4571cdc,
0x92f34d62616ce413, 0x77b020baf9c81d17,
0xace1474dc1d122e, 0xd819992132456ba,
0x10e1fff697ed6c69, 0xca8d3ffa1ef463c1,
0xbd308ff8a6b17cb2, 0xac7cb3f6d05ddbde,
0x6bcdf07a423aa96b, 0x86c16c98d2c953c6,
0xe871c7bf077ba8b7, 0x11471cd764ad4972,
0xd598e40d3dd89bcf, 0x4aff1d108d4ec2c3,
0xcedf722a585139ba, 0xc2974eb4ee658828,
0x733d226229feea32, 0x806357d5a3f525f,
0xca07c2dcb0cf26f7, 0xfc89b393dd02f0b5,
0xbbac2078d443ace2, 0xd54b944b84aa4c0d,
0xa9e795e65d4df11, 0x4d4617b5ff4a16d5,
0x504bced1bf8e4e45, 0xe45ec2862f71e1d6,
0x5d767327bb4e5a4c, 0x3a6a07f8d510f86f,
0x890489f70a55368b, 0x2b45ac74ccea842e,
0x3b0b8bc90012929d, 0x9ce6ebb40173744,
0xcc420a6a101d0515, 0x9fa946824a12232d,
0x47939822dc96abf9, 0x59787e2b93bc56f7,
0x57eb4edb3c55b65a, 0xede622920b6b23f1,
0xe95fab368e45eced, 0x11dbcb0218ebb414,
0xd652bdc29f26a119, 0x4be76d3346f0495f,
0x6f70a4400c562ddb, 0xcb4ccd500f6bb952,
0x7e2000a41346a7a7, 0x8ed400668c0c28c8,
0x728900802f0f32fa, 0x4f2b40a03ad2ffb9,
0xe2f610c84987bfa8, 0xdd9ca7d2df4d7c9,
0x91503d1c79720dbb, 0x75a44c6397ce912a,
0xc986afbe3ee11aba, 0xfbe85badce996168,
0xfae27299423fb9c3, 0xdccd879fc967d41a,
0x5400e987bbc1c920, 0x290123e9aab23b68,
0xf9a0b6720aaf6521, 0xf808e40e8d5b3e69,
0xb60b1d1230b20e04, 0xb1c6f22b5e6f48c2,
0x1e38aeb6360b1af3, 0x25c6da63c38de1b0,
0x579c487e5a38ad0e, 0x2d835a9df0c6d851,
0xf8e431456cf88e65, 0x1b8e9ecb641b58ff,
0xe272467e3d222f3f, 0x5b0ed81dcc6abb0f,
0x98e947129fc2b4e9, 0x3f2398d747b36224,
0x8eec7f0d19a03aad, 0x1953cf68300424ac,
0x5fa8c3423c052dd7, 0x3792f412cb06794d,
0xe2bbd88bbee40bd0, 0x5b6aceaeae9d0ec4,
0xf245825a5a445275, 0xeed6e2f0f0d56712,
0x55464dd69685606b, 0xaa97e14c3c26b886,
0xd53dd99f4b3066a8, 0xe546a8038efe4029,
0xde98520472bdd033, 0x963e66858f6d4440,
0xdde7001379a44aa8, 0x5560c018580d5d52,
0xaab8f01e6e10b4a6, 0xcab3961304ca70e8,
0x3d607b97c5fd0d22, 0x8cb89a7db77c506a,
0x77f3608e92adb242, 0x55f038b237591ed3,
0x6b6c46dec52f6688, 0x2323ac4b3b3da015,
0xabec975e0a0d081a, 0x96e7bd358c904a21,
0x7e50d64177da2e54, 0xdde50bd1d5d0b9e9,
0x955e4ec64b44e864, 0xbd5af13bef0b113e,
0xecb1ad8aeacdd58e, 0x67de18eda5814af2,
0x80eacf948770ced7, 0xa1258379a94d028d,
0x96ee45813a04330, 0x8bca9d6e188853fc,
0x775ea264cf55347d, 0x95364afe032a819d,
0x3a83ddbd83f52204, 0xc4926a9672793542,
0x75b7053c0f178293, 0x5324c68b12dd6338,
0xd3f6fc16ebca5e03, 0x88f4bb1ca6bcf584,
0x2b31e9e3d06c32e5, 0x3aff322e62439fcf,
0x9befeb9fad487c2, 0x4c2ebe687989a9b3,
0xf9d37014bf60a10, 0x538484c19ef38c94,
0x2865a5f206b06fb9, 0xf93f87b7442e45d3,
0xf78f69a51539d748, 0xb573440e5a884d1b,
0x31680a88f8953030, 0xfdc20d2b36ba7c3d,
0x3d32907604691b4c, 0xa63f9a49c2c1b10f,
0xfcf80dc33721d53, 0xd3c36113404ea4a8,
0x645a1cac083126e9, 0x3d70a3d70a3d70a3,
0xcccccccccccccccc, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x4000000000000000,
0x5000000000000000, 0xa400000000000000,
0x4d00000000000000, 0xf020000000000000,
0x6c28000000000000, 0xc732000000000000,
0x3c7f400000000000, 0x4b9f100000000000,
0x1e86d40000000000, 0x1314448000000000,
0x17d955a000000000, 0x5dcfab0800000000,
0x5aa1cae500000000, 0xf14a3d9e40000000,
0x6d9ccd05d0000000, 0xe4820023a2000000,
0xdda2802c8a800000, 0xd50b2037ad200000,
0x4526f422cc340000, 0x9670b12b7f410000,
0x3c0cdd765f114000, 0xa5880a69fb6ac800,
0x8eea0d047a457a00, 0x72a4904598d6d880,
0x47a6da2b7f864750, 0x999090b65f67d924,
0xfff4b4e3f741cf6d, 0xbff8f10e7a8921a4,
0xaff72d52192b6a0d, 0x9bf4f8a69f764490,
0x2f236d04753d5b4, 0x1d762422c946590,
0x424d3ad2b7b97ef5, 0xd2e0898765a7deb2,
0x63cc55f49f88eb2f, 0x3cbf6b71c76b25fb,
0x8bef464e3945ef7a, 0x97758bf0e3cbb5ac,
0x3d52eeed1cbea317, 0x4ca7aaa863ee4bdd,
0x8fe8caa93e74ef6a, 0xb3e2fd538e122b44,
0x60dbbca87196b616, 0xbc8955e946fe31cd,
0x6babab6398bdbe41, 0xc696963c7eed2dd1,
0xfc1e1de5cf543ca2, 0x3b25a55f43294bcb,
0x49ef0eb713f39ebe, 0x6e3569326c784337,
0x49c2c37f07965404, 0xdc33745ec97be906,
0x69a028bb3ded71a3, 0xc40832ea0d68ce0c,
0xf50a3fa490c30190, 0x792667c6da79e0fa,
0x577001b891185938, 0xed4c0226b55e6f86,
0x544f8158315b05b4, 0x696361ae3db1c721,
0x3bc3a19cd1e38e9, 0x4ab48a04065c723,
0x62eb0d64283f9c76, 0x3ba5d0bd324f8394,
0xca8f44ec7ee36479, 0x7e998b13cf4e1ecb,
0x9e3fedd8c321a67e, 0xc5cfe94ef3ea101e,
0xbba1f1d158724a12, 0x2a8a6e45ae8edc97,
0xf52d09d71a3293bd, 0x593c2626705f9c56,
0x6f8b2fb00c77836c, 0xb6dfb9c0f956447,
0x4724bd4189bd5eac, 0x58edec91ec2cb657,
0x2f2967b66737e3ed, 0xbd79e0d20082ee74,
0xecd8590680a3aa11, 0xe80e6f4820cc9495,
0x3109058d147fdcdd, 0xbd4b46f0599fd415,
0x6c9e18ac7007c91a, 0x3e2cf6bc604ddb0,
0x84db8346b786151c, 0xe612641865679a63,
0x4fcb7e8f3f60c07e, 0xe3be5e330f38f09d,
0x5cadf5bfd3072cc5, 0x73d9732fc7c8f7f6,
0x2867e7fddcdd9afa, 0xb281e1fd541501b8,
0x1f225a7ca91a4226, 0x3375788de9b06958,
0x52d6b1641c83ae, 0xc0678c5dbd23a49a,
0xf840b7ba963646e0, 0xb650e5a93bc3d898,
0xa3e51f138ab4cebe, 0xc66f336c36b10137,
0xb80b0047445d4184, 0xa60dc059157491e5,
0x87c89837ad68db2f, 0x29babe4598c311fb,
0xf4296dd6fef3d67a, 0x1899e4a65f58660c,
0x5ec05dcff72e7f8f, 0x76707543f4fa1f73,
0x6a06494a791c53a8, 0x487db9d17636892,
0x45a9d2845d3c42b6, 0xb8a2392ba45a9b2,
0x8e6cac7768d7141e, 0x3207d795430cd926,
0x7f44e6bd49e807b8, 0x5f16206c9c6209a6,
0x36dba887c37a8c0f, 0xc2494954da2c9789,
0xf2db9baa10b7bd6c, 0x6f92829494e5acc7,
0xcb772339ba1f17f9, 0xff2a760414536efb,
0xfef5138519684aba, 0x7eb258665fc25d69,
0xef2f773ffbd97a61, 0xaafb550ffacfd8fa,
0x95ba2a53f983cf38, 0xdd945a747bf26183,
0x94f971119aeef9e4, 0x7a37cd5601aab85d,
0xac62e055c10ab33a, 0x577b986b314d6009,
0xed5a7e85fda0b80b, 0x14588f13be847307,
0x596eb2d8ae258fc8, 0x6fca5f8ed9aef3bb,
0x25de7bb9480d5854, 0xaf561aa79a10ae6a,
0x1b2ba1518094da04, 0x90fb44d2f05d0842,
0x353a1607ac744a53, 0x42889b8997915ce8,
0x69956135febada11, 0x43fab9837e699095,
0x94f967e45e03f4bb, 0x1d1be0eebac278f5,
0x6462d92a69731732, 0x7d7b8f7503cfdcfe,
0x5cda735244c3d43e, 0x3a0888136afa64a7,
0x88aaa1845b8fdd0, 0x8aad549e57273d45,
0x36ac54e2f678864b, 0x84576a1bb416a7dd,
0x656d44a2a11c51d5, 0x9f644ae5a4b1b325,
0x873d5d9f0dde1fee, 0xa90cb506d155a7ea,
0x9a7f12442d588f2, 0xc11ed6d538aeb2f,
0x8f1668c8a86da5fa, 0xf96e017d694487bc,
0x37c981dcc395a9ac, 0x85bbe253f47b1417,
0x93956d7478ccec8e, 0x387ac8d1970027b2,
0x6997b05fcc0319e, 0x441fece3bdf81f03,
0xd527e81cad7626c3, 0x8a71e223d8d3b074,
0xf6872d5667844e49, 0xb428f8ac016561db,
0xe13336d701beba52, 0xecc0024661173473,
0x27f002d7f95d0190, 0x31ec038df7b441f4,
0x7e67047175a15271, 0xf0062c6e984d386,
0x52c07b78a3e60868, 0xa7709a56ccdf8a82,
0x88a66076400bb691, 0x6acff893d00ea435,
0x583f6b8c4124d43, 0xc3727a337a8b704a,
0x744f18c0592e4c5c, 0x1162def06f79df73,
0x8addcb5645ac2ba8, 0x6d953e2bd7173692,
0xc8fa8db6ccdd0437, 0x1d9c9892400a22a2,
0x2503beb6d00cab4b, 0x2e44ae64840fd61d,
0x5ceaecfed289e5d2, 0x7425a83e872c5f47,
0xd12f124e28f77719, 0x82bd6b70d99aaa6f,
0x636cc64d1001550b, 0x3c47f7e05401aa4e,
0x65acfaec34810a71, 0x7f1839a741a14d0d,
0x1ede48111209a050, 0x934aed0aab460432,
0xf81da84d5617853f, 0x36251260ab9d668e,
0xc1d72b7c6b426019, 0xb24cf65b8612f81f,
0xdee033f26797b627, 0x169840ef017da3b1,
0x8e1f289560ee864e, 0xf1a6f2bab92a27e2,
0xae10af696774b1db, 0xacca6da1e0a8ef29,
0x17fd090a58d32af3, 0xddfc4b4cef07f5b0,
0x4abdaf101564f98e, 0x9d6d1ad41abe37f1,
0x84c86189216dc5ed, 0x32fd3cf5b4e49bb4,
0x3fbc8c33221dc2a1, 0xfabaf3feaa5334a,
0x29cb4d87f2a7400e, 0x743e20e9ef511012,
0x914da9246b255416, 0x1ad089b6c2f7548e,
0xa184ac2473b529b1, 0xc9e5d72d90a2741e,
0x7e2fa67c7a658892, 0xddbb901b98feeab7,
0x552a74227f3ea565, 0xd53a88958f87275f,
0x8a892abaf368f137, 0x2d2b7569b0432d85,
0x9c3b29620e29fc73, 0x8349f3ba91b47b8f,
0x241c70a936219a73, 0xed238cd383aa0110,
0xf4363804324a40aa, 0xb143c6053edcd0d5,
0xdd94b7868e94050a, 0xca7cf2b4191c8326,
0xfd1c2f611f63a3f0, 0xbc633b39673c8cec,
0xd5be0503e085d813, 0x4b2d8644d8a74e18,
0xddf8e7d60ed1219e, 0xcabb90e5c942b503,
0x3d6a751f3b936243, 0xcc512670a783ad4,
0x27fb2b80668b24c5, 0xb1f9f660802dedf6,
0x5e7873f8a0396973, 0xdb0b487b6423e1e8,
0x91ce1a9a3d2cda62, 0x7641a140cc7810fb,
0xa9e904c87fcb0a9d, 0x546345fa9fbdcd44,
0xa97c177947ad4095, 0x49ed8eabcccc485d,
0x5c68f256bfff5a74, 0x73832eec6fff3111,
0xc831fd53c5ff7eab, 0xba3e7ca8b77f5e55,
0x28ce1bd2e55f35eb, 0x7980d163cf5b81b3,
0xd7e105bcc332621f, 0x8dd9472bf3fefaa7,
0xb14f98f6f0feb951, 0x6ed1bf9a569f33d3,
0xa862f80ec4700c8, 0xcd27bb612758c0fa,
0x8038d51cb897789c, 0xe0470a63e6bd56c3,
0x1858ccfce06cac74, 0xf37801e0c43ebc8,
0xd30560258f54e6ba, 0x47c6b82ef32a2069,
0x4cdc331d57fa5441, 0xe0133fe4adf8e952,
0x58180fddd97723a6, 0x570f09eaa7ea7648
};
}}}}} // Namespaces
#endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_SIGNIFICAND_TABLES_HPP
+33
View File
@@ -0,0 +1,33 @@
// Copyright 2022 Peter Dimov
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_JSON_DETAIL_CHARCONV_FROM_CHARS_HPP_INCLUDED
#define BOOST_JSON_DETAIL_CHARCONV_FROM_CHARS_HPP_INCLUDED
#include <boost/json/detail/charconv/detail/config.hpp>
#include <boost/json/detail/charconv/detail/from_chars_result.hpp>
#include <boost/json/detail/charconv/chars_format.hpp>
#include <system_error>
namespace boost { namespace json { namespace detail { namespace charconv {
//----------------------------------------------------------------------------------------------------------------------
// Floating Point
//----------------------------------------------------------------------------------------------------------------------
namespace detail {
std::errc errno_to_errc(int errno_value) noexcept;
} // Namespace detail
BOOST_JSON_DECL from_chars_result from_chars(const char* first, const char* last, double& value, chars_format fmt = chars_format::general) noexcept;
} // namespace charconv
} // namespace detail
} // namespace json
} // namespace boost
#endif // #ifndef BOOST_JSON_DETAIL_CHARCONV_FROM_CHARS_HPP_INCLUDED
+47
View File
@@ -0,0 +1,47 @@
// Copyright 2022 Peter Dimov
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
// https://stackoverflow.com/questions/38060411/visual-studio-2015-wont-suppress-error-c4996
#ifndef _SCL_SECURE_NO_WARNINGS
# define _SCL_SECURE_NO_WARNINGS
#endif
#ifndef NO_WARN_MBCS_MFC_DEPRECATION
# define NO_WARN_MBCS_MFC_DEPRECATION
#endif
#include <boost/json/detail/charconv/detail/fast_float/fast_float.hpp>
#include <boost/json/detail/charconv/detail/from_chars_float_impl.hpp>
#include <boost/json/detail/charconv/from_chars.hpp>
#include <system_error>
#include <string>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#if defined(__GNUC__) && __GNUC__ < 5
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
std::errc boost::json::detail::charconv::detail::errno_to_errc(int errno_value) noexcept
{
switch (errno_value)
{
case EINVAL:
return std::errc::invalid_argument;
case ERANGE:
return std::errc::result_out_of_range;
default:
return std::errc();
}
}
boost::json::detail::charconv::from_chars_result boost::json::detail::charconv::from_chars(const char* first, const char* last, double& value, boost::json::detail::charconv::chars_format fmt) noexcept
{
if (fmt != boost::json::detail::charconv::chars_format::hex)
{
return boost::json::detail::charconv::detail::fast_float::from_chars(first, last, value, fmt);
}
return boost::json::detail::charconv::detail::from_chars_float_impl(first, last, value, fmt);
}
+86
View File
@@ -0,0 +1,86 @@
// Copyright 2023 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#ifndef BOOST_JSON_DETAIL_CHARCONV_LIMITS_HPP
#define BOOST_JSON_DETAIL_CHARCONV_LIMITS_HPP
#include <boost/config.hpp>
#include <limits>
#include <type_traits>
namespace boost { namespace json { namespace detail { namespace charconv {
// limits<T>::max_chars10: the minimum size of the buffer that needs to be
// passed to to_chars to guarantee successful conversion for all values of
// type T, when either no base is passed, or base 10 is passed
//
// limits<T>::max_chars: the minimum size of the buffer that needs to be
// passed to to_chars to guarantee successful conversion for all values of
// type T, for any value of base
namespace detail
{
constexpr int exp_digits( int exp )
{
return exp < 100? 2: exp < 1000? 3: exp < 10000? 4: 5;
}
#if defined(BOOST_HAS_INT128)
template<class T> struct is_int128: std::is_same<T, boost::int128_type> {};
template<class T> struct is_uint128: std::is_same<T, boost::int128_type> {};
#else
template<class T> struct is_int128: std::false_type {};
template<class T> struct is_uint128: std::false_type {};
#endif
} // namespace detail
template<typename T> struct limits
{
static constexpr int max_chars10 =
// int128_t
detail::is_int128<T>::value? 38+2: // digits10 + 1 + sign
// uint128_t
detail::is_uint128<T>::value? 38+1: // digits10 + 1
// integral
std::numeric_limits<T>::is_integer? std::numeric_limits<T>::digits10 + 1 + std::numeric_limits<T>::is_signed:
// floating point
std::numeric_limits<T>::max_digits10 + 3 + 2 + detail::exp_digits( std::numeric_limits<T>::max_exponent10 ); // -1.(max_digits10)e+(max_exp)
static constexpr int max_chars =
// int128_t
detail::is_int128<T>::value? 127+2: // digits + 1 + sign
// uint128_t
detail::is_uint128<T>::value? 128+1: // digits + 1
// integral
std::numeric_limits<T>::is_integer? std::numeric_limits<T>::digits + 1 + std::numeric_limits<T>::is_signed:
// floating point
std::numeric_limits<T>::max_digits10 + 3 + 2 + detail::exp_digits( std::numeric_limits<T>::max_exponent10 ); // as above
};
#if defined(BOOST_NO_CXX17_INLINE_VARIABLES)
// Definitions of in-class constexpr members are allowed but deprecated in C++17
template<typename T> constexpr int limits<T>::max_chars10;
template<typename T> constexpr int limits<T>::max_chars;
#endif // defined(BOOST_NO_CXX17_INLINE_VARIABLES)
}}}} // namespace boost::charconv
#endif // BOOST_JSON_DETAIL_CHARCONV_LIMITS_HPP
+323
View File
@@ -0,0 +1,323 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_CONFIG_HPP
#define BOOST_JSON_DETAIL_CONFIG_HPP
#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <cstdint>
#include <type_traits>
#include <utility>
// detect 32/64 bit
#if UINTPTR_MAX == UINT64_MAX
# define BOOST_JSON_ARCH 64
#elif UINTPTR_MAX == UINT32_MAX
# define BOOST_JSON_ARCH 32
#else
# error Unknown or unsupported architecture, please open an issue
#endif
// VFALCO Copied from Boost.Config
// This is a derivative work.
#ifndef BOOST_JSON_NODISCARD
# ifdef __has_cpp_attribute
// clang-6 accepts [[nodiscard]] with -std=c++14, but warns about it -pedantic
# if __has_cpp_attribute(nodiscard) && !(defined(__clang__) && (__cplusplus < 201703L))
# define BOOST_JSON_NODISCARD [[nodiscard]]
# else
# define BOOST_JSON_NODISCARD
# endif
# else
# define BOOST_JSON_NODISCARD
# endif
#endif
#ifndef BOOST_JSON_REQUIRE_CONST_INIT
# define BOOST_JSON_REQUIRE_CONST_INIT
# if __cpp_constinit >= 201907L
# undef BOOST_JSON_REQUIRE_CONST_INIT
# define BOOST_JSON_REQUIRE_CONST_INIT constinit
# elif defined(__clang__) && defined(__has_cpp_attribute)
# if __has_cpp_attribute(clang::require_constant_initialization)
# undef BOOST_JSON_REQUIRE_CONST_INIT
# define BOOST_JSON_REQUIRE_CONST_INIT [[clang::require_constant_initialization]]
# endif
# endif
#endif
#ifndef BOOST_JSON_NO_DESTROY
# if defined(__clang__) && defined(__has_cpp_attribute)
# if __has_cpp_attribute(clang::no_destroy)
# define BOOST_JSON_NO_DESTROY [[clang::no_destroy]]
# endif
# endif
#endif
// BOOST_NORETURN ---------------------------------------------//
// Macro to use before a function declaration/definition to designate
// the function as not returning normally (i.e. with a return statement
// or by leaving the function scope, if the function return type is void).
#if !defined(BOOST_NORETURN)
# if defined(_MSC_VER)
# define BOOST_NORETURN __declspec(noreturn)
# elif defined(__GNUC__)
# define BOOST_NORETURN __attribute__ ((__noreturn__))
# elif defined(__has_attribute) && defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x5130)
# if __has_attribute(noreturn)
# define BOOST_NORETURN [[noreturn]]
# endif
# elif defined(__has_cpp_attribute)
# if __has_cpp_attribute(noreturn)
# define BOOST_NORETURN [[noreturn]]
# endif
# endif
#endif
#ifndef BOOST_ASSERT
#define BOOST_ASSERT assert
#endif
#ifndef BOOST_STATIC_ASSERT
#define BOOST_STATIC_ASSERT( ... ) static_assert(__VA_ARGS__, #__VA_ARGS__)
#endif
#ifndef BOOST_FALLTHROUGH
#define BOOST_FALLTHROUGH [[fallthrough]]
#endif
#ifndef BOOST_FORCEINLINE
# ifdef _MSC_VER
# define BOOST_FORCEINLINE __forceinline
# elif defined(__GNUC__) || defined(__clang__)
# define BOOST_FORCEINLINE inline __attribute__((always_inline))
# else
# define BOOST_FORCEINLINE inline
# endif
#endif
#ifndef BOOST_NOINLINE
# ifdef _MSC_VER
# define BOOST_NOINLINE __declspec(noinline)
# elif defined(__GNUC__) || defined(__clang__)
# define BOOST_NOINLINE __attribute__((noinline))
# else
# define BOOST_NOINLINE
# endif
#endif
#ifndef BOOST_THROW_EXCEPTION
# ifndef BOOST_NO_EXCEPTIONS
# define BOOST_THROW_EXCEPTION(x) throw(x)
# else
# define BOOST_THROW_EXCEPTION(x) do{}while(0)
# endif
#endif
#if ! defined(BOOST_JSON_NO_SSE2) && \
! defined(BOOST_JSON_USE_SSE2)
# if (defined(_M_IX86) && _M_IX86_FP == 2) || \
defined(_M_X64) || defined(__SSE2__)
# define BOOST_JSON_USE_SSE2
# endif
#endif
#ifndef BOOST_SYMBOL_VISIBLE
#define BOOST_SYMBOL_VISIBLE
#endif
#if defined(BOOST_JSON_DOCS)
# define BOOST_JSON_DECL
#else
# if (defined(BOOST_JSON_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)) && !defined(BOOST_JSON_STATIC_LINK)
# if defined(BOOST_JSON_SOURCE)
# define BOOST_JSON_DECL BOOST_SYMBOL_EXPORT
# else
# define BOOST_JSON_DECL BOOST_SYMBOL_IMPORT
# endif
# endif // shared lib
# ifndef BOOST_JSON_DECL
# define BOOST_JSON_DECL
# endif
# if !defined(BOOST_JSON_SOURCE) && !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_JSON_NO_LIB)
# define BOOST_LIB_NAME boost_json
# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_JSON_DYN_LINK)
# define BOOST_DYN_LINK
# endif
# include <boost/config/auto_link.hpp>
# endif
#endif
#ifndef BOOST_JSON_LIKELY
# if defined(__GNUC__) || defined(__clang__)
# define BOOST_JSON_LIKELY(x) __builtin_expect(!!(x), 1)
# else
# define BOOST_JSON_LIKELY(x) x
# endif
#endif
#ifndef BOOST_JSON_UNLIKELY
# if defined(__GNUC__) || defined(__clang__)
# define BOOST_JSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
# else
# define BOOST_JSON_UNLIKELY(x) x
# endif
#endif
#ifndef BOOST_JSON_UNREACHABLE
# ifdef _MSC_VER
# define BOOST_JSON_UNREACHABLE() __assume(0)
# elif defined(__GNUC__) || defined(__clang__)
# define BOOST_JSON_UNREACHABLE() __builtin_unreachable()
# elif defined(__has_builtin)
# if __has_builtin(__builtin_unreachable)
# define BOOST_JSON_UNREACHABLE() __builtin_unreachable()
# endif
# else
# define BOOST_JSON_UNREACHABLE() static_cast<void>(0)
# endif
#endif
#ifndef BOOST_JSON_ASSUME
# define BOOST_JSON_ASSUME(x) (!!(x) ? void() : BOOST_JSON_UNREACHABLE())
# ifdef _MSC_VER
# undef BOOST_JSON_ASSUME
# define BOOST_JSON_ASSUME(x) __assume(!!(x))
# elif defined(__has_builtin)
# if __has_builtin(__builtin_assume)
# undef BOOST_JSON_ASSUME
# define BOOST_JSON_ASSUME(x) __builtin_assume(!!(x))
# endif
# endif
#endif
// older versions of msvc and clang don't always
// constant initialize when they are supposed to
#ifndef BOOST_JSON_WEAK_CONSTINIT
# if defined(_MSC_VER) && ! defined(__clang__) && _MSC_VER < 1920
# define BOOST_JSON_WEAK_CONSTINIT
# elif defined(__clang__) && __clang_major__ < 4
# define BOOST_JSON_WEAK_CONSTINIT
# endif
#endif
// These macros are private, for tests, do not change
// them or else previously built libraries won't match.
#ifndef BOOST_JSON_MAX_STRING_SIZE
# define BOOST_JSON_NO_MAX_STRING_SIZE
# define BOOST_JSON_MAX_STRING_SIZE 0x7ffffffe
#endif
#ifndef BOOST_JSON_MAX_STRUCTURED_SIZE
# define BOOST_JSON_NO_MAX_STRUCTURED_SIZE
# define BOOST_JSON_MAX_STRUCTURED_SIZE 0x7ffffffe
#endif
#ifndef BOOST_JSON_STACK_BUFFER_SIZE
# define BOOST_JSON_NO_STACK_BUFFER_SIZE
# if defined(__i386__) || defined(__x86_64__) || \
defined(_M_IX86) || defined(_M_X64)
# define BOOST_JSON_STACK_BUFFER_SIZE 4096
# else
// If we are not on Intel, then assume we are on
// embedded and use a smaller stack size. If this
// is not suitable, the user can define the macro
// themselves when building the library or including
// src.hpp.
# define BOOST_JSON_STACK_BUFFER_SIZE 256
# endif
#endif
#if ! defined(BOOST_JSON_BIG_ENDIAN) && ! defined(BOOST_JSON_LITTLE_ENDIAN)
// Copied from Boost.Endian
# if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define BOOST_JSON_LITTLE_ENDIAN
# elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define BOOST_JSON_BIG_ENDIAN
# elif defined(__LITTLE_ENDIAN__)
# define BOOST_JSON_LITTLE_ENDIAN
# elif defined(__BIG_ENDIAN__)
# define BOOST_JSON_BIG_ENDIAN
# elif defined(_MSC_VER) || defined(__i386__) || defined(__x86_64__)
# define BOOST_JSON_LITTLE_ENDIAN
# else
# error The Boost.JSON library could not determine the endianness of this platform. Define either BOOST_JSON_BIG_ENDIAN or BOOST_JSON_LITTLE_ENDIAN.
# endif
#endif
#if defined(__cpp_constinit) && __cpp_constinit >= 201907L
# define BOOST_JSON_CONSTINIT constinit
#elif defined(__has_cpp_attribute) && defined(__clang__)
# if __has_cpp_attribute(clang::require_constant_initialization)
# define BOOST_JSON_CONSTINIT [[clang::require_constant_initialization]]
# endif
#elif defined(__GNUC__) && (__GNUC__ >= 10)
# define BOOST_JSON_CONSTINIT __constinit
#endif
#ifndef BOOST_JSON_CONSTINIT
# define BOOST_JSON_CONSTINIT
#endif
namespace boost {
namespace json {
namespace detail {
template<class...>
struct make_void
{
using type =void;
};
template<class... Ts>
using void_t = typename
make_void<Ts...>::type;
template<class T>
using remove_cvref = typename
std::remove_cv<typename
std::remove_reference<T>::type>::type;
template<class T, class U>
T exchange(T& t, U u) noexcept
{
T v = std::move(t);
t = std::move(u);
return v;
}
/* This is a derivative work, original copyright:
Copyright Eric Niebler 2013-present
Use, modification and distribution is subject to the
Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
Project home: https://github.com/ericniebler/range-v3
*/
template<typename T>
struct static_const
{
static constexpr T value {};
};
template<typename T>
constexpr T static_const<T>::value;
#define BOOST_JSON_INLINE_VARIABLE(name, type) \
namespace { constexpr auto& name = \
::boost::json::detail::static_const<type>::value; \
} struct _unused_ ## name ## _semicolon_bait_
} // detail
} // namespace json
} // namespace boost
#endif
+101
View File
@@ -0,0 +1,101 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DEFAULT_RESOURCE_HPP
#define BOOST_JSON_DEFAULT_RESOURCE_HPP
#include <boost/json/detail/config.hpp>
#include <new>
namespace boost {
namespace json {
namespace detail {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4251) // class needs to have dll-interface to be used by clients of class
#pragma warning(disable: 4275) // non dll-interface class used as base for dll-interface class
#endif
// A simple memory resource that uses operator new and delete.
class
BOOST_SYMBOL_VISIBLE
BOOST_JSON_DECL
default_resource final
: public memory_resource
{
union holder;
#ifndef BOOST_JSON_WEAK_CONSTINIT
# ifndef BOOST_JSON_NO_DESTROY
static holder instance_;
# else
BOOST_JSON_NO_DESTROY
static default_resource instance_;
# endif
#endif
public:
static
memory_resource*
get() noexcept
{
#ifdef BOOST_JSON_WEAK_CONSTINIT
static default_resource instance_;
#endif
return reinterpret_cast<memory_resource*>(
reinterpret_cast<std::uintptr_t*>(
&instance_));
}
~default_resource();
void*
do_allocate(
std::size_t n,
std::size_t) override;
void
do_deallocate(
void* p,
std::size_t,
std::size_t) override;
bool
do_is_equal(
memory_resource const& mr) const noexcept override;
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
union default_resource::
holder
{
#ifndef BOOST_JSON_WEAK_CONSTINIT
constexpr
#endif
holder()
: mr()
{
}
~holder()
{
}
default_resource mr;
};
} // detail
} // namespace json
} // namespace boost
#endif
+42
View File
@@ -0,0 +1,42 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_DIGEST_HPP
#define BOOST_JSON_DETAIL_DIGEST_HPP
namespace boost {
namespace json {
namespace detail {
// Calculate salted digest of string
template<class ForwardIterator>
std::size_t
digest(
ForwardIterator b,
ForwardIterator e,
std::size_t salt) noexcept
{
#if BOOST_JSON_ARCH == 64
std::uint64_t const prime = 0x100000001B3ULL;
std::uint64_t hash = 0xcbf29ce484222325ULL;
#else
std::uint32_t const prime = 0x01000193UL;
std::uint32_t hash = 0x811C9DC5UL;
#endif
hash += salt;
for(; b != e; ++b)
hash = (*b ^ hash) * prime;
return hash;
}
} // detail
} // namespace json
} // namespace boost
#endif
+41
View File
@@ -0,0 +1,41 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_EXCEPT_HPP
#define BOOST_JSON_DETAIL_EXCEPT_HPP
#include <boost/json/error.hpp>
namespace boost {
namespace json {
namespace detail {
#define BOOST_JSON_FAIL(ec, e) \
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION; \
(ec).assign(e, &loc);
BOOST_JSON_DECL
void
BOOST_NORETURN
throw_system_error(
error_code const& ec,
source_location const& loc = BOOST_CURRENT_LOCATION);
BOOST_JSON_DECL
void
BOOST_NORETURN
throw_system_error(
error e,
source_location const* loc);
} // detail
} // namespace json
} // namespace boost
#endif
+44
View File
@@ -0,0 +1,44 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_FORMAT_HPP
#define BOOST_JSON_DETAIL_FORMAT_HPP
namespace boost {
namespace json {
namespace detail {
int constexpr max_number_chars =
1 + // '-'
19 + // unsigned 64-bit mantissa
1 + // 'e'
1 + // '-'
5; // unsigned 16-bit exponent
BOOST_JSON_DECL
unsigned
format_uint64(
char* dest,
std::uint64_t value) noexcept;
BOOST_JSON_DECL
unsigned
format_int64(
char* dest, int64_t i) noexcept;
BOOST_JSON_DECL
unsigned
format_double(
char* dest, double d, bool allow_infinity_and_nan = false) noexcept;
} // detail
} // namespace json
} // namespace boost
#endif
+68
View File
@@ -0,0 +1,68 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_HANDLER_HPP
#define BOOST_JSON_DETAIL_HANDLER_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/string_view.hpp>
#include <boost/json/array.hpp>
#include <boost/json/object.hpp>
#include <boost/json/string.hpp>
#include <boost/json/value_stack.hpp>
namespace boost {
namespace json {
namespace detail {
struct handler
{
static constexpr std::size_t
max_object_size = object::max_size();
static constexpr std::size_t
max_array_size = array::max_size();
static constexpr std::size_t
max_key_size = string::max_size();
static constexpr std::size_t
max_string_size = string::max_size();
value_stack st;
template<class... Args>
explicit
handler(Args&&... args);
inline bool on_document_begin(error_code& ec);
inline bool on_document_end(error_code& ec);
inline bool on_object_begin(error_code& ec);
inline bool on_object_end(std::size_t n, error_code& ec);
inline bool on_array_begin(error_code& ec);
inline bool on_array_end(std::size_t n, error_code& ec);
inline bool on_key_part(string_view s, std::size_t n, error_code& ec);
inline bool on_key(string_view s, std::size_t n, error_code& ec);
inline bool on_string_part(string_view s, std::size_t n, error_code& ec);
inline bool on_string(string_view s, std::size_t n, error_code& ec);
inline bool on_number_part(string_view, error_code&);
inline bool on_int64(std::int64_t i, string_view, error_code& ec);
inline bool on_uint64(std::uint64_t u, string_view, error_code& ec);
inline bool on_double(double d, string_view, error_code& ec);
inline bool on_bool(bool b, error_code& ec);
inline bool on_null(error_code& ec);
inline bool on_comment_part(string_view, error_code&);
inline bool on_comment(string_view, error_code&);
};
} // detail
} // namespace json
} // namespace boost
#endif
+43
View File
@@ -0,0 +1,43 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_IMPL_ARRAY_HPP
#define BOOST_JSON_DETAIL_IMPL_ARRAY_HPP
namespace boost {
namespace json {
namespace detail {
unchecked_array::
~unchecked_array()
{
if(! data_ ||
sp_.is_not_shared_and_deallocate_is_trivial())
return;
for(unsigned long i = 0;
i < size_; ++i)
data_[i].~value();
}
void
unchecked_array::
relocate(value* dest) noexcept
{
if(size_ > 0)
std::memcpy(
static_cast<void*>(dest),
data_, size_ * sizeof(value));
data_ = nullptr;
}
} // detail
} // namespace json
} // namespace boost
#endif
+68
View File
@@ -0,0 +1,68 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_IMPL_DEFAULT_RESOURCE_IPP
#define BOOST_JSON_DETAIL_IMPL_DEFAULT_RESOURCE_IPP
#include <boost/json/detail/default_resource.hpp>
namespace boost {
namespace json {
namespace detail {
#ifndef BOOST_JSON_WEAK_CONSTINIT
# ifndef BOOST_JSON_NO_DESTROY
BOOST_JSON_REQUIRE_CONST_INIT
default_resource::holder
default_resource::instance_;
# else
BOOST_JSON_REQUIRE_CONST_INIT
default_resource
default_resource::instance_;
# endif
#endif
// this is here so that ~memory_resource
// is emitted in the library instead of
// the user's TU.
default_resource::
~default_resource() = default;
void*
default_resource::
do_allocate(
std::size_t n,
std::size_t)
{
return ::operator new(n);
}
void
default_resource::
do_deallocate(
void* p,
std::size_t,
std::size_t)
{
::operator delete(p);
}
bool
default_resource::
do_is_equal(
memory_resource const& mr) const noexcept
{
return this == &mr;
}
} // detail
} // namespace json
} // namespace boost
#endif
+50
View File
@@ -0,0 +1,50 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_IMPL_EXCEPT_IPP
#define BOOST_JSON_DETAIL_IMPL_EXCEPT_IPP
#include <boost/json/detail/except.hpp>
#include <boost/version.hpp>
#include <boost/throw_exception.hpp>
#include <stdexcept>
namespace boost {
namespace json {
namespace detail {
void
throw_system_error(
error_code const& ec,
source_location const& loc)
{
throw_exception(
system_error(ec),
loc);
}
void
throw_system_error(
error e,
source_location const* loc)
{
error_code ec;
ec.assign(e, loc);
throw_exception(
system_error(ec),
*loc);
}
} // detail
} // namespace json
} // namespace boost
#endif
+125
View File
@@ -0,0 +1,125 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Peter Dimov (pdimov at gmail dot com),
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_IMPL_FORMAT_IPP
#define BOOST_JSON_DETAIL_IMPL_FORMAT_IPP
#include <boost/json/detail/ryu/ryu.hpp>
#include <cstring>
namespace boost {
namespace json {
namespace detail {
/* Reference work:
https://www.ampl.com/netlib/fp/dtoa.c
https://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
https://kkimdev.github.io/posts/2018/06/15/IEEE-754-Floating-Point-Type-in-C++.html
*/
inline char const* digits_lut() noexcept
{
return
"00010203040506070809"
"10111213141516171819"
"20212223242526272829"
"30313233343536373839"
"40414243444546474849"
"50515253545556575859"
"60616263646566676869"
"70717273747576777879"
"80818283848586878889"
"90919293949596979899";
}
inline void format_four_digits( char * dest, unsigned v )
{
std::memcpy( dest + 2, digits_lut() + (v % 100) * 2, 2 );
std::memcpy( dest , digits_lut() + (v / 100) * 2, 2 );
}
inline void format_two_digits( char * dest, unsigned v )
{
std::memcpy( dest, digits_lut() + v * 2, 2 );
}
inline void format_digit( char * dest, unsigned v )
{
*dest = static_cast<char>( v + '0' );
}
unsigned
format_uint64(
char* dest,
std::uint64_t v) noexcept
{
if(v < 10)
{
*dest = static_cast<char>( '0' + v );
return 1;
}
char buffer[ 24 ];
char * p = buffer + 24;
while( v >= 1000 )
{
p -= 4;
format_four_digits( p, v % 10000 );
v /= 10000;
}
if( v >= 10 )
{
p -= 2;
format_two_digits( p, v % 100 );
v /= 100;
}
if( v )
{
p -= 1;
format_digit( p, static_cast<unsigned>(v) );
}
unsigned const n = static_cast<unsigned>( buffer + 24 - p );
std::memcpy( dest, p, n );
return n;
}
unsigned
format_int64(
char* dest, int64_t i) noexcept
{
std::uint64_t ui = static_cast<
std::uint64_t>(i);
if(i >= 0)
return format_uint64(dest, ui);
*dest++ = '-';
ui = ~ui + 1;
return 1 + format_uint64(dest, ui);
}
unsigned
format_double(
char* dest, double d, bool allow_infinity_and_nan) noexcept
{
return static_cast<int>(
ryu::d2s_buffered_n(d, dest, allow_infinity_and_nan));
}
} // detail
} // namespace json
} // namespace boost
#endif
+204
View File
@@ -0,0 +1,204 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_IMPL_HANDLER_HPP
#define BOOST_JSON_DETAIL_IMPL_HANDLER_HPP
#include <boost/json/detail/handler.hpp>
#include <utility>
namespace boost {
namespace json {
namespace detail {
template<class... Args>
handler::
handler(Args&&... args)
: st(std::forward<Args>(args)...)
{
}
bool
handler::
on_document_begin(
error_code&)
{
return true;
}
bool
handler::
on_document_end(
error_code&)
{
return true;
}
bool
handler::
on_object_begin(
error_code&)
{
return true;
}
bool
handler::
on_object_end(
std::size_t n,
error_code&)
{
st.push_object(n);
return true;
}
bool
handler::
on_array_begin(
error_code&)
{
return true;
}
bool
handler::
on_array_end(
std::size_t n,
error_code&)
{
st.push_array(n);
return true;
}
bool
handler::
on_key_part(
string_view s,
std::size_t,
error_code&)
{
st.push_chars(s);
return true;
}
bool
handler::
on_key(
string_view s,
std::size_t,
error_code&)
{
st.push_key(s);
return true;
}
bool
handler::
on_string_part(
string_view s,
std::size_t,
error_code&)
{
st.push_chars(s);
return true;
}
bool
handler::
on_string(
string_view s,
std::size_t,
error_code&)
{
st.push_string(s);
return true;
}
bool
handler::
on_number_part(
string_view,
error_code&)
{
return true;
}
bool
handler::
on_int64(
std::int64_t i,
string_view,
error_code&)
{
st.push_int64(i);
return true;
}
bool
handler::
on_uint64(
std::uint64_t u,
string_view,
error_code&)
{
st.push_uint64(u);
return true;
}
bool
handler::
on_double(
double d,
string_view,
error_code&)
{
st.push_double(d);
return true;
}
bool
handler::
on_bool(
bool b,
error_code&)
{
st.push_bool(b);
return true;
}
bool
handler::
on_null(error_code&)
{
st.push_null();
return true;
}
bool
handler::
on_comment_part(
string_view,
error_code&)
{
return true;
}
bool
handler::
on_comment(
string_view, error_code&)
{
return true;
}
} // detail
} // namespace json
} // namespace boost
#endif
+37
View File
@@ -0,0 +1,37 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_IMPL_SHARED_RESOURCE_IPP
#define BOOST_JSON_DETAIL_IMPL_SHARED_RESOURCE_IPP
#include <boost/json/detail/shared_resource.hpp>
namespace boost {
namespace json {
namespace detail {
// these are here so that ~memory_resource
// is emitted in the library instead of
// the user's TU.
shared_resource::
shared_resource()
{
}
shared_resource::
~shared_resource()
{
}
} // detail
} // namespace json
} // namespace boost
#endif
+62
View File
@@ -0,0 +1,62 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_IMPL_STACK_IPP
#define BOOST_JSON_DETAIL_IMPL_STACK_IPP
#include <boost/json/detail/stack.hpp>
namespace boost {
namespace json {
namespace detail {
stack::
~stack()
{
if(base_ != buf_)
sp_->deallocate(
base_, cap_);
}
stack::
stack(
storage_ptr sp,
unsigned char* buf,
std::size_t buf_size) noexcept
: sp_(std::move(sp))
, cap_(buf_size)
, base_(buf)
, buf_(buf)
{
}
void
stack::
reserve(std::size_t n)
{
if(cap_ >= n)
return;
auto const base = static_cast<
unsigned char*>(sp_->allocate(n));
if(base_)
{
if(size_ > 0)
std::memcpy(base, base_, size_);
if(base_ != buf_)
sp_->deallocate(base_, cap_);
}
base_ = base;
cap_ = n;
}
} // detail
} // namespace json
} // namespace boost
#endif
+487
View File
@@ -0,0 +1,487 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_IMPL_STRING_IMPL_IPP
#define BOOST_JSON_DETAIL_IMPL_STRING_IMPL_IPP
#include <boost/json/detail/string_impl.hpp>
#include <boost/json/detail/except.hpp>
#include <cstring>
#include <functional>
namespace boost {
namespace json {
namespace detail {
inline
bool
ptr_in_range(
const char* first,
const char* last,
const char* ptr) noexcept
{
return std::less<const char*>()(ptr, last) &&
std::greater_equal<const char*>()(ptr, first);
}
string_impl::
string_impl() noexcept
{
s_.k = short_string_;
s_.buf[sbo_chars_] =
static_cast<char>(
sbo_chars_);
s_.buf[0] = 0;
}
string_impl::
string_impl(
std::size_t size,
storage_ptr const& sp)
{
if(size <= sbo_chars_)
{
s_.k = short_string_;
s_.buf[sbo_chars_] =
static_cast<char>(
sbo_chars_ - size);
s_.buf[size] = 0;
}
else
{
s_.k = kind::string;
auto const n = growth(
size, sbo_chars_ + 1);
p_.t = ::new(sp->allocate(
sizeof(table) +
n + 1,
alignof(table))) table{
static_cast<
std::uint32_t>(size),
static_cast<
std::uint32_t>(n)};
data()[n] = 0;
}
}
// construct a key, unchecked
string_impl::
string_impl(
key_t,
string_view s,
storage_ptr const& sp)
{
BOOST_ASSERT(
s.size() <= max_size());
k_.k = key_string_;
k_.n = static_cast<
std::uint32_t>(s.size());
k_.s = reinterpret_cast<char*>(
sp->allocate(s.size() + 1,
alignof(char)));
k_.s[s.size()] = 0; // null term
std::memcpy(&k_.s[0],
s.data(), s.size());
}
// construct a key, unchecked
string_impl::
string_impl(
key_t,
string_view s1,
string_view s2,
storage_ptr const& sp)
{
auto len = s1.size() + s2.size();
BOOST_ASSERT(len <= max_size());
k_.k = key_string_;
k_.n = static_cast<
std::uint32_t>(len);
k_.s = reinterpret_cast<char*>(
sp->allocate(len + 1,
alignof(char)));
k_.s[len] = 0; // null term
std::memcpy(&k_.s[0],
s1.data(), s1.size());
std::memcpy(&k_.s[s1.size()],
s2.data(), s2.size());
}
std::uint32_t
string_impl::
growth(
std::size_t new_size,
std::size_t capacity)
{
if(new_size > max_size())
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::string_too_large, &loc );
}
// growth factor 2
if( capacity >
max_size() - capacity)
return static_cast<
std::uint32_t>(max_size()); // overflow
return static_cast<std::uint32_t>(
(std::max)(capacity * 2, new_size));
}
char*
string_impl::
assign(
std::size_t new_size,
storage_ptr const& sp)
{
if(new_size > capacity())
{
string_impl tmp(growth(
new_size,
capacity()), sp);
destroy(sp);
*this = tmp;
}
term(new_size);
return data();
}
char*
string_impl::
append(
std::size_t n,
storage_ptr const& sp)
{
if(n > max_size() - size())
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::string_too_large, &loc );
}
if(n <= capacity() - size())
{
term(size() + n);
return end() - n;
}
string_impl tmp(growth(
size() + n, capacity()), sp);
std::memcpy(
tmp.data(), data(), size());
tmp.term(size() + n);
destroy(sp);
*this = tmp;
return end() - n;
}
void
string_impl::
insert(
std::size_t pos,
const char* s,
std::size_t n,
storage_ptr const& sp)
{
const auto curr_size = size();
if(pos > curr_size)
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::out_of_range, &loc );
}
const auto curr_data = data();
if(n <= capacity() - curr_size)
{
const bool inside = detail::ptr_in_range(curr_data, curr_data + curr_size, s);
if (!inside || (inside && ((s - curr_data) + n <= pos)))
{
std::memmove(&curr_data[pos + n], &curr_data[pos], curr_size - pos + 1);
std::memcpy(&curr_data[pos], s, n);
}
else
{
const std::size_t offset = s - curr_data;
std::memmove(&curr_data[pos + n], &curr_data[pos], curr_size - pos + 1);
if (offset < pos)
{
const std::size_t diff = pos - offset;
std::memcpy(&curr_data[pos], &curr_data[offset], diff);
std::memcpy(&curr_data[pos + diff], &curr_data[pos + n], n - diff);
}
else
{
std::memcpy(&curr_data[pos], &curr_data[offset + n], n);
}
}
size(curr_size + n);
}
else
{
if(n > max_size() - curr_size)
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::string_too_large, &loc );
}
string_impl tmp(growth(
curr_size + n, capacity()), sp);
tmp.size(curr_size + n);
std::memcpy(
tmp.data(),
curr_data,
pos);
std::memcpy(
tmp.data() + pos + n,
curr_data + pos,
curr_size + 1 - pos);
std::memcpy(
tmp.data() + pos,
s,
n);
destroy(sp);
*this = tmp;
}
}
char*
string_impl::
insert_unchecked(
std::size_t pos,
std::size_t n,
storage_ptr const& sp)
{
const auto curr_size = size();
if(pos > curr_size)
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::out_of_range, &loc );
}
const auto curr_data = data();
if(n <= capacity() - size())
{
auto const dest =
curr_data + pos;
std::memmove(
dest + n,
dest,
curr_size + 1 - pos);
size(curr_size + n);
return dest;
}
if(n > max_size() - curr_size)
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::string_too_large, &loc );
}
string_impl tmp(growth(
curr_size + n, capacity()), sp);
tmp.size(curr_size + n);
std::memcpy(
tmp.data(),
curr_data,
pos);
std::memcpy(
tmp.data() + pos + n,
curr_data + pos,
curr_size + 1 - pos);
destroy(sp);
*this = tmp;
return data() + pos;
}
void
string_impl::
replace(
std::size_t pos,
std::size_t n1,
const char* s,
std::size_t n2,
storage_ptr const& sp)
{
const auto curr_size = size();
if (pos > curr_size)
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::out_of_range, &loc );
}
const auto curr_data = data();
n1 = (std::min)(n1, curr_size - pos);
const auto delta = (std::max)(n1, n2) -
(std::min)(n1, n2);
// if we are shrinking in size or we have enough
// capacity, dont reallocate
if (n1 > n2 || delta <= capacity() - curr_size)
{
const bool inside = detail::ptr_in_range(curr_data, curr_data + curr_size, s);
// there is nothing to replace; return
if (inside && s == curr_data + pos && n1 == n2)
return;
if (!inside || (inside && ((s - curr_data) + n2 <= pos)))
{
// source outside
std::memmove(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
std::memcpy(&curr_data[pos], s, n2);
}
else
{
// source inside
const std::size_t offset = s - curr_data;
if (n2 >= n1)
{
// grow/unchanged
const std::size_t diff = offset <= pos + n1 ? (std::min)((pos + n1) - offset, n2) : 0;
// shift all right of splice point by n2 - n1 to the right
std::memmove(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
// copy all before splice point
std::memmove(&curr_data[pos], &curr_data[offset], diff);
// copy all after splice point
std::memmove(&curr_data[pos + diff], &curr_data[(offset - n1) + n2 + diff], n2 - diff);
}
else
{
// shrink
// copy all elements into place
std::memmove(&curr_data[pos], &curr_data[offset], n2);
// shift all elements after splice point left
std::memmove(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
}
}
size((curr_size - n1) + n2);
}
else
{
if (delta > max_size() - curr_size)
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::string_too_large, &loc );
}
// would exceed capacity, reallocate
string_impl tmp(growth(
curr_size + delta, capacity()), sp);
tmp.size(curr_size + delta);
std::memcpy(
tmp.data(),
curr_data,
pos);
std::memcpy(
tmp.data() + pos + n2,
curr_data + pos + n1,
curr_size - pos - n1 + 1);
std::memcpy(
tmp.data() + pos,
s,
n2);
destroy(sp);
*this = tmp;
}
}
// unlike the replace overload, this function does
// not move any characters
char*
string_impl::
replace_unchecked(
std::size_t pos,
std::size_t n1,
std::size_t n2,
storage_ptr const& sp)
{
const auto curr_size = size();
if(pos > curr_size)
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::out_of_range, &loc );
}
const auto curr_data = data();
const auto delta = (std::max)(n1, n2) -
(std::min)(n1, n2);
// if the size doesn't change, we don't need to
// do anything
if (!delta)
return curr_data + pos;
// if we are shrinking in size or we have enough
// capacity, dont reallocate
if(n1 > n2 || delta <= capacity() - curr_size)
{
auto const replace_pos = curr_data + pos;
std::memmove(
replace_pos + n2,
replace_pos + n1,
curr_size - pos - n1 + 1);
size((curr_size - n1) + n2);
return replace_pos;
}
if(delta > max_size() - curr_size)
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::string_too_large, &loc );
}
// would exceed capacity, reallocate
string_impl tmp(growth(
curr_size + delta, capacity()), sp);
tmp.size(curr_size + delta);
std::memcpy(
tmp.data(),
curr_data,
pos);
std::memcpy(
tmp.data() + pos + n2,
curr_data + pos + n1,
curr_size - pos - n1 + 1);
destroy(sp);
*this = tmp;
return data() + pos;
}
void
string_impl::
shrink_to_fit(
storage_ptr const& sp) noexcept
{
if(s_.k == short_string_)
return;
auto const t = p_.t;
if(t->size <= sbo_chars_)
{
s_.k = short_string_;
std::memcpy(
s_.buf, data(), t->size);
s_.buf[sbo_chars_] =
static_cast<char>(
sbo_chars_ - t->size);
s_.buf[t->size] = 0;
sp->deallocate(t,
sizeof(table) +
t->capacity + 1,
alignof(table));
return;
}
if(t->size >= t->capacity)
return;
#ifndef BOOST_NO_EXCEPTIONS
try
{
#endif
string_impl tmp(t->size, sp);
std::memcpy(
tmp.data(),
data(),
size());
destroy(sp);
*this = tmp;
#ifndef BOOST_NO_EXCEPTIONS
}
catch(std::exception const&)
{
// eat the exception
}
#endif
}
} // detail
} // namespace json
} // namespace boost
#endif
+96
View File
@@ -0,0 +1,96 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_OBJECT_HPP
#define BOOST_JSON_DETAIL_OBJECT_HPP
#include <boost/json/storage_ptr.hpp>
#include <boost/json/string_view.hpp>
#include <cstdlib>
namespace boost {
namespace json {
class object;
class value;
class key_value_pair;
namespace detail {
class unchecked_object
{
// each element is two values,
// first one is a string key,
// second one is the value.
value* data_;
std::size_t size_;
storage_ptr const& sp_;
public:
inline
~unchecked_object();
unchecked_object(
value* data,
std::size_t size, // # of kv-pairs
storage_ptr const& sp) noexcept
: data_(data)
, size_(size)
, sp_(sp)
{
}
unchecked_object(
unchecked_object&& other) noexcept
: data_(other.data_)
, size_(other.size_)
, sp_(other.sp_)
{
other.data_ = nullptr;
}
storage_ptr const&
storage() const noexcept
{
return sp_;
}
std::size_t
size() const noexcept
{
return size_;
}
value*
release() noexcept
{
auto const data = data_;
data_ = nullptr;
return data;
}
};
template<class CharRange>
std::pair<key_value_pair*, std::size_t>
find_in_object(
object const& obj,
CharRange key) noexcept;
extern template
BOOST_JSON_DECL
std::pair<key_value_pair*, std::size_t>
find_in_object<string_view>(
object const&,
string_view key) noexcept;
} // detail
} // namespace json
} // namespace boost
#endif
File diff suppressed because it is too large Load Diff
+144
View File
@@ -0,0 +1,144 @@
// Copyright 2018 Ulf Adams
//
// The contents of this file may be used under the terms of the Apache License,
// Version 2.0.
//
// (See accompanying file LICENSE-Apache or copy at
// http://www.apache.org/licenses/LICENSE-2.0)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
// (See accompanying file LICENSE-Boost or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.
/*
This is a derivative work
*/
#ifndef BOOST_JSON_DETAIL_RYU_DETAIL_COMMON_HPP
#define BOOST_JSON_DETAIL_RYU_DETAIL_COMMON_HPP
#include <boost/json/detail/config.hpp>
#include <string.h>
namespace boost {
namespace json {
namespace detail {
namespace ryu {
namespace detail {
constexpr int DOUBLE_MANTISSA_BITS = 52;
constexpr int DOUBLE_EXPONENT_BITS = 11;
constexpr int DOUBLE_BIAS = 1023;
#if defined(_M_IX86) || defined(_M_ARM)
#define BOOST_JSON_RYU_32_BIT_PLATFORM
#endif
inline uint32_t decimalLength9(const uint32_t v) {
// Function precondition: v is not a 10-digit number.
// (f2s: 9 digits are sufficient for round-tripping.)
// (d2fixed: We print 9-digit blocks.)
BOOST_ASSERT(v < 1000000000);
if (v >= 100000000) { return 9; }
if (v >= 10000000) { return 8; }
if (v >= 1000000) { return 7; }
if (v >= 100000) { return 6; }
if (v >= 10000) { return 5; }
if (v >= 1000) { return 4; }
if (v >= 100) { return 3; }
if (v >= 10) { return 2; }
return 1;
}
// Returns e == 0 ? 1 : ceil(log_2(5^e)).
inline int32_t pow5bits(const int32_t e) {
// This approximation works up to the point that the multiplication overflows at e = 3529.
// If the multiplication were done in 64 bits, it would fail at 5^4004 which is just greater
// than 2^9297.
BOOST_ASSERT(e >= 0);
BOOST_ASSERT(e <= 3528);
return (int32_t) (((((uint32_t) e) * 1217359) >> 19) + 1);
}
// Returns floor(log_10(2^e)).
inline uint32_t log10Pow2(const int32_t e) {
// The first value this approximation fails for is 2^1651 which is just greater than 10^297.
BOOST_ASSERT(e >= 0);
BOOST_ASSERT(e <= 1650);
return (((uint32_t) e) * 78913) >> 18;
}
// Returns floor(log_10(5^e)).
inline uint32_t log10Pow5(const int32_t e) {
// The first value this approximation fails for is 5^2621 which is just greater than 10^1832.
BOOST_ASSERT(e >= 0);
BOOST_ASSERT(e <= 2620);
return (((uint32_t) e) * 732923) >> 20;
}
inline int copy_special_str(char * const result, const bool sign, const bool exponent, const bool mantissa) {
if (mantissa) {
memcpy(result, "NaN", 3);
return 3;
}
if (sign) {
result[0] = '-';
}
if (exponent) {
memcpy(result + sign, "Infinity", 8);
return sign + 8;
}
memcpy(result + sign, "0E0", 3);
return sign + 3;
}
inline
int
copy_special_str_conforming(
char* const result, bool sign, bool exponent, bool mantissa)
{
if (mantissa)
{
memcpy(result, "null", 4);
return 4;
}
if (sign)
result[0] = '-';
if (exponent)
{
memcpy(result + sign, "1e99999", 7);
return sign + 7;
}
memcpy(result + sign, "0E0", 3);
return sign + 3;
}
inline uint32_t float_to_bits(const float f) {
uint32_t bits = 0;
memcpy(&bits, &f, sizeof(float));
return bits;
}
inline uint64_t double_to_bits(const double d) {
uint64_t bits = 0;
memcpy(&bits, &d, sizeof(double));
return bits;
}
} // detail
} // ryu
} // detail
} // namespace json
} // namespace boost
#endif
+264
View File
@@ -0,0 +1,264 @@
// Copyright 2018 Ulf Adams
//
// The contents of this file may be used under the terms of the Apache License,
// Version 2.0.
//
// (See accompanying file LICENSE-Apache or copy at
// http://www.apache.org/licenses/LICENSE-2.0)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
// (See accompanying file LICENSE-Boost or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.
/*
This is a derivative work
*/
#ifndef BOOST_JSON_DETAIL_RYU_DETAIL_D2S_HPP
#define BOOST_JSON_DETAIL_RYU_DETAIL_D2S_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/detail/ryu/detail/common.hpp>
// Only include the full table if we're not optimizing for size.
#if !defined(BOOST_JSON_RYU_OPTIMIZE_SIZE)
#include <boost/json/detail/ryu/detail/d2s_full_table.hpp>
#endif
#if defined(BOOST_JSON_RYU_HAS_UINT128)
typedef __uint128_t uint128_t;
#else
#include <boost/json/detail/ryu/detail/d2s_intrinsics.hpp>
#endif
namespace boost {
namespace json {
namespace detail {
namespace ryu {
namespace detail {
constexpr int DOUBLE_POW5_INV_BITCOUNT = 122;
constexpr int DOUBLE_POW5_BITCOUNT = 121;
#if defined(BOOST_JSON_RYU_OPTIMIZE_SIZE)
constexpr int POW5_TABLE_SIZE = 26;
inline
std::uint64_t const
(&DOUBLE_POW5_TABLE() noexcept)[POW5_TABLE_SIZE]
{
static constexpr std::uint64_t arr[26] = {
1ull, 5ull, 25ull, 125ull, 625ull, 3125ull, 15625ull, 78125ull, 390625ull,
1953125ull, 9765625ull, 48828125ull, 244140625ull, 1220703125ull, 6103515625ull,
30517578125ull, 152587890625ull, 762939453125ull, 3814697265625ull,
19073486328125ull, 95367431640625ull, 476837158203125ull,
2384185791015625ull, 11920928955078125ull, 59604644775390625ull,
298023223876953125ull //, 1490116119384765625ull
};
return arr;
}
inline
std::uint64_t const
(&DOUBLE_POW5_SPLIT2() noexcept)[13][2]
{
static constexpr std::uint64_t arr[13][2] = {
{ 0u, 72057594037927936u },
{ 10376293541461622784u, 93132257461547851u },
{ 15052517733678820785u, 120370621524202240u },
{ 6258995034005762182u, 77787690973264271u },
{ 14893927168346708332u, 100538234169297439u },
{ 4272820386026678563u, 129942622070561240u },
{ 7330497575943398595u, 83973451344588609u },
{ 18377130505971182927u, 108533142064701048u },
{ 10038208235822497557u, 140275798336537794u },
{ 7017903361312433648u, 90651109995611182u },
{ 6366496589810271835u, 117163813585596168u },
{ 9264989777501460624u, 75715339914673581u },
{ 17074144231291089770u, 97859783203563123u }};
return arr;
}
// Unfortunately, the results are sometimes off by one. We use an additional
// lookup table to store those cases and adjust the result.
inline
std::uint32_t const
(&POW5_OFFSETS() noexcept)[13]
{
static constexpr std::uint32_t arr[13] = {
0x00000000, 0x00000000, 0x00000000, 0x033c55be, 0x03db77d8, 0x0265ffb2,
0x00000800, 0x01a8ff56, 0x00000000, 0x0037a200, 0x00004000, 0x03fffffc,
0x00003ffe};
return arr;
}
inline
std::uint64_t const
(&DOUBLE_POW5_INV_SPLIT2() noexcept)[13][2]
{
static constexpr std::uint64_t arr[13][2] = {
{ 1u, 288230376151711744u },
{ 7661987648932456967u, 223007451985306231u },
{ 12652048002903177473u, 172543658669764094u },
{ 5522544058086115566u, 266998379490113760u },
{ 3181575136763469022u, 206579990246952687u },
{ 4551508647133041040u, 159833525776178802u },
{ 1116074521063664381u, 247330401473104534u },
{ 17400360011128145022u, 191362629322552438u },
{ 9297997190148906106u, 148059663038321393u },
{ 11720143854957885429u, 229111231347799689u },
{ 15401709288678291155u, 177266229209635622u },
{ 3003071137298187333u, 274306203439684434u },
{ 17516772882021341108u, 212234145163966538u }};
return arr;
}
inline
std::uint32_t const
(&POW5_INV_OFFSETS() noexcept)[20]
{
static constexpr std::uint32_t arr[20] = {
0x51505404, 0x55054514, 0x45555545, 0x05511411, 0x00505010, 0x00000004,
0x00000000, 0x00000000, 0x55555040, 0x00505051, 0x00050040, 0x55554000,
0x51659559, 0x00001000, 0x15000010, 0x55455555, 0x41404051, 0x00001010,
0x00000014, 0x00000000};
return arr;
}
#if defined(BOOST_JSON_RYU_HAS_UINT128)
// Computes 5^i in the form required by Ryu, and stores it in the given pointer.
inline
void
double_computePow5(
const std::uint32_t i,
std::uint64_t* const result)
{
const std::uint32_t base = i / POW5_TABLE_SIZE;
const std::uint32_t base2 = base * POW5_TABLE_SIZE;
const std::uint32_t offset = i - base2;
const std::uint64_t* const mul = DOUBLE_POW5_SPLIT2()[base];
if (offset == 0)
{
result[0] = mul[0];
result[1] = mul[1];
return;
}
const std::uint64_t m = DOUBLE_POW5_TABLE()[offset];
const uint128_t b0 = ((uint128_t)m) * mul[0];
const uint128_t b2 = ((uint128_t)m) * mul[1];
const std::uint32_t delta = pow5bits(i) - pow5bits(base2);
const uint128_t shiftedSum = (b0 >> delta) + (b2 << (64 - delta)) + ((POW5_OFFSETS()[base] >> offset) & 1);
result[0] = (std::uint64_t)shiftedSum;
result[1] = (std::uint64_t)(shiftedSum >> 64);
}
// Computes 5^-i in the form required by Ryu, and stores it in the given pointer.
inline
void
double_computeInvPow5(
const std::uint32_t i,
std::uint64_t* const result)
{
const std::uint32_t base = (i + POW5_TABLE_SIZE - 1) / POW5_TABLE_SIZE;
const std::uint32_t base2 = base * POW5_TABLE_SIZE;
const std::uint32_t offset = base2 - i;
const std::uint64_t* const mul = DOUBLE_POW5_INV_SPLIT2()[base]; // 1/5^base2
if (offset == 0)
{
result[0] = mul[0];
result[1] = mul[1];
return;
}
const std::uint64_t m = DOUBLE_POW5_TABLE()[offset]; // 5^offset
const uint128_t b0 = ((uint128_t)m) * (mul[0] - 1);
const uint128_t b2 = ((uint128_t)m) * mul[1]; // 1/5^base2 * 5^offset = 1/5^(base2-offset) = 1/5^i
const std::uint32_t delta = pow5bits(base2) - pow5bits(i);
const uint128_t shiftedSum =
((b0 >> delta) + (b2 << (64 - delta))) + 1 + ((POW5_INV_OFFSETS()[i / 16] >> ((i % 16) << 1)) & 3);
result[0] = (std::uint64_t)shiftedSum;
result[1] = (std::uint64_t)(shiftedSum >> 64);
}
#else // defined(BOOST_JSON_RYU_HAS_UINT128)
// Computes 5^i in the form required by Ryu, and stores it in the given pointer.
inline
void
double_computePow5(
const std::uint32_t i,
std::uint64_t* const result)
{
const std::uint32_t base = i / POW5_TABLE_SIZE;
const std::uint32_t base2 = base * POW5_TABLE_SIZE;
const std::uint32_t offset = i - base2;
const std::uint64_t* const mul = DOUBLE_POW5_SPLIT2()[base];
if (offset == 0)
{
result[0] = mul[0];
result[1] = mul[1];
return;
}
std::uint64_t const m = DOUBLE_POW5_TABLE()[offset];
std::uint64_t high1;
std::uint64_t const low1 = umul128(m, mul[1], &high1);
std::uint64_t high0;
std::uint64_t const low0 = umul128(m, mul[0], &high0);
std::uint64_t const sum = high0 + low1;
if (sum < high0)
++high1; // overflow into high1
// high1 | sum | low0
std::uint32_t const delta = pow5bits(i) - pow5bits(base2);
result[0] = shiftright128(low0, sum, delta) + ((POW5_OFFSETS()[base] >> offset) & 1);
result[1] = shiftright128(sum, high1, delta);
}
// Computes 5^-i in the form required by Ryu, and stores it in the given pointer.
inline
void
double_computeInvPow5(
const std::uint32_t i,
std::uint64_t* const result)
{
const std::uint32_t base = (i + POW5_TABLE_SIZE - 1) / POW5_TABLE_SIZE;
const std::uint32_t base2 = base * POW5_TABLE_SIZE;
const std::uint32_t offset = base2 - i;
const std::uint64_t* const mul = DOUBLE_POW5_INV_SPLIT2()[base]; // 1/5^base2
if (offset == 0)
{
result[0] = mul[0];
result[1] = mul[1];
return;
}
std::uint64_t const m = DOUBLE_POW5_TABLE()[offset];
std::uint64_t high1;
std::uint64_t const low1 = umul128(m, mul[1], &high1);
std::uint64_t high0;
std::uint64_t const low0 = umul128(m, mul[0] - 1, &high0);
std::uint64_t const sum = high0 + low1;
if (sum < high0)
++high1; // overflow into high1
// high1 | sum | low0
std::uint32_t const delta = pow5bits(base2) - pow5bits(i);
result[0] = shiftright128(low0, sum, delta) + 1 + ((POW5_INV_OFFSETS()[i / 16] >> ((i % 16) << 1)) & 3);
result[1] = shiftright128(sum, high1, delta);
}
#endif // defined(BOOST_JSON_RYU_HAS_UINT128)
#endif // defined(BOOST_JSON_RYU_OPTIMIZE_SIZE)
} // detail
} // ryu
} // detail
} // namespace json
} // namespace boost
#endif
+365
View File
@@ -0,0 +1,365 @@
// Copyright 2018 Ulf Adams
//
// The contents of this file may be used under the terms of the Apache License,
// Version 2.0.
//
// (See accompanying file LICENSE-Apache or copy at
// http://www.apache.org/licenses/LICENSE-2.0)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
// (See accompanying file LICENSE-Boost or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.
/*
This is a derivative work
*/
#ifndef BOOST_JSON_DETAIL_RYU_DETAIL_D2S_FULL_TABLE_HPP
#define BOOST_JSON_DETAIL_RYU_DETAIL_D2S_FULL_TABLE_HPP
#include <boost/json/detail/config.hpp>
namespace boost {
namespace json {
namespace detail {
namespace ryu {
// These tables are generated by PrintDoubleLookupTable.
inline
std::uint64_t const
(&DOUBLE_POW5_INV_SPLIT() noexcept)[292][2]
{
static constexpr std::uint64_t arr[292][2] = {
{ 1u, 288230376151711744u }, { 3689348814741910324u, 230584300921369395u },
{ 2951479051793528259u, 184467440737095516u }, { 17118578500402463900u, 147573952589676412u },
{ 12632330341676300947u, 236118324143482260u }, { 10105864273341040758u, 188894659314785808u },
{ 15463389048156653253u, 151115727451828646u }, { 17362724847566824558u, 241785163922925834u },
{ 17579528692795369969u, 193428131138340667u }, { 6684925324752475329u, 154742504910672534u },
{ 18074578149087781173u, 247588007857076054u }, { 18149011334012135262u, 198070406285660843u },
{ 3451162622983977240u, 158456325028528675u }, { 5521860196774363583u, 253530120045645880u },
{ 4417488157419490867u, 202824096036516704u }, { 7223339340677503017u, 162259276829213363u },
{ 7867994130342094503u, 259614842926741381u }, { 2605046489531765280u, 207691874341393105u },
{ 2084037191625412224u, 166153499473114484u }, { 10713157136084480204u, 265845599156983174u },
{ 12259874523609494487u, 212676479325586539u }, { 13497248433629505913u, 170141183460469231u },
{ 14216899864323388813u, 272225893536750770u }, { 11373519891458711051u, 217780714829400616u },
{ 5409467098425058518u, 174224571863520493u }, { 4965798542738183305u, 278759314981632789u },
{ 7661987648932456967u, 223007451985306231u }, { 2440241304404055250u, 178405961588244985u },
{ 3904386087046488400u, 285449538541191976u }, { 17880904128604832013u, 228359630832953580u },
{ 14304723302883865611u, 182687704666362864u }, { 15133127457049002812u, 146150163733090291u },
{ 16834306301794583852u, 233840261972944466u }, { 9778096226693756759u, 187072209578355573u },
{ 15201174610838826053u, 149657767662684458u }, { 2185786488890659746u, 239452428260295134u },
{ 5437978005854438120u, 191561942608236107u }, { 15418428848909281466u, 153249554086588885u },
{ 6222742084545298729u, 245199286538542217u }, { 16046240111861969953u, 196159429230833773u },
{ 1768945645263844993u, 156927543384667019u }, { 10209010661905972635u, 251084069415467230u },
{ 8167208529524778108u, 200867255532373784u }, { 10223115638361732810u, 160693804425899027u },
{ 1599589762411131202u, 257110087081438444u }, { 4969020624670815285u, 205688069665150755u },
{ 3975216499736652228u, 164550455732120604u }, { 13739044029062464211u, 263280729171392966u },
{ 7301886408508061046u, 210624583337114373u }, { 13220206756290269483u, 168499666669691498u },
{ 17462981995322520850u, 269599466671506397u }, { 6591687966774196033u, 215679573337205118u },
{ 12652048002903177473u, 172543658669764094u }, { 9175230360419352987u, 276069853871622551u },
{ 3650835473593572067u, 220855883097298041u }, { 17678063637842498946u, 176684706477838432u },
{ 13527506561580357021u, 282695530364541492u }, { 3443307619780464970u, 226156424291633194u },
{ 6443994910566282300u, 180925139433306555u }, { 5155195928453025840u, 144740111546645244u },
{ 15627011115008661990u, 231584178474632390u }, { 12501608892006929592u, 185267342779705912u },
{ 2622589484121723027u, 148213874223764730u }, { 4196143174594756843u, 237142198758023568u },
{ 10735612169159626121u, 189713759006418854u }, { 12277838550069611220u, 151771007205135083u },
{ 15955192865369467629u, 242833611528216133u }, { 1696107848069843133u, 194266889222572907u },
{ 12424932722681605476u, 155413511378058325u }, { 1433148282581017146u, 248661618204893321u },
{ 15903913885032455010u, 198929294563914656u }, { 9033782293284053685u, 159143435651131725u },
{ 14454051669254485895u, 254629497041810760u }, { 11563241335403588716u, 203703597633448608u },
{ 16629290697806691620u, 162962878106758886u }, { 781423413297334329u, 260740604970814219u },
{ 4314487545379777786u, 208592483976651375u }, { 3451590036303822229u, 166873987181321100u },
{ 5522544058086115566u, 266998379490113760u }, { 4418035246468892453u, 213598703592091008u },
{ 10913125826658934609u, 170878962873672806u }, { 10082303693170474728u, 273406340597876490u },
{ 8065842954536379782u, 218725072478301192u }, { 17520720807854834795u, 174980057982640953u },
{ 5897060404116273733u, 279968092772225526u }, { 1028299508551108663u, 223974474217780421u },
{ 15580034865808528224u, 179179579374224336u }, { 17549358155809824511u, 286687326998758938u },
{ 2971440080422128639u, 229349861599007151u }, { 17134547323305344204u, 183479889279205720u },
{ 13707637858644275364u, 146783911423364576u }, { 14553522944347019935u, 234854258277383322u },
{ 4264120725993795302u, 187883406621906658u }, { 10789994210278856888u, 150306725297525326u },
{ 9885293106962350374u, 240490760476040522u }, { 529536856086059653u, 192392608380832418u },
{ 7802327114352668369u, 153914086704665934u }, { 1415676938738538420u, 246262538727465495u },
{ 1132541550990830736u, 197010030981972396u }, { 15663428499760305882u, 157608024785577916u },
{ 17682787970132668764u, 252172839656924666u }, { 10456881561364224688u, 201738271725539733u },
{ 15744202878575200397u, 161390617380431786u }, { 17812026976236499989u, 258224987808690858u },
{ 3181575136763469022u, 206579990246952687u }, { 13613306553636506187u, 165263992197562149u },
{ 10713244041592678929u, 264422387516099439u }, { 12259944048016053467u, 211537910012879551u },
{ 6118606423670932450u, 169230328010303641u }, { 2411072648389671274u, 270768524816485826u },
{ 16686253377679378312u, 216614819853188660u }, { 13349002702143502650u, 173291855882550928u },
{ 17669055508687693916u, 277266969412081485u }, { 14135244406950155133u, 221813575529665188u },
{ 240149081334393137u, 177450860423732151u }, { 11452284974360759988u, 283921376677971441u },
{ 5472479164746697667u, 227137101342377153u }, { 11756680961281178780u, 181709681073901722u },
{ 2026647139541122378u, 145367744859121378u }, { 18000030682233437097u, 232588391774594204u },
{ 18089373360528660001u, 186070713419675363u }, { 3403452244197197031u, 148856570735740291u },
{ 16513570034941246220u, 238170513177184465u }, { 13210856027952996976u, 190536410541747572u },
{ 3189987192878576934u, 152429128433398058u }, { 1414630693863812771u, 243886605493436893u },
{ 8510402184574870864u, 195109284394749514u }, { 10497670562401807014u, 156087427515799611u },
{ 9417575270359070576u, 249739884025279378u }, { 14912757845771077107u, 199791907220223502u },
{ 4551508647133041040u, 159833525776178802u }, { 10971762650154775986u, 255733641241886083u },
{ 16156107749607641435u, 204586912993508866u }, { 9235537384944202825u, 163669530394807093u },
{ 11087511001168814197u, 261871248631691349u }, { 12559357615676961681u, 209496998905353079u },
{ 13736834907283479668u, 167597599124282463u }, { 18289587036911657145u, 268156158598851941u },
{ 10942320814787415393u, 214524926879081553u }, { 16132554281313752961u, 171619941503265242u },
{ 11054691591134363444u, 274591906405224388u }, { 16222450902391311402u, 219673525124179510u },
{ 12977960721913049122u, 175738820099343608u }, { 17075388340318968271u, 281182112158949773u },
{ 2592264228029443648u, 224945689727159819u }, { 5763160197165465241u, 179956551781727855u },
{ 9221056315464744386u, 287930482850764568u }, { 14755542681855616155u, 230344386280611654u },
{ 15493782960226403247u, 184275509024489323u }, { 1326979923955391628u, 147420407219591459u },
{ 9501865507812447252u, 235872651551346334u }, { 11290841220991868125u, 188698121241077067u },
{ 1653975347309673853u, 150958496992861654u }, { 10025058185179298811u, 241533595188578646u },
{ 4330697733401528726u, 193226876150862917u }, { 14532604630946953951u, 154581500920690333u },
{ 1116074521063664381u, 247330401473104534u }, { 4582208431592841828u, 197864321178483627u },
{ 14733813189500004432u, 158291456942786901u }, { 16195403473716186445u, 253266331108459042u },
{ 5577625149489128510u, 202613064886767234u }, { 8151448934333213131u, 162090451909413787u },
{ 16731667109675051333u, 259344723055062059u }, { 17074682502481951390u, 207475778444049647u },
{ 6281048372501740465u, 165980622755239718u }, { 6360328581260874421u, 265568996408383549u },
{ 8777611679750609860u, 212455197126706839u }, { 10711438158542398211u, 169964157701365471u },
{ 9759603424184016492u, 271942652322184754u }, { 11497031554089123517u, 217554121857747803u },
{ 16576322872755119460u, 174043297486198242u }, { 11764721337440549842u, 278469275977917188u },
{ 16790474699436260520u, 222775420782333750u }, { 13432379759549008416u, 178220336625867000u },
{ 3045063541568861850u, 285152538601387201u }, { 17193446092222730773u, 228122030881109760u },
{ 13754756873778184618u, 182497624704887808u }, { 18382503128506368341u, 145998099763910246u },
{ 3586563302416817083u, 233596959622256395u }, { 2869250641933453667u, 186877567697805116u },
{ 17052795772514404226u, 149502054158244092u }, { 12527077977055405469u, 239203286653190548u },
{ 17400360011128145022u, 191362629322552438u }, { 2852241564676785048u, 153090103458041951u },
{ 15631632947708587046u, 244944165532867121u }, { 8815957543424959314u, 195955332426293697u },
{ 18120812478965698421u, 156764265941034957u }, { 14235904707377476180u, 250822825505655932u },
{ 4010026136418160298u, 200658260404524746u }, { 17965416168102169531u, 160526608323619796u },
{ 2919224165770098987u, 256842573317791675u }, { 2335379332616079190u, 205474058654233340u },
{ 1868303466092863352u, 164379246923386672u }, { 6678634360490491686u, 263006795077418675u },
{ 5342907488392393349u, 210405436061934940u }, { 4274325990713914679u, 168324348849547952u },
{ 10528270399884173809u, 269318958159276723u }, { 15801313949391159694u, 215455166527421378u },
{ 1573004715287196786u, 172364133221937103u }, { 17274202803427156150u, 275782613155099364u },
{ 17508711057483635243u, 220626090524079491u }, { 10317620031244997871u, 176500872419263593u },
{ 12818843235250086271u, 282401395870821749u }, { 13944423402941979340u, 225921116696657399u },
{ 14844887537095493795u, 180736893357325919u }, { 15565258844418305359u, 144589514685860735u },
{ 6457670077359736959u, 231343223497377177u }, { 16234182506113520537u, 185074578797901741u },
{ 9297997190148906106u, 148059663038321393u }, { 11187446689496339446u, 236895460861314229u },
{ 12639306166338981880u, 189516368689051383u }, { 17490142562555006151u, 151613094951241106u },
{ 2158786396894637579u, 242580951921985771u }, { 16484424376483351356u, 194064761537588616u },
{ 9498190686444770762u, 155251809230070893u }, { 11507756283569722895u, 248402894768113429u },
{ 12895553841597688639u, 198722315814490743u }, { 17695140702761971558u, 158977852651592594u },
{ 17244178680193423523u, 254364564242548151u }, { 10105994129412828495u, 203491651394038521u },
{ 4395446488788352473u, 162793321115230817u }, { 10722063196803274280u, 260469313784369307u },
{ 1198952927958798777u, 208375451027495446u }, { 15716557601334680315u, 166700360821996356u },
{ 17767794532651667857u, 266720577315194170u }, { 14214235626121334286u, 213376461852155336u },
{ 7682039686155157106u, 170701169481724269u }, { 1223217053622520399u, 273121871170758831u },
{ 15735968901865657612u, 218497496936607064u }, { 16278123936234436413u, 174797997549285651u },
{ 219556594781725998u, 279676796078857043u }, { 7554342905309201445u, 223741436863085634u },
{ 9732823138989271479u, 178993149490468507u }, { 815121763415193074u, 286389039184749612u },
{ 11720143854957885429u, 229111231347799689u }, { 13065463898708218666u, 183288985078239751u },
{ 6763022304224664610u, 146631188062591801u }, { 3442138057275642729u, 234609900900146882u },
{ 13821756890046245153u, 187687920720117505u }, { 11057405512036996122u, 150150336576094004u },
{ 6623802375033462826u, 240240538521750407u }, { 16367088344252501231u, 192192430817400325u },
{ 13093670675402000985u, 153753944653920260u }, { 2503129006933649959u, 246006311446272417u },
{ 13070549649772650937u, 196805049157017933u }, { 17835137349301941396u, 157444039325614346u },
{ 2710778055689733971u, 251910462920982955u }, { 2168622444551787177u, 201528370336786364u },
{ 5424246770383340065u, 161222696269429091u }, { 1300097203129523457u, 257956314031086546u },
{ 15797473021471260058u, 206365051224869236u }, { 8948629602435097724u, 165092040979895389u },
{ 3249760919670425388u, 264147265567832623u }, { 9978506365220160957u, 211317812454266098u },
{ 15361502721659949412u, 169054249963412878u }, { 2442311466204457120u, 270486799941460606u },
{ 16711244431931206989u, 216389439953168484u }, { 17058344360286875914u, 173111551962534787u },
{ 12535955717491360170u, 276978483140055660u }, { 10028764573993088136u, 221582786512044528u },
{ 15401709288678291155u, 177266229209635622u }, { 9885339602917624555u, 283625966735416996u },
{ 4218922867592189321u, 226900773388333597u }, { 14443184738299482427u, 181520618710666877u },
{ 4175850161155765295u, 145216494968533502u }, { 10370709072591134795u, 232346391949653603u },
{ 15675264887556728482u, 185877113559722882u }, { 5161514280561562140u, 148701690847778306u },
{ 879725219414678777u, 237922705356445290u }, { 703780175531743021u, 190338164285156232u },
{ 11631070584651125387u, 152270531428124985u }, { 162968861732249003u, 243632850284999977u },
{ 11198421533611530172u, 194906280227999981u }, { 5269388412147313814u, 155925024182399985u },
{ 8431021459435702103u, 249480038691839976u }, { 3055468352806651359u, 199584030953471981u },
{ 17201769941212962380u, 159667224762777584u }, { 16454785461715008838u, 255467559620444135u },
{ 13163828369372007071u, 204374047696355308u }, { 17909760324981426303u, 163499238157084246u },
{ 2830174816776909822u, 261598781051334795u }, { 2264139853421527858u, 209279024841067836u },
{ 16568707141704863579u, 167423219872854268u }, { 4373838538276319787u, 267877151796566830u },
{ 3499070830621055830u, 214301721437253464u }, { 6488605479238754987u, 171441377149802771u },
{ 3003071137298187333u, 274306203439684434u }, { 6091805724580460189u, 219444962751747547u },
{ 15941491023890099121u, 175555970201398037u }, { 10748990379256517301u, 280889552322236860u },
{ 8599192303405213841u, 224711641857789488u }, { 14258051472207991719u, 179769313486231590u }};
return arr;
}
inline
std::uint64_t const
(&DOUBLE_POW5_SPLIT() noexcept)[326][2]
{
static constexpr std::uint64_t arr[326][2] = {
{ 0u, 72057594037927936u }, { 0u, 90071992547409920u },
{ 0u, 112589990684262400u }, { 0u, 140737488355328000u },
{ 0u, 87960930222080000u }, { 0u, 109951162777600000u },
{ 0u, 137438953472000000u }, { 0u, 85899345920000000u },
{ 0u, 107374182400000000u }, { 0u, 134217728000000000u },
{ 0u, 83886080000000000u }, { 0u, 104857600000000000u },
{ 0u, 131072000000000000u }, { 0u, 81920000000000000u },
{ 0u, 102400000000000000u }, { 0u, 128000000000000000u },
{ 0u, 80000000000000000u }, { 0u, 100000000000000000u },
{ 0u, 125000000000000000u }, { 0u, 78125000000000000u },
{ 0u, 97656250000000000u }, { 0u, 122070312500000000u },
{ 0u, 76293945312500000u }, { 0u, 95367431640625000u },
{ 0u, 119209289550781250u }, { 4611686018427387904u, 74505805969238281u },
{ 10376293541461622784u, 93132257461547851u }, { 8358680908399640576u, 116415321826934814u },
{ 612489549322387456u, 72759576141834259u }, { 14600669991935148032u, 90949470177292823u },
{ 13639151471491547136u, 113686837721616029u }, { 3213881284082270208u, 142108547152020037u },
{ 4314518811765112832u, 88817841970012523u }, { 781462496279003136u, 111022302462515654u },
{ 10200200157203529728u, 138777878078144567u }, { 13292654125893287936u, 86736173798840354u },
{ 7392445620511834112u, 108420217248550443u }, { 4628871007212404736u, 135525271560688054u },
{ 16728102434789916672u, 84703294725430033u }, { 7075069988205232128u, 105879118406787542u },
{ 18067209522111315968u, 132348898008484427u }, { 8986162942105878528u, 82718061255302767u },
{ 6621017659204960256u, 103397576569128459u }, { 3664586055578812416u, 129246970711410574u },
{ 16125424340018921472u, 80779356694631608u }, { 1710036351314100224u, 100974195868289511u },
{ 15972603494424788992u, 126217744835361888u }, { 9982877184015493120u, 78886090522101180u },
{ 12478596480019366400u, 98607613152626475u }, { 10986559581596820096u, 123259516440783094u },
{ 2254913720070624656u, 77037197775489434u }, { 12042014186943056628u, 96296497219361792u },
{ 15052517733678820785u, 120370621524202240u }, { 9407823583549262990u, 75231638452626400u },
{ 11759779479436578738u, 94039548065783000u }, { 14699724349295723422u, 117549435082228750u },
{ 4575641699882439235u, 73468396926392969u }, { 10331238143280436948u, 91835496157991211u },
{ 8302361660673158281u, 114794370197489014u }, { 1154580038986672043u, 143492962746861268u },
{ 9944984561221445835u, 89683101716788292u }, { 12431230701526807293u, 112103877145985365u },
{ 1703980321626345405u, 140129846432481707u }, { 17205888765512323542u, 87581154020301066u },
{ 12283988920035628619u, 109476442525376333u }, { 1519928094762372062u, 136845553156720417u },
{ 12479170105294952299u, 85528470722950260u }, { 15598962631618690374u, 106910588403687825u },
{ 5663645234241199255u, 133638235504609782u }, { 17374836326682913246u, 83523897190381113u },
{ 7883487353071477846u, 104404871487976392u }, { 9854359191339347308u, 130506089359970490u },
{ 10770660513014479971u, 81566305849981556u }, { 13463325641268099964u, 101957882312476945u },
{ 2994098996302961243u, 127447352890596182u }, { 15706369927971514489u, 79654595556622613u },
{ 5797904354682229399u, 99568244445778267u }, { 2635694424925398845u, 124460305557222834u },
{ 6258995034005762182u, 77787690973264271u }, { 3212057774079814824u, 97234613716580339u },
{ 17850130272881932242u, 121543267145725423u }, { 18073860448192289507u, 75964541966078389u },
{ 8757267504958198172u, 94955677457597987u }, { 6334898362770359811u, 118694596821997484u },
{ 13182683513586250689u, 74184123013748427u }, { 11866668373555425458u, 92730153767185534u },
{ 5609963430089506015u, 115912692208981918u }, { 17341285199088104971u, 72445432630613698u },
{ 12453234462005355406u, 90556790788267123u }, { 10954857059079306353u, 113195988485333904u },
{ 13693571323849132942u, 141494985606667380u }, { 17781854114260483896u, 88434366004167112u },
{ 3780573569116053255u, 110542957505208891u }, { 114030942967678664u, 138178696881511114u },
{ 4682955357782187069u, 86361685550944446u }, { 15077066234082509644u, 107952106938680557u },
{ 5011274737320973344u, 134940133673350697u }, { 14661261756894078100u, 84337583545844185u },
{ 4491519140835433913u, 105421979432305232u }, { 5614398926044292391u, 131777474290381540u },
{ 12732371365632458552u, 82360921431488462u }, { 6692092170185797382u, 102951151789360578u },
{ 17588487249587022536u, 128688939736700722u }, { 15604490549419276989u, 80430587335437951u },
{ 14893927168346708332u, 100538234169297439u }, { 14005722942005997511u, 125672792711621799u },
{ 15671105866394830300u, 78545495444763624u }, { 1142138259283986260u, 98181869305954531u },
{ 15262730879387146537u, 122727336632443163u }, { 7233363790403272633u, 76704585395276977u },
{ 13653390756431478696u, 95880731744096221u }, { 3231680390257184658u, 119850914680120277u },
{ 4325643253124434363u, 74906821675075173u }, { 10018740084832930858u, 93633527093843966u },
{ 3300053069186387764u, 117041908867304958u }, { 15897591223523656064u, 73151193042065598u },
{ 10648616992549794273u, 91438991302581998u }, { 4087399203832467033u, 114298739128227498u },
{ 14332621041645359599u, 142873423910284372u }, { 18181260187883125557u, 89295889943927732u },
{ 4279831161144355331u, 111619862429909666u }, { 14573160988285219972u, 139524828037387082u },
{ 13719911636105650386u, 87203017523366926u }, { 7926517508277287175u, 109003771904208658u },
{ 684774848491833161u, 136254714880260823u }, { 7345513307948477581u, 85159196800163014u },
{ 18405263671790372785u, 106448996000203767u }, { 18394893571310578077u, 133061245000254709u },
{ 13802651491282805250u, 83163278125159193u }, { 3418256308821342851u, 103954097656448992u },
{ 4272820386026678563u, 129942622070561240u }, { 2670512741266674102u, 81214138794100775u },
{ 17173198981865506339u, 101517673492625968u }, { 3019754653622331308u, 126897091865782461u },
{ 4193189667727651020u, 79310682416114038u }, { 14464859121514339583u, 99138353020142547u },
{ 13469387883465536574u, 123922941275178184u }, { 8418367427165960359u, 77451838296986365u },
{ 15134645302384838353u, 96814797871232956u }, { 471562554271496325u, 121018497339041196u },
{ 9518098633274461011u, 75636560836900747u }, { 7285937273165688360u, 94545701046125934u },
{ 18330793628311886258u, 118182126307657417u }, { 4539216990053847055u, 73863828942285886u },
{ 14897393274422084627u, 92329786177857357u }, { 4786683537745442072u, 115412232722321697u },
{ 14520892257159371055u, 72132645451451060u }, { 18151115321449213818u, 90165806814313825u },
{ 8853836096529353561u, 112707258517892282u }, { 1843923083806916143u, 140884073147365353u },
{ 12681666973447792349u, 88052545717103345u }, { 2017025661527576725u, 110065682146379182u },
{ 11744654113764246714u, 137582102682973977u }, { 422879793461572340u, 85988814176858736u },
{ 528599741826965425u, 107486017721073420u }, { 660749677283706782u, 134357522151341775u },
{ 7330497575943398595u, 83973451344588609u }, { 13774807988356636147u, 104966814180735761u },
{ 3383451930163631472u, 131208517725919702u }, { 15949715511634433382u, 82005323578699813u },
{ 6102086334260878016u, 102506654473374767u }, { 3015921899398709616u, 128133318091718459u },
{ 18025852251620051174u, 80083323807324036u }, { 4085571240815512351u, 100104154759155046u },
{ 14330336087874166247u, 125130193448943807u }, { 15873989082562435760u, 78206370905589879u },
{ 15230800334775656796u, 97757963631987349u }, { 5203442363187407284u, 122197454539984187u },
{ 946308467778435600u, 76373409087490117u }, { 5794571603150432404u, 95466761359362646u },
{ 16466586540792816313u, 119333451699203307u }, { 7985773578781816244u, 74583407312002067u },
{ 5370530955049882401u, 93229259140002584u }, { 6713163693812353001u, 116536573925003230u },
{ 18030785363914884337u, 72835358703127018u }, { 13315109668038829614u, 91044198378908773u },
{ 2808829029766373305u, 113805247973635967u }, { 17346094342490130344u, 142256559967044958u },
{ 6229622945628943561u, 88910349979403099u }, { 3175342663608791547u, 111137937474253874u },
{ 13192550366365765242u, 138922421842817342u }, { 3633657960551215372u, 86826513651760839u },
{ 18377130505971182927u, 108533142064701048u }, { 4524669058754427043u, 135666427580876311u },
{ 9745447189362598758u, 84791517238047694u }, { 2958436949848472639u, 105989396547559618u },
{ 12921418224165366607u, 132486745684449522u }, { 12687572408530742033u, 82804216052780951u },
{ 11247779492236039638u, 103505270065976189u }, { 224666310012885835u, 129381587582470237u },
{ 2446259452971747599u, 80863492239043898u }, { 12281196353069460307u, 101079365298804872u },
{ 15351495441336825384u, 126349206623506090u }, { 14206370669262903769u, 78968254139691306u },
{ 8534591299723853903u, 98710317674614133u }, { 15279925143082205283u, 123387897093267666u },
{ 14161639232853766206u, 77117435683292291u }, { 13090363022639819853u, 96396794604115364u },
{ 16362953778299774816u, 120495993255144205u }, { 12532689120651053212u, 75309995784465128u },
{ 15665861400813816515u, 94137494730581410u }, { 10358954714162494836u, 117671868413226763u },
{ 4168503687137865320u, 73544917758266727u }, { 598943590494943747u, 91931147197833409u },
{ 5360365506546067587u, 114913933997291761u }, { 11312142901609972388u, 143642417496614701u },
{ 9375932322719926695u, 89776510935384188u }, { 11719915403399908368u, 112220638669230235u },
{ 10038208235822497557u, 140275798336537794u }, { 10885566165816448877u, 87672373960336121u },
{ 18218643725697949000u, 109590467450420151u }, { 18161618638695048346u, 136988084313025189u },
{ 13656854658398099168u, 85617552695640743u }, { 12459382304570236056u, 107021940869550929u },
{ 1739169825430631358u, 133777426086938662u }, { 14922039196176308311u, 83610891304336663u },
{ 14040862976792997485u, 104513614130420829u }, { 3716020665709083144u, 130642017663026037u },
{ 4628355925281870917u, 81651261039391273u }, { 10397130925029726550u, 102064076299239091u },
{ 8384727637859770284u, 127580095374048864u }, { 5240454773662356427u, 79737559608780540u },
{ 6550568467077945534u, 99671949510975675u }, { 3576524565420044014u, 124589936888719594u },
{ 6847013871814915412u, 77868710555449746u }, { 17782139376623420074u, 97335888194312182u },
{ 13004302183924499284u, 121669860242890228u }, { 17351060901807587860u, 76043662651806392u },
{ 3242082053549933210u, 95054578314757991u }, { 17887660622219580224u, 118818222893447488u },
{ 11179787888887237640u, 74261389308404680u }, { 13974734861109047050u, 92826736635505850u },
{ 8245046539531533005u, 116033420794382313u }, { 16682369133275677888u, 72520887996488945u },
{ 7017903361312433648u, 90651109995611182u }, { 17995751238495317868u, 113313887494513977u },
{ 8659630992836983623u, 141642359368142472u }, { 5412269370523114764u, 88526474605089045u },
{ 11377022731581281359u, 110658093256361306u }, { 4997906377621825891u, 138322616570451633u },
{ 14652906532082110942u, 86451635356532270u }, { 9092761128247862869u, 108064544195665338u },
{ 2142579373455052779u, 135080680244581673u }, { 12868327154477877747u, 84425425152863545u },
{ 2250350887815183471u, 105531781441079432u }, { 2812938609768979339u, 131914726801349290u },
{ 6369772649532999991u, 82446704250843306u }, { 17185587848771025797u, 103058380313554132u },
{ 3035240737254230630u, 128822975391942666u }, { 6508711479211282048u, 80514359619964166u },
{ 17359261385868878368u, 100642949524955207u }, { 17087390713908710056u, 125803686906194009u },
{ 3762090168551861929u, 78627304316371256u }, { 4702612710689827411u, 98284130395464070u },
{ 15101637925217060072u, 122855162994330087u }, { 16356052730901744401u, 76784476871456304u },
{ 1998321839917628885u, 95980596089320381u }, { 7109588318324424010u, 119975745111650476u },
{ 13666864735807540814u, 74984840694781547u }, { 12471894901332038114u, 93731050868476934u },
{ 6366496589810271835u, 117163813585596168u }, { 3979060368631419896u, 73227383490997605u },
{ 9585511479216662775u, 91534229363747006u }, { 2758517312166052660u, 114417786704683758u },
{ 12671518677062341634u, 143022233380854697u }, { 1002170145522881665u, 89388895863034186u },
{ 10476084718758377889u, 111736119828792732u }, { 13095105898447972362u, 139670149785990915u },
{ 5878598177316288774u, 87293843616244322u }, { 16571619758500136775u, 109117304520305402u },
{ 11491152661270395161u, 136396630650381753u }, { 264441385652915120u, 85247894156488596u },
{ 330551732066143900u, 106559867695610745u }, { 5024875683510067779u, 133199834619513431u },
{ 10058076329834874218u, 83249896637195894u }, { 3349223375438816964u, 104062370796494868u },
{ 4186529219298521205u, 130077963495618585u }, { 14145795808130045513u, 81298727184761615u },
{ 13070558741735168987u, 101623408980952019u }, { 11726512408741573330u, 127029261226190024u },
{ 7329070255463483331u, 79393288266368765u }, { 13773023837756742068u, 99241610332960956u },
{ 17216279797195927585u, 124052012916201195u }, { 8454331864033760789u, 77532508072625747u },
{ 5956228811614813082u, 96915635090782184u }, { 7445286014518516353u, 121144543863477730u },
{ 9264989777501460624u, 75715339914673581u }, { 16192923240304213684u, 94644174893341976u },
{ 1794409976670715490u, 118305218616677471u }, { 8039035263060279037u, 73940761635423419u },
{ 5437108060397960892u, 92425952044279274u }, { 16019757112352226923u, 115532440055349092u },
{ 788976158365366019u, 72207775034593183u }, { 14821278253238871236u, 90259718793241478u },
{ 9303225779693813237u, 112824648491551848u }, { 11629032224617266546u, 141030810614439810u },
{ 11879831158813179495u, 88144256634024881u }, { 1014730893234310657u, 110180320792531102u },
{ 10491785653397664129u, 137725400990663877u }, { 8863209042587234033u, 86078375619164923u },
{ 6467325284806654637u, 107597969523956154u }, { 17307528642863094104u, 134497461904945192u },
{ 10817205401789433815u, 84060913690590745u }, { 18133192770664180173u, 105076142113238431u },
{ 18054804944902837312u, 131345177641548039u }, { 18201782118205355176u, 82090736025967524u },
{ 4305483574047142354u, 102613420032459406u }, { 14605226504413703751u, 128266775040574257u },
{ 2210737537617482988u, 80166734400358911u }, { 16598479977304017447u, 100208418000448638u },
{ 11524727934775246001u, 125260522500560798u }, { 2591268940807140847u, 78287826562850499u },
{ 17074144231291089770u, 97859783203563123u }, { 16730994270686474309u, 122324729004453904u },
{ 10456871419179046443u, 76452955627783690u }, { 3847717237119032246u, 95566194534729613u },
{ 9421332564826178211u, 119457743168412016u }, { 5888332853016361382u, 74661089480257510u },
{ 16583788103125227536u, 93326361850321887u }, { 16118049110479146516u, 116657952312902359u },
{ 16991309721690548428u, 72911220195563974u }, { 12015765115258409727u, 91139025244454968u },
{ 15019706394073012159u, 113923781555568710u }, { 9551260955736489391u, 142404726944460888u },
{ 5969538097335305869u, 89002954340288055u }, { 2850236603241744433u, 111253692925360069u }};
return arr;
}
} // ryu
} // detail
} // namespace json
} // namespace boost
#endif
+231
View File
@@ -0,0 +1,231 @@
// Copyright 2018 Ulf Adams
//
// The contents of this file may be used under the terms of the Apache License,
// Version 2.0.
//
// (See accompanying file LICENSE-Apache or copy at
// http://www.apache.org/licenses/LICENSE-2.0)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
// (See accompanying file LICENSE-Boost or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.
/*
This is a derivative work
*/
#ifndef BOOST_JSON_DETAIL_RYU_DETAIL_D2S_INTRINSICS_HPP
#define BOOST_JSON_DETAIL_RYU_DETAIL_D2S_INTRINSICS_HPP
#include <boost/json/detail/config.hpp>
// This sets BOOST_JSON_RYU_32_BIT_PLATFORM as a side effect if applicable.
#include <boost/json/detail/ryu/detail/common.hpp>
#if defined(BOOST_JSON_RYU_HAS_64_BIT_INTRINSICS)
#include <intrin.h>
#endif
namespace boost {
namespace json {
namespace detail {
namespace ryu {
namespace detail {
#if defined(BOOST_JSON_RYU_HAS_64_BIT_INTRINSICS)
inline uint64_t umul128(const uint64_t a, const uint64_t b, uint64_t* const productHi) {
return _umul128(a, b, productHi);
}
inline uint64_t shiftright128(const uint64_t lo, const uint64_t hi, const uint32_t dist) {
// For the __shiftright128 intrinsic, the shift value is always
// modulo 64.
// In the current implementation of the double-precision version
// of Ryu, the shift value is always < 64. (In the case
// RYU_OPTIMIZE_SIZE == 0, the shift value is in the range [49, 58].
// Otherwise in the range [2, 59].)
// Check this here in case a future change requires larger shift
// values. In this case this function needs to be adjusted.
BOOST_ASSERT(dist < 64);
return __shiftright128(lo, hi, (unsigned char) dist);
}
#else // defined(HAS_64_BIT_INTRINSICS)
inline uint64_t umul128(const uint64_t a, const uint64_t b, uint64_t* const productHi) {
// The casts here help MSVC to avoid calls to the __allmul library function.
const uint32_t aLo = (uint32_t)a;
const uint32_t aHi = (uint32_t)(a >> 32);
const uint32_t bLo = (uint32_t)b;
const uint32_t bHi = (uint32_t)(b >> 32);
const uint64_t b00 = (uint64_t)aLo * bLo;
const uint64_t b01 = (uint64_t)aLo * bHi;
const uint64_t b10 = (uint64_t)aHi * bLo;
const uint64_t b11 = (uint64_t)aHi * bHi;
const uint32_t b00Lo = (uint32_t)b00;
const uint32_t b00Hi = (uint32_t)(b00 >> 32);
const uint64_t mid1 = b10 + b00Hi;
const uint32_t mid1Lo = (uint32_t)(mid1);
const uint32_t mid1Hi = (uint32_t)(mid1 >> 32);
const uint64_t mid2 = b01 + mid1Lo;
const uint32_t mid2Lo = (uint32_t)(mid2);
const uint32_t mid2Hi = (uint32_t)(mid2 >> 32);
const uint64_t pHi = b11 + mid1Hi + mid2Hi;
const uint64_t pLo = ((uint64_t)mid2Lo << 32) | b00Lo;
*productHi = pHi;
return pLo;
}
inline uint64_t shiftright128(const uint64_t lo, const uint64_t hi, const uint32_t dist) {
// We don't need to handle the case dist >= 64 here (see above).
BOOST_ASSERT(dist < 64);
#if defined(RYU_OPTIMIZE_SIZE) || !defined(RYU_32_BIT_PLATFORM)
BOOST_ASSERT(dist > 0);
return (hi << (64 - dist)) | (lo >> dist);
#else
// Avoid a 64-bit shift by taking advantage of the range of shift values.
BOOST_ASSERT(dist >= 32);
return (hi << (64 - dist)) | ((uint32_t)(lo >> 32) >> (dist - 32));
#endif
}
#endif // defined(HAS_64_BIT_INTRINSICS)
#ifdef RYU_32_BIT_PLATFORM
// Returns the high 64 bits of the 128-bit product of a and b.
inline uint64_t umulh(const uint64_t a, const uint64_t b) {
// Reuse the umul128 implementation.
// Optimizers will likely eliminate the instructions used to compute the
// low part of the product.
uint64_t hi;
umul128(a, b, &hi);
return hi;
}
// On 32-bit platforms, compilers typically generate calls to library
// functions for 64-bit divisions, even if the divisor is a constant.
//
// E.g.:
// https://bugs.llvm.org/show_bug.cgi?id=37932
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=17958
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37443
//
// The functions here perform division-by-constant using multiplications
// in the same way as 64-bit compilers would do.
//
// NB:
// The multipliers and shift values are the ones generated by clang x64
// for expressions like x/5, x/10, etc.
inline uint64_t div5(const uint64_t x) {
return umulh(x, 0xCCCCCCCCCCCCCCCDu) >> 2;
}
inline uint64_t div10(const uint64_t x) {
return umulh(x, 0xCCCCCCCCCCCCCCCDu) >> 3;
}
inline uint64_t div100(const uint64_t x) {
return umulh(x >> 2, 0x28F5C28F5C28F5C3u) >> 2;
}
inline uint64_t div1e8(const uint64_t x) {
return umulh(x, 0xABCC77118461CEFDu) >> 26;
}
inline uint64_t div1e9(const uint64_t x) {
return umulh(x >> 9, 0x44B82FA09B5A53u) >> 11;
}
inline uint32_t mod1e9(const uint64_t x) {
// Avoid 64-bit math as much as possible.
// Returning (uint32_t) (x - 1000000000 * div1e9(x)) would
// perform 32x64-bit multiplication and 64-bit subtraction.
// x and 1000000000 * div1e9(x) are guaranteed to differ by
// less than 10^9, so their highest 32 bits must be identical,
// so we can truncate both sides to uint32_t before subtracting.
// We can also simplify (uint32_t) (1000000000 * div1e9(x)).
// We can truncate before multiplying instead of after, as multiplying
// the highest 32 bits of div1e9(x) can't affect the lowest 32 bits.
return ((uint32_t) x) - 1000000000 * ((uint32_t) div1e9(x));
}
#else // RYU_32_BIT_PLATFORM
inline uint64_t div5(const uint64_t x) {
return x / 5;
}
inline uint64_t div10(const uint64_t x) {
return x / 10;
}
inline uint64_t div100(const uint64_t x) {
return x / 100;
}
inline uint64_t div1e8(const uint64_t x) {
return x / 100000000;
}
inline uint64_t div1e9(const uint64_t x) {
return x / 1000000000;
}
inline uint32_t mod1e9(const uint64_t x) {
return (uint32_t) (x - 1000000000 * div1e9(x));
}
#endif // RYU_32_BIT_PLATFORM
inline uint32_t pow5Factor(uint64_t value) {
uint32_t count = 0;
for (;;) {
BOOST_ASSERT(value != 0);
const uint64_t q = div5(value);
const uint32_t r = ((uint32_t) value) - 5 * ((uint32_t) q);
if (r != 0) {
break;
}
value = q;
++count;
}
return count;
}
// Returns true if value is divisible by 5^p.
inline bool multipleOfPowerOf5(const uint64_t value, const uint32_t p) {
// I tried a case distinction on p, but there was no performance difference.
return pow5Factor(value) >= p;
}
// Returns true if value is divisible by 2^p.
inline bool multipleOfPowerOf2(const uint64_t value, const uint32_t p) {
BOOST_ASSERT(value != 0);
// return __builtin_ctzll(value) >= p;
return (value & ((1ull << p) - 1)) == 0;
}
} // detail
} // ryu
} // detail
} // namespace json
} // namespace boost
#endif
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2018 Ulf Adams
//
// The contents of this file may be used under the terms of the Apache License,
// Version 2.0.
//
// (See accompanying file LICENSE-Apache or copy at
// http://www.apache.org/licenses/LICENSE-2.0)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
// (See accompanying file LICENSE-Boost or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.
/*
This is a derivative work
*/
#ifndef BOOST_JSON_DETAIL_RYU_DETAIL_DIGIT_TABLE_HPP
#define BOOST_JSON_DETAIL_RYU_DETAIL_DIGIT_TABLE_HPP
#include <boost/json/detail/config.hpp>
namespace boost {
namespace json {
namespace detail {
namespace ryu {
namespace detail {
// A table of all two-digit numbers. This is used to speed up decimal digit
// generation by copying pairs of digits into the final output.
inline
char const
(&DIGIT_TABLE() noexcept)[200]
{
static constexpr char arr[200] = {
'0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
'1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
'2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
'3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
'4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
'5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
'6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
'7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
'8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
'9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' };
return arr;
}
} // detail
} // ryu
} // detail
} // namespace json
} // namespace boost
#endif
+732
View File
@@ -0,0 +1,732 @@
// Copyright 2018 Ulf Adams
//
// The contents of this file may be used under the terms of the Apache License,
// Version 2.0.
//
// (See accompanying file LICENSE-Apache or copy at
// http://www.apache.org/licenses/LICENSE-2.0)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
// (See accompanying file LICENSE-Boost or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.
// Runtime compiler options:
// -DRYU_DEBUG Generate verbose debugging output to stdout.
//
// -DRYU_ONLY_64_BIT_OPS Avoid using uint128_t or 64-bit intrinsics. Slower,
// depending on your compiler.
//
// -DRYU_OPTIMIZE_SIZE Use smaller lookup tables. Instead of storing every
// required power of 5, only store every 26th entry, and compute
// intermediate values with a multiplication. This reduces the lookup table
// size by about 10x (only one case, and only double) at the cost of some
// performance. Currently requires MSVC intrinsics.
/*
This is a derivative work
*/
#ifndef BOOST_JSON_DETAIL_RYU_IMPL_D2S_IPP
#define BOOST_JSON_DETAIL_RYU_IMPL_D2S_IPP
#include <boost/json/detail/ryu/ryu.hpp>
#include <cstdlib>
#include <cstring>
#ifdef RYU_DEBUG
#include <stdio.h>
#endif
// ABSL avoids uint128_t on Win32 even if __SIZEOF_INT128__ is defined.
// Let's do the same for now.
#if defined(__SIZEOF_INT128__) && !defined(_MSC_VER) && !defined(RYU_ONLY_64_BIT_OPS)
#define BOOST_JSON_RYU_HAS_UINT128
#elif defined(_MSC_VER) && !defined(RYU_ONLY_64_BIT_OPS) && defined(_M_X64)
#define BOOST_JSON_RYU_HAS_64_BIT_INTRINSICS
#endif
#include <boost/json/detail/ryu/detail/common.hpp>
#include <boost/json/detail/ryu/detail/digit_table.hpp>
#include <boost/json/detail/ryu/detail/d2s.hpp>
#include <boost/json/detail/ryu/detail/d2s_intrinsics.hpp>
namespace boost {
namespace json {
namespace detail {
namespace ryu {
namespace detail {
// We need a 64x128-bit multiplication and a subsequent 128-bit shift.
// Multiplication:
// The 64-bit factor is variable and passed in, the 128-bit factor comes
// from a lookup table. We know that the 64-bit factor only has 55
// significant bits (i.e., the 9 topmost bits are zeros). The 128-bit
// factor only has 124 significant bits (i.e., the 4 topmost bits are
// zeros).
// Shift:
// In principle, the multiplication result requires 55 + 124 = 179 bits to
// represent. However, we then shift this value to the right by j, which is
// at least j >= 115, so the result is guaranteed to fit into 179 - 115 = 64
// bits. This means that we only need the topmost 64 significant bits of
// the 64x128-bit multiplication.
//
// There are several ways to do this:
// 1. Best case: the compiler exposes a 128-bit type.
// We perform two 64x64-bit multiplications, add the higher 64 bits of the
// lower result to the higher result, and shift by j - 64 bits.
//
// We explicitly cast from 64-bit to 128-bit, so the compiler can tell
// that these are only 64-bit inputs, and can map these to the best
// possible sequence of assembly instructions.
// x64 machines happen to have matching assembly instructions for
// 64x64-bit multiplications and 128-bit shifts.
//
// 2. Second best case: the compiler exposes intrinsics for the x64 assembly
// instructions mentioned in 1.
//
// 3. We only have 64x64 bit instructions that return the lower 64 bits of
// the result, i.e., we have to use plain C.
// Our inputs are less than the full width, so we have three options:
// a. Ignore this fact and just implement the intrinsics manually.
// b. Split both into 31-bit pieces, which guarantees no internal overflow,
// but requires extra work upfront (unless we change the lookup table).
// c. Split only the first factor into 31-bit pieces, which also guarantees
// no internal overflow, but requires extra work since the intermediate
// results are not perfectly aligned.
#if defined(BOOST_JSON_RYU_HAS_UINT128)
// Best case: use 128-bit type.
inline
std::uint64_t
mulShift(
const std::uint64_t m,
const std::uint64_t* const mul,
const std::int32_t j) noexcept
{
const uint128_t b0 = ((uint128_t) m) * mul[0];
const uint128_t b2 = ((uint128_t) m) * mul[1];
return (std::uint64_t) (((b0 >> 64) + b2) >> (j - 64));
}
inline
uint64_t
mulShiftAll(
const std::uint64_t m,
const std::uint64_t* const mul,
std::int32_t const j,
std::uint64_t* const vp,
std::uint64_t* const vm,
const std::uint32_t mmShift) noexcept
{
// m <<= 2;
// uint128_t b0 = ((uint128_t) m) * mul[0]; // 0
// uint128_t b2 = ((uint128_t) m) * mul[1]; // 64
//
// uint128_t hi = (b0 >> 64) + b2;
// uint128_t lo = b0 & 0xffffffffffffffffull;
// uint128_t factor = (((uint128_t) mul[1]) << 64) + mul[0];
// uint128_t vpLo = lo + (factor << 1);
// *vp = (std::uint64_t) ((hi + (vpLo >> 64)) >> (j - 64));
// uint128_t vmLo = lo - (factor << mmShift);
// *vm = (std::uint64_t) ((hi + (vmLo >> 64) - (((uint128_t) 1ull) << 64)) >> (j - 64));
// return (std::uint64_t) (hi >> (j - 64));
*vp = mulShift(4 * m + 2, mul, j);
*vm = mulShift(4 * m - 1 - mmShift, mul, j);
return mulShift(4 * m, mul, j);
}
#elif defined(BOOST_JSON_RYU_HAS_64_BIT_INTRINSICS)
inline
std::uint64_t
mulShift(
const std::uint64_t m,
const std::uint64_t* const mul,
const std::int32_t j) noexcept
{
// m is maximum 55 bits
std::uint64_t high1; // 128
std::uint64_t const low1 = umul128(m, mul[1], &high1); // 64
std::uint64_t high0; // 64
umul128(m, mul[0], &high0); // 0
std::uint64_t const sum = high0 + low1;
if (sum < high0)
++high1; // overflow into high1
return shiftright128(sum, high1, j - 64);
}
inline
std::uint64_t
mulShiftAll(
const std::uint64_t m,
const std::uint64_t* const mul,
const std::int32_t j,
std::uint64_t* const vp,
std::uint64_t* const vm,
const std::uint32_t mmShift) noexcept
{
*vp = mulShift(4 * m + 2, mul, j);
*vm = mulShift(4 * m - 1 - mmShift, mul, j);
return mulShift(4 * m, mul, j);
}
#else // !defined(BOOST_JSON_RYU_HAS_UINT128) && !defined(BOOST_JSON_RYU_HAS_64_BIT_INTRINSICS)
inline
std::uint64_t
mulShiftAll(
std::uint64_t m,
const std::uint64_t* const mul,
const std::int32_t j,
std::uint64_t* const vp,
std::uint64_t* const vm,
const std::uint32_t mmShift)
{
m <<= 1;
// m is maximum 55 bits
std::uint64_t tmp;
std::uint64_t const lo = umul128(m, mul[0], &tmp);
std::uint64_t hi;
std::uint64_t const mid = tmp + umul128(m, mul[1], &hi);
hi += mid < tmp; // overflow into hi
const std::uint64_t lo2 = lo + mul[0];
const std::uint64_t mid2 = mid + mul[1] + (lo2 < lo);
const std::uint64_t hi2 = hi + (mid2 < mid);
*vp = shiftright128(mid2, hi2, (std::uint32_t)(j - 64 - 1));
if (mmShift == 1)
{
const std::uint64_t lo3 = lo - mul[0];
const std::uint64_t mid3 = mid - mul[1] - (lo3 > lo);
const std::uint64_t hi3 = hi - (mid3 > mid);
*vm = shiftright128(mid3, hi3, (std::uint32_t)(j - 64 - 1));
}
else
{
const std::uint64_t lo3 = lo + lo;
const std::uint64_t mid3 = mid + mid + (lo3 < lo);
const std::uint64_t hi3 = hi + hi + (mid3 < mid);
const std::uint64_t lo4 = lo3 - mul[0];
const std::uint64_t mid4 = mid3 - mul[1] - (lo4 > lo3);
const std::uint64_t hi4 = hi3 - (mid4 > mid3);
*vm = shiftright128(mid4, hi4, (std::uint32_t)(j - 64));
}
return shiftright128(mid, hi, (std::uint32_t)(j - 64 - 1));
}
#endif // BOOST_JSON_RYU_HAS_64_BIT_INTRINSICS
inline
std::uint32_t
decimalLength17(
const std::uint64_t v)
{
// This is slightly faster than a loop.
// The average output length is 16.38 digits, so we check high-to-low.
// Function precondition: v is not an 18, 19, or 20-digit number.
// (17 digits are sufficient for round-tripping.)
BOOST_ASSERT(v < 100000000000000000L);
if (v >= 10000000000000000L) { return 17; }
if (v >= 1000000000000000L) { return 16; }
if (v >= 100000000000000L) { return 15; }
if (v >= 10000000000000L) { return 14; }
if (v >= 1000000000000L) { return 13; }
if (v >= 100000000000L) { return 12; }
if (v >= 10000000000L) { return 11; }
if (v >= 1000000000L) { return 10; }
if (v >= 100000000L) { return 9; }
if (v >= 10000000L) { return 8; }
if (v >= 1000000L) { return 7; }
if (v >= 100000L) { return 6; }
if (v >= 10000L) { return 5; }
if (v >= 1000L) { return 4; }
if (v >= 100L) { return 3; }
if (v >= 10L) { return 2; }
return 1;
}
// A floating decimal representing m * 10^e.
struct floating_decimal_64
{
std::uint64_t mantissa;
// Decimal exponent's range is -324 to 308
// inclusive, and can fit in a short if needed.
std::int32_t exponent;
};
inline
floating_decimal_64
d2d(
const std::uint64_t ieeeMantissa,
const std::uint32_t ieeeExponent)
{
std::int32_t e2;
std::uint64_t m2;
if (ieeeExponent == 0)
{
// We subtract 2 so that the bounds computation has 2 additional bits.
e2 = 1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS - 2;
m2 = ieeeMantissa;
}
else
{
e2 = (std::int32_t)ieeeExponent - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS - 2;
m2 = (1ull << DOUBLE_MANTISSA_BITS) | ieeeMantissa;
}
const bool even = (m2 & 1) == 0;
const bool acceptBounds = even;
#ifdef RYU_DEBUG
printf("-> %" PRIu64 " * 2^%d\n", m2, e2 + 2);
#endif
// Step 2: Determine the interval of valid decimal representations.
const std::uint64_t mv = 4 * m2;
// Implicit bool -> int conversion. True is 1, false is 0.
const std::uint32_t mmShift = ieeeMantissa != 0 || ieeeExponent <= 1;
// We would compute mp and mm like this:
// uint64_t mp = 4 * m2 + 2;
// uint64_t mm = mv - 1 - mmShift;
// Step 3: Convert to a decimal power base using 128-bit arithmetic.
std::uint64_t vr, vp, vm;
std::int32_t e10;
bool vmIsTrailingZeros = false;
bool vrIsTrailingZeros = false;
if (e2 >= 0) {
// I tried special-casing q == 0, but there was no effect on performance.
// This expression is slightly faster than max(0, log10Pow2(e2) - 1).
const std::uint32_t q = log10Pow2(e2) - (e2 > 3);
e10 = (std::int32_t)q;
const std::int32_t k = DOUBLE_POW5_INV_BITCOUNT + pow5bits((int32_t)q) - 1;
const std::int32_t i = -e2 + (std::int32_t)q + k;
#if defined(BOOST_JSON_RYU_OPTIMIZE_SIZE)
uint64_t pow5[2];
double_computeInvPow5(q, pow5);
vr = mulShiftAll(m2, pow5, i, &vp, &vm, mmShift);
#else
vr = mulShiftAll(m2, DOUBLE_POW5_INV_SPLIT()[q], i, &vp, &vm, mmShift);
#endif
#ifdef RYU_DEBUG
printf("%" PRIu64 " * 2^%d / 10^%u\n", mv, e2, q);
printf("V+=%" PRIu64 "\nV =%" PRIu64 "\nV-=%" PRIu64 "\n", vp, vr, vm);
#endif
if (q <= 21)
{
// This should use q <= 22, but I think 21 is also safe. Smaller values
// may still be safe, but it's more difficult to reason about them.
// Only one of mp, mv, and mm can be a multiple of 5, if any.
const std::uint32_t mvMod5 = ((std::uint32_t)mv) - 5 * ((std::uint32_t)div5(mv));
if (mvMod5 == 0)
{
vrIsTrailingZeros = multipleOfPowerOf5(mv, q);
}
else if (acceptBounds)
{
// Same as min(e2 + (~mm & 1), pow5Factor(mm)) >= q
// <=> e2 + (~mm & 1) >= q && pow5Factor(mm) >= q
// <=> true && pow5Factor(mm) >= q, since e2 >= q.
vmIsTrailingZeros = multipleOfPowerOf5(mv - 1 - mmShift, q);
}
else
{
// Same as min(e2 + 1, pow5Factor(mp)) >= q.
vp -= multipleOfPowerOf5(mv + 2, q);
}
}
}
else
{
// This expression is slightly faster than max(0, log10Pow5(-e2) - 1).
const std::uint32_t q = log10Pow5(-e2) - (-e2 > 1);
e10 = (std::int32_t)q + e2;
const std::int32_t i = -e2 - (std::int32_t)q;
const std::int32_t k = pow5bits(i) - DOUBLE_POW5_BITCOUNT;
const std::int32_t j = (std::int32_t)q - k;
#if defined(BOOST_JSON_RYU_OPTIMIZE_SIZE)
std::uint64_t pow5[2];
double_computePow5(i, pow5);
vr = mulShiftAll(m2, pow5, j, &vp, &vm, mmShift);
#else
vr = mulShiftAll(m2, DOUBLE_POW5_SPLIT()[i], j, &vp, &vm, mmShift);
#endif
#ifdef RYU_DEBUG
printf("%" PRIu64 " * 5^%d / 10^%u\n", mv, -e2, q);
printf("%u %d %d %d\n", q, i, k, j);
printf("V+=%" PRIu64 "\nV =%" PRIu64 "\nV-=%" PRIu64 "\n", vp, vr, vm);
#endif
if (q <= 1)
{
// {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.
// mv = 4 * m2, so it always has at least two trailing 0 bits.
vrIsTrailingZeros = true;
if (acceptBounds)
{
// mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1.
vmIsTrailingZeros = mmShift == 1;
}
else
{
// mp = mv + 2, so it always has at least one trailing 0 bit.
--vp;
}
}
else if (q < 63)
{
// TODO(ulfjack): Use a tighter bound here.
// We want to know if the full product has at least q trailing zeros.
// We need to compute min(p2(mv), p5(mv) - e2) >= q
// <=> p2(mv) >= q && p5(mv) - e2 >= q
// <=> p2(mv) >= q (because -e2 >= q)
vrIsTrailingZeros = multipleOfPowerOf2(mv, q);
#ifdef RYU_DEBUG
printf("vr is trailing zeros=%s\n", vrIsTrailingZeros ? "true" : "false");
#endif
}
}
#ifdef RYU_DEBUG
printf("e10=%d\n", e10);
printf("V+=%" PRIu64 "\nV =%" PRIu64 "\nV-=%" PRIu64 "\n", vp, vr, vm);
printf("vm is trailing zeros=%s\n", vmIsTrailingZeros ? "true" : "false");
printf("vr is trailing zeros=%s\n", vrIsTrailingZeros ? "true" : "false");
#endif
// Step 4: Find the shortest decimal representation in the interval of valid representations.
std::int32_t removed = 0;
std::uint8_t lastRemovedDigit = 0;
std::uint64_t output;
// On average, we remove ~2 digits.
if (vmIsTrailingZeros || vrIsTrailingZeros)
{
// General case, which happens rarely (~0.7%).
for (;;)
{
const std::uint64_t vpDiv10 = div10(vp);
const std::uint64_t vmDiv10 = div10(vm);
if (vpDiv10 <= vmDiv10)
break;
const std::uint32_t vmMod10 = ((std::uint32_t)vm) - 10 * ((std::uint32_t)vmDiv10);
const std::uint64_t vrDiv10 = div10(vr);
const std::uint32_t vrMod10 = ((std::uint32_t)vr) - 10 * ((std::uint32_t)vrDiv10);
vmIsTrailingZeros &= vmMod10 == 0;
vrIsTrailingZeros &= lastRemovedDigit == 0;
lastRemovedDigit = (uint8_t)vrMod10;
vr = vrDiv10;
vp = vpDiv10;
vm = vmDiv10;
++removed;
}
#ifdef RYU_DEBUG
printf("V+=%" PRIu64 "\nV =%" PRIu64 "\nV-=%" PRIu64 "\n", vp, vr, vm);
printf("d-10=%s\n", vmIsTrailingZeros ? "true" : "false");
#endif
if (vmIsTrailingZeros)
{
for (;;)
{
const std::uint64_t vmDiv10 = div10(vm);
const std::uint32_t vmMod10 = ((std::uint32_t)vm) - 10 * ((std::uint32_t)vmDiv10);
if (vmMod10 != 0)
break;
const std::uint64_t vpDiv10 = div10(vp);
const std::uint64_t vrDiv10 = div10(vr);
const std::uint32_t vrMod10 = ((std::uint32_t)vr) - 10 * ((std::uint32_t)vrDiv10);
vrIsTrailingZeros &= lastRemovedDigit == 0;
lastRemovedDigit = (uint8_t)vrMod10;
vr = vrDiv10;
vp = vpDiv10;
vm = vmDiv10;
++removed;
}
}
#ifdef RYU_DEBUG
printf("%" PRIu64 " %d\n", vr, lastRemovedDigit);
printf("vr is trailing zeros=%s\n", vrIsTrailingZeros ? "true" : "false");
#endif
if (vrIsTrailingZeros && lastRemovedDigit == 5 && vr % 2 == 0)
{
// Round even if the exact number is .....50..0.
lastRemovedDigit = 4;
}
// We need to take vr + 1 if vr is outside bounds or we need to round up.
output = vr + ((vr == vm && (!acceptBounds || !vmIsTrailingZeros)) || lastRemovedDigit >= 5);
}
else
{
// Specialized for the common case (~99.3%). Percentages below are relative to this.
bool roundUp = false;
const std::uint64_t vpDiv100 = div100(vp);
const std::uint64_t vmDiv100 = div100(vm);
if (vpDiv100 > vmDiv100)
{
// Optimization: remove two digits at a time (~86.2%).
const std::uint64_t vrDiv100 = div100(vr);
const std::uint32_t vrMod100 = ((std::uint32_t)vr) - 100 * ((std::uint32_t)vrDiv100);
roundUp = vrMod100 >= 50;
vr = vrDiv100;
vp = vpDiv100;
vm = vmDiv100;
removed += 2;
}
// Loop iterations below (approximately), without optimization above:
// 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02%
// Loop iterations below (approximately), with optimization above:
// 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02%
for (;;)
{
const std::uint64_t vpDiv10 = div10(vp);
const std::uint64_t vmDiv10 = div10(vm);
if (vpDiv10 <= vmDiv10)
break;
const std::uint64_t vrDiv10 = div10(vr);
const std::uint32_t vrMod10 = ((std::uint32_t)vr) - 10 * ((std::uint32_t)vrDiv10);
roundUp = vrMod10 >= 5;
vr = vrDiv10;
vp = vpDiv10;
vm = vmDiv10;
++removed;
}
#ifdef RYU_DEBUG
printf("%" PRIu64 " roundUp=%s\n", vr, roundUp ? "true" : "false");
printf("vr is trailing zeros=%s\n", vrIsTrailingZeros ? "true" : "false");
#endif
// We need to take vr + 1 if vr is outside bounds or we need to round up.
output = vr + (vr == vm || roundUp);
}
const std::int32_t exp = e10 + removed;
#ifdef RYU_DEBUG
printf("V+=%" PRIu64 "\nV =%" PRIu64 "\nV-=%" PRIu64 "\n", vp, vr, vm);
printf("O=%" PRIu64 "\n", output);
printf("EXP=%d\n", exp);
#endif
floating_decimal_64 fd;
fd.exponent = exp;
fd.mantissa = output;
return fd;
}
inline
int
to_chars(
const floating_decimal_64 v,
const bool sign,
char* const result)
{
// Step 5: Print the decimal representation.
int index = 0;
if (sign)
result[index++] = '-';
std::uint64_t output = v.mantissa;
std::uint32_t const olength = decimalLength17(output);
#ifdef RYU_DEBUG
printf("DIGITS=%" PRIu64 "\n", v.mantissa);
printf("OLEN=%u\n", olength);
printf("EXP=%u\n", v.exponent + olength);
#endif
// Print the decimal digits.
// The following code is equivalent to:
// for (uint32_t i = 0; i < olength - 1; ++i) {
// const uint32_t c = output % 10; output /= 10;
// result[index + olength - i] = (char) ('0' + c);
// }
// result[index] = '0' + output % 10;
std::uint32_t i = 0;
// We prefer 32-bit operations, even on 64-bit platforms.
// We have at most 17 digits, and uint32_t can store 9 digits.
// If output doesn't fit into uint32_t, we cut off 8 digits,
// so the rest will fit into uint32_t.
if ((output >> 32) != 0)
{
// Expensive 64-bit division.
std::uint64_t const q = div1e8(output);
std::uint32_t output2 = ((std::uint32_t)output) - 100000000 * ((std::uint32_t)q);
output = q;
const std::uint32_t c = output2 % 10000;
output2 /= 10000;
const std::uint32_t d = output2 % 10000;
const std::uint32_t c0 = (c % 100) << 1;
const std::uint32_t c1 = (c / 100) << 1;
const std::uint32_t d0 = (d % 100) << 1;
const std::uint32_t d1 = (d / 100) << 1;
std::memcpy(result + index + olength - i - 1, DIGIT_TABLE() + c0, 2);
std::memcpy(result + index + olength - i - 3, DIGIT_TABLE() + c1, 2);
std::memcpy(result + index + olength - i - 5, DIGIT_TABLE() + d0, 2);
std::memcpy(result + index + olength - i - 7, DIGIT_TABLE() + d1, 2);
i += 8;
}
uint32_t output2 = (std::uint32_t)output;
while (output2 >= 10000)
{
#ifdef __clang__ // https://bugs.llvm.org/show_bug.cgi?id=38217
const uint32_t c = output2 - 10000 * (output2 / 10000);
#else
const uint32_t c = output2 % 10000;
#endif
output2 /= 10000;
const uint32_t c0 = (c % 100) << 1;
const uint32_t c1 = (c / 100) << 1;
memcpy(result + index + olength - i - 1, DIGIT_TABLE() + c0, 2);
memcpy(result + index + olength - i - 3, DIGIT_TABLE() + c1, 2);
i += 4;
}
if (output2 >= 100) {
const uint32_t c = (output2 % 100) << 1;
output2 /= 100;
memcpy(result + index + olength - i - 1, DIGIT_TABLE() + c, 2);
i += 2;
}
if (output2 >= 10) {
const uint32_t c = output2 << 1;
// We can't use memcpy here: the decimal dot goes between these two digits.
result[index + olength - i] = DIGIT_TABLE()[c + 1];
result[index] = DIGIT_TABLE()[c];
}
else {
result[index] = (char)('0' + output2);
}
// Print decimal point if needed.
if (olength > 1) {
result[index + 1] = '.';
index += olength + 1;
}
else {
++index;
}
// Print the exponent.
result[index++] = 'E';
int32_t exp = v.exponent + (int32_t)olength - 1;
if (exp < 0) {
result[index++] = '-';
exp = -exp;
}
if (exp >= 100) {
const int32_t c = exp % 10;
memcpy(result + index, DIGIT_TABLE() + 2 * (exp / 10), 2);
result[index + 2] = (char)('0' + c);
index += 3;
}
else if (exp >= 10) {
memcpy(result + index, DIGIT_TABLE() + 2 * exp, 2);
index += 2;
}
else {
result[index++] = (char)('0' + exp);
}
return index;
}
static inline bool d2d_small_int(const uint64_t ieeeMantissa, const uint32_t ieeeExponent,
floating_decimal_64* const v) {
const uint64_t m2 = (1ull << DOUBLE_MANTISSA_BITS) | ieeeMantissa;
const int32_t e2 = (int32_t) ieeeExponent - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS;
if (e2 > 0) {
// f = m2 * 2^e2 >= 2^53 is an integer.
// Ignore this case for now.
return false;
}
if (e2 < -52) {
// f < 1.
return false;
}
// Since 2^52 <= m2 < 2^53 and 0 <= -e2 <= 52: 1 <= f = m2 / 2^-e2 < 2^53.
// Test if the lower -e2 bits of the significand are 0, i.e. whether the fraction is 0.
const uint64_t mask = (1ull << -e2) - 1;
const uint64_t fraction = m2 & mask;
if (fraction != 0) {
return false;
}
// f is an integer in the range [1, 2^53).
// Note: mantissa might contain trailing (decimal) 0's.
// Note: since 2^53 < 10^16, there is no need to adjust decimalLength17().
v->mantissa = m2 >> -e2;
v->exponent = 0;
return true;
}
} // detail
int
d2s_buffered_n(
double f,
char* result,
bool allow_infinity_and_nan) noexcept
{
using namespace detail;
// Step 1: Decode the floating-point number, and unify normalized and subnormal cases.
std::uint64_t const bits = double_to_bits(f);
#ifdef RYU_DEBUG
printf("IN=");
for (std::int32_t bit = 63; bit >= 0; --bit) {
printf("%d", (int)((bits >> bit) & 1));
}
printf("\n");
#endif
// Decode bits into sign, mantissa, and exponent.
const bool ieeeSign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0;
const std::uint64_t ieeeMantissa = bits & ((1ull << DOUBLE_MANTISSA_BITS) - 1);
const std::uint32_t ieeeExponent = (std::uint32_t)((bits >> DOUBLE_MANTISSA_BITS) & ((1u << DOUBLE_EXPONENT_BITS) - 1));
// Case distinction; exit early for the easy cases.
if (ieeeExponent == ((1u << DOUBLE_EXPONENT_BITS) - 1u) || (ieeeExponent == 0 && ieeeMantissa == 0)) {
// We changed how special numbers are output by default
if (allow_infinity_and_nan)
return copy_special_str(result, ieeeSign, ieeeExponent != 0, ieeeMantissa != 0);
else
return copy_special_str_conforming(result, ieeeSign, ieeeExponent != 0, ieeeMantissa != 0);
}
floating_decimal_64 v;
const bool isSmallInt = d2d_small_int(ieeeMantissa, ieeeExponent, &v);
if (isSmallInt) {
// For small integers in the range [1, 2^53), v.mantissa might contain trailing (decimal) zeros.
// For scientific notation we need to move these zeros into the exponent.
// (This is not needed for fixed-point notation, so it might be beneficial to trim
// trailing zeros in to_chars only if needed - once fixed-point notation output is implemented.)
for (;;) {
std::uint64_t const q = div10(v.mantissa);
std::uint32_t const r = ((std::uint32_t) v.mantissa) - 10 * ((std::uint32_t) q);
if (r != 0)
break;
v.mantissa = q;
++v.exponent;
}
}
else {
v = d2d(ieeeMantissa, ieeeExponent);
}
return to_chars(v, ieeeSign, result);
}
} // ryu
} // detail
} // namespace json
} // namespace boost
#endif
+43
View File
@@ -0,0 +1,43 @@
// Copyright 2018 Ulf Adams
//
// The contents of this file may be used under the terms of the Apache License,
// Version 2.0.
//
// (See accompanying file LICENSE-Apache or copy at
// http://www.apache.org/licenses/LICENSE-2.0)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
// (See accompanying file LICENSE-Boost or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.
/*
This is a derivative work
*/
#ifndef BOOST_JSON_DETAIL_RYU_HPP
#define BOOST_JSON_DETAIL_RYU_HPP
#include <boost/json/detail/config.hpp>
namespace boost {
namespace json {
namespace detail {
namespace ryu {
BOOST_JSON_DECL
int d2s_buffered_n(
double f, char* result, bool allow_infinity_and_nan = true) noexcept;
} // ryu
} // detail
} // namespace json
} // namespace boost
#endif
+189
View File
@@ -0,0 +1,189 @@
//
// Copyright (c) 2023 Dmitry Arkhipov (grisumbras@yandex.ru)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_SBO_BUFFER_HPP
#define BOOST_JSON_DETAIL_SBO_BUFFER_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/detail/except.hpp>
#include <string>
#include <array>
namespace boost {
namespace json {
namespace detail {
template< std::size_t N >
class sbo_buffer
{
struct size_ptr_pair
{
std::size_t size;
char* ptr;
};
BOOST_STATIC_ASSERT( N >= sizeof(size_ptr_pair) );
union {
std::array<char, N> buffer_;
std::size_t capacity_;
};
char* data_ = buffer_.data();
std::size_t size_ = 0;
bool
is_small() const noexcept
{
return data_ == buffer_.data();
}
void
dispose()
{
if( is_small() )
return;
delete[] data_;
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
buffer_ = {};
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
data_ = buffer_.data();
}
static constexpr
std::size_t
max_size() noexcept
{
return BOOST_JSON_MAX_STRING_SIZE;
}
public:
sbo_buffer()
: buffer_()
{}
sbo_buffer( sbo_buffer&& other ) noexcept
: size_(other.size_)
{
if( other.is_small() )
{
buffer_ = other.buffer_;
data_ = buffer_.data();
}
else
{
data_ = other.data_;
other.data_ = other.buffer_.data();
}
BOOST_ASSERT( other.is_small() );
}
sbo_buffer&
operator=( sbo_buffer&& other ) noexcept
{
if( &other == this )
return this;
if( other.is_small() )
{
buffer_ = other.buffer_;
data_ = buffer_.data();
}
else
{
data_ = other.data_;
other.data_ = other.buffer_.data();
}
size_ = other.size_;
other.size_ = 0;
return *this;
}
~sbo_buffer()
{
if( !is_small() )
delete[] data_;
}
std::size_t
capacity() const noexcept
{
return is_small() ? buffer_.size() : capacity_;
}
void
reset() noexcept
{
dispose();
clear();
}
void
clear()
{
size_ = 0;
}
void
grow( std::size_t size )
{
if( !size )
return;
if( max_size() - size_ < size )
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::number_too_large, &loc );
}
std::size_t const old_capacity = this->capacity();
std::size_t new_capacity = size_ + size;
// growth factor 2
if( old_capacity <= max_size() - old_capacity ) // check for overflow
new_capacity = (std::max)(old_capacity * 2, new_capacity);
char* new_data = new char[new_capacity];
std::memcpy(new_data, data_, size_);
dispose();
data_ = new_data;
capacity_ = new_capacity;
}
char*
append( char const* ptr, std::size_t size )
{
grow(size);
if(BOOST_JSON_LIKELY( size ))
std::memcpy( data_ + size_, ptr, size );
size_ += size;
return data_;
}
std::size_t
size() noexcept
{
return size_;
}
};
} // namespace detail
} // namespace json
} // namespace boost
#endif // BOOST_JSON_DETAIL_SBO_BUFFER_HPP
+87
View File
@@ -0,0 +1,87 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_SHARED_RESOURCE_HPP
#define BOOST_JSON_DETAIL_SHARED_RESOURCE_HPP
#include <boost/json/memory_resource.hpp>
#include <atomic>
#include <utility>
namespace boost {
namespace json {
namespace detail {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4275) // non dll-interface class used as base for dll-interface class
#endif
struct BOOST_SYMBOL_VISIBLE
shared_resource
: memory_resource
{
BOOST_JSON_DECL
shared_resource();
BOOST_JSON_DECL
~shared_resource();
std::atomic<std::size_t> refs{ 1 };
};
template<class T>
class shared_resource_impl final
: public shared_resource
{
T t;
public:
template<class... Args>
shared_resource_impl(
Args&&... args)
: t(std::forward<Args>(args)...)
{
}
void*
do_allocate(
std::size_t n,
std::size_t align) override
{
return t.allocate(n, align);
}
void
do_deallocate(
void* p,
std::size_t n,
std::size_t align) override
{
return t.deallocate(p, n, align);
}
bool
do_is_equal(
memory_resource const&) const noexcept override
{
// VFALCO Is always false ok?
return false;
}
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
} // detail
} // namespace json
} // namespace boost
#endif
+549
View File
@@ -0,0 +1,549 @@
//
// Copyright (c) 2019 Peter Dimov (pdimov at gmail dot com),
// Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_SSE2_HPP
#define BOOST_JSON_DETAIL_SSE2_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/detail/utf8.hpp>
#include <cstddef>
#include <cstring>
#ifdef BOOST_JSON_USE_SSE2
# include <emmintrin.h>
# include <xmmintrin.h>
# ifdef _MSC_VER
# include <intrin.h>
# endif
#endif
namespace boost {
namespace json {
namespace detail {
#ifdef BOOST_JSON_USE_SSE2
template<bool AllowBadUTF8>
inline
const char*
count_valid(
char const* p,
const char* end) noexcept
{
__m128i const q1 = _mm_set1_epi8( '\x22' ); // '"'
__m128i const q2 = _mm_set1_epi8( '\\' ); // '\\'
__m128i const q3 = _mm_set1_epi8( 0x1F );
while(end - p >= 16)
{
__m128i v1 = _mm_loadu_si128( (__m128i const*)p );
__m128i v2 = _mm_cmpeq_epi8( v1, q1 ); // quote
__m128i v3 = _mm_cmpeq_epi8( v1, q2 ); // backslash
__m128i v4 = _mm_or_si128( v2, v3 ); // combine quotes and backslash
__m128i v5 = _mm_min_epu8( v1, q3 );
__m128i v6 = _mm_cmpeq_epi8( v5, v1 ); // controls
__m128i v7 = _mm_or_si128( v4, v6 ); // combine with control
int w = _mm_movemask_epi8( v7 );
if( w != 0 )
{
int m;
#if defined(__GNUC__) || defined(__clang__)
m = __builtin_ffs( w ) - 1;
#else
unsigned long index;
_BitScanForward( &index, w );
m = index;
#endif
return p + m;
}
p += 16;
}
while(p != end)
{
const unsigned char c = *p;
if(c == '\x22' || c == '\\' || c < 0x20)
break;
++p;
}
return p;
}
template<>
inline
const char*
count_valid<false>(
char const* p,
const char* end) noexcept
{
__m128i const q1 = _mm_set1_epi8( '\x22' ); // '"'
__m128i const q2 = _mm_set1_epi8( '\\' );
__m128i const q3 = _mm_set1_epi8( 0x20 );
while(end - p >= 16)
{
__m128i v1 = _mm_loadu_si128( (__m128i const*)p );
__m128i v2 = _mm_cmpeq_epi8( v1, q1 );
__m128i v3 = _mm_cmpeq_epi8( v1, q2 );
__m128i v4 = _mm_cmplt_epi8( v1, q3 );
__m128i v5 = _mm_or_si128( v2, v3 );
__m128i v6 = _mm_or_si128( v5, v4 );
int w = _mm_movemask_epi8( v6 );
if( w != 0 )
{
int m;
#if defined(__GNUC__) || defined(__clang__)
m = __builtin_ffs( w ) - 1;
#else
unsigned long index;
_BitScanForward( &index, w );
m = index;
#endif
p += m;
break;
}
p += 16;
}
while(p != end)
{
const unsigned char c = *p;
if(c == '\x22' || c == '\\' || c < 0x20)
break;
if(c < 0x80)
{
++p;
continue;
}
// validate utf-8
uint16_t first = classify_utf8(c);
uint8_t len = first & 0xFF;
if(BOOST_JSON_UNLIKELY(end - p < len))
break;
if(BOOST_JSON_UNLIKELY(! is_valid_utf8(p, first)))
break;
p += len;
}
return p;
}
#else
template<bool AllowBadUTF8>
char const*
count_valid(
char const* p,
char const* end) noexcept
{
while(p != end)
{
const unsigned char c = *p;
if(c == '\x22' || c == '\\' || c < 0x20)
break;
++p;
}
return p;
}
template<>
inline
char const*
count_valid<false>(
char const* p,
char const* end) noexcept
{
while(p != end)
{
const unsigned char c = *p;
if(c == '\x22' || c == '\\' || c < 0x20)
break;
if(c < 0x80)
{
++p;
continue;
}
// validate utf-8
uint16_t first = classify_utf8(c);
uint8_t len = first & 0xFF;
if(BOOST_JSON_UNLIKELY(end - p < len))
break;
if(BOOST_JSON_UNLIKELY(! is_valid_utf8(p, first)))
break;
p += len;
}
return p;
}
#endif
// KRYSTIAN NOTE: does not stop to validate
// count_unescaped
#ifdef BOOST_JSON_USE_SSE2
inline
size_t
count_unescaped(
char const* s,
size_t n) noexcept
{
__m128i const q1 = _mm_set1_epi8( '\x22' ); // '"'
__m128i const q2 = _mm_set1_epi8( '\\' ); // '\\'
__m128i const q3 = _mm_set1_epi8( 0x1F );
char const * s0 = s;
while( n >= 16 )
{
__m128i v1 = _mm_loadu_si128( (__m128i const*)s );
__m128i v2 = _mm_cmpeq_epi8( v1, q1 ); // quote
__m128i v3 = _mm_cmpeq_epi8( v1, q2 ); // backslash
__m128i v4 = _mm_or_si128( v2, v3 ); // combine quotes and backslash
__m128i v5 = _mm_min_epu8( v1, q3 );
__m128i v6 = _mm_cmpeq_epi8( v5, v1 ); // controls
__m128i v7 = _mm_or_si128( v4, v6 ); // combine with control
int w = _mm_movemask_epi8( v7 );
if( w != 0 )
{
int m;
#if defined(__GNUC__) || defined(__clang__)
m = __builtin_ffs( w ) - 1;
#else
unsigned long index;
_BitScanForward( &index, w );
m = index;
#endif
s += m;
break;
}
s += 16;
n -= 16;
}
return s - s0;
}
#else
inline
std::size_t
count_unescaped(
char const*,
std::size_t) noexcept
{
return 0;
}
#endif
// count_digits
#ifdef BOOST_JSON_USE_SSE2
// assumes p..p+15 are valid
inline int count_digits( char const* p ) noexcept
{
__m128i v1 = _mm_loadu_si128( (__m128i const*)p );
v1 = _mm_add_epi8(v1, _mm_set1_epi8(70));
v1 = _mm_cmplt_epi8(v1, _mm_set1_epi8(118));
int m = _mm_movemask_epi8(v1);
int n;
if( m == 0 )
{
n = 16;
}
else
{
#if defined(__GNUC__) || defined(__clang__)
n = __builtin_ffs( m ) - 1;
#else
unsigned long index;
_BitScanForward( &index, m );
n = static_cast<int>(index);
#endif
}
return n;
}
#else
// assumes p..p+15 are valid
inline int count_digits( char const* p ) noexcept
{
int n = 0;
for( ; n < 16; ++n )
{
unsigned char const d = *p++ - '0';
if(d > 9) break;
}
return n;
}
#endif
// parse_unsigned
inline uint64_t parse_unsigned( uint64_t r, char const * p, std::size_t n ) noexcept
{
while( n >= 4 )
{
// faster on on clang for x86,
// slower on gcc
#ifdef __clang__
r = r * 10 + p[0] - '0';
r = r * 10 + p[1] - '0';
r = r * 10 + p[2] - '0';
r = r * 10 + p[3] - '0';
#else
uint32_t v;
std::memcpy( &v, p, 4 );
v -= 0x30303030;
unsigned w0 = v & 0xFF;
unsigned w1 = (v >> 8) & 0xFF;
unsigned w2 = (v >> 16) & 0xFF;
unsigned w3 = (v >> 24);
#ifdef BOOST_JSON_BIG_ENDIAN
r = (((r * 10 + w3) * 10 + w2) * 10 + w1) * 10 + w0;
#else
r = (((r * 10 + w0) * 10 + w1) * 10 + w2) * 10 + w3;
#endif
#endif
p += 4;
n -= 4;
}
switch( n )
{
case 0:
break;
case 1:
r = r * 10 + p[0] - '0';
break;
case 2:
r = r * 10 + p[0] - '0';
r = r * 10 + p[1] - '0';
break;
case 3:
r = r * 10 + p[0] - '0';
r = r * 10 + p[1] - '0';
r = r * 10 + p[2] - '0';
break;
}
return r;
}
// KRYSTIAN: this function is unused
// count_leading
/*
#ifdef BOOST_JSON_USE_SSE2
// assumes p..p+15
inline std::size_t count_leading( char const * p, char ch ) noexcept
{
__m128i const q1 = _mm_set1_epi8( ch );
__m128i v = _mm_loadu_si128( (__m128i const*)p );
__m128i w = _mm_cmpeq_epi8( v, q1 );
int m = _mm_movemask_epi8( w ) ^ 0xFFFF;
std::size_t n;
if( m == 0 )
{
n = 16;
}
else
{
#if defined(__GNUC__) || defined(__clang__)
n = __builtin_ffs( m ) - 1;
#else
unsigned long index;
_BitScanForward( &index, m );
n = index;
#endif
}
return n;
}
#else
// assumes p..p+15
inline std::size_t count_leading( char const * p, char ch ) noexcept
{
std::size_t n = 0;
for( ; n < 16 && *p == ch; ++p, ++n );
return n;
}
#endif
*/
// count_whitespace
#ifdef BOOST_JSON_USE_SSE2
inline const char* count_whitespace( char const* p, const char* end ) noexcept
{
if( p == end )
{
return p;
}
if( static_cast<unsigned char>( *p ) > 0x20 )
{
return p;
}
__m128i const q1 = _mm_set1_epi8( ' ' );
__m128i const q2 = _mm_set1_epi8( '\n' );
__m128i const q3 = _mm_set1_epi8( 4 ); // '\t' | 4 == '\r'
__m128i const q4 = _mm_set1_epi8( '\r' );
while( end - p >= 16 )
{
__m128i v0 = _mm_loadu_si128( (__m128i const*)p );
__m128i w0 = _mm_or_si128(
_mm_cmpeq_epi8( v0, q1 ),
_mm_cmpeq_epi8( v0, q2 ));
__m128i v1 = _mm_or_si128( v0, q3 );
__m128i w1 = _mm_cmpeq_epi8( v1, q4 );
__m128i w2 = _mm_or_si128( w0, w1 );
int m = _mm_movemask_epi8( w2 ) ^ 0xFFFF;
if( m != 0 )
{
#if defined(__GNUC__) || defined(__clang__)
std::size_t c = __builtin_ffs( m ) - 1;
#else
unsigned long index;
_BitScanForward( &index, m );
std::size_t c = index;
#endif
p += c;
return p;
}
p += 16;
}
while( p != end )
{
if( *p != ' ' && *p != '\t' && *p != '\r' && *p != '\n' )
{
return p;
}
++p;
}
return p;
}
/*
// slightly faster on msvc-14.2, slightly slower on clang-win
inline std::size_t count_whitespace( char const * p, std::size_t n ) noexcept
{
char const * p0 = p;
while( n > 0 )
{
char ch = *p;
if( ch == '\n' || ch == '\r' )
{
++p;
--n;
continue;
}
if( ch != ' ' && ch != '\t' )
{
break;
}
++p;
--n;
while( n >= 16 )
{
std::size_t n2 = count_leading( p, ch );
p += n2;
n -= n2;
if( n2 < 16 )
{
break;
}
}
}
return p - p0;
}
*/
#else
inline const char* count_whitespace( char const* p, const char* end ) noexcept
{
for(; p != end; ++p)
{
char const c = *p;
if( c != ' ' && c != '\n' && c != '\r' && c != '\t' ) break;
}
return p;
}
#endif
} // detail
} // namespace json
} // namespace boost
#endif
+109
View File
@@ -0,0 +1,109 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_STACK_HPP
#define BOOST_JSON_DETAIL_STACK_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/storage_ptr.hpp>
#include <cstring>
namespace boost {
namespace json {
namespace detail {
class stack
{
storage_ptr sp_;
std::size_t cap_ = 0;
std::size_t size_ = 0;
unsigned char* base_ = nullptr;
unsigned char* buf_ = nullptr;
public:
BOOST_JSON_DECL
~stack();
stack() = default;
stack(
storage_ptr sp,
unsigned char* buf,
std::size_t buf_size) noexcept;
bool
empty() const noexcept
{
return size_ == 0;
}
void
clear() noexcept
{
size_ = 0;
}
BOOST_JSON_DECL
void
reserve(std::size_t n);
template<class T>
void
push(T const& t)
{
auto const n = sizeof(T);
// If this assert goes off, it
// means the calling code did not
// reserve enough to prevent a
// reallocation.
//BOOST_ASSERT(cap_ >= size_ + n);
reserve(size_ + n);
std::memcpy(
base_ + size_, &t, n);
size_ += n;
}
template<class T>
void
push_unchecked(T const& t)
{
auto const n = sizeof(T);
BOOST_ASSERT(size_ + n <= cap_);
std::memcpy(
base_ + size_, &t, n);
size_ += n;
}
template<class T>
void
peek(T& t)
{
auto const n = sizeof(T);
BOOST_ASSERT(size_ >= n);
std::memcpy(&t,
base_ + size_ - n, n);
}
template<class T>
void
pop(T& t)
{
auto const n = sizeof(T);
BOOST_ASSERT(size_ >= n);
size_ -= n;
std::memcpy(
&t, base_ + size_, n);
}
};
} // detail
} // namespace json
} // namespace boost
#endif
+347
View File
@@ -0,0 +1,347 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_STREAM_HPP
#define BOOST_JSON_DETAIL_STREAM_HPP
namespace boost {
namespace json {
namespace detail {
class const_stream
{
friend class local_const_stream;
char const* p_;
char const* end_;
public:
const_stream() = default;
const_stream(
char const* data,
std::size_t size) noexcept
: p_(data)
, end_(data + size)
{
}
size_t
used(char const* begin) const noexcept
{
return static_cast<
size_t>(p_ - begin);
}
size_t
remain() const noexcept
{
return end_ - p_;
}
char const*
data() const noexcept
{
return p_;
}
operator bool() const noexcept
{
return p_ < end_;
}
// unchecked
char
operator*() const noexcept
{
BOOST_ASSERT(p_ < end_);
return *p_;
}
// unchecked
const_stream&
operator++() noexcept
{
BOOST_ASSERT(p_ < end_);
++p_;
return *this;
}
void
skip(std::size_t n) noexcept
{
BOOST_ASSERT(n <= remain());
p_ += n;
}
void
skip_to(const char* p) noexcept
{
BOOST_ASSERT(p <= end_ && p >= p_);
p_ = p;
}
};
class local_const_stream
: public const_stream
{
const_stream& src_;
public:
explicit
local_const_stream(
const_stream& src) noexcept
: const_stream(src)
, src_(src)
{
}
~local_const_stream()
{
src_.p_ = p_;
}
void
clip(std::size_t n) noexcept
{
if(static_cast<std::size_t>(
src_.end_ - p_) > n)
end_ = p_ + n;
else
end_ = src_.end_;
}
};
class const_stream_wrapper
{
const char*& p_;
const char* const end_;
friend class clipped_const_stream;
public:
const_stream_wrapper(
const char*& p,
const char* end)
: p_(p)
, end_(end)
{
}
void operator++() noexcept
{
++p_;
}
void operator+=(std::size_t n) noexcept
{
p_ += n;
}
void operator=(const char* p) noexcept
{
p_ = p;
}
char operator*() const noexcept
{
return *p_;
}
operator bool() const noexcept
{
return p_ < end_;
}
const char* begin() const noexcept
{
return p_;
}
const char* end() const noexcept
{
return end_;
}
std::size_t remain() const noexcept
{
return end_ - p_;
}
std::size_t remain(const char* p) const noexcept
{
return end_ - p;
}
std::size_t used(const char* p) const noexcept
{
return p_ - p;
}
};
class clipped_const_stream
: public const_stream_wrapper
{
const char* clip_;
public:
clipped_const_stream(
const char*& p,
const char* end)
: const_stream_wrapper(p, end)
, clip_(end)
{
}
void operator=(const char* p)
{
p_ = p;
}
const char* end() const noexcept
{
return clip_;
}
operator bool() const noexcept
{
return p_ < clip_;
}
std::size_t remain() const noexcept
{
return clip_ - p_;
}
std::size_t remain(const char* p) const noexcept
{
return clip_ - p;
}
void
clip(std::size_t n) noexcept
{
if(static_cast<std::size_t>(
end_ - p_) > n)
clip_ = p_ + n;
else
clip_ = end_;
}
};
//--------------------------------------
class stream
{
friend class local_stream;
char* p_;
char* end_;
public:
stream(
char* data,
std::size_t size) noexcept
: p_(data)
, end_(data + size)
{
}
size_t
used(char* begin) const noexcept
{
return static_cast<
size_t>(p_ - begin);
}
size_t
remain() const noexcept
{
return end_ - p_;
}
char*
data() noexcept
{
return p_;
}
operator bool() const noexcept
{
return p_ < end_;
}
// unchecked
char&
operator*() noexcept
{
BOOST_ASSERT(p_ < end_);
return *p_;
}
// unchecked
stream&
operator++() noexcept
{
BOOST_ASSERT(p_ < end_);
++p_;
return *this;
}
// unchecked
void
append(
char const* src,
std::size_t n) noexcept
{
BOOST_ASSERT(remain() >= n);
std::memcpy(p_, src, n);
p_ += n;
}
// unchecked
void
append(char c) noexcept
{
BOOST_ASSERT(p_ < end_);
*p_++ = c;
}
void
advance(std::size_t n) noexcept
{
BOOST_ASSERT(remain() >= n);
p_ += n;
}
};
class local_stream
: public stream
{
stream& src_;
public:
explicit
local_stream(
stream& src)
: stream(src)
, src_(src)
{
}
~local_stream()
{
src_.p_ = p_;
}
};
} // detail
} // namespace json
} // namespace boost
#endif
+385
View File
@@ -0,0 +1,385 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_STRING_IMPL_HPP
#define BOOST_JSON_DETAIL_STRING_IMPL_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/kind.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/json/detail/value.hpp>
#include <algorithm>
#include <iterator>
namespace boost {
namespace json {
class value;
class string;
namespace detail {
class string_impl
{
struct table
{
std::uint32_t size;
std::uint32_t capacity;
};
#if BOOST_JSON_ARCH == 64
static constexpr std::size_t sbo_chars_ = 14;
#elif BOOST_JSON_ARCH == 32
static constexpr std::size_t sbo_chars_ = 10;
#else
# error Unknown architecture
#endif
static
constexpr
kind
short_string_ =
static_cast<kind>(
((unsigned char)
kind::string) | 0x80);
static
constexpr
kind
key_string_ =
static_cast<kind>(
((unsigned char)
kind::string) | 0x40);
struct sbo
{
kind k; // must come first
char buf[sbo_chars_ + 1];
};
struct pointer
{
kind k; // must come first
table* t;
};
struct key
{
kind k; // must come first
std::uint32_t n;
char* s;
};
union
{
sbo s_;
pointer p_;
key k_;
};
#if BOOST_JSON_ARCH == 64
BOOST_STATIC_ASSERT(sizeof(sbo) <= 16);
BOOST_STATIC_ASSERT(sizeof(pointer) <= 16);
BOOST_STATIC_ASSERT(sizeof(key) <= 16);
#elif BOOST_JSON_ARCH == 32
BOOST_STATIC_ASSERT(sizeof(sbo) <= 24);
BOOST_STATIC_ASSERT(sizeof(pointer) <= 24);
BOOST_STATIC_ASSERT(sizeof(key) <= 24);
#endif
public:
static
constexpr
std::size_t
max_size() noexcept
{
// max_size depends on the address model
using min = std::integral_constant<std::size_t,
std::size_t(-1) - sizeof(table)>;
return min::value < BOOST_JSON_MAX_STRING_SIZE ?
min::value : BOOST_JSON_MAX_STRING_SIZE;
}
BOOST_JSON_DECL
string_impl() noexcept;
BOOST_JSON_DECL
string_impl(
std::size_t new_size,
storage_ptr const& sp);
BOOST_JSON_DECL
string_impl(
key_t,
string_view s,
storage_ptr const& sp);
BOOST_JSON_DECL
string_impl(
key_t,
string_view s1,
string_view s2,
storage_ptr const& sp);
BOOST_JSON_DECL
string_impl(
char** dest,
std::size_t len,
storage_ptr const& sp);
template<class InputIt>
string_impl(
InputIt first,
InputIt last,
storage_ptr const& sp,
std::random_access_iterator_tag)
: string_impl(last - first, sp)
{
char* out = data();
#if defined(_MSC_VER) && _MSC_VER <= 1900
while( first != last )
*out++ = *first++;
#else
std::copy(first, last, out);
#endif
}
template<class InputIt>
string_impl(
InputIt first,
InputIt last,
storage_ptr const& sp,
std::input_iterator_tag)
: string_impl(0, sp)
{
struct undo
{
string_impl* s;
storage_ptr const& sp;
~undo()
{
if(s)
s->destroy(sp);
}
};
undo u{this, sp};
auto dest = data();
while(first != last)
{
if(size() < capacity())
size(size() + 1);
else
dest = append(1, sp);
*dest++ = *first++;
}
term(size());
u.s = nullptr;
}
std::size_t
size() const noexcept
{
return s_.k == kind::string ?
p_.t->size :
sbo_chars_ -
s_.buf[sbo_chars_];
}
std::size_t
capacity() const noexcept
{
return s_.k == kind::string ?
p_.t->capacity :
sbo_chars_;
}
void
size(std::size_t n)
{
if(s_.k == kind::string)
p_.t->size = static_cast<
std::uint32_t>(n);
else
s_.buf[sbo_chars_] =
static_cast<char>(
sbo_chars_ - n);
}
BOOST_JSON_DECL
static
std::uint32_t
growth(
std::size_t new_size,
std::size_t capacity);
char const*
release_key(
std::size_t& n) noexcept
{
BOOST_ASSERT(
k_.k == key_string_);
n = k_.n;
auto const s = k_.s;
// prevent deallocate
k_.k = short_string_;
return s;
}
void
destroy(
storage_ptr const& sp) noexcept
{
if(s_.k == kind::string)
{
sp->deallocate(p_.t,
sizeof(table) +
p_.t->capacity + 1,
alignof(table));
}
else if(s_.k != key_string_)
{
// do nothing
}
else
{
BOOST_ASSERT(
s_.k == key_string_);
// VFALCO unfortunately the key string
// kind increases the cost of the destructor.
// This function should be skipped when using
// monotonic_resource.
sp->deallocate(k_.s, k_.n + 1);
}
}
BOOST_JSON_DECL
char*
assign(
std::size_t new_size,
storage_ptr const& sp);
BOOST_JSON_DECL
char*
append(
std::size_t n,
storage_ptr const& sp);
BOOST_JSON_DECL
void
insert(
std::size_t pos,
const char* s,
std::size_t n,
storage_ptr const& sp);
BOOST_JSON_DECL
char*
insert_unchecked(
std::size_t pos,
std::size_t n,
storage_ptr const& sp);
BOOST_JSON_DECL
void
replace(
std::size_t pos,
std::size_t n1,
const char* s,
std::size_t n2,
storage_ptr const& sp);
BOOST_JSON_DECL
char*
replace_unchecked(
std::size_t pos,
std::size_t n1,
std::size_t n2,
storage_ptr const& sp);
BOOST_JSON_DECL
void
shrink_to_fit(
storage_ptr const& sp) noexcept;
void
term(std::size_t n) noexcept
{
if(s_.k == short_string_)
{
s_.buf[sbo_chars_] =
static_cast<char>(
sbo_chars_ - n);
s_.buf[n] = 0;
}
else
{
p_.t->size = static_cast<
std::uint32_t>(n);
data()[n] = 0;
}
}
char*
data() noexcept
{
if(s_.k == short_string_)
return s_.buf;
return reinterpret_cast<
char*>(p_.t + 1);
}
char const*
data() const noexcept
{
if(s_.k == short_string_)
return s_.buf;
return reinterpret_cast<
char const*>(p_.t + 1);
}
char*
end() noexcept
{
return data() + size();
}
char const*
end() const noexcept
{
return data() + size();
}
};
template<class T>
string_view
to_string_view(T const& t) noexcept
{
return string_view(t);
}
template<class T, class U>
using string_and_stringlike = std::integral_constant<bool,
std::is_same<T, string>::value &&
std::is_convertible<U const&, string_view>::value>;
template<class T, class U>
using string_comp_op_requirement
= typename std::enable_if<
string_and_stringlike<T, U>::value ||
string_and_stringlike<U, T>::value,
bool>::type;
} // detail
} // namespace json
} // namespace boost
#endif
+199
View File
@@ -0,0 +1,199 @@
//
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_UTF8_HPP
#define BOOST_JSON_DETAIL_UTF8_HPP
#include <boost/json/detail/config.hpp>
#include <cstddef>
#include <cstring>
#include <cstdint>
namespace boost {
namespace json {
namespace detail {
template<int N>
std::uint32_t
load_little_endian(void const* p)
{
std::uint32_t v = 0;
std::memcpy(&v, p, N);
#ifdef BOOST_JSON_BIG_ENDIAN
v = ((v & 0xFF000000) >> 24) |
((v & 0x00FF0000) >> 8) |
((v & 0x0000FF00) << 8) |
((v & 0x000000FF) << 24);
#endif
return v;
}
inline
uint16_t
classify_utf8(char c)
{
// 0x000 = invalid
// 0x102 = 2 bytes, second byte [80, BF]
// 0x203 = 3 bytes, second byte [A0, BF]
// 0x303 = 3 bytes, second byte [80, BF]
// 0x403 = 3 bytes, second byte [80, 9F]
// 0x504 = 4 bytes, second byte [90, BF]
// 0x604 = 4 bytes, second byte [80, BF]
// 0x704 = 4 bytes, second byte [80, 8F]
static constexpr uint16_t first[128]
{
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
0x000, 0x000, 0x102, 0x102, 0x102, 0x102, 0x102, 0x102,
0x102, 0x102, 0x102, 0x102, 0x102, 0x102, 0x102, 0x102,
0x102, 0x102, 0x102, 0x102, 0x102, 0x102, 0x102, 0x102,
0x102, 0x102, 0x102, 0x102, 0x102, 0x102, 0x102, 0x102,
0x203, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303, 0x303,
0x303, 0x303, 0x303, 0x303, 0x303, 0x403, 0x303, 0x303,
0x504, 0x604, 0x604, 0x604, 0x704, 0x000, 0x000, 0x000,
0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
};
return first[static_cast<unsigned char>(c & 0x7F)];
}
inline
bool
is_valid_utf8(const char* p, uint16_t first)
{
uint32_t v;
switch(first >> 8)
{
default:
return false;
// 2 bytes, second byte [80, BF]
case 1:
v = load_little_endian<2>(p);
return (v & 0xC000) == 0x8000;
// 3 bytes, second byte [A0, BF]
case 2:
v = load_little_endian<3>(p);
return (v & 0xC0E000) == 0x80A000;
// 3 bytes, second byte [80, BF]
case 3:
v = load_little_endian<3>(p);
return (v & 0xC0C000) == 0x808000;
// 3 bytes, second byte [80, 9F]
case 4:
v = load_little_endian<3>(p);
return (v & 0xC0E000) == 0x808000;
// 4 bytes, second byte [90, BF]
case 5:
v = load_little_endian<4>(p);
return (v & 0xC0C0FF00) + 0x7F7F7000 <= 0x2F00;
// 4 bytes, second byte [80, BF]
case 6:
v = load_little_endian<4>(p);
return (v & 0xC0C0C000) == 0x80808000;
// 4 bytes, second byte [80, 8F]
case 7:
v = load_little_endian<4>(p);
return (v & 0xC0C0F000) == 0x80808000;
}
}
class utf8_sequence
{
char seq_[4];
uint16_t first_;
uint8_t size_;
public:
void
save(
const char* p,
std::size_t remain) noexcept
{
first_ = classify_utf8(*p );
if(remain >= length())
size_ = length();
else
size_ = static_cast<uint8_t>(remain);
std::memcpy(seq_, p, size_);
}
uint8_t
length() const noexcept
{
return first_ & 0xFF;
}
bool
complete() const noexcept
{
return size_ >= length();
}
// returns true if complete
bool
append(
const char* p,
std::size_t remain) noexcept
{
if(BOOST_JSON_UNLIKELY(needed() == 0))
return true;
if(BOOST_JSON_LIKELY(remain >= needed()))
{
std::memcpy(
seq_ + size_, p, needed());
size_ = length();
return true;
}
if(BOOST_JSON_LIKELY(remain > 0))
{
std::memcpy(seq_ + size_, p, remain);
size_ += static_cast<uint8_t>(remain);
}
return false;
}
const char*
data() const noexcept
{
return seq_;
}
uint8_t
needed() const noexcept
{
return length() - size_;
}
bool
valid() const noexcept
{
BOOST_ASSERT(size_ >= length());
return is_valid_utf8(seq_, first_);
}
};
} // detail
} // namespace json
} // namespace boost
#endif
+283
View File
@@ -0,0 +1,283 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_VALUE_HPP
#define BOOST_JSON_DETAIL_VALUE_HPP
#include <boost/json/kind.hpp>
#include <boost/json/storage_ptr.hpp>
#include <cstdint>
#include <limits>
#include <new>
#include <utility>
namespace boost {
namespace json {
namespace detail {
struct key_t
{
};
#if 0
template<class T>
struct to_number_limit
: std::numeric_limits<T>
{
};
template<class T>
struct to_number_limit<T const>
: to_number_limit<T>
{
};
template<>
struct to_number_limit<long long>
{
static constexpr long long (min)() noexcept
{
return -9223372036854774784;
}
static constexpr long long (max)() noexcept
{
return 9223372036854774784;
}
};
template<>
struct to_number_limit<unsigned long long>
{
static constexpr
unsigned long long (min)() noexcept
{
return 0;
}
static constexpr
unsigned long long (max)() noexcept
{
return 18446744073709549568ULL;
}
};
#else
template<class T>
class to_number_limit
{
// unsigned
static constexpr
double min1(std::false_type)
{
return 0.0;
}
static constexpr
double max1(std::false_type)
{
return max2u(std::integral_constant<
bool, (std::numeric_limits<T>::max)() ==
UINT64_MAX>{});
}
static constexpr
double max2u(std::false_type)
{
return static_cast<double>(
(std::numeric_limits<T>::max)());
}
static constexpr
double max2u(std::true_type)
{
return 18446744073709549568.0;
}
// signed
static constexpr
double min1(std::true_type)
{
return min2s(std::integral_constant<
bool, (std::numeric_limits<T>::max)() ==
INT64_MAX>{});
}
static constexpr
double min2s(std::false_type)
{
return static_cast<double>(
(std::numeric_limits<T>::min)());
}
static constexpr
double min2s(std::true_type)
{
return -9223372036854774784.0;
}
static constexpr
double max1(std::true_type)
{
return max2s(std::integral_constant<
bool, (std::numeric_limits<T>::max)() ==
INT64_MAX>{});
}
static constexpr
double max2s(std::false_type)
{
return static_cast<double>(
(std::numeric_limits<T>::max)());
}
static constexpr
double max2s(std::true_type)
{
return 9223372036854774784.0;
}
public:
static constexpr
double (min)() noexcept
{
return min1(std::is_signed<T>{});
}
static constexpr
double (max)() noexcept
{
return max1(std::is_signed<T>{});
}
};
#endif
struct scalar
{
storage_ptr sp; // must come first
kind k; // must come second
union
{
bool b;
std::int64_t i;
std::uint64_t u;
double d;
};
explicit
scalar(storage_ptr sp_ = {}) noexcept
: sp(std::move(sp_))
, k(json::kind::null)
{
}
explicit
scalar(bool b_,
storage_ptr sp_ = {}) noexcept
: sp(std::move(sp_))
, k(json::kind::bool_)
, b(b_)
{
}
explicit
scalar(std::int64_t i_,
storage_ptr sp_ = {}) noexcept
: sp(std::move(sp_))
, k(json::kind::int64)
, i(i_)
{
}
explicit
scalar(std::uint64_t u_,
storage_ptr sp_ = {}) noexcept
: sp(std::move(sp_))
, k(json::kind::uint64)
, u(u_)
{
}
explicit
scalar(double d_,
storage_ptr sp_ = {}) noexcept
: sp(std::move(sp_))
, k(json::kind::double_)
, d(d_)
{
}
};
struct access
{
template<class Value, class... Args>
static
Value&
construct_value(Value* p, Args&&... args)
{
return *reinterpret_cast<
Value*>(::new(p) Value(
std::forward<Args>(args)...));
}
template<class KeyValuePair, class... Args>
static
KeyValuePair&
construct_key_value_pair(
KeyValuePair* p, Args&&... args)
{
return *reinterpret_cast<
KeyValuePair*>(::new(p)
KeyValuePair(
std::forward<Args>(args)...));
}
template<class Value>
static
char const*
release_key(
Value& jv,
std::size_t& len) noexcept
{
BOOST_ASSERT(jv.is_string());
jv.str_.sp_.~storage_ptr();
return jv.str_.impl_.release_key(len);
}
using index_t = std::uint32_t;
template<class KeyValuePair>
static
index_t&
next(KeyValuePair& e) noexcept
{
return e.next_;
}
template<class KeyValuePair>
static
index_t const&
next(KeyValuePair const& e) noexcept
{
return e.next_;
}
};
BOOST_JSON_DECL
std::size_t
hash_value_impl( value const& jv ) noexcept;
} // detail
} // namespace json
} // namespace boost
#endif
+278
View File
@@ -0,0 +1,278 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
// Copyright (c) 2022 Dmitry Arkhipov (grisumbras@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_VALUE_FROM_HPP
#define BOOST_JSON_DETAIL_VALUE_FROM_HPP
#include <boost/json/conversion.hpp>
#include <boost/describe/enum_to_string.hpp>
#include <boost/mp11/algorithm.hpp>
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
# include <optional>
#endif
namespace boost {
namespace json {
namespace detail {
template< class Ctx, class T >
struct append_tuple_element {
array& arr;
Ctx const& ctx;
T&& t;
template<std::size_t I>
void
operator()(mp11::mp_size_t<I>) const
{
using std::get;
arr.emplace_back(value_from(
get<I>(std::forward<T>(t)), ctx, arr.storage() ));
}
};
//----------------------------------------------------------
// User-provided conversion
template< class T, class Ctx >
void
value_from_impl( user_conversion_tag, value& jv, T&& from, Ctx const& )
{
tag_invoke( value_from_tag(), jv, static_cast<T&&>(from) );
}
template< class T, class Ctx >
void
value_from_impl( context_conversion_tag, value& jv, T&& from, Ctx const& ctx)
{
using Sup = supported_context<Ctx, T, value_from_conversion>;
tag_invoke( value_from_tag(), jv, static_cast<T&&>(from), Sup::get(ctx) );
}
template< class T, class Ctx >
void
value_from_impl(
full_context_conversion_tag, value& jv, T&& from, Ctx const& ctx)
{
using Sup = supported_context<Ctx, T, value_from_conversion>;
tag_invoke(
value_from_tag(), jv, static_cast<T&&>(from), Sup::get(ctx), ctx );
}
//----------------------------------------------------------
// Native conversion
template< class T, class Ctx >
void
value_from_impl( native_conversion_tag, value& jv, T&& from, Ctx const& )
{
jv = std::forward<T>(from);
}
// null-like types
template< class T, class Ctx >
void
value_from_impl( null_like_conversion_tag, value& jv, T&&, Ctx const& )
{
// do nothing
BOOST_ASSERT(jv.is_null());
(void)jv;
}
// string-like types
template< class T, class Ctx >
void
value_from_impl( string_like_conversion_tag, value& jv, T&& from, Ctx const& )
{
auto sv = static_cast<string_view>(from);
jv.emplace_string().assign(sv);
}
// map-like types
template< class T, class Ctx >
void
value_from_impl( map_like_conversion_tag, value& jv, T&& from, Ctx const& ctx )
{
using std::get;
object& obj = jv.emplace_object();
obj.reserve(detail::try_size(from, size_implementation<T>()));
for (auto&& elem : from)
obj.emplace(
get<0>(elem),
value_from( get<1>(elem), ctx, obj.storage() ));
}
// ranges
template< class T, class Ctx >
void
value_from_impl( sequence_conversion_tag, value& jv, T&& from, Ctx const& ctx )
{
array& result = jv.emplace_array();
result.reserve(detail::try_size(from, size_implementation<T>()));
using ForwardedValue = forwarded_value<T&&>;
for (auto&& elem : from)
result.emplace_back(
value_from(
// not a static_cast in order to appease clang < 4.0
ForwardedValue(elem),
ctx,
result.storage() ));
}
// tuple-like types
template< class T, class Ctx >
void
value_from_impl( tuple_conversion_tag, value& jv, T&& from, Ctx const& ctx )
{
constexpr std::size_t n =
std::tuple_size<remove_cvref<T>>::value;
array& arr = jv.emplace_array();
arr.reserve(n);
mp11::mp_for_each<mp11::mp_iota_c<n>>(
append_tuple_element< Ctx, T >{ arr, ctx, std::forward<T>(from) });
}
// no suitable conversion implementation
template< class T, class Ctx >
void
value_from_impl( no_conversion_tag, value&, T&&, Ctx const& )
{
static_assert(
!std::is_same<T, T>::value,
"No suitable tag_invoke overload found for the type");
}
template< class Ctx, class T >
struct from_described_member
{
using Ds = describe::describe_members<
remove_cvref<T>, describe::mod_public | describe::mod_inherited>;
object& obj;
Ctx const& ctx;
T&& from;
template< class I >
void
operator()(I) const
{
using D = mp11::mp_at<Ds, I>;
obj.emplace(
D::name,
value_from(
static_cast<T&&>(from).* D::pointer,
ctx,
obj.storage()));
}
};
// described classes
template< class T, class Ctx >
void
value_from_impl(
described_class_conversion_tag, value& jv, T&& from, Ctx const& ctx )
{
object& obj = jv.emplace_object();
from_described_member<Ctx, T> member_converter{
obj, ctx, static_cast<T&&>(from)};
using Ds = typename decltype(member_converter)::Ds;
constexpr std::size_t N = mp11::mp_size<Ds>::value;
obj.reserve(N);
mp11::mp_for_each< mp11::mp_iota_c<N> >(member_converter);
}
// described enums
template< class T, class Ctx >
void
value_from_impl(
described_enum_conversion_tag, value& jv, T from, Ctx const& )
{
(void)jv;
(void)from;
#ifdef BOOST_DESCRIBE_CXX14
char const* const name = describe::enum_to_string(from, nullptr);
if( name )
{
string& str = jv.emplace_string();
str.assign(name);
}
else
{
using Integer = typename std::underlying_type< remove_cvref<T> >::type;
jv = static_cast<Integer>(from);
}
#endif
}
// optionals
template< class T, class Ctx >
void
value_from_impl(
optional_conversion_tag, value& jv, T&& from, Ctx const& ctx )
{
if( from )
value_from( *from, ctx, jv );
else
jv = nullptr;
}
// variants
template< class Ctx >
struct value_from_visitor
{
value& jv;
Ctx const& ctx;
template<class T>
void
operator()(T&& t)
{
value_from( static_cast<T&&>(t), ctx, jv );
}
};
template< class Ctx, class T >
void
value_from_impl( variant_conversion_tag, value& jv, T&& from, Ctx const& ctx )
{
visit( value_from_visitor<Ctx>{ jv, ctx }, static_cast<T&&>(from) );
}
//----------------------------------------------------------
// Contextual conversions
template< class Ctx, class T >
using value_from_category = conversion_category<
Ctx, T, value_from_conversion >;
} // detail
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
inline
void
tag_invoke(
value_from_tag,
value& jv,
std::nullopt_t)
{
// do nothing
BOOST_ASSERT(jv.is_null());
(void)jv;
}
#endif
} // namespace json
} // namespace boost
#endif
+883
View File
@@ -0,0 +1,883 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
// Copyright (c) 2021 Dmitry Arkhipov (grisumbras@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_DETAIL_VALUE_TO_HPP
#define BOOST_JSON_DETAIL_VALUE_TO_HPP
#include <boost/json/value.hpp>
#include <boost/json/conversion.hpp>
#include <boost/describe/enum_from_string.hpp>
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
# include <optional>
#endif
namespace boost {
namespace json {
namespace detail {
template<class T>
using has_reserve_member_helper = decltype(std::declval<T&>().reserve(0));
template<class T>
using has_reserve_member = mp11::mp_valid<has_reserve_member_helper, T>;
template<class T>
using reserve_implementation = mp11::mp_cond<
is_tuple_like<T>, mp11::mp_int<2>,
has_reserve_member<T>, mp11::mp_int<1>,
mp11::mp_true, mp11::mp_int<0>>;
template<class T>
error
try_reserve(
T&,
std::size_t size,
mp11::mp_int<2>)
{
constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
if ( N != size )
return error::size_mismatch;
return error();
}
template<typename T>
error
try_reserve(
T& cont,
std::size_t size,
mp11::mp_int<1>)
{
cont.reserve(size);
return error();
}
template<typename T>
error
try_reserve(
T&,
std::size_t,
mp11::mp_int<0>)
{
return error();
}
// identity conversion
template< class Ctx >
result<value>
value_to_impl(
value_conversion_tag,
try_value_to_tag<value>,
value const& jv,
Ctx const& )
{
return jv;
}
template< class Ctx >
value
value_to_impl(
value_conversion_tag, value_to_tag<value>, value const& jv, Ctx const& )
{
return jv;
}
// object
template< class Ctx >
result<object>
value_to_impl(
object_conversion_tag,
try_value_to_tag<object>,
value const& jv,
Ctx const& )
{
object const* obj = jv.if_object();
if( obj )
return *obj;
error_code ec;
BOOST_JSON_FAIL(ec, error::not_object);
return ec;
}
// array
template< class Ctx >
result<array>
value_to_impl(
array_conversion_tag,
try_value_to_tag<array>,
value const& jv,
Ctx const& )
{
array const* arr = jv.if_array();
if( arr )
return *arr;
error_code ec;
BOOST_JSON_FAIL(ec, error::not_array);
return ec;
}
// string
template< class Ctx >
result<string>
value_to_impl(
string_conversion_tag,
try_value_to_tag<string>,
value const& jv,
Ctx const& )
{
string const* str = jv.if_string();
if( str )
return *str;
error_code ec;
BOOST_JSON_FAIL(ec, error::not_string);
return ec;
}
// bool
template< class Ctx >
result<bool>
value_to_impl(
bool_conversion_tag, try_value_to_tag<bool>, value const& jv, Ctx const& )
{
auto b = jv.if_bool();
if( b )
return *b;
error_code ec;
BOOST_JSON_FAIL(ec, error::not_bool);
return {boost::system::in_place_error, ec};
}
// integral and floating point
template< class T, class Ctx >
result<T>
value_to_impl(
number_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
{
error_code ec;
auto const n = jv.to_number<T>(ec);
if( ec.failed() )
return {boost::system::in_place_error, ec};
return {boost::system::in_place_value, n};
}
// null-like conversion
template< class T, class Ctx >
result<T>
value_to_impl(
null_like_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& )
{
if( jv.is_null() )
return {boost::system::in_place_value, T{}};
error_code ec;
BOOST_JSON_FAIL(ec, error::not_null);
return {boost::system::in_place_error, ec};
}
// string-like types
template< class T, class Ctx >
result<T>
value_to_impl(
string_like_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& )
{
auto str = jv.if_string();
if( str )
return {boost::system::in_place_value, T(str->subview())};
error_code ec;
BOOST_JSON_FAIL(ec, error::not_string);
return {boost::system::in_place_error, ec};
}
// map-like containers
template< class T, class Ctx >
result<T>
value_to_impl(
map_like_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& ctx )
{
object const* obj = jv.if_object();
if( !obj )
{
error_code ec;
BOOST_JSON_FAIL(ec, error::not_object);
return {boost::system::in_place_error, ec};
}
T res;
error const e = detail::try_reserve(
res, obj->size(), reserve_implementation<T>());
if( e != error() )
{
error_code ec;
BOOST_JSON_FAIL( ec, e );
return {boost::system::in_place_error, ec};
}
auto ins = detail::inserter(res, inserter_implementation<T>());
for( key_value_pair const& kv: *obj )
{
auto elem_res = try_value_to<mapped_type<T>>( kv.value(), ctx );
if( elem_res.has_error() )
return {boost::system::in_place_error, elem_res.error()};
*ins++ = value_type<T>{
key_type<T>(kv.key()),
std::move(*elem_res)};
}
return res;
}
// all other containers
template< class T, class Ctx >
result<T>
value_to_impl(
sequence_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& ctx )
{
array const* arr = jv.if_array();
if( !arr )
{
error_code ec;
BOOST_JSON_FAIL(ec, error::not_array);
return {boost::system::in_place_error, ec};
}
T result;
error const e = detail::try_reserve(
result, arr->size(), reserve_implementation<T>());
if( e != error() )
{
error_code ec;
BOOST_JSON_FAIL( ec, e );
return {boost::system::in_place_error, ec};
}
auto ins = detail::inserter(result, inserter_implementation<T>());
for( value const& val: *arr )
{
auto elem_res = try_value_to<value_type<T>>( val, ctx );
if( elem_res.has_error() )
return {boost::system::in_place_error, elem_res.error()};
*ins++ = std::move(*elem_res);
}
return result;
}
// tuple-like types
template< class T, class Ctx >
result<T>
try_make_tuple_elem(value const& jv, Ctx const& ctx, error_code& ec)
{
if( ec.failed() )
return {boost::system::in_place_error, ec};
auto result = try_value_to<T>( jv, ctx );
ec = result.error();
return result;
}
template <class T, class Ctx, std::size_t... Is>
result<T>
try_make_tuple_like(
array const& arr, Ctx const& ctx, boost::mp11::index_sequence<Is...>)
{
error_code ec;
auto items = std::make_tuple(
try_make_tuple_elem<
typename std::decay<tuple_element_t<Is, T>>::type >(
arr[Is], ctx, ec)
...);
if( ec.failed() )
return {boost::system::in_place_error, ec};
return {
boost::system::in_place_value, T(std::move(*std::get<Is>(items))...)};
}
template< class T, class Ctx >
result<T>
value_to_impl(
tuple_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& ctx )
{
error_code ec;
array const* arr = jv.if_array();
if( !arr )
{
BOOST_JSON_FAIL(ec, error::not_array);
return {boost::system::in_place_error, ec};
}
constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
if( N != arr->size() )
{
BOOST_JSON_FAIL(ec, error::size_mismatch);
return {boost::system::in_place_error, ec};
}
return try_make_tuple_like<T>(
*arr, ctx, boost::mp11::make_index_sequence<N>());
}
template< class Ctx, class T, bool non_throwing = true >
struct to_described_member
{
using Ds = describe::describe_members<
T, describe::mod_public | describe::mod_inherited>;
using result_type = mp11::mp_eval_if_c< !non_throwing, T, result, T >;
result_type& res;
object const& obj;
std::size_t count;
Ctx const& ctx;
template< class I >
void
operator()(I)
{
if( !res )
return;
using D = mp11::mp_at<Ds, I>;
using M = described_member_t<T, D>;
auto const found = obj.find(D::name);
if( found == obj.end() )
{
BOOST_IF_CONSTEXPR( !is_optional_like<M>::value )
{
error_code ec;
BOOST_JSON_FAIL(ec, error::unknown_name);
res = {boost::system::in_place_error, ec};
}
return;
}
#if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused"
# pragma GCC diagnostic ignored "-Wunused-variable"
#endif
auto member_res = try_value_to<M>( found->value(), ctx );
#if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
# pragma GCC diagnostic pop
#endif
if( member_res )
{
(*res).* D::pointer = std::move(*member_res);
++count;
}
else
res = {boost::system::in_place_error, member_res.error()};
}
};
// described classes
template< class T, class Ctx >
result<T>
value_to_impl(
described_class_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& ctx )
{
result<T> res;
auto* obj = jv.if_object();
if( !obj )
{
error_code ec;
BOOST_JSON_FAIL(ec, error::not_object);
res = {boost::system::in_place_error, ec};
return res;
}
to_described_member< Ctx, T > member_converter{ res, *obj, 0u, ctx };
using Ds = typename decltype(member_converter)::Ds;
constexpr std::size_t N = mp11::mp_size<Ds>::value;
mp11::mp_for_each< mp11::mp_iota_c<N> >(member_converter);
if( !res )
return res;
if( member_converter.count != obj->size() )
{
error_code ec;
BOOST_JSON_FAIL(ec, error::size_mismatch);
res = {boost::system::in_place_error, ec};
return res;
}
return res;
}
// described enums
template< class T, class Ctx >
result<T>
value_to_impl(
described_enum_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& )
{
T val = {};
(void)jv;
#ifdef BOOST_DESCRIBE_CXX14
error_code ec;
auto str = jv.if_string();
if( !str )
{
BOOST_JSON_FAIL(ec, error::not_string);
return {system::in_place_error, ec};
}
if( !describe::enum_from_string(str->data(), val) )
{
BOOST_JSON_FAIL(ec, error::unknown_name);
return {system::in_place_error, ec};
}
#endif
return {system::in_place_value, val};
}
// optionals
template< class T, class Ctx >
result<T>
value_to_impl(
optional_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& ctx)
{
using Inner = value_result_type<T>;
if( jv.is_null() )
return {};
else
return try_value_to<Inner>(jv, ctx);
}
// variants
template< class T, class V, class I >
using variant_construction_category = mp11::mp_cond<
std::is_constructible< T, variant2::in_place_index_t<I::value>, V >,
mp11::mp_int<2>,
#ifndef BOOST_NO_CXX17_HDR_VARIANT
std::is_constructible< T, std::in_place_index_t<I::value>, V >,
mp11::mp_int<1>,
#endif // BOOST_NO_CXX17_HDR_VARIANT
mp11::mp_true,
mp11::mp_int<0> >;
template< class T, class I, class V >
T
initialize_variant( V&& v, mp11::mp_int<0> )
{
T t;
t.template emplace<I::value>( std::move(v) );
return t;
}
template< class T, class I, class V >
T
initialize_variant( V&& v, mp11::mp_int<2> )
{
return T( variant2::in_place_index_t<I::value>(), std::move(v) );
}
#ifndef BOOST_NO_CXX17_HDR_VARIANT
template< class T, class I, class V >
T
initialize_variant( V&& v, mp11::mp_int<1> )
{
return T( std::in_place_index_t<I::value>(), std::move(v) );
}
#endif // BOOST_NO_CXX17_HDR_VARIANT
template< class T, class Ctx >
struct alternative_converter
{
result<T>& res;
value const& jv;
Ctx const& ctx;
template< class I >
void operator()( I ) const
{
if( res )
return;
using V = mp11::mp_at<T, I>;
auto attempt = try_value_to<V>(jv, ctx);
if( attempt )
{
using cat = variant_construction_category<T, V, I>;
res = initialize_variant<T, I>( std::move(*attempt), cat() );
}
}
};
template< class T, class Ctx >
result<T>
value_to_impl(
variant_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& ctx)
{
error_code ec;
BOOST_JSON_FAIL(ec, error::exhausted_variants);
using Is = mp11::mp_iota< mp11::mp_size<T> >;
result<T> res = {system::in_place_error, ec};
mp11::mp_for_each<Is>( alternative_converter<T, Ctx>{res, jv, ctx} );
return res;
}
//----------------------------------------------------------
// User-provided conversions; throwing -> throwing
template< class T, class Ctx >
mp11::mp_if< mp11::mp_valid<has_user_conversion_to_impl, T>, T >
value_to_impl(
user_conversion_tag, value_to_tag<T> tag, value const& jv, Ctx const&)
{
return tag_invoke(tag, jv);
}
template<
class T,
class Ctx,
class Sup = supported_context<Ctx, T, value_to_conversion>
>
mp11::mp_if<
mp11::mp_valid< has_context_conversion_to_impl, typename Sup::type, T>, T >
value_to_impl(
context_conversion_tag,
value_to_tag<T> tag,
value const& jv,
Ctx const& ctx )
{
return tag_invoke( tag, jv, Sup::get(ctx) );
}
template<
class T,
class Ctx,
class Sup = supported_context<Ctx, T, value_to_conversion>
>
mp11::mp_if<
mp11::mp_valid<
has_full_context_conversion_to_impl, typename Sup::type, T>,
T>
value_to_impl(
full_context_conversion_tag,
value_to_tag<T> tag,
value const& jv,
Ctx const& ctx )
{
return tag_invoke( tag, jv, Sup::get(ctx), ctx );
}
//----------------------------------------------------------
// User-provided conversions; throwing -> nonthrowing
template< class T, class Ctx >
mp11::mp_if_c< !mp11::mp_valid<has_user_conversion_to_impl, T>::value, T>
value_to_impl(
user_conversion_tag, value_to_tag<T>, value const& jv, Ctx const& )
{
auto res = tag_invoke(try_value_to_tag<T>(), jv);
if( res.has_error() )
throw_system_error( res.error() );
return std::move(*res);
}
template<
class T,
class Ctx,
class Sup = supported_context<Ctx, T, value_to_conversion>
>
mp11::mp_if_c<
!mp11::mp_valid<
has_context_conversion_to_impl, typename Sup::type, T>::value,
T>
value_to_impl(
context_conversion_tag, value_to_tag<T>, value const& jv, Ctx const& ctx )
{
auto res = tag_invoke( try_value_to_tag<T>(), jv, Sup::get(ctx) );
if( res.has_error() )
throw_system_error( res.error() );
return std::move(*res);
}
template< class Ctx >
std::tuple<allow_exceptions, Ctx>
make_throwing_context(Ctx const& ctx)
{
return std::tuple<allow_exceptions, Ctx>(allow_exceptions(), ctx);
}
template< class... Ctxes >
std::tuple<allow_exceptions, Ctxes...>
make_throwing_context(std::tuple<Ctxes...> const& ctx)
{
return std::tuple_cat(std::make_tuple( allow_exceptions() ), ctx);
}
template< class... Ctxes >
std::tuple<allow_exceptions, Ctxes...> const&
make_throwing_context(std::tuple<allow_exceptions, Ctxes...> const& ctx)
noexcept
{
return ctx;
}
template<
class T,
class Ctx,
class Sup = supported_context<Ctx, T, value_to_conversion>
>
mp11::mp_if_c<
!mp11::mp_valid<
has_full_context_conversion_to_impl, typename Sup::type, T>::value,
T>
value_to_impl(
full_context_conversion_tag,
value_to_tag<T>,
value const& jv,
Ctx const& ctx )
{
auto res = tag_invoke(
try_value_to_tag<T>(),
jv,
Sup::get(ctx),
make_throwing_context(ctx));
if( res.has_error() )
throw_system_error( res.error() );
return std::move(*res);
}
//----------------------------------------------------------
// User-provided conversions; nonthrowing -> nonthrowing
template< class T, class Ctx >
mp11::mp_if<
mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>, result<T> >
value_to_impl(
user_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
{
return tag_invoke(try_value_to_tag<T>(), jv);
}
template<
class T,
class Ctx,
class Sup = supported_context<Ctx, T, value_to_conversion>
>
mp11::mp_if<
mp11::mp_valid<
has_nonthrowing_context_conversion_to_impl, typename Sup::type, T>,
result<T> >
value_to_impl(
context_conversion_tag,
try_value_to_tag<T> tag,
value const& jv,
Ctx const& ctx )
{
return tag_invoke( tag, jv, Sup::get(ctx) );
}
template<
class T,
class Ctx,
class Sup = supported_context<Ctx, T, value_to_conversion>
>
mp11::mp_if<
mp11::mp_valid<
has_nonthrowing_full_context_conversion_to_impl,
typename Sup::type,
T>,
result<T> >
value_to_impl(
full_context_conversion_tag,
try_value_to_tag<T> tag,
value const& jv,
Ctx const& ctx )
{
return tag_invoke( tag, jv, Sup::get(ctx), ctx );
}
//----------------------------------------------------------
// User-provided conversions; nonthrowing -> throwing
template< class Ctx >
struct does_allow_exceptions : std::false_type
{ };
template< class... Ctxes >
struct does_allow_exceptions< std::tuple<allow_exceptions, Ctxes...> >
: std::true_type
{ };
template< class T, class... Args >
result<T>
wrap_conversion_exceptions( std::true_type, value_to_tag<T>, Args&& ... args )
{
return {
boost::system::in_place_value,
tag_invoke( value_to_tag<T>(), static_cast<Args&&>(args)... )};
}
template< class T, class... Args >
result<T>
wrap_conversion_exceptions( std::false_type, value_to_tag<T>, Args&& ... args )
{
#ifndef BOOST_NO_EXCEPTIONS
try
{
#endif
return wrap_conversion_exceptions(
std::true_type(),
value_to_tag<T>(),
static_cast<Args&&>(args)... );
#ifndef BOOST_NO_EXCEPTIONS
}
catch( std::bad_alloc const&)
{
throw;
}
catch( system_error const& e)
{
return {boost::system::in_place_error, e.code()};
}
catch( ... )
{
error_code ec;
BOOST_JSON_FAIL(ec, error::exception);
return {boost::system::in_place_error, ec};
}
#endif
}
template< class T, class Ctx >
mp11::mp_if_c<
!mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>::value,
result<T> >
value_to_impl(
user_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
{
return wrap_conversion_exceptions(
does_allow_exceptions<Ctx>(), value_to_tag<T>(), jv);
}
template<
class T,
class Ctx,
class Sup = supported_context<Ctx, T, value_to_conversion>
>
mp11::mp_if_c<
!mp11::mp_valid<
has_nonthrowing_context_conversion_to_impl,
typename Sup::type,
T>::value,
result<T> >
value_to_impl(
context_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& ctx )
{
return wrap_conversion_exceptions(
does_allow_exceptions<Ctx>(), value_to_tag<T>(), jv, Sup::get(ctx) );
}
template<
class T,
class Ctx,
class Sup = supported_context<Ctx, T, value_to_conversion>
>
mp11::mp_if_c<
!mp11::mp_valid<
has_nonthrowing_full_context_conversion_to_impl,
typename Sup::type,
T>::value,
result<T> >
value_to_impl(
full_context_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& ctx )
{
return wrap_conversion_exceptions(
does_allow_exceptions<Ctx>(),
value_to_tag<T>(),
jv,
Sup::get(ctx),
ctx);
}
// no suitable conversion implementation
template< class T, class Ctx >
T
value_to_impl( no_conversion_tag, value_to_tag<T>, value const&, Ctx const& )
{
static_assert(
!std::is_same<T, T>::value,
"No suitable tag_invoke overload found for the type");
}
// generic wrapper over non-throwing implementations
template< class Impl, class T, class Ctx >
T
value_to_impl( Impl impl, value_to_tag<T>, value const& jv, Ctx const& ctx )
{
return value_to_impl(
impl, try_value_to_tag<T>(), jv, make_throwing_context(ctx) ).value();
}
template< class Ctx, class T >
using value_to_category = conversion_category<
Ctx, T, value_to_conversion >;
} // detail
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
inline
result<std::nullopt_t>
tag_invoke(
try_value_to_tag<std::nullopt_t>,
value const& jv)
{
if( jv.is_null() )
return std::nullopt;
error_code ec;
BOOST_JSON_FAIL(ec, error::not_null);
return ec;
}
#endif
} // namespace json
} // namespace boost
#endif
+183
View File
@@ -0,0 +1,183 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_ERROR_HPP
#define BOOST_JSON_ERROR_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/system_error.hpp>
namespace boost {
namespace json {
/** Error codes returned by JSON operations
*/
enum class error
{
//
// parse errors
//
/// syntax error
syntax = 1,
/// extra data
extra_data,
/// incomplete JSON
incomplete,
/// exponent too large
exponent_overflow,
/// too deep
too_deep,
/// illegal leading surrogate
illegal_leading_surrogate,
/// illegal trailing surrogate
illegal_trailing_surrogate,
/// expected hex digit
expected_hex_digit,
/// expected utf16 escape
expected_utf16_escape,
/// An object contains too many elements
object_too_large,
/// An array contains too many elements
array_too_large,
/// A key is too large
key_too_large,
/// A string is too large
string_too_large,
/// A number is too large
number_too_large,
/// error occured when trying to read input
input_error,
//
// generic errors
//
/// An exception was thrown during operation
exception,
/// A requested element is outside of container's range
out_of_range,
/// test failure
test_failure,
//
// JSON Pointer errors
//
/// missing slash character before token reference
missing_slash,
/// invalid escape sequence
invalid_escape,
/// token should be a number but cannot be parsed as such
token_not_number,
/// current value is neither an object nor an array
value_is_scalar,
/// current value does not contain referenced value
not_found,
/// token cannot be represented by std::size_t
token_overflow,
/// past-the-end index is not supported
past_the_end,
//
// Conversion errors
//
/// JSON number was expected during conversion
not_number,
/// number cast is not exact
not_exact,
/// JSON null was expected during conversion
not_null,
/// JSON bool was expected during conversion
not_bool,
/// JSON array was expected during conversion
not_array,
/// JSON object was expected during conversion
not_object,
/// JSON string was expected during conversion
not_string,
/// std::int64_t was expected during conversion
not_int64,
/// std::uint64_t was expected during conversion
not_uint64,
/// `double` was expected during conversion
not_double,
/// JSON integer was expected during conversion
not_integer,
/// source composite has size incompatible with target
size_mismatch,
/// none of the possible conversions were successful
exhausted_variants,
/// the key does not correspond to a known name
unknown_name,
};
/** Error conditions corresponding to JSON errors
*/
enum class condition
{
/// A parser-related error
parse_error = 1,
/// An error related to parsing JSON pointer string
pointer_parse_error,
/// An error related to applying JSON pointer string to a value
pointer_use_error,
/// A conversion error
conversion_error,
/// A generic error
generic_error,
};
} // namespace json
} // namespace boost
#include <boost/json/impl/error.hpp>
#endif
+92
View File
@@ -0,0 +1,92 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_FWD_HPP
#define BOOST_JSON_FWD_HPP
#include <boost/json/detail/config.hpp>
namespace boost {
namespace json {
// Forward declarations
#ifndef BOOST_JSON_DOCS
class array;
class object;
class string;
class value;
class key_value_pair;
class storage_ptr;
struct value_from_tag;
template<class T>
struct value_to_tag;
template<class T>
struct try_value_to_tag;
template<class T1, class T2>
struct result_for;
template<class T>
struct is_string_like;
template<class T>
struct is_sequence_like;
template<class T>
struct is_map_like;
template<class T>
struct is_tuple_like;
template<class T>
struct is_null_like;
template<class T>
struct is_described_class;
template<class T>
struct is_described_enum;
template<class T>
void value_from( T&& t, value& jv );
template<class T, class Context>
void value_from( T&& t, value& jv, Context const& ctx );
template<class T>
T value_to( value const & v );
template<class T, class Context>
T value_to( value const & v, Context const& ctx );
template<class T>
typename result_for<T, value>::type
try_value_to( value const & jv );
template<class T, class Context>
typename result_for<T, value>::type
try_value_to( value const & jv, Context const& ctx );
template<class T>
typename result_for<T, value>::type
result_from_errno( int e, boost::source_location const* loc ) noexcept;
#endif
} // namespace json
} // namespace boost
#endif
+590
View File
@@ -0,0 +1,590 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_ARRAY_HPP
#define BOOST_JSON_IMPL_ARRAY_HPP
#include <boost/json/value.hpp>
#include <boost/json/detail/except.hpp>
#include <algorithm>
#include <stdexcept>
#include <type_traits>
namespace boost {
namespace json {
//----------------------------------------------------------
struct alignas(value)
array::table
{
std::uint32_t size = 0;
std::uint32_t capacity = 0;
constexpr table();
value&
operator[](std::size_t pos) noexcept
{
return (reinterpret_cast<
value*>(this + 1))[pos];
}
BOOST_JSON_DECL
static
table*
allocate(
std::size_t capacity,
storage_ptr const& sp);
BOOST_JSON_DECL
static
void
deallocate(
table* p,
storage_ptr const& sp);
};
//----------------------------------------------------------
class array::revert_construct
{
array* arr_;
public:
explicit
revert_construct(
array& arr) noexcept
: arr_(&arr)
{
}
~revert_construct()
{
if(! arr_)
return;
arr_->destroy();
}
void
commit() noexcept
{
arr_ = nullptr;
}
};
//----------------------------------------------------------
class array::revert_insert
{
array* arr_;
std::size_t const i_;
std::size_t const n_;
public:
value* p;
BOOST_JSON_DECL
revert_insert(
const_iterator pos,
std::size_t n,
array& arr);
BOOST_JSON_DECL
~revert_insert();
value*
commit() noexcept
{
auto it =
arr_->data() + i_;
arr_ = nullptr;
return it;
}
};
//----------------------------------------------------------
void
array::
relocate(
value* dest,
value* src,
std::size_t n) noexcept
{
if(n == 0)
return;
std::memmove(
static_cast<void*>(dest),
static_cast<void const*>(src),
n * sizeof(value));
}
//----------------------------------------------------------
//
// Construction
//
//----------------------------------------------------------
template<class InputIt, class>
array::
array(
InputIt first, InputIt last,
storage_ptr sp)
: array(
first, last,
std::move(sp),
iter_cat<InputIt>{})
{
BOOST_STATIC_ASSERT(
std::is_constructible<value,
decltype(*first)>::value);
}
//----------------------------------------------------------
//
// Modifiers
//
//----------------------------------------------------------
template<class InputIt, class>
auto
array::
insert(
const_iterator pos,
InputIt first, InputIt last) ->
iterator
{
BOOST_STATIC_ASSERT(
std::is_constructible<value,
decltype(*first)>::value);
return insert(pos, first, last,
iter_cat<InputIt>{});
}
template<class Arg>
auto
array::
emplace(
const_iterator pos,
Arg&& arg) ->
iterator
{
BOOST_ASSERT(
pos >= begin() &&
pos <= end());
value jv(
std::forward<Arg>(arg),
storage());
return insert(pos, pilfer(jv));
}
template<class Arg>
value&
array::
emplace_back(Arg&& arg)
{
value jv(
std::forward<Arg>(arg),
storage());
return push_back(pilfer(jv));
}
//----------------------------------------------------------
//
// Element access
//
//----------------------------------------------------------
value&
array::
at(std::size_t pos) &
{
auto const& self = *this;
return const_cast< value& >( self.at(pos) );
}
value&&
array::
at(std::size_t pos) &&
{
return std::move( at(pos) );
}
value const&
array::
at(std::size_t pos) const&
{
if(pos >= t_->size)
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::out_of_range, &loc );
}
return (*t_)[pos];
}
value&
array::
operator[](std::size_t pos) & noexcept
{
BOOST_ASSERT(pos < t_->size);
return (*t_)[pos];
}
value&&
array::
operator[](std::size_t pos) && noexcept
{
return std::move( (*this)[pos] );
}
value const&
array::
operator[](std::size_t pos) const& noexcept
{
BOOST_ASSERT(pos < t_->size);
return (*t_)[pos];
}
value&
array::
front() & noexcept
{
BOOST_ASSERT(t_->size > 0);
return (*t_)[0];
}
value&&
array::
front() && noexcept
{
return std::move( front() );
}
value const&
array::
front() const& noexcept
{
BOOST_ASSERT(t_->size > 0);
return (*t_)[0];
}
value&
array::
back() & noexcept
{
BOOST_ASSERT(
t_->size > 0);
return (*t_)[t_->size - 1];
}
value&&
array::
back() && noexcept
{
return std::move( back() );
}
value const&
array::
back() const& noexcept
{
BOOST_ASSERT(
t_->size > 0);
return (*t_)[t_->size - 1];
}
value*
array::
data() noexcept
{
return &(*t_)[0];
}
value const*
array::
data() const noexcept
{
return &(*t_)[0];
}
value const*
array::
if_contains(
std::size_t pos) const noexcept
{
if( pos < t_->size )
return &(*t_)[pos];
return nullptr;
}
value*
array::
if_contains(
std::size_t pos) noexcept
{
if( pos < t_->size )
return &(*t_)[pos];
return nullptr;
}
//----------------------------------------------------------
//
// Iterators
//
//----------------------------------------------------------
auto
array::
begin() noexcept ->
iterator
{
return &(*t_)[0];
}
auto
array::
begin() const noexcept ->
const_iterator
{
return &(*t_)[0];
}
auto
array::
cbegin() const noexcept ->
const_iterator
{
return &(*t_)[0];
}
auto
array::
end() noexcept ->
iterator
{
return &(*t_)[t_->size];
}
auto
array::
end() const noexcept ->
const_iterator
{
return &(*t_)[t_->size];
}
auto
array::
cend() const noexcept ->
const_iterator
{
return &(*t_)[t_->size];
}
auto
array::
rbegin() noexcept ->
reverse_iterator
{
return reverse_iterator(end());
}
auto
array::
rbegin() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(end());
}
auto
array::
crbegin() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(end());
}
auto
array::
rend() noexcept ->
reverse_iterator
{
return reverse_iterator(begin());
}
auto
array::
rend() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(begin());
}
auto
array::
crend() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(begin());
}
//----------------------------------------------------------
//
// Capacity
//
//----------------------------------------------------------
std::size_t
array::
size() const noexcept
{
return t_->size;
}
constexpr
std::size_t
array::
max_size() noexcept
{
// max_size depends on the address model
using min = std::integral_constant<std::size_t,
(std::size_t(-1) - sizeof(table)) / sizeof(value)>;
return min::value < BOOST_JSON_MAX_STRUCTURED_SIZE ?
min::value : BOOST_JSON_MAX_STRUCTURED_SIZE;
}
std::size_t
array::
capacity() const noexcept
{
return t_->capacity;
}
bool
array::
empty() const noexcept
{
return t_->size == 0;
}
void
array::
reserve(
std::size_t new_capacity)
{
// never shrink
if(new_capacity <= t_->capacity)
return;
reserve_impl(new_capacity);
}
//----------------------------------------------------------
//
// private
//
//----------------------------------------------------------
template<class InputIt>
array::
array(
InputIt first, InputIt last,
storage_ptr sp,
std::input_iterator_tag)
: sp_(std::move(sp))
, t_(&empty_)
{
revert_construct r(*this);
while(first != last)
{
reserve(size() + 1);
::new(end()) value(
*first++, sp_);
++t_->size;
}
r.commit();
}
template<class InputIt>
array::
array(
InputIt first, InputIt last,
storage_ptr sp,
std::forward_iterator_tag)
: sp_(std::move(sp))
{
std::size_t n =
std::distance(first, last);
if( n == 0 )
{
t_ = &empty_;
return;
}
t_ = table::allocate(n, sp_);
t_->size = 0;
revert_construct r(*this);
while(n--)
{
::new(end()) value(
*first++, sp_);
++t_->size;
}
r.commit();
}
template<class InputIt>
auto
array::
insert(
const_iterator pos,
InputIt first, InputIt last,
std::input_iterator_tag) ->
iterator
{
BOOST_ASSERT(
pos >= begin() && pos <= end());
if(first == last)
return data() + (pos - data());
array temp(first, last, sp_);
revert_insert r(
pos, temp.size(), *this);
relocate(
r.p,
temp.data(),
temp.size());
temp.t_->size = 0;
return r.commit();
}
template<class InputIt>
auto
array::
insert(
const_iterator pos,
InputIt first, InputIt last,
std::forward_iterator_tag) ->
iterator
{
std::size_t n =
std::distance(first, last);
revert_insert r(pos, n, *this);
while(n--)
{
::new(r.p) value(*first++);
++r.p;
}
return r.commit();
}
} // namespace json
} // namespace boost
#endif
+776
View File
@@ -0,0 +1,776 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_ARRAY_IPP
#define BOOST_JSON_IMPL_ARRAY_IPP
#include <boost/container_hash/hash.hpp>
#include <boost/json/array.hpp>
#include <boost/json/pilfer.hpp>
#include <boost/json/detail/except.hpp>
#include <cstdlib>
#include <limits>
#include <new>
#include <utility>
namespace boost {
namespace json {
//----------------------------------------------------------
constexpr array::table::table() = default;
// empty arrays point here
BOOST_JSON_REQUIRE_CONST_INIT
array::table array::empty_;
auto
array::
table::
allocate(
std::size_t capacity,
storage_ptr const& sp) ->
table*
{
BOOST_ASSERT(capacity > 0);
if(capacity > array::max_size())
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::array_too_large, &loc );
}
auto p = reinterpret_cast<
table*>(sp->allocate(
sizeof(table) +
capacity * sizeof(value),
alignof(value)));
p->capacity = static_cast<
std::uint32_t>(capacity);
return p;
}
void
array::
table::
deallocate(
table* p,
storage_ptr const& sp)
{
if(p->capacity == 0)
return;
sp->deallocate(p,
sizeof(table) +
p->capacity * sizeof(value),
alignof(value));
}
//----------------------------------------------------------
array::
revert_insert::
revert_insert(
const_iterator pos,
std::size_t n,
array& arr)
: arr_(&arr)
, i_(pos - arr_->data())
, n_(n)
{
BOOST_ASSERT(
pos >= arr_->begin() &&
pos <= arr_->end());
if( n_ <= arr_->capacity() -
arr_->size())
{
// fast path
p = arr_->data() + i_;
if(n_ == 0)
return;
relocate(
p + n_,
p,
arr_->size() - i_);
arr_->t_->size = static_cast<
std::uint32_t>(
arr_->t_->size + n_);
return;
}
if(n_ > max_size() - arr_->size())
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::array_too_large, &loc );
}
auto t = table::allocate(
arr_->growth(arr_->size() + n_),
arr_->sp_);
t->size = static_cast<std::uint32_t>(
arr_->size() + n_);
p = &(*t)[0] + i_;
relocate(
&(*t)[0],
arr_->data(),
i_);
relocate(
&(*t)[i_ + n_],
arr_->data() + i_,
arr_->size() - i_);
t = detail::exchange(arr_->t_, t);
table::deallocate(t, arr_->sp_);
}
array::
revert_insert::
~revert_insert()
{
if(! arr_)
return;
BOOST_ASSERT(n_ != 0);
auto const pos =
arr_->data() + i_;
arr_->destroy(pos, p);
arr_->t_->size = static_cast<
std::uint32_t>(
arr_->t_->size - n_);
relocate(
pos,
pos + n_,
arr_->size() - i_);
}
//----------------------------------------------------------
void
array::
destroy(
value* first, value* last) noexcept
{
if(sp_.is_not_shared_and_deallocate_is_trivial())
return;
while(last-- != first)
last->~value();
}
void
array::
destroy() noexcept
{
if(sp_.is_not_shared_and_deallocate_is_trivial())
return;
auto last = end();
auto const first = begin();
while(last-- != first)
last->~value();
table::deallocate(t_, sp_);
}
//----------------------------------------------------------
//
// Special Members
//
//----------------------------------------------------------
array::
array(detail::unchecked_array&& ua)
: sp_(ua.storage())
{
BOOST_STATIC_ASSERT(
alignof(table) == alignof(value));
if(ua.size() == 0)
{
t_ = &empty_;
return;
}
t_= table::allocate(
ua.size(), sp_);
t_->size = static_cast<
std::uint32_t>(ua.size());
ua.relocate(data());
}
array::
~array() noexcept
{
destroy();
}
array::
array(
std::size_t count,
value const& v,
storage_ptr sp)
: sp_(std::move(sp))
{
if(count == 0)
{
t_ = &empty_;
return;
}
t_= table::allocate(
count, sp_);
t_->size = 0;
revert_construct r(*this);
while(count--)
{
::new(end()) value(v, sp_);
++t_->size;
}
r.commit();
}
array::
array(
std::size_t count,
storage_ptr sp)
: sp_(std::move(sp))
{
if(count == 0)
{
t_ = &empty_;
return;
}
t_ = table::allocate(
count, sp_);
t_->size = static_cast<
std::uint32_t>(count);
auto p = data();
do
{
::new(p++) value(sp_);
}
while(--count);
}
array::
array(array const& other)
: array(other, other.sp_)
{
}
array::
array(
array const& other,
storage_ptr sp)
: sp_(std::move(sp))
{
if(other.empty())
{
t_ = &empty_;
return;
}
t_ = table::allocate(
other.size(), sp_);
t_->size = 0;
revert_construct r(*this);
auto src = other.data();
auto dest = data();
auto const n = other.size();
do
{
::new(dest++) value(
*src++, sp_);
++t_->size;
}
while(t_->size < n);
r.commit();
}
array::
array(
array&& other,
storage_ptr sp)
: sp_(std::move(sp))
{
if(*sp_ == *other.sp_)
{
// same resource
t_ = detail::exchange(
other.t_, &empty_);
return;
}
else if(other.empty())
{
t_ = &empty_;
return;
}
// copy
t_ = table::allocate(
other.size(), sp_);
t_->size = 0;
revert_construct r(*this);
auto src = other.data();
auto dest = data();
auto const n = other.size();
do
{
::new(dest++) value(
*src++, sp_);
++t_->size;
}
while(t_->size < n);
r.commit();
}
array::
array(
std::initializer_list<
value_ref> init,
storage_ptr sp)
: sp_(std::move(sp))
{
if(init.size() == 0)
{
t_ = &empty_;
return;
}
t_ = table::allocate(
init.size(), sp_);
t_->size = 0;
revert_construct r(*this);
value_ref::write_array(
data(), init, sp_);
t_->size = static_cast<
std::uint32_t>(init.size());
r.commit();
}
//----------------------------------------------------------
array&
array::
operator=(array const& other)
{
array(other,
storage()).swap(*this);
return *this;
}
array&
array::
operator=(array&& other)
{
array(std::move(other),
storage()).swap(*this);
return *this;
}
array&
array::
operator=(
std::initializer_list<value_ref> init)
{
array(init,
storage()).swap(*this);
return *this;
}
//----------------------------------------------------------
//
// Capacity
//
//----------------------------------------------------------
void
array::
shrink_to_fit() noexcept
{
if(capacity() <= size())
return;
if(size() == 0)
{
table::deallocate(t_, sp_);
t_ = &empty_;
return;
}
#ifndef BOOST_NO_EXCEPTIONS
try
{
#endif
auto t = table::allocate(
size(), sp_);
relocate(
&(*t)[0],
data(),
size());
t->size = static_cast<
std::uint32_t>(size());
t = detail::exchange(
t_, t);
table::deallocate(t, sp_);
#ifndef BOOST_NO_EXCEPTIONS
}
catch(...)
{
// eat the exception
return;
}
#endif
}
//----------------------------------------------------------
//
// Modifiers
//
//----------------------------------------------------------
void
array::
clear() noexcept
{
if(size() == 0)
return;
destroy(
begin(), end());
t_->size = 0;
}
auto
array::
insert(
const_iterator pos,
value const& v) ->
iterator
{
return emplace(pos, v);
}
auto
array::
insert(
const_iterator pos,
value&& v) ->
iterator
{
return emplace(pos, std::move(v));
}
auto
array::
insert(
const_iterator pos,
std::size_t count,
value const& v) ->
iterator
{
revert_insert r(
pos, count, *this);
while(count--)
{
::new(r.p) value(v, sp_);
++r.p;
}
return r.commit();
}
auto
array::
insert(
const_iterator pos,
std::initializer_list<
value_ref> init) ->
iterator
{
revert_insert r(
pos, init.size(), *this);
value_ref::write_array(
r.p, init, sp_);
return r.commit();
}
auto
array::
erase(
const_iterator pos) noexcept ->
iterator
{
BOOST_ASSERT(
pos >= begin() &&
pos <= end());
return erase(pos, pos + 1);
}
auto
array::
erase(
const_iterator first,
const_iterator last) noexcept ->
iterator
{
BOOST_ASSERT(
first >= begin() &&
last >= first &&
last <= end());
std::size_t const n =
last - first;
auto const p = &(*t_)[0] +
(first - &(*t_)[0]);
destroy(p, p + n);
relocate(p, p + n,
t_->size - (last -
&(*t_)[0]));
t_->size = static_cast<
std::uint32_t>(t_->size - n);
return p;
}
void
array::
push_back(value const& v)
{
emplace_back(v);
}
void
array::
push_back(value&& v)
{
emplace_back(std::move(v));
}
void
array::
pop_back() noexcept
{
auto const p = &back();
destroy(p, p + 1);
--t_->size;
}
void
array::
resize(std::size_t count)
{
if(count <= t_->size)
{
// shrink
destroy(
&(*t_)[0] + count,
&(*t_)[0] + t_->size);
t_->size = static_cast<
std::uint32_t>(count);
return;
}
reserve(count);
auto p = &(*t_)[t_->size];
auto const end = &(*t_)[count];
while(p != end)
::new(p++) value(sp_);
t_->size = static_cast<
std::uint32_t>(count);
}
void
array::
resize(
std::size_t count,
value const& v)
{
if(count <= size())
{
// shrink
destroy(
data() + count,
data() + size());
t_->size = static_cast<
std::uint32_t>(count);
return;
}
count -= size();
revert_insert r(
end(), count, *this);
while(count--)
{
::new(r.p) value(v, sp_);
++r.p;
}
r.commit();
}
void
array::
swap(array& other)
{
if(*sp_ == *other.sp_)
{
t_ = detail::exchange(
other.t_, t_);
return;
}
array temp1(
std::move(*this),
other.storage());
array temp2(
std::move(other),
this->storage());
this->~array();
::new(this) array(
pilfer(temp2));
other.~array();
::new(&other) array(
pilfer(temp1));
}
//----------------------------------------------------------
//
// Private
//
//----------------------------------------------------------
std::size_t
array::
growth(
std::size_t new_size) const
{
if(new_size > max_size())
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::array_too_large, &loc );
}
std::size_t const old = capacity();
if(old > max_size() - old / 2)
return new_size;
std::size_t const g =
old + old / 2; // 1.5x
if(g < new_size)
return new_size;
return g;
}
// precondition: new_capacity > capacity()
void
array::
reserve_impl(
std::size_t new_capacity)
{
BOOST_ASSERT(
new_capacity > t_->capacity);
auto t = table::allocate(
growth(new_capacity), sp_);
relocate(
&(*t)[0],
&(*t_)[0],
t_->size);
t->size = t_->size;
t = detail::exchange(t_, t);
table::deallocate(t, sp_);
}
// precondition: pv is not aliased
value&
array::
push_back(
pilfered<value> pv)
{
auto const n = t_->size;
if(n < t_->capacity)
{
// fast path
auto& v = *::new(
&(*t_)[n]) value(pv);
++t_->size;
return v;
}
auto const t =
detail::exchange(t_,
table::allocate(
growth(n + 1),
sp_));
auto& v = *::new(
&(*t_)[n]) value(pv);
relocate(
&(*t_)[0],
&(*t)[0],
n);
t_->size = n + 1;
table::deallocate(t, sp_);
return v;
}
// precondition: pv is not aliased
auto
array::
insert(
const_iterator pos,
pilfered<value> pv) ->
iterator
{
BOOST_ASSERT(
pos >= begin() &&
pos <= end());
std::size_t const n =
t_->size;
std::size_t const i =
pos - &(*t_)[0];
if(n < t_->capacity)
{
// fast path
auto const p =
&(*t_)[i];
relocate(
p + 1,
p,
n - i);
::new(p) value(pv);
++t_->size;
return p;
}
auto t =
table::allocate(
growth(n + 1), sp_);
auto const p = &(*t)[i];
::new(p) value(pv);
relocate(
&(*t)[0],
&(*t_)[0],
i);
relocate(
p + 1,
&(*t_)[i],
n - i);
t->size = static_cast<
std::uint32_t>(size() + 1);
t = detail::exchange(t_, t);
table::deallocate(t, sp_);
return p;
}
//----------------------------------------------------------
bool
array::
equal(
array const& other) const noexcept
{
if(size() != other.size())
return false;
for(std::size_t i = 0; i < size(); ++i)
if((*this)[i] != other[i])
return false;
return true;
}
} // namespace json
} // namespace boost
//----------------------------------------------------------
//
// std::hash specialization
//
//----------------------------------------------------------
std::size_t
std::hash<::boost::json::array>::operator()(
::boost::json::array const& ja) const noexcept
{
return ::boost::hash< ::boost::json::array >()( ja );
}
//----------------------------------------------------------
#endif
+545
View File
@@ -0,0 +1,545 @@
//
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
// Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_CONVERSION_HPP
#define BOOST_JSON_IMPL_CONVERSION_HPP
#include <boost/json/fwd.hpp>
#include <boost/json/value.hpp>
#include <boost/json/string_view.hpp>
#include <boost/describe/enumerators.hpp>
#include <boost/describe/members.hpp>
#include <boost/describe/bases.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/utility.hpp>
#include <iterator>
#include <tuple>
#include <utility>
#ifndef BOOST_NO_CXX17_HDR_VARIANT
# include <variant>
#endif // BOOST_NO_CXX17_HDR_VARIANT
namespace boost {
namespace json {
namespace detail {
#ifdef __cpp_lib_nonmember_container_access
using std::size;
#endif
template<std::size_t I, class T>
using tuple_element_t = typename std::tuple_element<I, T>::type;
template<class T>
using iterator_type = decltype(std::begin(std::declval<T&>()));
template<class T>
using iterator_traits = std::iterator_traits< iterator_type<T> >;
template<class T>
using value_type = typename iterator_traits<T>::value_type;
template<class T>
using mapped_type = tuple_element_t< 1, value_type<T> >;
// had to make the metafunction always succeeding in order to make it work
// with msvc 14.0
template<class T>
using key_type_helper = tuple_element_t< 0, value_type<T> >;
template<class T>
using key_type = mp11::mp_eval_or<
void,
key_type_helper,
T>;
template<class T>
using are_begin_and_end_same = std::is_same<
iterator_type<T>,
decltype(std::end(std::declval<T&>()))>;
template<class T>
using begin_iterator_category = typename std::iterator_traits<
iterator_type<T>>::iterator_category;
template<class T>
using has_positive_tuple_size = mp11::mp_bool<
(std::tuple_size<T>::value > 0) >;
template<class T>
using has_unique_keys = has_positive_tuple_size<decltype(
std::declval<T&>().emplace(
std::declval<value_type<T>>()))>;
template<class T>
struct is_value_type_pair_helper : std::false_type
{ };
template<class T1, class T2>
struct is_value_type_pair_helper<std::pair<T1, T2>> : std::true_type
{ };
template<class T>
using is_value_type_pair = is_value_type_pair_helper<value_type<T>>;
template<class T>
using has_size_member_helper
= std::is_convertible<decltype(std::declval<T&>().size()), std::size_t>;
template<class T>
using has_size_member = mp11::mp_valid_and_true<has_size_member_helper, T>;
template<class T>
using has_free_size_helper
= std::is_convertible<
decltype(size(std::declval<T const&>())),
std::size_t>;
template<class T>
using has_free_size = mp11::mp_valid_and_true<has_free_size_helper, T>;
template<class T>
using size_implementation = mp11::mp_cond<
has_size_member<T>, mp11::mp_int<3>,
has_free_size<T>, mp11::mp_int<2>,
std::is_array<T>, mp11::mp_int<1>,
mp11::mp_true, mp11::mp_int<0>>;
template<class T>
std::size_t
try_size(T&& cont, mp11::mp_int<3>)
{
return cont.size();
}
template<class T>
std::size_t
try_size(T& cont, mp11::mp_int<2>)
{
return size(cont);
}
template<class T, std::size_t N>
std::size_t
try_size(T(&)[N], mp11::mp_int<1>)
{
return N;
}
template<class T>
std::size_t
try_size(T&, mp11::mp_int<0>)
{
return 0;
}
template<class T>
using has_push_back_helper
= decltype(std::declval<T&>().push_back(std::declval<value_type<T>>()));
template<class T>
using has_push_back = mp11::mp_valid<has_push_back_helper, T>;
template<class T>
using inserter_implementation = mp11::mp_cond<
is_tuple_like<T>, mp11::mp_int<2>,
has_push_back<T>, mp11::mp_int<1>,
mp11::mp_true, mp11::mp_int<0>>;
template<class T>
iterator_type<T>
inserter(
T& target,
mp11::mp_int<2>)
{
return target.begin();
}
template<class T>
std::back_insert_iterator<T>
inserter(
T& target,
mp11::mp_int<1>)
{
return std::back_inserter(target);
}
template<class T>
std::insert_iterator<T>
inserter(
T& target,
mp11::mp_int<0>)
{
return std::inserter( target, target.end() );
}
using value_from_conversion = mp11::mp_true;
using value_to_conversion = mp11::mp_false;
struct user_conversion_tag { };
struct context_conversion_tag : user_conversion_tag { };
struct full_context_conversion_tag : context_conversion_tag { };
struct native_conversion_tag { };
struct value_conversion_tag : native_conversion_tag { };
struct object_conversion_tag : native_conversion_tag { };
struct array_conversion_tag : native_conversion_tag { };
struct string_conversion_tag : native_conversion_tag { };
struct bool_conversion_tag : native_conversion_tag { };
struct number_conversion_tag : native_conversion_tag { };
struct integral_conversion_tag : number_conversion_tag { };
struct floating_point_conversion_tag : number_conversion_tag { };
struct null_like_conversion_tag { };
struct string_like_conversion_tag { };
struct map_like_conversion_tag { };
struct sequence_conversion_tag { };
struct tuple_conversion_tag { };
struct described_class_conversion_tag { };
struct described_enum_conversion_tag { };
struct variant_conversion_tag { };
struct optional_conversion_tag { };
struct no_conversion_tag { };
template<class... Args>
using supports_tag_invoke = decltype(tag_invoke( std::declval<Args>()... ));
template<class T>
using has_user_conversion_from_impl = supports_tag_invoke<
value_from_tag, value&, T&& >;
template<class T>
using has_user_conversion_to_impl = supports_tag_invoke<
value_to_tag<T>, value const& >;
template<class T>
using has_nonthrowing_user_conversion_to_impl = supports_tag_invoke<
try_value_to_tag<T>, value const& >;
template< class T, class Dir >
using has_user_conversion1 = mp11::mp_if<
std::is_same<Dir, value_from_conversion>,
mp11::mp_valid<has_user_conversion_from_impl, T>,
mp11::mp_or<
mp11::mp_valid<has_user_conversion_to_impl, T>,
mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>>>;
template< class Ctx, class T >
using has_context_conversion_from_impl = supports_tag_invoke<
value_from_tag, value&, T&&, Ctx const& >;
template< class Ctx, class T >
using has_context_conversion_to_impl = supports_tag_invoke<
value_to_tag<T>, value const&, Ctx const& >;
template< class Ctx, class T >
using has_nonthrowing_context_conversion_to_impl = supports_tag_invoke<
try_value_to_tag<T>, value const&, Ctx const& >;
template< class Ctx, class T, class Dir >
using has_user_conversion2 = mp11::mp_if<
std::is_same<Dir, value_from_conversion>,
mp11::mp_valid<has_context_conversion_from_impl, Ctx, T>,
mp11::mp_or<
mp11::mp_valid<has_context_conversion_to_impl, Ctx, T>,
mp11::mp_valid<has_nonthrowing_context_conversion_to_impl, Ctx, T>>>;
template< class Ctx, class T >
using has_full_context_conversion_from_impl = supports_tag_invoke<
value_from_tag, value&, T&&, Ctx const&, Ctx const& >;
template< class Ctx, class T >
using has_full_context_conversion_to_impl = supports_tag_invoke<
value_to_tag<T>, value const&, Ctx const&, Ctx const& >;
template< class Ctx, class T >
using has_nonthrowing_full_context_conversion_to_impl = supports_tag_invoke<
try_value_to_tag<T>, value const&, Ctx const&, Ctx const& >;
template< class Ctx, class T, class Dir >
using has_user_conversion3 = mp11::mp_if<
std::is_same<Dir, value_from_conversion>,
mp11::mp_valid<has_full_context_conversion_from_impl, Ctx, T>,
mp11::mp_or<
mp11::mp_valid<has_full_context_conversion_to_impl, Ctx, T>,
mp11::mp_valid<
has_nonthrowing_full_context_conversion_to_impl, Ctx, T>>>;
template< class T >
using described_non_public_members = describe::describe_members<
T, describe::mod_private | describe::mod_protected>;
template< class T >
using described_bases = describe::describe_bases<
T, describe::mod_any_access>;
#if defined(BOOST_MSVC) && BOOST_MSVC < 1920
template< class T >
struct described_member_t_impl;
template< class T, class C >
struct described_member_t_impl<T C::*>
{
using type = T;
};
template< class T, class D >
using described_member_t = remove_cvref<
typename described_member_t_impl<
remove_cvref<decltype(D::pointer)> >::type>;
#else
template< class T, class D >
using described_member_t = remove_cvref<decltype(
std::declval<T&>().* D::pointer )>;
#endif
// user conversion (via tag_invoke)
template< class Ctx, class T, class Dir >
using user_conversion_category = mp11::mp_cond<
has_user_conversion3<Ctx, T, Dir>, full_context_conversion_tag,
has_user_conversion2<Ctx, T, Dir>, context_conversion_tag,
has_user_conversion1<T, Dir>, user_conversion_tag>;
// native conversions (constructors and member functions of value)
template< class T >
using native_conversion_category = mp11::mp_cond<
std::is_same<T, value>, value_conversion_tag,
std::is_same<T, array>, array_conversion_tag,
std::is_same<T, object>, object_conversion_tag,
std::is_same<T, string>, string_conversion_tag>;
// generic conversions
template< class T >
using generic_conversion_category = mp11::mp_cond<
std::is_same<T, bool>, bool_conversion_tag,
std::is_integral<T>, integral_conversion_tag,
std::is_floating_point<T>, floating_point_conversion_tag,
is_null_like<T>, null_like_conversion_tag,
is_string_like<T>, string_like_conversion_tag,
is_map_like<T>, map_like_conversion_tag,
is_sequence_like<T>, sequence_conversion_tag,
is_tuple_like<T>, tuple_conversion_tag,
is_described_class<T>, described_class_conversion_tag,
is_described_enum<T>, described_enum_conversion_tag,
is_variant_like<T>, variant_conversion_tag,
is_optional_like<T>, optional_conversion_tag,
// failed to find a suitable implementation
mp11::mp_true, no_conversion_tag>;
template< class T >
using nested_type = typename T::type;
template< class T1, class T2 >
using conversion_category_impl_helper = mp11::mp_eval_if_not<
std::is_same<detail::no_conversion_tag, T1>,
T1,
mp11::mp_eval_or_q, T1, mp11::mp_quote<nested_type>, T2>;
template< class Ctx, class T, class Dir >
struct conversion_category_impl
{
using type = mp11::mp_fold<
mp11::mp_list<
mp11::mp_defer<user_conversion_category, Ctx, T, Dir>,
mp11::mp_defer<native_conversion_category, T>,
mp11::mp_defer<generic_conversion_category, T>>,
no_conversion_tag,
conversion_category_impl_helper>;
};
template< class Ctx, class T, class Dir >
using conversion_category =
typename conversion_category_impl< Ctx, T, Dir >::type;
template< class T >
using any_conversion_tag = mp11::mp_not<
std::is_same< T, no_conversion_tag > >;
template< class T, class Dir, class... Ctxs >
struct conversion_category_impl< std::tuple<Ctxs...>, T, Dir >
{
using ctxs = mp11::mp_list< remove_cvref<Ctxs>... >;
using cats = mp11::mp_list<
conversion_category<remove_cvref<Ctxs>, T, Dir>... >;
template< class I >
using exists = mp11::mp_less< I, mp11::mp_size<cats> >;
using context2 = mp11::mp_find< cats, full_context_conversion_tag >;
using context1 = mp11::mp_find< cats, context_conversion_tag >;
using context0 = mp11::mp_find< cats, user_conversion_tag >;
using index = mp11::mp_cond<
exists<context2>, context2,
exists<context1>, context1,
exists<context0>, context0,
mp11::mp_true, mp11::mp_find_if< cats, any_conversion_tag > >;
using type = mp11::mp_eval_or<
no_conversion_tag,
mp11::mp_at, cats, index >;
};
struct no_context
{};
struct allow_exceptions
{};
template <class T, class Dir>
using can_convert = mp11::mp_not<
std::is_same<
detail::conversion_category<no_context, T, Dir>,
detail::no_conversion_tag>>;
template<class Impl1, class Impl2>
using conversion_round_trips_helper = mp11::mp_or<
std::is_same<Impl1, Impl2>,
std::is_base_of<user_conversion_tag, Impl1>,
std::is_base_of<user_conversion_tag, Impl2>>;
template< class Ctx, class T, class Dir >
using conversion_round_trips = conversion_round_trips_helper<
conversion_category<Ctx, T, Dir>,
conversion_category<Ctx, T, mp11::mp_not<Dir>>>;
template< class T1, class T2 >
struct copy_cref_helper
{
using type = remove_cvref<T2>;
};
template< class T1, class T2 >
using copy_cref = typename copy_cref_helper< T1, T2 >::type;
template< class T1, class T2 >
struct copy_cref_helper<T1 const, T2>
{
using type = remove_cvref<T2> const;
};
template< class T1, class T2 >
struct copy_cref_helper<T1&, T2>
{
using type = copy_cref<T1, T2>&;
};
template< class T1, class T2 >
struct copy_cref_helper<T1&&, T2>
{
using type = copy_cref<T1, T2>&&;
};
template< class Rng, class Traits >
using forwarded_value_helper = mp11::mp_if<
std::is_convertible<
typename Traits::reference,
copy_cref<Rng, typename Traits::value_type> >,
copy_cref<Rng, typename Traits::value_type>,
typename Traits::value_type >;
template< class Rng >
using forwarded_value = forwarded_value_helper<
Rng, iterator_traits< Rng > >;
template< class Ctx, class T, class Dir >
struct supported_context
{
using type = Ctx;
static
type const&
get( Ctx const& ctx ) noexcept
{
return ctx;
}
};
template< class T, class Dir, class... Ctxs >
struct supported_context< std::tuple<Ctxs...>, T, Dir >
{
using Ctx = std::tuple<Ctxs...>;
using impl = conversion_category_impl<Ctx, T, Dir>;
using index = typename impl::index;
using next_supported = supported_context<
mp11::mp_at< typename impl::ctxs, index >, T, Dir >;
using type = typename next_supported::type;
static
type const&
get( Ctx const& ctx ) noexcept
{
return next_supported::get( std::get<index::value>( ctx ) );
}
};
template< class T >
using value_result_type = typename std::decay<
decltype( std::declval<T&>().value() )>::type;
template< class T >
using can_reset = decltype( std::declval<T&>().reset() );
template< class T >
using has_valueless_by_exception =
decltype( std::declval<T const&>().valueless_by_exception() );
} // namespace detail
template <class T>
struct result_for<T, value>
{
using type = result< detail::remove_cvref<T> >;
};
template<class T>
struct is_string_like
: std::is_convertible<T, string_view>
{ };
template<class T>
struct is_sequence_like
: mp11::mp_all<
mp11::mp_valid_and_true<detail::are_begin_and_end_same, T>,
mp11::mp_valid<detail::begin_iterator_category, T>>
{ };
template<class T>
struct is_map_like
: mp11::mp_all<
is_sequence_like<T>,
mp11::mp_valid_and_true<detail::is_value_type_pair, T>,
is_string_like<detail::key_type<T>>,
mp11::mp_valid_and_true<detail::has_unique_keys, T>>
{ };
template<class T>
struct is_tuple_like
: mp11::mp_valid_and_true<detail::has_positive_tuple_size, T>
{ };
template<>
struct is_null_like<std::nullptr_t>
: std::true_type
{ };
#ifndef BOOST_NO_CXX17_HDR_VARIANT
template<>
struct is_null_like<std::monostate>
: std::true_type
{ };
#endif // BOOST_NO_CXX17_HDR_VARIANT
template<class T>
struct is_described_class
: mp11::mp_and<
describe::has_describe_members<T>,
mp11::mp_not< std::is_union<T> >,
mp11::mp_empty<
mp11::mp_eval_or<
mp11::mp_list<>, detail::described_non_public_members, T>>,
mp11::mp_empty<
mp11::mp_eval_or<mp11::mp_list<>, detail::described_bases, T>>>
{ };
template<class T>
struct is_described_enum
: describe::has_describe_enumerators<T>
{ };
template<class T>
struct is_variant_like : mp11::mp_valid<detail::has_valueless_by_exception, T>
{ };
template<class T>
struct is_optional_like
: mp11::mp_and<
mp11::mp_not<std::is_void<
mp11::mp_eval_or<void, detail::value_result_type, T>>>,
mp11::mp_valid<detail::can_reset, T>>
{ };
} // namespace json
} // namespace boost
#endif // BOOST_JSON_IMPL_CONVERSION_HPP
+127
View File
@@ -0,0 +1,127 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_ERROR_HPP
#define BOOST_JSON_IMPL_ERROR_HPP
#include <type_traits>
namespace boost {
namespace system {
template<>
struct is_error_code_enum< ::boost::json::error >
{
static bool const value = true;
};
template<>
struct is_error_condition_enum< ::boost::json::condition >
{
static bool const value = true;
};
} // system
} // boost
namespace std {
template<>
struct is_error_code_enum< ::boost::json::error >
{
static bool const value = true;
};
template<>
struct is_error_condition_enum< ::boost::json::condition >
{
static bool const value = true;
};
} // std
namespace boost {
namespace json {
namespace detail {
struct error_code_category_t
: error_category
{
constexpr
error_code_category_t()
: error_category(0xB9A9B9922177C772)
{}
BOOST_JSON_DECL
const char*
name() const noexcept override;
BOOST_JSON_DECL
char const*
message( int ev, char* buf, std::size_t len ) const noexcept override;
BOOST_JSON_DECL
std::string
message( int ev ) const override;
BOOST_JSON_DECL
error_condition
default_error_condition( int ev ) const noexcept override;
};
extern
BOOST_JSON_DECL
error_code_category_t error_code_category;
struct error_condition_category_t
: error_category
{
constexpr
error_condition_category_t()
: error_category(0x37CEF5A036D24FD1)
{}
BOOST_JSON_DECL
const char*
name() const noexcept override;
BOOST_JSON_DECL
char const*
message( int ev, char*, std::size_t ) const noexcept override;
BOOST_JSON_DECL
std::string
message( int cv ) const override;
};
extern
BOOST_JSON_DECL
error_condition_category_t error_condition_category;
} // namespace detail
inline
BOOST_SYSTEM_CONSTEXPR
error_code
make_error_code(error e) noexcept
{
return error_code(
static_cast<std::underlying_type<error>::type>(e),
detail::error_code_category );
}
inline
BOOST_SYSTEM_CONSTEXPR
error_condition
make_error_condition(condition c) noexcept
{
return error_condition(
static_cast<std::underlying_type<condition>::type>(c),
detail::error_condition_category );
}
} // namespace json
} // namespace boost
#endif
+190
View File
@@ -0,0 +1,190 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_ERROR_IPP
#define BOOST_JSON_IMPL_ERROR_IPP
#include <boost/json/error.hpp>
namespace boost {
namespace json {
namespace detail {
// msvc 14.0 has a bug that warns about inability to use constexpr
// construction here, even though there's no constexpr construction
#if defined(_MSC_VER) && _MSC_VER <= 1900
# pragma warning( push )
# pragma warning( disable : 4592 )
#endif
BOOST_JSON_CONSTINIT
error_code_category_t error_code_category;
BOOST_JSON_CONSTINIT
error_condition_category_t error_condition_category;
#if defined(_MSC_VER) && _MSC_VER <= 1900
# pragma warning( pop )
#endif
char const*
error_code_category_t::name() const noexcept
{
return "boost.json";
}
char const*
error_code_category_t::message( int ev, char*, std::size_t ) const noexcept
{
switch(static_cast<error>(ev))
{
default:
case error::syntax: return "syntax error";
case error::extra_data: return "extra data";
case error::incomplete: return "incomplete JSON";
case error::exponent_overflow: return "exponent overflow";
case error::too_deep: return "too deep";
case error::illegal_leading_surrogate: return "illegal leading surrogate";
case error::illegal_trailing_surrogate: return "illegal trailing surrogate";
case error::expected_hex_digit: return "expected hex digit";
case error::expected_utf16_escape: return "expected utf16 escape";
case error::object_too_large: return "object too large";
case error::array_too_large: return "array too large";
case error::key_too_large: return "key too large";
case error::string_too_large: return "string too large";
case error::number_too_large: return "number too large";
case error::input_error: return "input error";
case error::exception: return "got exception";
case error::out_of_range: return "out of range";
case error::test_failure: return "test failure";
case error::missing_slash: return "missing slash character";
case error::invalid_escape: return "invalid escape sequence";
case error::token_not_number: return "token is not a number";
case error::value_is_scalar: return "current value is scalar";
case error::not_found: return "no referenced value";
case error::token_overflow: return "token overflow";
case error::past_the_end: return "past-the-end token not supported";
case error::not_number: return "not a number";
case error::not_exact: return "not exact";
case error::not_null: return "value is not null";
case error::not_bool: return "value is not boolean";
case error::not_array: return "value is not an array";
case error::not_object: return "value is not an object";
case error::not_string: return "value is not a string";
case error::not_int64: return "value is not a std::int64_t number";
case error::not_uint64: return "value is not a std::uint64_t number";
case error::not_double: return "value is not a double";
case error::not_integer: return "value is not integer";
case error::size_mismatch: return "source composite size does not match target size";
case error::exhausted_variants: return "exhausted all variants";
case error::unknown_name: return "unknown name";
}
}
std::string
error_code_category_t::message( int ev ) const
{
return message( ev, nullptr, 0 );
}
error_condition
error_code_category_t::default_error_condition( int ev) const noexcept
{
switch(static_cast<error>(ev))
{
default:
return {ev, *this};
case error::syntax:
case error::extra_data:
case error::incomplete:
case error::exponent_overflow:
case error::too_deep:
case error::illegal_leading_surrogate:
case error::illegal_trailing_surrogate:
case error::expected_hex_digit:
case error::expected_utf16_escape:
case error::object_too_large:
case error::array_too_large:
case error::key_too_large:
case error::string_too_large:
case error::number_too_large:
case error::input_error:
return condition::parse_error;
case error::missing_slash:
case error::invalid_escape:
return condition::pointer_parse_error;
case error::token_not_number:
case error::value_is_scalar:
case error::not_found:
case error::token_overflow:
case error::past_the_end:
return condition::pointer_use_error;
case error::not_number:
case error::not_exact:
case error::not_null:
case error::not_bool:
case error::not_array:
case error::not_object:
case error::not_string:
case error::not_int64:
case error::not_uint64:
case error::not_double:
case error::not_integer:
case error::size_mismatch:
case error::exhausted_variants:
case error::unknown_name:
return condition::conversion_error;
case error::exception:
case error::out_of_range:
return condition::generic_error;
}
}
char const*
error_condition_category_t::name() const noexcept
{
return "boost.json";
}
char const*
error_condition_category_t::message( int cv, char*, std::size_t ) const noexcept
{
switch(static_cast<condition>(cv))
{
default:
case condition::parse_error:
return "A JSON parse error occurred";
case condition::pointer_parse_error:
return "A JSON Pointer parse error occurred";
case condition::pointer_use_error:
return "An error occurred when JSON Pointer was used with"
" a value";
case condition::conversion_error:
return "An error occurred during conversion";
}
}
std::string
error_condition_category_t::message( int cv ) const
{
return message( cv, nullptr, 0 );
}
} // namespace detail
} // namespace json
} // namespace boost
#endif
+46
View File
@@ -0,0 +1,46 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_KIND_IPP
#define BOOST_JSON_IMPL_KIND_IPP
#include <boost/json/kind.hpp>
#include <ostream>
namespace boost {
namespace json {
string_view
to_string(kind k) noexcept
{
switch(k)
{
case kind::array: return "array";
case kind::object: return "object";
case kind::string: return "string";
case kind::int64: return "int64";
case kind::uint64: return "uint64";
case kind::double_: return "double";
case kind::bool_: return "bool";
default: // satisfy warnings
case kind::null: return "null";
}
}
std::ostream&
operator<<(std::ostream& os, kind k)
{
os << to_string(k);
return os;
}
} // namespace json
} // namespace boost
#endif
+174
View File
@@ -0,0 +1,174 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_MONOTONIC_RESOURCE_IPP
#define BOOST_JSON_IMPL_MONOTONIC_RESOURCE_IPP
#include <boost/json/monotonic_resource.hpp>
#include <boost/json/detail/except.hpp>
#include <boost/align/align.hpp>
#include <boost/core/max_align.hpp>
#include <memory>
namespace boost {
namespace json {
struct alignas(core::max_align_t)
monotonic_resource::block : block_base
{
};
constexpr
std::size_t
monotonic_resource::
max_size()
{
return std::size_t(-1) - sizeof(block);
}
// lowest power of 2 greater than or equal to n
std::size_t
monotonic_resource::
round_pow2(
std::size_t n) noexcept
{
if(n & (n - 1))
return next_pow2(n);
return n;
}
// lowest power of 2 greater than n
std::size_t
monotonic_resource::
next_pow2(
std::size_t n) noexcept
{
std::size_t result = min_size_;
while(result <= n)
{
if(result >= max_size() - result)
{
// overflow
result = max_size();
break;
}
result *= 2;
}
return result;
}
//----------------------------------------------------------
monotonic_resource::
~monotonic_resource()
{
release();
}
monotonic_resource::
monotonic_resource(
std::size_t initial_size,
storage_ptr upstream) noexcept
: buffer_{
nullptr, 0, 0, nullptr}
, next_size_(round_pow2(initial_size))
, upstream_(std::move(upstream))
{
}
monotonic_resource::
monotonic_resource(
unsigned char* buffer,
std::size_t size,
storage_ptr upstream) noexcept
: buffer_{
buffer, size, size, nullptr}
, next_size_(next_pow2(size))
, upstream_(std::move(upstream))
{
}
void
monotonic_resource::
release() noexcept
{
auto p = head_;
while(p != &buffer_)
{
auto next = p->next;
upstream_->deallocate(p, p->size);
p = next;
}
buffer_.p = reinterpret_cast<
unsigned char*>(buffer_.p) - (
buffer_.size - buffer_.avail);
buffer_.avail = buffer_.size;
head_ = &buffer_;
}
void*
monotonic_resource::
do_allocate(
std::size_t n,
std::size_t align)
{
auto p = alignment::align(
align, n, head_->p, head_->avail);
if(p)
{
head_->p = reinterpret_cast<
unsigned char*>(p) + n;
head_->avail -= n;
return p;
}
if(next_size_ < n)
next_size_ = round_pow2(n);
auto b = ::new(upstream_->allocate(
sizeof(block) + next_size_)) block;
b->p = b + 1;
b->avail = next_size_;
b->size = next_size_;
b->next = head_;
head_ = b;
next_size_ = next_pow2(next_size_);
p = alignment::align(
align, n, head_->p, head_->avail);
BOOST_ASSERT(p);
head_->p = reinterpret_cast<
unsigned char*>(p) + n;
head_->avail -= n;
return p;
}
void
monotonic_resource::
do_deallocate(
void*,
std::size_t,
std::size_t)
{
// do nothing
}
bool
monotonic_resource::
do_is_equal(
memory_resource const& mr) const noexcept
{
return this == &mr;
}
} // namespace json
} // namespace boost
#endif
+90
View File
@@ -0,0 +1,90 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_NULL_RESOURCE_IPP
#define BOOST_JSON_IMPL_NULL_RESOURCE_IPP
#include <boost/json/null_resource.hpp>
#include <boost/throw_exception.hpp>
namespace boost {
namespace json {
namespace detail {
/** A resource which always fails.
This memory resource always throws the exception
`std::bad_alloc` in calls to `allocate`.
*/
class null_resource final
: public memory_resource
{
public:
/// Copy constructor (deleted)
null_resource(
null_resource const&) = delete;
/// Copy assignment (deleted)
null_resource& operator=(
null_resource const&) = delete;
/** Constructor
This constructs the resource.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
/** @{ */
null_resource() noexcept = default;
protected:
void*
do_allocate(
std::size_t,
std::size_t) override
{
throw_exception( std::bad_alloc(), BOOST_CURRENT_LOCATION );
}
void
do_deallocate(
void*,
std::size_t,
std::size_t) override
{
// do nothing
}
bool
do_is_equal(
memory_resource const& mr
) const noexcept override
{
return this == &mr;
}
};
} // detail
memory_resource*
get_null_resource() noexcept
{
static detail::null_resource mr;
return &mr;
}
} // namespace json
} // namespace boost
#endif
+587
View File
@@ -0,0 +1,587 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_OBJECT_HPP
#define BOOST_JSON_IMPL_OBJECT_HPP
#include <boost/json/value.hpp>
#include <iterator>
#include <cmath>
#include <type_traits>
#include <utility>
namespace boost {
namespace json {
namespace detail {
// Objects with size less than or equal
// to this number will use a linear search
// instead of the more expensive hash function.
static
constexpr
std::size_t
small_object_size_ = 18;
BOOST_STATIC_ASSERT(
small_object_size_ <
BOOST_JSON_MAX_STRUCTURED_SIZE);
} // detail
//----------------------------------------------------------
struct alignas(key_value_pair)
object::table
{
std::uint32_t size = 0;
std::uint32_t capacity = 0;
std::uintptr_t salt = 0;
#if defined(_MSC_VER) && BOOST_JSON_ARCH == 32
// VFALCO If we make key_value_pair smaller,
// then we might want to revisit this
// padding.
BOOST_STATIC_ASSERT(
sizeof(key_value_pair) == 32);
char pad[4] = {}; // silence warnings
#endif
constexpr table();
// returns true if we use a linear
// search instead of the hash table.
bool is_small() const noexcept
{
return capacity <=
detail::small_object_size_;
}
key_value_pair&
operator[](
std::size_t pos) noexcept
{
return reinterpret_cast<
key_value_pair*>(
this + 1)[pos];
}
// VFALCO This is exported for tests
BOOST_JSON_DECL
std::size_t
digest(string_view key) const noexcept;
inline
index_t&
bucket(std::size_t hash) noexcept;
inline
index_t&
bucket(string_view key) noexcept;
inline
void
clear() noexcept;
static
inline
table*
allocate(
std::size_t capacity,
std::uintptr_t salt,
storage_ptr const& sp);
static
void
deallocate(
table* p,
storage_ptr const& sp) noexcept
{
if(p->capacity == 0)
return;
if(! p->is_small())
sp->deallocate(p,
sizeof(table) + p->capacity * (
sizeof(key_value_pair) +
sizeof(index_t)));
else
sp->deallocate(p,
sizeof(table) + p->capacity *
sizeof(key_value_pair));
}
};
//----------------------------------------------------------
class object::revert_construct
{
object* obj_;
BOOST_JSON_DECL
void
destroy() noexcept;
public:
explicit
revert_construct(
object& obj) noexcept
: obj_(&obj)
{
}
~revert_construct()
{
if(! obj_)
return;
destroy();
}
void
commit() noexcept
{
obj_ = nullptr;
}
};
//----------------------------------------------------------
class object::revert_insert
{
object* obj_;
table* t_ = nullptr;
std::size_t size_;
BOOST_JSON_DECL
void
destroy() noexcept;
public:
explicit
revert_insert(
object& obj,
std::size_t capacity)
: obj_(&obj)
, size_(obj_->size())
{
if( capacity > obj_->capacity() )
t_ = obj_->reserve_impl(capacity);
}
~revert_insert()
{
if(! obj_)
return;
destroy();
if( t_ )
{
table::deallocate( obj_->t_, obj_->sp_ );
obj_->t_ = t_;
}
else
{
obj_->t_->size = static_cast<index_t>(size_);
}
}
void
commit() noexcept
{
BOOST_ASSERT(obj_);
if( t_ )
table::deallocate( t_, obj_->sp_ );
obj_ = nullptr;
}
};
//----------------------------------------------------------
//
// Iterators
//
//----------------------------------------------------------
auto
object::
begin() noexcept ->
iterator
{
return &(*t_)[0];
}
auto
object::
begin() const noexcept ->
const_iterator
{
return &(*t_)[0];
}
auto
object::
cbegin() const noexcept ->
const_iterator
{
return &(*t_)[0];
}
auto
object::
end() noexcept ->
iterator
{
return &(*t_)[t_->size];
}
auto
object::
end() const noexcept ->
const_iterator
{
return &(*t_)[t_->size];
}
auto
object::
cend() const noexcept ->
const_iterator
{
return &(*t_)[t_->size];
}
auto
object::
rbegin() noexcept ->
reverse_iterator
{
return reverse_iterator(end());
}
auto
object::
rbegin() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(end());
}
auto
object::
crbegin() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(end());
}
auto
object::
rend() noexcept ->
reverse_iterator
{
return reverse_iterator(begin());
}
auto
object::
rend() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(begin());
}
auto
object::
crend() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(begin());
}
//----------------------------------------------------------
//
// Capacity
//
//----------------------------------------------------------
bool
object::
empty() const noexcept
{
return t_->size == 0;
}
auto
object::
size() const noexcept ->
std::size_t
{
return t_->size;
}
constexpr
std::size_t
object::
max_size() noexcept
{
// max_size depends on the address model
using min = std::integral_constant<std::size_t,
(std::size_t(-1) - sizeof(table)) /
(sizeof(key_value_pair) + sizeof(index_t))>;
return min::value < BOOST_JSON_MAX_STRUCTURED_SIZE ?
min::value : BOOST_JSON_MAX_STRUCTURED_SIZE;
}
auto
object::
capacity() const noexcept ->
std::size_t
{
return t_->capacity;
}
void
object::
reserve(std::size_t new_capacity)
{
if( new_capacity <= capacity() )
return;
table* const old_table = reserve_impl(new_capacity);
table::deallocate( old_table, sp_ );
}
//----------------------------------------------------------
//
// Lookup
//
//----------------------------------------------------------
auto
object::
at(string_view key) & ->
value&
{
auto const& self = *this;
return const_cast< value& >( self.at(key) );
}
auto
object::
at(string_view key) && ->
value&&
{
return std::move( at(key) );
}
auto
object::
at(string_view key) const& ->
value const&
{
auto it = find(key);
if(it == end())
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::out_of_range, &loc );
}
return it->value();
}
//----------------------------------------------------------
template<class P, class>
auto
object::
insert(P&& p) ->
std::pair<iterator, bool>
{
key_value_pair v(
std::forward<P>(p), sp_);
return emplace_impl( v.key(), pilfer(v) );
}
template<class M>
auto
object::
insert_or_assign(
string_view key, M&& m) ->
std::pair<iterator, bool>
{
std::pair<iterator, bool> result = emplace_impl(
key, key, static_cast<M&&>(m) );
if( !result.second )
{
value(static_cast<M>(m), sp_).swap(
result.first->value());
}
return result;
}
template<class Arg>
auto
object::
emplace(
string_view key,
Arg&& arg) ->
std::pair<iterator, bool>
{
return emplace_impl( key, key, static_cast<Arg&&>(arg) );
}
//----------------------------------------------------------
//
// (private)
//
//----------------------------------------------------------
template<class InputIt>
void
object::
construct(
InputIt first,
InputIt last,
std::size_t min_capacity,
std::input_iterator_tag)
{
reserve(min_capacity);
revert_construct r(*this);
while(first != last)
{
insert(*first);
++first;
}
r.commit();
}
template<class InputIt>
void
object::
construct(
InputIt first,
InputIt last,
std::size_t min_capacity,
std::forward_iterator_tag)
{
auto n = static_cast<
std::size_t>(std::distance(
first, last));
if( n < min_capacity)
n = min_capacity;
reserve(n);
revert_construct r(*this);
while(first != last)
{
insert(*first);
++first;
}
r.commit();
}
template<class InputIt>
void
object::
insert(
InputIt first,
InputIt last,
std::input_iterator_tag)
{
// Since input iterators cannot be rewound,
// we keep inserted elements on an exception.
//
while(first != last)
{
insert(*first);
++first;
}
}
template<class InputIt>
void
object::
insert(
InputIt first,
InputIt last,
std::forward_iterator_tag)
{
auto const n =
static_cast<std::size_t>(
std::distance(first, last));
auto const n0 = size();
if(n > max_size() - n0)
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::object_too_large, &loc );
}
revert_insert r( *this, n0 + n );
while(first != last)
{
insert(*first);
++first;
}
r.commit();
}
template< class... Args >
std::pair<object::iterator, bool>
object::
emplace_impl( string_view key, Args&& ... args )
{
std::pair<iterator, std::size_t> search_result(nullptr, 0);
if( !empty() )
{
search_result = detail::find_in_object(*this, key);
if( search_result.first )
return { search_result.first, false };
}
// we create the new value before reserving, in case it is a reference to
// a subobject of the current object
key_value_pair kv( static_cast<Args&&>(args)..., sp_ );
// the key might get deallocated too
key = kv.key();
std::size_t const old_capacity = capacity();
reserve(size() + 1);
if( (empty() && capacity() > detail::small_object_size_)
|| (capacity() != old_capacity) )
search_result.second = detail::digest(
key.begin(), key.end(), t_->salt);
BOOST_ASSERT(
t_->is_small() ||
(search_result.second ==
detail::digest(key.begin(), key.end(), t_->salt)) );
return { insert_impl(pilfer(kv), search_result.second), true };
}
//----------------------------------------------------------
namespace detail {
unchecked_object::
~unchecked_object()
{
if(! data_)
return;
if(sp_.is_not_shared_and_deallocate_is_trivial())
return;
value* p = data_;
while(size_--)
{
p[0].~value();
p[1].~value();
p += 2;
}
}
} // detail
} // namespace json
} // namespace boost
#endif
+897
View File
@@ -0,0 +1,897 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_OBJECT_IPP
#define BOOST_JSON_IMPL_OBJECT_IPP
#include <boost/container_hash/hash.hpp>
#include <boost/json/object.hpp>
#include <boost/json/detail/digest.hpp>
#include <boost/json/detail/except.hpp>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <new>
#include <stdexcept>
#include <type_traits>
namespace boost {
namespace json {
namespace detail {
template<class CharRange>
std::pair<key_value_pair*, std::size_t>
find_in_object(
object const& obj,
CharRange key) noexcept
{
BOOST_ASSERT(obj.t_->capacity > 0);
if(obj.t_->is_small())
{
auto it = &(*obj.t_)[0];
auto const last =
&(*obj.t_)[obj.t_->size];
for(;it != last; ++it)
if( key == it->key() )
return { it, 0 };
return { nullptr, 0 };
}
std::pair<
key_value_pair*,
std::size_t> result;
BOOST_ASSERT(obj.t_->salt != 0);
result.second = detail::digest(key.begin(), key.end(), obj.t_->salt);
auto i = obj.t_->bucket(
result.second);
while(i != object::null_index_)
{
auto& v = (*obj.t_)[i];
if( key == v.key() )
{
result.first = &v;
return result;
}
i = access::next(v);
}
result.first = nullptr;
return result;
}
template
std::pair<key_value_pair*, std::size_t>
find_in_object<string_view>(
object const& obj,
string_view key) noexcept;
} // namespace detail
//----------------------------------------------------------
constexpr object::table::table() = default;
// empty objects point here
BOOST_JSON_REQUIRE_CONST_INIT
object::table object::empty_;
std::size_t
object::table::
digest(string_view key) const noexcept
{
BOOST_ASSERT(salt != 0);
return detail::digest(
key.begin(), key.end(), salt);
}
auto
object::table::
bucket(std::size_t hash) noexcept ->
index_t&
{
return reinterpret_cast<
index_t*>(&(*this)[capacity])[
hash % capacity];
}
auto
object::table::
bucket(string_view key) noexcept ->
index_t&
{
return bucket(digest(key));
}
void
object::table::
clear() noexcept
{
BOOST_ASSERT(! is_small());
// initialize buckets
std::memset(
reinterpret_cast<index_t*>(
&(*this)[capacity]),
0xff, // null_index_
capacity * sizeof(index_t));
}
object::table*
object::table::
allocate(
std::size_t capacity,
std::uintptr_t salt,
storage_ptr const& sp)
{
BOOST_STATIC_ASSERT(
alignof(key_value_pair) >=
alignof(index_t));
BOOST_ASSERT(capacity > 0);
BOOST_ASSERT(capacity <= max_size());
table* p;
if(capacity <= detail::small_object_size_)
{
p = reinterpret_cast<
table*>(sp->allocate(
sizeof(table) + capacity *
sizeof(key_value_pair)));
p->capacity = static_cast<
std::uint32_t>(capacity);
}
else
{
p = reinterpret_cast<
table*>(sp->allocate(
sizeof(table) + capacity * (
sizeof(key_value_pair) +
sizeof(index_t))));
p->capacity = static_cast<
std::uint32_t>(capacity);
p->clear();
}
if(salt)
{
p->salt = salt;
}
else
{
// VFALCO This would be better if it
// was random, but maybe this
// is good enough.
p->salt = reinterpret_cast<
std::uintptr_t>(p);
}
return p;
}
//----------------------------------------------------------
void
object::
revert_construct::
destroy() noexcept
{
obj_->destroy();
}
//----------------------------------------------------------
void
object::
revert_insert::
destroy() noexcept
{
obj_->destroy(
&(*obj_->t_)[size_],
obj_->end());
}
//----------------------------------------------------------
//
// Construction
//
//----------------------------------------------------------
object::
object(detail::unchecked_object&& uo)
: sp_(uo.storage())
{
if(uo.size() == 0)
{
t_ = &empty_;
return;
}
// should already be checked
BOOST_ASSERT(
uo.size() <= max_size());
t_ = table::allocate(
uo.size(), 0, sp_);
// insert all elements, keeping
// the last of any duplicate keys.
auto dest = begin();
auto src = uo.release();
auto const end = src + 2 * uo.size();
if(t_->is_small())
{
t_->size = 0;
while(src != end)
{
access::construct_key_value_pair(
dest, pilfer(src[0]), pilfer(src[1]));
src += 2;
auto result = detail::find_in_object(*this, dest->key());
if(! result.first)
{
++dest;
++t_->size;
continue;
}
// handle duplicate
auto& v = *result.first;
// don't bother to check if
// storage deallocate is trivial
v.~key_value_pair();
// trivial relocate
std::memcpy(
static_cast<void*>(&v),
dest, sizeof(v));
}
return;
}
while(src != end)
{
access::construct_key_value_pair(
dest, pilfer(src[0]), pilfer(src[1]));
src += 2;
auto& head = t_->bucket(dest->key());
auto i = head;
for(;;)
{
if(i == null_index_)
{
// end of bucket
access::next(
*dest) = head;
head = static_cast<index_t>(
dest - begin());
++dest;
break;
}
auto& v = (*t_)[i];
if(v.key() != dest->key())
{
i = access::next(v);
continue;
}
// handle duplicate
access::next(*dest) =
access::next(v);
// don't bother to check if
// storage deallocate is trivial
v.~key_value_pair();
// trivial relocate
std::memcpy(
static_cast<void*>(&v),
dest, sizeof(v));
break;
}
}
t_->size = static_cast<
index_t>(dest - begin());
}
object::
~object() noexcept
{
if(sp_.is_not_shared_and_deallocate_is_trivial())
return;
if(t_->capacity == 0)
return;
destroy();
}
object::
object(
std::size_t min_capacity,
storage_ptr sp)
: sp_(std::move(sp))
, t_(&empty_)
{
reserve(min_capacity);
}
object::
object(object&& other) noexcept
: sp_(other.sp_)
, t_(detail::exchange(
other.t_, &empty_))
{
}
object::
object(
object&& other,
storage_ptr sp)
: sp_(std::move(sp))
{
if(*sp_ == *other.sp_)
{
t_ = detail::exchange(
other.t_, &empty_);
return;
}
t_ = &empty_;
object(other, sp_).swap(*this);
}
object::
object(
object const& other,
storage_ptr sp)
: sp_(std::move(sp))
, t_(&empty_)
{
reserve(other.size());
revert_construct r(*this);
if(t_->is_small())
{
for(auto const& v : other)
{
::new(end())
key_value_pair(v, sp_);
++t_->size;
}
r.commit();
return;
}
for(auto const& v : other)
{
// skip duplicate checking
auto& head =
t_->bucket(v.key());
auto pv = ::new(end())
key_value_pair(v, sp_);
access::next(*pv) = head;
head = t_->size;
++t_->size;
}
r.commit();
}
object::
object(
std::initializer_list<std::pair<
string_view, value_ref>> init,
std::size_t min_capacity,
storage_ptr sp)
: sp_(std::move(sp))
, t_(&empty_)
{
if( min_capacity < init.size())
min_capacity = init.size();
reserve(min_capacity);
revert_construct r(*this);
insert(init);
r.commit();
}
//----------------------------------------------------------
//
// Assignment
//
//----------------------------------------------------------
object&
object::
operator=(object const& other)
{
object tmp(other, sp_);
this->~object();
::new(this) object(pilfer(tmp));
return *this;
}
object&
object::
operator=(object&& other)
{
object tmp(std::move(other), sp_);
this->~object();
::new(this) object(pilfer(tmp));
return *this;
}
object&
object::
operator=(
std::initializer_list<std::pair<
string_view, value_ref>> init)
{
object tmp(init, sp_);
this->~object();
::new(this) object(pilfer(tmp));
return *this;
}
//----------------------------------------------------------
//
// Modifiers
//
//----------------------------------------------------------
void
object::
clear() noexcept
{
if(empty())
return;
if(! sp_.is_not_shared_and_deallocate_is_trivial())
destroy(begin(), end());
if(! t_->is_small())
t_->clear();
t_->size = 0;
}
void
object::
insert(
std::initializer_list<std::pair<
string_view, value_ref>> init)
{
auto const n0 = size();
if(init.size() > max_size() - n0)
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::object_too_large, &loc );
}
revert_insert r( *this, n0 + init.size() );
if(t_->is_small())
{
for(auto& iv : init)
{
auto result =
detail::find_in_object(*this, iv.first);
if(result.first)
{
// ignore duplicate
continue;
}
::new(end()) key_value_pair(
iv.first,
iv.second.make_value(sp_));
++t_->size;
}
r.commit();
return;
}
for(auto& iv : init)
{
auto& head = t_->bucket(iv.first);
auto i = head;
for(;;)
{
if(i == null_index_)
{
// VFALCO value_ref should construct
// a key_value_pair using placement
auto& v = *::new(end())
key_value_pair(
iv.first,
iv.second.make_value(sp_));
access::next(v) = head;
head = static_cast<index_t>(
t_->size);
++t_->size;
break;
}
auto& v = (*t_)[i];
if(v.key() == iv.first)
{
// ignore duplicate
break;
}
i = access::next(v);
}
}
r.commit();
}
auto
object::
erase(const_iterator pos) noexcept ->
iterator
{
return do_erase(pos,
[this](iterator p) {
// the casts silence warnings
std::memcpy(
static_cast<void*>(p),
static_cast<void const*>(end()),
sizeof(*p));
},
[this](iterator p) {
reindex_relocate(end(), p);
});
}
auto
object::
erase(string_view key) noexcept ->
std::size_t
{
auto it = find(key);
if(it == end())
return 0;
erase(it);
return 1;
}
auto
object::
stable_erase(const_iterator pos) noexcept ->
iterator
{
return do_erase(pos,
[this](iterator p) {
// the casts silence warnings
std::memmove(
static_cast<void*>(p),
static_cast<void const*>(p + 1),
sizeof(*p) * (end() - p));
},
[this](iterator p) {
for (; p != end(); ++p)
{
reindex_relocate(p + 1, p);
}
});
}
auto
object::
stable_erase(string_view key) noexcept ->
std::size_t
{
auto it = find(key);
if(it == end())
return 0;
stable_erase(it);
return 1;
}
void
object::
swap(object& other)
{
if(*sp_ == *other.sp_)
{
t_ = detail::exchange(
other.t_, t_);
return;
}
object temp1(
std::move(*this),
other.storage());
object temp2(
std::move(other),
this->storage());
other.~object();
::new(&other) object(pilfer(temp1));
this->~object();
::new(this) object(pilfer(temp2));
}
//----------------------------------------------------------
//
// Lookup
//
//----------------------------------------------------------
auto
object::
operator[](string_view key) ->
value&
{
auto const result =
emplace(key, nullptr);
return result.first->value();
}
auto
object::
count(string_view key) const noexcept ->
std::size_t
{
if(find(key) == end())
return 0;
return 1;
}
auto
object::
find(string_view key) noexcept ->
iterator
{
if(empty())
return end();
auto const p =
detail::find_in_object(*this, key).first;
if(p)
return p;
return end();
}
auto
object::
find(string_view key) const noexcept ->
const_iterator
{
if(empty())
return end();
auto const p =
detail::find_in_object(*this, key).first;
if(p)
return p;
return end();
}
bool
object::
contains(
string_view key) const noexcept
{
if(empty())
return false;
return detail::find_in_object(*this, key).first
!= nullptr;
}
value const*
object::
if_contains(
string_view key) const noexcept
{
auto const it = find(key);
if(it != end())
return &it->value();
return nullptr;
}
value*
object::
if_contains(
string_view key) noexcept
{
auto const it = find(key);
if(it != end())
return &it->value();
return nullptr;
}
//----------------------------------------------------------
//
// (private)
//
//----------------------------------------------------------
key_value_pair*
object::
insert_impl(
pilfered<key_value_pair> p,
std::size_t hash)
{
BOOST_ASSERT(
capacity() > size());
if(t_->is_small())
{
auto const pv = ::new(end())
key_value_pair(p);
++t_->size;
return pv;
}
auto& head =
t_->bucket(hash);
auto const pv = ::new(end())
key_value_pair(p);
access::next(*pv) = head;
head = t_->size;
++t_->size;
return pv;
}
// allocate new table, copy elements there, and rehash them
object::table*
object::
reserve_impl(std::size_t new_capacity)
{
BOOST_ASSERT(
new_capacity > t_->capacity);
auto t = table::allocate(
growth(new_capacity),
t_->salt, sp_);
if(! empty())
std::memcpy(
static_cast<
void*>(&(*t)[0]),
begin(),
size() * sizeof(
key_value_pair));
t->size = t_->size;
std::swap(t_, t);
if(! t_->is_small())
{
// rebuild hash table,
// without dup checks
auto p = end();
index_t i = t_->size;
while(i-- > 0)
{
--p;
auto& head =
t_->bucket(p->key());
access::next(*p) = head;
head = i;
}
}
return t;
}
bool
object::
equal(object const& other) const noexcept
{
if(size() != other.size())
return false;
auto const end_ = other.end();
for(auto e : *this)
{
auto it = other.find(e.key());
if(it == end_)
return false;
if(it->value() != e.value())
return false;
}
return true;
}
std::size_t
object::
growth(
std::size_t new_size) const
{
if(new_size > max_size())
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::object_too_large, &loc );
}
std::size_t const old = capacity();
if(old > max_size() - old / 2)
return new_size;
std::size_t const g =
old + old / 2; // 1.5x
if(g < new_size)
return new_size;
return g;
}
void
object::
remove(
index_t& head,
key_value_pair& v) noexcept
{
BOOST_ASSERT(! t_->is_small());
auto const i = static_cast<
index_t>(&v - begin());
if(head == i)
{
head = access::next(v);
return;
}
auto* pn =
&access::next((*t_)[head]);
while(*pn != i)
pn = &access::next((*t_)[*pn]);
*pn = access::next(v);
}
void
object::
destroy() noexcept
{
BOOST_ASSERT(t_->capacity > 0);
BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
destroy(begin(), end());
table::deallocate(t_, sp_);
}
void
object::
destroy(
key_value_pair* first,
key_value_pair* last) noexcept
{
BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
while(last != first)
(--last)->~key_value_pair();
}
template<class FS, class FB>
auto
object::
do_erase(
const_iterator pos,
FS small_reloc,
FB big_reloc) noexcept
-> iterator
{
auto p = begin() + (pos - begin());
if(t_->is_small())
{
p->~value_type();
--t_->size;
if(p != end())
{
small_reloc(p);
}
return p;
}
remove(t_->bucket(p->key()), *p);
p->~value_type();
--t_->size;
if(p != end())
{
big_reloc(p);
}
return p;
}
void
object::
reindex_relocate(
key_value_pair* src,
key_value_pair* dst) noexcept
{
BOOST_ASSERT(! t_->is_small());
auto& head = t_->bucket(src->key());
remove(head, *src);
// the casts silence warnings
std::memcpy(
static_cast<void*>(dst),
static_cast<void const*>(src),
sizeof(*dst));
access::next(*dst) = head;
head = static_cast<
index_t>(dst - begin());
}
} // namespace json
} // namespace boost
//----------------------------------------------------------
//
// std::hash specialization
//
//----------------------------------------------------------
std::size_t
std::hash<::boost::json::object>::operator()(
::boost::json::object const& jo) const noexcept
{
return ::boost::hash< ::boost::json::object >()( jo );
}
//----------------------------------------------------------
#endif
+136
View File
@@ -0,0 +1,136 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_PARSE_IPP
#define BOOST_JSON_IMPL_PARSE_IPP
#include <boost/json/parse.hpp>
#include <boost/json/parser.hpp>
#include <boost/json/detail/except.hpp>
#include <istream>
namespace boost {
namespace json {
value
parse(
string_view s,
error_code& ec,
storage_ptr sp,
const parse_options& opt)
{
unsigned char temp[
BOOST_JSON_STACK_BUFFER_SIZE];
parser p(storage_ptr(), opt, temp);
p.reset(std::move(sp));
p.write(s, ec);
if(ec)
return nullptr;
return p.release();
}
value
parse(
string_view s,
std::error_code& ec,
storage_ptr sp,
parse_options const& opt)
{
error_code jec;
value result = parse(s, jec, std::move(sp), opt);
ec = jec;
return result;
}
value
parse(
string_view s,
storage_ptr sp,
const parse_options& opt)
{
error_code ec;
auto jv = parse(
s, ec, std::move(sp), opt);
if(ec)
detail::throw_system_error( ec );
return jv;
}
value
parse(
std::istream& is,
error_code& ec,
storage_ptr sp,
parse_options const& opt)
{
unsigned char parser_buffer[BOOST_JSON_STACK_BUFFER_SIZE / 2];
stream_parser p(storage_ptr(), opt, parser_buffer);
p.reset(std::move(sp));
char read_buffer[BOOST_JSON_STACK_BUFFER_SIZE / 2];
do
{
if( is.eof() )
{
p.finish(ec);
break;
}
if( !is )
{
BOOST_JSON_FAIL( ec, error::input_error );
break;
}
is.read(read_buffer, sizeof(read_buffer));
auto const consumed = is.gcount();
p.write( read_buffer, static_cast<std::size_t>(consumed), ec );
}
while( !ec.failed() );
if( ec.failed() )
return nullptr;
return p.release();
}
value
parse(
std::istream& is,
std::error_code& ec,
storage_ptr sp,
parse_options const& opt)
{
error_code jec;
value result = parse(is, jec, std::move(sp), opt);
ec = jec;
return result;
}
value
parse(
std::istream& is,
storage_ptr sp,
parse_options const& opt)
{
error_code ec;
auto jv = parse(
is, ec, std::move(sp), opt);
if(ec)
detail::throw_system_error( ec );
return jv;
}
} // namespace json
} // namespace boost
#endif
+131
View File
@@ -0,0 +1,131 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_PARSE_INTO_HPP
#define BOOST_JSON_IMPL_PARSE_INTO_HPP
#include <boost/json/basic_parser_impl.hpp>
#include <boost/json/error.hpp>
#include <istream>
namespace boost {
namespace json {
template<class V>
void
parse_into(
V& v,
string_view sv,
error_code& ec,
parse_options const& opt )
{
parser_for<V> p( opt, &v );
std::size_t n = p.write_some( false, sv.data(), sv.size(), ec );
if( !ec && n < sv.size() )
{
BOOST_JSON_FAIL( ec, error::extra_data );
}
}
template<class V>
void
parse_into(
V& v,
string_view sv,
std::error_code& ec,
parse_options const& opt )
{
error_code jec;
parse_into(v, sv, jec, opt);
ec = jec;
}
template<class V>
void
parse_into(
V& v,
string_view sv,
parse_options const& opt )
{
error_code ec;
parse_into(v, sv, ec, opt);
if( ec.failed() )
detail::throw_system_error( ec );
}
template<class V>
void
parse_into(
V& v,
std::istream& is,
error_code& ec,
parse_options const& opt )
{
parser_for<V> p( opt, &v );
char read_buffer[BOOST_JSON_STACK_BUFFER_SIZE];
do
{
if( is.eof() )
{
p.write_some(false, nullptr, 0, ec);
break;
}
if( !is )
{
BOOST_JSON_FAIL( ec, error::input_error );
break;
}
is.read(read_buffer, sizeof(read_buffer));
std::size_t const consumed = static_cast<std::size_t>( is.gcount() );
std::size_t const n = p.write_some( true, read_buffer, consumed, ec );
if( !ec.failed() && n < consumed )
{
BOOST_JSON_FAIL( ec, error::extra_data );
}
}
while( !ec.failed() );
}
template<class V>
void
parse_into(
V& v,
std::istream& is,
std::error_code& ec,
parse_options const& opt )
{
error_code jec;
parse_into(v, is, jec, opt);
ec = jec;
}
template<class V>
void
parse_into(
V& v,
std::istream& is,
parse_options const& opt )
{
error_code ec;
parse_into(v, is, ec, opt);
if( ec.failed() )
detail::throw_system_error( ec );
}
} // namespace boost
} // namespace json
#endif
+165
View File
@@ -0,0 +1,165 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_PARSER_IPP
#define BOOST_JSON_IMPL_PARSER_IPP
#include <boost/json/parser.hpp>
#include <boost/json/basic_parser_impl.hpp>
#include <boost/json/error.hpp>
#include <cstring>
#include <stdexcept>
#include <utility>
namespace boost {
namespace json {
parser::
parser(
storage_ptr sp,
parse_options const& opt,
unsigned char* buffer,
std::size_t size) noexcept
: p_(
opt,
std::move(sp),
buffer,
size)
{
reset();
}
parser::
parser(
storage_ptr sp,
parse_options const& opt) noexcept
: p_(
opt,
std::move(sp),
nullptr,
0)
{
reset();
}
void
parser::
reset(storage_ptr sp) noexcept
{
p_.reset();
p_.handler().st.reset(sp);
}
std::size_t
parser::
write_some(
char const* data,
std::size_t size,
error_code& ec)
{
auto const n = p_.write_some(
false, data, size, ec);
BOOST_ASSERT(ec || p_.done());
return n;
}
std::size_t
parser::
write_some(
char const* data,
std::size_t size,
std::error_code& ec)
{
error_code jec;
std::size_t const result = write_some(data, size, jec);
ec = jec;
return result;
}
std::size_t
parser::
write_some(
char const* data,
std::size_t size)
{
error_code ec;
auto const n = write_some(
data, size, ec);
if(ec)
detail::throw_system_error( ec );
return n;
}
std::size_t
parser::
write(
char const* data,
std::size_t size,
error_code& ec)
{
auto const n = write_some(
data, size, ec);
if(! ec && n < size)
{
BOOST_JSON_FAIL(ec, error::extra_data);
p_.fail(ec);
}
return n;
}
std::size_t
parser::
write(
char const* data,
std::size_t size,
std::error_code& ec)
{
error_code jec;
std::size_t const result = write(data, size, jec);
ec = jec;
return result;
}
std::size_t
parser::
write(
char const* data,
std::size_t size)
{
error_code ec;
auto const n = write(
data, size, ec);
if(ec)
detail::throw_system_error( ec );
return n;
}
value
parser::
release()
{
if( ! p_.done())
{
// prevent undefined behavior
if(! p_.last_error())
{
error_code ec;
BOOST_JSON_FAIL(ec, error::incomplete);
p_.fail(ec);
}
detail::throw_system_error(
p_.last_error());
}
return p_.handler().st.release();
}
} // namespace json
} // namespace boost
#endif
+501
View File
@@ -0,0 +1,501 @@
//
// Copyright (c) 2022 Dmitry Arkhipov (grisumbras@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_POINTER_IPP
#define BOOST_JSON_IMPL_POINTER_IPP
#include <boost/json/value.hpp>
namespace boost {
namespace json {
namespace detail {
class pointer_token
{
public:
class iterator;
pointer_token(
string_view sv) noexcept
: b_( sv.begin() + 1 )
, e_( sv.end() )
{
BOOST_ASSERT( !sv.empty() );
BOOST_ASSERT( *sv.data() == '/' );
}
iterator begin() const noexcept;
iterator end() const noexcept;
private:
char const* b_;
char const* e_;
};
class pointer_token::iterator
{
public:
using value_type = char;
using reference = char;
using pointer = value_type*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
explicit iterator(char const* base) noexcept
: base_(base)
{
}
char operator*() const noexcept
{
switch( char c = *base_ )
{
case '~':
c = base_[1];
if( '0' == c )
return '~';
BOOST_ASSERT('1' == c);
return '/';
default:
return c;
}
}
iterator& operator++() noexcept
{
if( '~' == *base_ )
base_ += 2;
else
++base_;
return *this;
}
iterator operator++(int) noexcept
{
iterator result = *this;
++(*this);
return result;
}
char const* base() const noexcept
{
return base_;
}
private:
char const* base_;
};
bool operator==(pointer_token::iterator l, pointer_token::iterator r) noexcept
{
return l.base() == r.base();
}
bool operator!=(pointer_token::iterator l, pointer_token::iterator r) noexcept
{
return l.base() != r.base();
}
pointer_token::iterator pointer_token::begin() const noexcept
{
return iterator(b_);
}
pointer_token::iterator pointer_token::end() const noexcept
{
return iterator(e_);
}
bool operator==(pointer_token token, string_view sv) noexcept
{
auto t_b = token.begin();
auto const t_e = token.end();
auto s_b = sv.begin();
auto const s_e = sv.end();
while( s_b != s_e )
{
if( t_e == t_b )
return false;
if( *t_b != *s_b )
return false;
++t_b;
++s_b;
}
return t_b == t_e;
}
bool is_invalid_zero(
char const* b,
char const* e) noexcept
{
// in JSON Pointer only zero index can start character '0'
if( *b != '0' )
return false;
// if an index token starts with '0', then it should not have any more
// characters: either the string should end, or new token should start
++b;
if( b == e )
return false;
BOOST_ASSERT( *b != '/' );
return true;
}
bool is_past_the_end_token(
char const* b,
char const* e) noexcept
{
if( *b != '-' )
return false;
++b;
BOOST_ASSERT( (b == e) || (*b != '/') );
return b == e;
}
std::size_t
parse_number_token(
string_view sv,
error_code& ec) noexcept
{
BOOST_ASSERT( !sv.empty() );
char const* b = sv.begin();
BOOST_ASSERT( *b == '/' );
++b;
char const* const e = sv.end();
if( ( b == e )
|| is_invalid_zero(b, e) )
{
BOOST_JSON_FAIL(ec, error::token_not_number);
return {};
}
if( is_past_the_end_token(b, e) )
{
++b;
BOOST_JSON_FAIL(ec, error::past_the_end);
return {};
}
std::size_t result = 0;
for( ; b != e; ++b )
{
char const c = *b;
BOOST_ASSERT( c != '/' );
unsigned d = c - '0';
if( d > 9 )
{
BOOST_JSON_FAIL(ec, error::token_not_number);
return {};
}
std::size_t new_result = result * 10 + d;
if( new_result < result )
{
BOOST_JSON_FAIL(ec, error::token_overflow);
return {};
}
result = new_result;
}
return result;
}
string_view
next_segment(
string_view& sv,
error_code& ec) noexcept
{
if( sv.empty() )
return sv;
char const* const start = sv.begin();
char const* b = start;
if( *b++ != '/' )
{
BOOST_JSON_FAIL( ec, error::missing_slash );
return {};
}
char const* e = sv.end();
for( ; b < e; ++b )
{
char const c = *b;
if( '/' == c )
break;
if( '~' == c )
{
if( ++b == e )
{
BOOST_JSON_FAIL( ec, error::invalid_escape );
break;
}
switch (*b)
{
case '0': // fall through
case '1':
// valid escape sequence
continue;
default: {
BOOST_JSON_FAIL( ec, error::invalid_escape );
break;
}
}
break;
}
}
sv.remove_prefix( b - start );
return string_view( start, b );
}
value*
if_contains_token(object const& obj, pointer_token token)
{
if( obj.empty() )
return nullptr;
auto const it = detail::find_in_object(obj, token).first;
if( !it )
return nullptr;
return &it->value();
}
template<
class Value,
class OnObject,
class OnArray,
class OnScalar >
Value*
walk_pointer(
Value& jv,
string_view sv,
error_code& ec,
OnObject on_object,
OnArray on_array,
OnScalar on_scalar)
{
ec.clear();
string_view segment = detail::next_segment( sv, ec );
Value* result = &jv;
while( true )
{
if( ec.failed() )
return nullptr;
if( !result )
{
BOOST_JSON_FAIL(ec, error::not_found);
return nullptr;
}
if( segment.empty() )
break;
switch( result->kind() )
{
case kind::object: {
auto& obj = result->get_object();
detail::pointer_token const token( segment );
segment = detail::next_segment( sv, ec );
result = on_object( obj, token );
break;
}
case kind::array: {
auto const index = detail::parse_number_token( segment, ec );
segment = detail::next_segment( sv, ec );
auto& arr = result->get_array();
result = on_array( arr, index, ec );
break;
}
default: {
if( on_scalar( *result, segment ) )
break;
BOOST_JSON_FAIL( ec, error::value_is_scalar );
}}
}
BOOST_ASSERT( result );
return result;
}
} // namespace detail
value const&
value::at_pointer(string_view ptr) const&
{
error_code ec;
auto const found = find_pointer(ptr, ec);
if( !found )
detail::throw_system_error( ec );
return *found;
}
value const*
value::find_pointer( string_view sv, error_code& ec ) const noexcept
{
return detail::walk_pointer(
*this,
sv,
ec,
[]( object const& obj, detail::pointer_token token )
{
return detail::if_contains_token(obj, token);
},
[]( array const& arr, std::size_t index, error_code& ec )
-> value const*
{
if( ec )
return nullptr;
return arr.if_contains(index);
},
[]( value const&, string_view)
{
return std::false_type();
});
}
value*
value::find_pointer(string_view ptr, error_code& ec) noexcept
{
value const& self = *this;
return const_cast<value*>(self.find_pointer(ptr, ec));
}
value const*
value::find_pointer(string_view ptr, std::error_code& ec) const noexcept
{
error_code jec;
value const* result = find_pointer(ptr, jec);
ec = jec;
return result;
}
value*
value::find_pointer(string_view ptr, std::error_code& ec) noexcept
{
value const& self = *this;
return const_cast<value*>(self.find_pointer(ptr, ec));
}
value*
value::set_at_pointer(
string_view sv,
value_ref ref,
error_code& ec,
set_pointer_options const& opts )
{
value* result = detail::walk_pointer(
*this,
sv,
ec,
[]( object& obj, detail::pointer_token token)
{
if( !obj.empty() )
{
key_value_pair* kv = detail::find_in_object( obj, token ).first;
if( kv )
return &kv->value();
}
string key( token.begin(), token.end(), obj.storage() );
return &obj.emplace( std::move(key), nullptr ).first->value();
},
[ &opts ]( array& arr, std::size_t index, error_code& ec ) -> value*
{
if( ec == error::past_the_end )
index = arr.size();
else if( ec.failed() )
return nullptr;
if( index >= arr.size() )
{
std::size_t const n = index - arr.size();
if( n >= opts.max_created_elements )
return nullptr;
arr.resize( arr.size() + n + 1 );
}
ec.clear();
return arr.data() + index;
},
[ &opts ]( value& jv, string_view segment )
{
if( jv.is_null() || opts.replace_any_scalar )
{
if( opts.create_arrays )
{
error_code ec;
detail::parse_number_token( segment, ec );
if( !ec.failed() || ec == error::past_the_end )
{
jv = array( jv.storage() );
return true;
}
}
if( opts.create_objects )
{
jv = object( jv.storage() );
return true;
}
}
return false;
});
if( result )
*result = ref.make_value( storage() );
return result;
}
value*
value::set_at_pointer(
string_view sv,
value_ref ref,
std::error_code& ec,
set_pointer_options const& opts )
{
error_code jec;
value* result = set_at_pointer( sv, ref, jec, opts );
ec = jec;
return result;
}
value&
value::set_at_pointer(
string_view sv, value_ref ref, set_pointer_options const& opts )
{
error_code ec;
value* result = set_at_pointer( sv, ref, ec, opts );
if( !result )
detail::throw_system_error( ec );
return *result;
}
} // namespace json
} // namespace boost
#endif // BOOST_JSON_IMPL_POINTER_IPP
+259
View File
@@ -0,0 +1,259 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_SERIALIZE_IPP
#define BOOST_JSON_IMPL_SERIALIZE_IPP
#include <boost/json/serialize.hpp>
#include <boost/json/serializer.hpp>
#include <ostream>
namespace boost {
namespace json {
namespace {
int serialize_xalloc = std::ios::xalloc();
enum class serialize_stream_flags : long
{
allow_infinity_and_nan = 1,
};
std::underlying_type<serialize_stream_flags>::type
to_bitmask( serialize_options const& opts )
{
using E = serialize_stream_flags;
using I = std::underlying_type<E>::type;
return (opts.allow_infinity_and_nan
? static_cast<I>(E::allow_infinity_and_nan) : 0);
}
serialize_options
get_stream_flags( std::ostream& os )
{
auto const flags = os.iword(serialize_xalloc);
serialize_options opts;
using E = serialize_stream_flags;
using I = std::underlying_type<E>::type;
opts.allow_infinity_and_nan =
flags & static_cast<I>(E::allow_infinity_and_nan);
return opts;
}
} // namespace
static
void
serialize_impl(
std::string& s,
serializer& sr)
{
// serialize to a small buffer to avoid
// the first few allocations in std::string
char buf[BOOST_JSON_STACK_BUFFER_SIZE];
string_view sv;
sv = sr.read(buf);
if(sr.done())
{
// fast path
s.append(
sv.data(), sv.size());
return;
}
std::size_t len = sv.size();
s.reserve(len * 2);
s.resize(s.capacity());
BOOST_ASSERT(
s.size() >= len * 2);
std::memcpy(&s[0],
sv.data(), sv.size());
auto const lim =
s.max_size() / 2;
for(;;)
{
sv = sr.read(
&s[0] + len,
s.size() - len);
len += sv.size();
if(sr.done())
break;
// growth factor 2x
if(s.size() < lim)
s.resize(s.size() * 2);
else
s.resize(2 * lim);
}
s.resize(len);
}
std::string
serialize(
value const& jv,
serialize_options const& opts)
{
unsigned char buf[256];
serializer sr(
storage_ptr(),
buf,
sizeof(buf),
opts);
sr.reset(&jv);
std::string s;
serialize_impl(s, sr);
return s;
}
std::string
serialize(
array const& arr,
serialize_options const& opts)
{
unsigned char buf[256];
serializer sr(
storage_ptr(),
buf,
sizeof(buf),
opts);
std::string s;
sr.reset(&arr);
serialize_impl(s, sr);
return s;
}
std::string
serialize(
object const& obj,
serialize_options const& opts)
{
unsigned char buf[256];
serializer sr(
storage_ptr(),
buf,
sizeof(buf),
opts);
std::string s;
sr.reset(&obj);
serialize_impl(s, sr);
return s;
}
std::string
serialize(
string const& str,
serialize_options const& opts)
{
return serialize( str.subview(), opts );
}
// this is here for key_value_pair::key()
std::string
serialize(
string_view sv,
serialize_options const& opts)
{
unsigned char buf[256];
serializer sr(
storage_ptr(),
buf,
sizeof(buf),
opts);
std::string s;
sr.reset(sv);
serialize_impl(s, sr);
return s;
}
//----------------------------------------------------------
//[example_operator_lt__lt_
// Serialize a value into an output stream
std::ostream&
operator<<( std::ostream& os, value const& jv )
{
// Create a serializer
serializer sr( get_stream_flags(os) );
// Set the serializer up for our value
sr.reset( &jv );
// Loop until all output is produced.
while( ! sr.done() )
{
// Use a local buffer to avoid allocation.
char buf[ BOOST_JSON_STACK_BUFFER_SIZE ];
// Fill our buffer with serialized characters and write it to the output stream.
os << sr.read( buf );
}
return os;
}
//]
static
void
to_ostream(
std::ostream& os,
serializer& sr)
{
while(! sr.done())
{
char buf[BOOST_JSON_STACK_BUFFER_SIZE];
auto s = sr.read(buf);
os.write(s.data(), s.size());
}
}
std::ostream&
operator<<(
std::ostream& os,
array const& arr)
{
serializer sr( get_stream_flags(os) );
sr.reset(&arr);
to_ostream(os, sr);
return os;
}
std::ostream&
operator<<(
std::ostream& os,
object const& obj)
{
serializer sr( get_stream_flags(os) );
sr.reset(&obj);
to_ostream(os, sr);
return os;
}
std::ostream&
operator<<(
std::ostream& os,
string const& str)
{
serializer sr( get_stream_flags(os) );
sr.reset(&str);
to_ostream(os, sr);
return os;
}
std::ostream&
operator<<( std::ostream& os, serialize_options const& opts )
{
os.iword(serialize_xalloc) = to_bitmask(opts);
return os;
}
} // namespace json
} // namespace boost
#endif
+839
View File
@@ -0,0 +1,839 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_SERIALIZER_IPP
#define BOOST_JSON_IMPL_SERIALIZER_IPP
#include <boost/json/serializer.hpp>
#include <boost/json/detail/format.hpp>
#include <boost/json/detail/sse2.hpp>
#include <ostream>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4127) // conditional expression is constant
#endif
namespace boost {
namespace json {
enum class serializer::state : char
{
nul1, nul2, nul3, nul4,
tru1, tru2, tru3, tru4,
fal1, fal2, fal3, fal4, fal5,
str1, str2, str3, str4, esc1,
utf1, utf2, utf3, utf4, utf5,
num,
arr1, arr2, arr3, arr4,
obj1, obj2, obj3, obj4, obj5, obj6
};
//----------------------------------------------------------
serializer::
serializer(
storage_ptr sp,
unsigned char* buf,
std::size_t buf_size,
serialize_options const& opts) noexcept
: st_(
std::move(sp),
buf,
buf_size)
, opts_(opts)
{
}
bool
serializer::
suspend(state st)
{
st_.push(st);
return false;
}
bool
serializer::
suspend(
state st,
array::const_iterator it,
array const* pa)
{
st_.push(pa);
st_.push(it);
st_.push(st);
return false;
}
bool
serializer::
suspend(
state st,
object::const_iterator it,
object const* po)
{
st_.push(po);
st_.push(it);
st_.push(st);
return false;
}
template<bool StackEmpty>
bool
serializer::
write_null(stream& ss0)
{
local_stream ss(ss0);
if(! StackEmpty && ! st_.empty())
{
state st;
st_.pop(st);
switch(st)
{
default:
case state::nul1: goto do_nul1;
case state::nul2: goto do_nul2;
case state::nul3: goto do_nul3;
case state::nul4: goto do_nul4;
}
}
do_nul1:
if(BOOST_JSON_LIKELY(ss))
ss.append('n');
else
return suspend(state::nul1);
do_nul2:
if(BOOST_JSON_LIKELY(ss))
ss.append('u');
else
return suspend(state::nul2);
do_nul3:
if(BOOST_JSON_LIKELY(ss))
ss.append('l');
else
return suspend(state::nul3);
do_nul4:
if(BOOST_JSON_LIKELY(ss))
ss.append('l');
else
return suspend(state::nul4);
return true;
}
template<bool StackEmpty>
bool
serializer::
write_true(stream& ss0)
{
local_stream ss(ss0);
if(! StackEmpty && ! st_.empty())
{
state st;
st_.pop(st);
switch(st)
{
default:
case state::tru1: goto do_tru1;
case state::tru2: goto do_tru2;
case state::tru3: goto do_tru3;
case state::tru4: goto do_tru4;
}
}
do_tru1:
if(BOOST_JSON_LIKELY(ss))
ss.append('t');
else
return suspend(state::tru1);
do_tru2:
if(BOOST_JSON_LIKELY(ss))
ss.append('r');
else
return suspend(state::tru2);
do_tru3:
if(BOOST_JSON_LIKELY(ss))
ss.append('u');
else
return suspend(state::tru3);
do_tru4:
if(BOOST_JSON_LIKELY(ss))
ss.append('e');
else
return suspend(state::tru4);
return true;
}
template<bool StackEmpty>
bool
serializer::
write_false(stream& ss0)
{
local_stream ss(ss0);
if(! StackEmpty && ! st_.empty())
{
state st;
st_.pop(st);
switch(st)
{
default:
case state::fal1: goto do_fal1;
case state::fal2: goto do_fal2;
case state::fal3: goto do_fal3;
case state::fal4: goto do_fal4;
case state::fal5: goto do_fal5;
}
}
do_fal1:
if(BOOST_JSON_LIKELY(ss))
ss.append('f');
else
return suspend(state::fal1);
do_fal2:
if(BOOST_JSON_LIKELY(ss))
ss.append('a');
else
return suspend(state::fal2);
do_fal3:
if(BOOST_JSON_LIKELY(ss))
ss.append('l');
else
return suspend(state::fal3);
do_fal4:
if(BOOST_JSON_LIKELY(ss))
ss.append('s');
else
return suspend(state::fal4);
do_fal5:
if(BOOST_JSON_LIKELY(ss))
ss.append('e');
else
return suspend(state::fal5);
return true;
}
template<bool StackEmpty>
bool
serializer::
write_string(stream& ss0)
{
local_stream ss(ss0);
local_const_stream cs(cs0_);
if(! StackEmpty && ! st_.empty())
{
state st;
st_.pop(st);
switch(st)
{
default:
case state::str1: goto do_str1;
case state::str2: goto do_str2;
case state::str3: goto do_str3;
case state::str4: goto do_str4;
case state::esc1: goto do_esc1;
case state::utf1: goto do_utf1;
case state::utf2: goto do_utf2;
case state::utf3: goto do_utf3;
case state::utf4: goto do_utf4;
case state::utf5: goto do_utf5;
}
}
static constexpr char hex[] = "0123456789abcdef";
static constexpr char esc[] =
"uuuuuuuubtnufruuuuuuuuuuuuuuuuuu"
"\0\0\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
// opening quote
do_str1:
if(BOOST_JSON_LIKELY(ss))
ss.append('\x22'); // '"'
else
return suspend(state::str1);
// fast loop,
// copy unescaped
do_str2:
if(BOOST_JSON_LIKELY(ss))
{
std::size_t n = cs.remain();
if(BOOST_JSON_LIKELY(n > 0))
{
if(ss.remain() > n)
n = detail::count_unescaped(
cs.data(), n);
else
n = detail::count_unescaped(
cs.data(), ss.remain());
if(n > 0)
{
ss.append(cs.data(), n);
cs.skip(n);
if(! ss)
return suspend(state::str2);
}
}
else
{
ss.append('\x22'); // '"'
return true;
}
}
else
{
return suspend(state::str2);
}
// slow loop,
// handle escapes
do_str3:
while(BOOST_JSON_LIKELY(ss))
{
if(BOOST_JSON_LIKELY(cs))
{
auto const ch = *cs;
auto const c = esc[static_cast<
unsigned char>(ch)];
++cs;
if(! c)
{
ss.append(ch);
}
else if(c != 'u')
{
ss.append('\\');
if(BOOST_JSON_LIKELY(ss))
{
ss.append(c);
}
else
{
buf_[0] = c;
return suspend(
state::esc1);
}
}
else
{
if(BOOST_JSON_LIKELY(
ss.remain() >= 6))
{
ss.append("\\u00", 4);
ss.append(hex[static_cast<
unsigned char>(ch) >> 4]);
ss.append(hex[static_cast<
unsigned char>(ch) & 15]);
}
else
{
ss.append('\\');
buf_[0] = hex[static_cast<
unsigned char>(ch) >> 4];
buf_[1] = hex[static_cast<
unsigned char>(ch) & 15];
goto do_utf1;
}
}
}
else
{
ss.append('\x22'); // '"'
return true;
}
}
return suspend(state::str3);
do_str4:
if(BOOST_JSON_LIKELY(ss))
ss.append('\x22'); // '"'
else
return suspend(state::str4);
do_esc1:
if(BOOST_JSON_LIKELY(ss))
ss.append(buf_[0]);
else
return suspend(state::esc1);
goto do_str3;
do_utf1:
if(BOOST_JSON_LIKELY(ss))
ss.append('u');
else
return suspend(state::utf1);
do_utf2:
if(BOOST_JSON_LIKELY(ss))
ss.append('0');
else
return suspend(state::utf2);
do_utf3:
if(BOOST_JSON_LIKELY(ss))
ss.append('0');
else
return suspend(state::utf3);
do_utf4:
if(BOOST_JSON_LIKELY(ss))
ss.append(buf_[0]);
else
return suspend(state::utf4);
do_utf5:
if(BOOST_JSON_LIKELY(ss))
ss.append(buf_[1]);
else
return suspend(state::utf5);
goto do_str3;
}
template<bool StackEmpty>
bool
serializer::
write_number(stream& ss0)
{
local_stream ss(ss0);
if(StackEmpty || st_.empty())
{
switch(jv_->kind())
{
default:
case kind::int64:
if(BOOST_JSON_LIKELY(
ss.remain() >=
detail::max_number_chars))
{
ss.advance(detail::format_int64(
ss.data(), jv_->get_int64()));
return true;
}
cs0_ = { buf_, detail::format_int64(
buf_, jv_->get_int64()) };
break;
case kind::uint64:
if(BOOST_JSON_LIKELY(
ss.remain() >=
detail::max_number_chars))
{
ss.advance(detail::format_uint64(
ss.data(), jv_->get_uint64()));
return true;
}
cs0_ = { buf_, detail::format_uint64(
buf_, jv_->get_uint64()) };
break;
case kind::double_:
if(BOOST_JSON_LIKELY(
ss.remain() >=
detail::max_number_chars))
{
ss.advance(
detail::format_double(
ss.data(),
jv_->get_double(),
opts_.allow_infinity_and_nan));
return true;
}
cs0_ = { buf_, detail::format_double(
buf_, jv_->get_double(), opts_.allow_infinity_and_nan) };
break;
}
}
else
{
state st;
st_.pop(st);
BOOST_ASSERT(
st == state::num);
}
auto const n = ss.remain();
if(n < cs0_.remain())
{
ss.append(cs0_.data(), n);
cs0_.skip(n);
return suspend(state::num);
}
ss.append(
cs0_.data(), cs0_.remain());
return true;
}
template<bool StackEmpty>
bool
serializer::
write_array(stream& ss0)
{
array const* pa;
local_stream ss(ss0);
array::const_iterator it;
array::const_iterator end;
if(StackEmpty || st_.empty())
{
pa = pa_;
it = pa->begin();
end = pa->end();
}
else
{
state st;
st_.pop(st);
st_.pop(it);
st_.pop(pa);
end = pa->end();
switch(st)
{
default:
case state::arr1: goto do_arr1;
case state::arr2: goto do_arr2;
case state::arr3: goto do_arr3;
case state::arr4: goto do_arr4;
break;
}
}
do_arr1:
if(BOOST_JSON_LIKELY(ss))
ss.append('[');
else
return suspend(
state::arr1, it, pa);
if(it == end)
goto do_arr4;
for(;;)
{
do_arr2:
jv_ = &*it;
if(! write_value<StackEmpty>(ss))
return suspend(
state::arr2, it, pa);
if(BOOST_JSON_UNLIKELY(
++it == end))
break;
do_arr3:
if(BOOST_JSON_LIKELY(ss))
ss.append(',');
else
return suspend(
state::arr3, it, pa);
}
do_arr4:
if(BOOST_JSON_LIKELY(ss))
ss.append(']');
else
return suspend(
state::arr4, it, pa);
return true;
}
template<bool StackEmpty>
bool
serializer::
write_object(stream& ss0)
{
object const* po;
local_stream ss(ss0);
object::const_iterator it;
object::const_iterator end;
if(StackEmpty || st_.empty())
{
po = po_;
it = po->begin();
end = po->end();
}
else
{
state st;
st_.pop(st);
st_.pop(it);
st_.pop(po);
end = po->end();
switch(st)
{
default:
case state::obj1: goto do_obj1;
case state::obj2: goto do_obj2;
case state::obj3: goto do_obj3;
case state::obj4: goto do_obj4;
case state::obj5: goto do_obj5;
case state::obj6: goto do_obj6;
break;
}
}
do_obj1:
if(BOOST_JSON_LIKELY(ss))
ss.append('{');
else
return suspend(
state::obj1, it, po);
if(BOOST_JSON_UNLIKELY(
it == end))
goto do_obj6;
for(;;)
{
cs0_ = {
it->key().data(),
it->key().size() };
do_obj2:
if(BOOST_JSON_UNLIKELY(
! write_string<StackEmpty>(ss)))
return suspend(
state::obj2, it, po);
do_obj3:
if(BOOST_JSON_LIKELY(ss))
ss.append(':');
else
return suspend(
state::obj3, it, po);
do_obj4:
jv_ = &it->value();
if(BOOST_JSON_UNLIKELY(
! write_value<StackEmpty>(ss)))
return suspend(
state::obj4, it, po);
++it;
if(BOOST_JSON_UNLIKELY(it == end))
break;
do_obj5:
if(BOOST_JSON_LIKELY(ss))
ss.append(',');
else
return suspend(
state::obj5, it, po);
}
do_obj6:
if(BOOST_JSON_LIKELY(ss))
{
ss.append('}');
return true;
}
return suspend(
state::obj6, it, po);
}
template<bool StackEmpty>
bool
serializer::
write_value(stream& ss)
{
if(StackEmpty || st_.empty())
{
auto const& jv(*jv_);
switch(jv.kind())
{
default:
case kind::object:
po_ = &jv.get_object();
return write_object<true>(ss);
case kind::array:
pa_ = &jv.get_array();
return write_array<true>(ss);
case kind::string:
{
auto const& js = jv.get_string();
cs0_ = { js.data(), js.size() };
return write_string<true>(ss);
}
case kind::int64:
case kind::uint64:
case kind::double_:
return write_number<true>(ss);
case kind::bool_:
if(jv.get_bool())
{
if(BOOST_JSON_LIKELY(
ss.remain() >= 4))
{
ss.append("true", 4);
return true;
}
return write_true<true>(ss);
}
else
{
if(BOOST_JSON_LIKELY(
ss.remain() >= 5))
{
ss.append("false", 5);
return true;
}
return write_false<true>(ss);
}
case kind::null:
if(BOOST_JSON_LIKELY(
ss.remain() >= 4))
{
ss.append("null", 4);
return true;
}
return write_null<true>(ss);
}
}
else
{
state st;
st_.peek(st);
switch(st)
{
default:
case state::nul1: case state::nul2:
case state::nul3: case state::nul4:
return write_null<StackEmpty>(ss);
case state::tru1: case state::tru2:
case state::tru3: case state::tru4:
return write_true<StackEmpty>(ss);
case state::fal1: case state::fal2:
case state::fal3: case state::fal4:
case state::fal5:
return write_false<StackEmpty>(ss);
case state::str1: case state::str2:
case state::str3: case state::str4:
case state::esc1:
case state::utf1: case state::utf2:
case state::utf3: case state::utf4:
case state::utf5:
return write_string<StackEmpty>(ss);
case state::num:
return write_number<StackEmpty>(ss);
case state::arr1: case state::arr2:
case state::arr3: case state::arr4:
return write_array<StackEmpty>(ss);
case state::obj1: case state::obj2:
case state::obj3: case state::obj4:
case state::obj5: case state::obj6:
return write_object<StackEmpty>(ss);
}
}
}
string_view
serializer::
read_some(
char* dest, std::size_t size)
{
// If this goes off it means you forgot
// to call reset() before seriailzing a
// new value, or you never checked done()
// to see if you should stop.
BOOST_ASSERT(! done_);
stream ss(dest, size);
if(st_.empty())
(this->*fn0_)(ss);
else
(this->*fn1_)(ss);
if(st_.empty())
{
done_ = true;
jv_ = nullptr;
}
return string_view(
dest, ss.used(dest));
}
//----------------------------------------------------------
serializer::
serializer( serialize_options const& opts ) noexcept
: opts_(opts)
{
// ensure room for \uXXXX escape plus one
BOOST_STATIC_ASSERT(
sizeof(serializer::buf_) >= 7);
}
void
serializer::
reset(value const* p) noexcept
{
pv_ = p;
fn0_ = &serializer::write_value<true>;
fn1_ = &serializer::write_value<false>;
jv_ = p;
st_.clear();
done_ = false;
}
void
serializer::
reset(array const* p) noexcept
{
pa_ = p;
fn0_ = &serializer::write_array<true>;
fn1_ = &serializer::write_array<false>;
st_.clear();
done_ = false;
}
void
serializer::
reset(object const* p) noexcept
{
po_ = p;
fn0_ = &serializer::write_object<true>;
fn1_ = &serializer::write_object<false>;
st_.clear();
done_ = false;
}
void
serializer::
reset(string const* p) noexcept
{
cs0_ = { p->data(), p->size() };
fn0_ = &serializer::write_string<true>;
fn1_ = &serializer::write_string<false>;
st_.clear();
done_ = false;
}
void
serializer::
reset(string_view sv) noexcept
{
cs0_ = { sv.data(), sv.size() };
fn0_ = &serializer::write_string<true>;
fn1_ = &serializer::write_string<false>;
st_.clear();
done_ = false;
}
string_view
serializer::
read(char* dest, std::size_t size)
{
if(! jv_)
{
static value const null;
jv_ = &null;
}
return read_some(dest, size);
}
} // namespace json
} // namespace boost
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif
+76
View File
@@ -0,0 +1,76 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_STATIC_RESOURCE_IPP
#define BOOST_JSON_IMPL_STATIC_RESOURCE_IPP
#include <boost/json/static_resource.hpp>
#include <boost/throw_exception.hpp>
#include <boost/align/align.hpp>
#include <memory>
namespace boost {
namespace json {
static_resource::
static_resource(
unsigned char* buffer,
std::size_t size) noexcept
: p_(buffer)
, n_(size)
, size_(size)
{
}
void
static_resource::
release() noexcept
{
p_ = reinterpret_cast<
char*>(p_) - (size_ - n_);
n_ = size_;
}
void*
static_resource::
do_allocate(
std::size_t n,
std::size_t align)
{
auto p = alignment::align(
align, n, p_, n_);
if(! p)
throw_exception( std::bad_alloc(), BOOST_CURRENT_LOCATION );
p_ = reinterpret_cast<char*>(p) + n;
n_ -= n;
return p;
}
void
static_resource::
do_deallocate(
void*,
std::size_t,
std::size_t)
{
// do nothing
}
bool
static_resource::
do_is_equal(
memory_resource const& mr) const noexcept
{
return this == &mr;
}
} // namespace json
} // namespace boost
#endif
+182
View File
@@ -0,0 +1,182 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_STREAM_PARSER_IPP
#define BOOST_JSON_IMPL_STREAM_PARSER_IPP
#include <boost/json/stream_parser.hpp>
#include <boost/json/basic_parser_impl.hpp>
#include <boost/json/error.hpp>
#include <cstring>
#include <stdexcept>
#include <utility>
namespace boost {
namespace json {
stream_parser::
stream_parser(
storage_ptr sp,
parse_options const& opt,
unsigned char* buffer,
std::size_t size) noexcept
: p_(
opt,
std::move(sp),
buffer,
size)
{
reset();
}
stream_parser::
stream_parser(
storage_ptr sp,
parse_options const& opt) noexcept
: p_(
opt,
std::move(sp),
nullptr,
0)
{
reset();
}
void
stream_parser::
reset(storage_ptr sp) noexcept
{
p_.reset();
p_.handler().st.reset(sp);
}
std::size_t
stream_parser::
write_some(
char const* data,
std::size_t size,
error_code& ec)
{
return p_.write_some(
true, data, size, ec);
}
std::size_t
stream_parser::
write_some(
char const* data,
std::size_t size,
std::error_code& ec)
{
error_code jec;
std::size_t const result = write_some(data, size, jec);
ec = jec;
return result;
}
std::size_t
stream_parser::
write_some(
char const* data,
std::size_t size)
{
error_code ec;
auto const n = write_some(
data, size, ec);
if(ec)
detail::throw_system_error( ec );
return n;
}
std::size_t
stream_parser::
write(
char const* data,
std::size_t size,
error_code& ec)
{
auto const n = write_some(
data, size, ec);
if(! ec && n < size)
{
BOOST_JSON_FAIL(ec, error::extra_data);
p_.fail(ec);
}
return n;
}
std::size_t
stream_parser::
write(
char const* data,
std::size_t size,
std::error_code& ec)
{
error_code jec;
std::size_t const result = write(data, size, jec);
ec = jec;
return result;
}
std::size_t
stream_parser::
write(
char const* data,
std::size_t size)
{
error_code ec;
auto const n = write(
data, size, ec);
if(ec)
detail::throw_system_error( ec );
return n;
}
void
stream_parser::
finish(error_code& ec)
{
p_.write_some(false, nullptr, 0, ec);
}
void
stream_parser::
finish()
{
error_code ec;
finish(ec);
if(ec)
detail::throw_system_error( ec );
}
void
stream_parser::
finish(std::error_code& ec)
{
error_code jec;
finish(jec);
ec = jec;
}
value
stream_parser::
release()
{
if(! p_.done())
{
// prevent undefined behavior
finish();
}
return p_.handler().st.release();
}
} // namespace json
} // namespace boost
#endif
+244
View File
@@ -0,0 +1,244 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_STRING_HPP
#define BOOST_JSON_IMPL_STRING_HPP
#include <utility>
namespace boost {
namespace json {
string::
string(
detail::key_t const&,
string_view s,
storage_ptr sp)
: sp_(std::move(sp))
, impl_(detail::key_t{},
s, sp_)
{
}
string::
string(
detail::key_t const&,
string_view s1,
string_view s2,
storage_ptr sp)
: sp_(std::move(sp))
, impl_(detail::key_t{},
s1, s2, sp_)
{
}
template<class InputIt, class>
string::
string(
InputIt first,
InputIt last,
storage_ptr sp)
: sp_(std::move(sp))
, impl_(first, last, sp_,
iter_cat<InputIt>{})
{
}
template<class InputIt, class>
string&
string::
assign(
InputIt first,
InputIt last)
{
assign(first, last,
iter_cat<InputIt>{});
return *this;
}
template<class InputIt, class>
string&
string::
append(InputIt first, InputIt last)
{
append(first, last,
iter_cat<InputIt>{});
return *this;
}
// KRYSTIAN TODO: this can be done without copies when
// reallocation is not needed, when the iterator is a
// FowardIterator or better, as we can use std::distance
template<class InputIt, class>
auto
string::
insert(
size_type pos,
InputIt first,
InputIt last) ->
string&
{
struct cleanup
{
detail::string_impl& s;
storage_ptr const& sp;
~cleanup()
{
s.destroy(sp);
}
};
// We use the default storage because
// the allocation is immediately freed.
storage_ptr dsp;
detail::string_impl tmp(
first, last, dsp,
iter_cat<InputIt>{});
cleanup c{tmp, dsp};
std::memcpy(
impl_.insert_unchecked(pos, tmp.size(), sp_),
tmp.data(),
tmp.size());
return *this;
}
// KRYSTIAN TODO: this can be done without copies when
// reallocation is not needed, when the iterator is a
// FowardIterator or better, as we can use std::distance
template<class InputIt, class>
auto
string::
replace(
const_iterator first,
const_iterator last,
InputIt first2,
InputIt last2) ->
string&
{
struct cleanup
{
detail::string_impl& s;
storage_ptr const& sp;
~cleanup()
{
s.destroy(sp);
}
};
// We use the default storage because
// the allocation is immediately freed.
storage_ptr dsp;
detail::string_impl tmp(
first2, last2, dsp,
iter_cat<InputIt>{});
cleanup c{tmp, dsp};
std::memcpy(
impl_.replace_unchecked(
first - begin(),
last - first,
tmp.size(),
sp_),
tmp.data(),
tmp.size());
return *this;
}
//----------------------------------------------------------
template<class InputIt>
void
string::
assign(
InputIt first,
InputIt last,
std::random_access_iterator_tag)
{
auto dest = impl_.assign(static_cast<
size_type>(last - first), sp_);
while(first != last)
*dest++ = *first++;
}
template<class InputIt>
void
string::
assign(
InputIt first,
InputIt last,
std::input_iterator_tag)
{
if(first == last)
{
impl_.term(0);
return;
}
detail::string_impl tmp(
first, last, sp_,
std::input_iterator_tag{});
impl_.destroy(sp_);
impl_ = tmp;
}
template<class InputIt>
void
string::
append(
InputIt first,
InputIt last,
std::random_access_iterator_tag)
{
auto const n = static_cast<
size_type>(last - first);
char* out = impl_.append(n, sp_);
#if defined(_MSC_VER) && _MSC_VER <= 1900
while( first != last )
*out++ = *first++;
#else
std::copy(first, last, out);
#endif
}
template<class InputIt>
void
string::
append(
InputIt first,
InputIt last,
std::input_iterator_tag)
{
struct cleanup
{
detail::string_impl& s;
storage_ptr const& sp;
~cleanup()
{
s.destroy(sp);
}
};
// We use the default storage because
// the allocation is immediately freed.
storage_ptr dsp;
detail::string_impl tmp(
first, last, dsp,
std::input_iterator_tag{});
cleanup c{tmp, dsp};
std::memcpy(
impl_.append(tmp.size(), sp_),
tmp.data(), tmp.size());
}
} // namespace json
} // namespace boost
#endif
+431
View File
@@ -0,0 +1,431 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_STRING_IPP
#define BOOST_JSON_IMPL_STRING_IPP
#include <boost/json/detail/except.hpp>
#include <algorithm>
#include <new>
#include <ostream>
#include <stdexcept>
#include <string>
#include <utility>
namespace boost {
namespace json {
//----------------------------------------------------------
//
// Construction
//
//----------------------------------------------------------
string::
string(
std::size_t count,
char ch,
storage_ptr sp)
: sp_(std::move(sp))
{
assign(count, ch);
}
string::
string(
char const* s,
storage_ptr sp)
: sp_(std::move(sp))
{
assign(s);
}
string::
string(
char const* s,
std::size_t count,
storage_ptr sp)
: sp_(std::move(sp))
{
assign(s, count);
}
string::
string(string const& other)
: sp_(other.sp_)
{
assign(other);
}
string::
string(
string const& other,
storage_ptr sp)
: sp_(std::move(sp))
{
assign(other);
}
string::
string(
string&& other,
storage_ptr sp)
: sp_(std::move(sp))
{
assign(std::move(other));
}
string::
string(
string_view s,
storage_ptr sp)
: sp_(std::move(sp))
{
assign(s);
}
//----------------------------------------------------------
//
// Assignment
//
//----------------------------------------------------------
string&
string::
operator=(string const& other)
{
return assign(other);
}
string&
string::
operator=(string&& other)
{
return assign(std::move(other));
}
string&
string::
operator=(char const* s)
{
return assign(s);
}
string&
string::
operator=(string_view s)
{
return assign(s);
}
string&
string::
assign(
size_type count,
char ch)
{
std::char_traits<char>::assign(
impl_.assign(count, sp_),
count,
ch);
return *this;
}
string&
string::
assign(
string const& other)
{
if(this == &other)
return *this;
return assign(
other.data(),
other.size());
}
string&
string::
assign(string&& other)
{
if( &other == this )
return *this;
if(*sp_ == *other.sp_)
{
impl_.destroy(sp_);
impl_ = other.impl_;
::new(&other.impl_) detail::string_impl();
return *this;
}
// copy
return assign(other);
}
string&
string::
assign(
char const* s,
size_type count)
{
std::char_traits<char>::copy(
impl_.assign(count, sp_),
s, count);
return *this;
}
string&
string::
assign(
char const* s)
{
return assign(s, std::char_traits<
char>::length(s));
}
//----------------------------------------------------------
//
// Capacity
//
//----------------------------------------------------------
void
string::
shrink_to_fit()
{
impl_.shrink_to_fit(sp_);
}
//----------------------------------------------------------
//
// Operations
//
//----------------------------------------------------------
void
string::
clear() noexcept
{
impl_.term(0);
}
//----------------------------------------------------------
void
string::
push_back(char ch)
{
*impl_.append(1, sp_) = ch;
}
void
string::
pop_back()
{
back() = 0;
impl_.size(impl_.size() - 1);
}
//----------------------------------------------------------
string&
string::
append(size_type count, char ch)
{
std::char_traits<char>::assign(
impl_.append(count, sp_),
count, ch);
return *this;
}
string&
string::
append(string_view sv)
{
std::char_traits<char>::copy(
impl_.append(sv.size(), sp_),
sv.data(), sv.size());
return *this;
}
//----------------------------------------------------------
string&
string::
insert(
size_type pos,
string_view sv)
{
impl_.insert(pos, sv.data(), sv.size(), sp_);
return *this;
}
string&
string::
insert(
std::size_t pos,
std::size_t count,
char ch)
{
std::char_traits<char>::assign(
impl_.insert_unchecked(pos, count, sp_),
count, ch);
return *this;
}
//----------------------------------------------------------
string&
string::
replace(
std::size_t pos,
std::size_t count,
string_view sv)
{
impl_.replace(pos, count, sv.data(), sv.size(), sp_);
return *this;
}
string&
string::
replace(
std::size_t pos,
std::size_t count,
std::size_t count2,
char ch)
{
std::char_traits<char>::assign(
impl_.replace_unchecked(pos, count, count2, sp_),
count2, ch);
return *this;
}
//----------------------------------------------------------
string&
string::
erase(
size_type pos,
size_type count)
{
if(pos > impl_.size())
{
BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
detail::throw_system_error( error::out_of_range, &loc );
}
if( count > impl_.size() - pos)
count = impl_.size() - pos;
std::char_traits<char>::move(
impl_.data() + pos,
impl_.data() + pos + count,
impl_.size() - pos - count + 1);
impl_.term(impl_.size() - count);
return *this;
}
auto
string::
erase(const_iterator pos) ->
iterator
{
return erase(pos, pos+1);
}
auto
string::
erase(
const_iterator first,
const_iterator last) ->
iterator
{
auto const pos = first - begin();
auto const count = last - first;
erase(pos, count);
return data() + pos;
}
//----------------------------------------------------------
void
string::
resize(size_type count, char ch)
{
if(count <= impl_.size())
{
impl_.term(count);
return;
}
reserve(count);
std::char_traits<char>::assign(
impl_.end(),
count - impl_.size(),
ch);
grow(count - size());
}
//----------------------------------------------------------
void
string::
swap(string& other)
{
if(*sp_ == *other.sp_)
{
std::swap(impl_, other.impl_);
return;
}
string temp1(
std::move(*this), other.sp_);
string temp2(
std::move(other), sp_);
this->~string();
::new(this) string(pilfer(temp2));
other.~string();
::new(&other) string(pilfer(temp1));
}
//----------------------------------------------------------
void
string::
reserve_impl(size_type new_cap)
{
BOOST_ASSERT(
new_cap >= impl_.capacity());
if(new_cap > impl_.capacity())
{
// grow
new_cap = detail::string_impl::growth(
new_cap, impl_.capacity());
detail::string_impl tmp(new_cap, sp_);
std::char_traits<char>::copy(tmp.data(),
impl_.data(), impl_.size() + 1);
tmp.size(impl_.size());
impl_.destroy(sp_);
impl_ = tmp;
return;
}
}
} // namespace json
} // namespace boost
//----------------------------------------------------------
std::size_t
std::hash< ::boost::json::string >::operator()(
::boost::json::string const& js ) const noexcept
{
return ::boost::hash< ::boost::json::string >()( js );
}
#endif
+32
View File
@@ -0,0 +1,32 @@
//
// Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_VALUE_HPP
#define BOOST_JSON_IMPL_VALUE_HPP
namespace boost {
namespace json {
value&
value::at_pointer(string_view ptr) &
{
auto const& self = *this;
return const_cast<value&>( self.at_pointer(ptr) );
}
value&&
value::at_pointer(string_view ptr) &&
{
return std::move( this->at_pointer(ptr) );
}
} // namespace json
} // namespace boost
#endif // BOOST_JSON_IMPL_VALUE_HPP
+714
View File
@@ -0,0 +1,714 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_VALUE_IPP
#define BOOST_JSON_IMPL_VALUE_IPP
#include <boost/container_hash/hash.hpp>
#include <boost/json/value.hpp>
#include <boost/json/parser.hpp>
#include <cstring>
#include <istream>
#include <limits>
#include <new>
#include <utility>
namespace boost {
namespace json {
namespace
{
int parse_depth_xalloc = std::ios::xalloc();
int parse_flags_xalloc = std::ios::xalloc();
struct value_hasher
{
std::size_t& seed;
template< class T >
void operator()( T&& t ) const noexcept
{
boost::hash_combine( seed, t );
}
};
enum class stream_parse_flags
{
allow_comments = 1 << 0,
allow_trailing_commas = 1 << 1,
allow_invalid_utf8 = 1 << 2,
};
long
to_bitmask( parse_options const& opts )
{
using E = stream_parse_flags;
return
(opts.allow_comments ?
static_cast<long>(E::allow_comments) : 0) |
(opts.allow_trailing_commas ?
static_cast<long>(E::allow_trailing_commas) : 0) |
(opts.allow_invalid_utf8 ?
static_cast<long>(E::allow_invalid_utf8) : 0);
}
parse_options
get_parse_options( std::istream& is )
{
long const flags = is.iword(parse_flags_xalloc);
using E = stream_parse_flags;
parse_options opts;
opts.allow_comments =
flags & static_cast<long>(E::allow_comments) ? true : false;
opts.allow_trailing_commas =
flags & static_cast<long>(E::allow_trailing_commas) ? true : false;
opts.allow_invalid_utf8 =
flags & static_cast<long>(E::allow_invalid_utf8) ? true : false;
return opts;
}
} // namespace
value::
~value() noexcept
{
switch(kind())
{
case json::kind::null:
case json::kind::bool_:
case json::kind::int64:
case json::kind::uint64:
case json::kind::double_:
sca_.~scalar();
break;
case json::kind::string:
str_.~string();
break;
case json::kind::array:
arr_.~array();
break;
case json::kind::object:
obj_.~object();
break;
}
}
value::
value(
value const& other,
storage_ptr sp)
{
switch(other.kind())
{
case json::kind::null:
::new(&sca_) scalar(
std::move(sp));
break;
case json::kind::bool_:
::new(&sca_) scalar(
other.sca_.b,
std::move(sp));
break;
case json::kind::int64:
::new(&sca_) scalar(
other.sca_.i,
std::move(sp));
break;
case json::kind::uint64:
::new(&sca_) scalar(
other.sca_.u,
std::move(sp));
break;
case json::kind::double_:
::new(&sca_) scalar(
other.sca_.d,
std::move(sp));
break;
case json::kind::string:
::new(&str_) string(
other.str_,
std::move(sp));
break;
case json::kind::array:
::new(&arr_) array(
other.arr_,
std::move(sp));
break;
case json::kind::object:
::new(&obj_) object(
other.obj_,
std::move(sp));
break;
}
}
value::
value(value&& other) noexcept
{
relocate(this, other);
::new(&other.sca_) scalar(sp_);
}
value::
value(
value&& other,
storage_ptr sp)
{
switch(other.kind())
{
case json::kind::null:
::new(&sca_) scalar(
std::move(sp));
break;
case json::kind::bool_:
::new(&sca_) scalar(
other.sca_.b, std::move(sp));
break;
case json::kind::int64:
::new(&sca_) scalar(
other.sca_.i, std::move(sp));
break;
case json::kind::uint64:
::new(&sca_) scalar(
other.sca_.u, std::move(sp));
break;
case json::kind::double_:
::new(&sca_) scalar(
other.sca_.d, std::move(sp));
break;
case json::kind::string:
::new(&str_) string(
std::move(other.str_),
std::move(sp));
break;
case json::kind::array:
::new(&arr_) array(
std::move(other.arr_),
std::move(sp));
break;
case json::kind::object:
::new(&obj_) object(
std::move(other.obj_),
std::move(sp));
break;
}
}
//----------------------------------------------------------
//
// Conversion
//
//----------------------------------------------------------
value::
value(
std::initializer_list<value_ref> init,
storage_ptr sp)
{
if(value_ref::maybe_object(init))
{
::new(&obj_) object(
value_ref::make_object(
init, std::move(sp)));
}
else
{
#ifndef BOOST_JSON_LEGACY_INIT_LIST_BEHAVIOR
if( init.size() == 1 )
{
::new(&sca_) scalar();
value temp = init.begin()->make_value( std::move(sp) );
swap(temp);
}
else
#endif
{
::new(&arr_) array(
value_ref::make_array(
init, std::move(sp)));
}
}
}
//----------------------------------------------------------
//
// Assignment
//
//----------------------------------------------------------
value&
value::
operator=(value const& other)
{
value(other,
storage()).swap(*this);
return *this;
}
value&
value::
operator=(value&& other)
{
value(std::move(other),
storage()).swap(*this);
return *this;
}
value&
value::
operator=(
std::initializer_list<value_ref> init)
{
value(init,
storage()).swap(*this);
return *this;
}
value&
value::
operator=(string_view s)
{
value(s, storage()).swap(*this);
return *this;
}
value&
value::
operator=(char const* s)
{
value(s, storage()).swap(*this);
return *this;
}
value&
value::
operator=(string const& str)
{
value(str, storage()).swap(*this);
return *this;
}
value&
value::
operator=(string&& str)
{
value(std::move(str),
storage()).swap(*this);
return *this;
}
value&
value::
operator=(array const& arr)
{
value(arr, storage()).swap(*this);
return *this;
}
value&
value::
operator=(array&& arr)
{
value(std::move(arr),
storage()).swap(*this);
return *this;
}
value&
value::
operator=(object const& obj)
{
value(obj, storage()).swap(*this);
return *this;
}
value&
value::
operator=(object&& obj)
{
value(std::move(obj),
storage()).swap(*this);
return *this;
}
//----------------------------------------------------------
//
// Modifiers
//
//----------------------------------------------------------
string&
value::
emplace_string() noexcept
{
return *::new(&str_) string(destroy());
}
array&
value::
emplace_array() noexcept
{
return *::new(&arr_) array(destroy());
}
object&
value::
emplace_object() noexcept
{
return *::new(&obj_) object(destroy());
}
void
value::
swap(value& other)
{
if(*storage() == *other.storage())
{
// fast path
union U
{
value tmp;
U(){}
~U(){}
};
U u;
relocate(&u.tmp, *this);
relocate(this, other);
relocate(&other, u.tmp);
return;
}
// copy
value temp1(
std::move(*this),
other.storage());
value temp2(
std::move(other),
this->storage());
other.~value();
::new(&other) value(pilfer(temp1));
this->~value();
::new(this) value(pilfer(temp2));
}
std::istream&
operator>>(
std::istream& is,
value& jv)
{
using Traits = std::istream::traits_type;
// sentry prepares the stream for reading and finalizes it in destructor
std::istream::sentry sentry(is);
if( !sentry )
return is;
parse_options opts = get_parse_options( is );
if( auto depth = static_cast<std::size_t>( is.iword(parse_depth_xalloc) ) )
opts.max_depth = depth;
unsigned char parser_buf[BOOST_JSON_STACK_BUFFER_SIZE / 2];
stream_parser p( {}, opts, parser_buf );
p.reset( jv.storage() );
char read_buf[BOOST_JSON_STACK_BUFFER_SIZE / 2];
std::streambuf& buf = *is.rdbuf();
std::ios::iostate err = std::ios::goodbit;
#ifndef BOOST_NO_EXCEPTIONS
try
#endif
{
while( true )
{
error_code ec;
// we peek the buffer; this either makes sure that there's no
// more input, or makes sure there's something in the internal
// buffer (so in_avail will return a positive number)
std::istream::int_type c = is.rdbuf()->sgetc();
// if we indeed reached EOF, we check if we parsed a full JSON
// document; if not, we error out
if( Traits::eq_int_type(c, Traits::eof()) )
{
err |= std::ios::eofbit;
p.finish(ec);
if( ec.failed() )
break;
}
// regardless of reaching EOF, we might have parsed a full JSON
// document; if so, we successfully finish
if( p.done() )
{
jv = p.release();
return is;
}
// at this point we definitely have more input, specifically in
// buf's internal buffer; we also definitely haven't parsed a whole
// document
std::streamsize available = buf.in_avail();
// if this assert fails, the streambuf is buggy
BOOST_ASSERT( available > 0 );
available = ( std::min )(
static_cast<std::size_t>(available), sizeof(read_buf) );
// we read from the internal buffer of buf into our buffer
available = buf.sgetn( read_buf, available );
std::size_t consumed = p.write_some(
read_buf, static_cast<std::size_t>(available), ec );
// if the parser hasn't consumed the entire input we've took from
// buf, we put the remaining data back; this should succeed,
// because we only read data from buf's internal buffer
while( consumed++ < static_cast<std::size_t>(available) )
{
std::istream::int_type const status = buf.sungetc();
BOOST_ASSERT( status != Traits::eof() );
(void)status;
}
if( ec.failed() )
break;
}
}
#ifndef BOOST_NO_EXCEPTIONS
catch(...)
{
try
{
is.setstate(std::ios::badbit);
}
// we ignore the exception, because we need to throw the original
// exception instead
catch( std::ios::failure const& ) { }
if( is.exceptions() & std::ios::badbit )
throw;
}
#endif
is.setstate(err | std::ios::failbit);
return is;
}
std::istream&
operator>>(
std::istream& is,
parse_options const& opts)
{
is.iword(parse_flags_xalloc) = to_bitmask(opts);
is.iword(parse_depth_xalloc) = static_cast<long>(opts.max_depth);
return is;
}
//----------------------------------------------------------
//
// private
//
//----------------------------------------------------------
storage_ptr
value::
destroy() noexcept
{
switch(kind())
{
case json::kind::null:
case json::kind::bool_:
case json::kind::int64:
case json::kind::uint64:
case json::kind::double_:
break;
case json::kind::string:
{
auto sp = str_.storage();
str_.~string();
return sp;
}
case json::kind::array:
{
auto sp = arr_.storage();
arr_.~array();
return sp;
}
case json::kind::object:
{
auto sp = obj_.storage();
obj_.~object();
return sp;
}
}
return std::move(sp_);
}
bool
value::
equal(value const& other) const noexcept
{
switch(kind())
{
default: // unreachable()?
case json::kind::null:
return other.kind() == json::kind::null;
case json::kind::bool_:
return
other.kind() == json::kind::bool_ &&
get_bool() == other.get_bool();
case json::kind::int64:
switch(other.kind())
{
case json::kind::int64:
return get_int64() == other.get_int64();
case json::kind::uint64:
if(get_int64() < 0)
return false;
return static_cast<std::uint64_t>(
get_int64()) == other.get_uint64();
default:
return false;
}
case json::kind::uint64:
switch(other.kind())
{
case json::kind::uint64:
return get_uint64() == other.get_uint64();
case json::kind::int64:
if(other.get_int64() < 0)
return false;
return static_cast<std::uint64_t>(
other.get_int64()) == get_uint64();
default:
return false;
}
case json::kind::double_:
return
other.kind() == json::kind::double_ &&
get_double() == other.get_double();
case json::kind::string:
return
other.kind() == json::kind::string &&
get_string() == other.get_string();
case json::kind::array:
return
other.kind() == json::kind::array &&
get_array() == other.get_array();
case json::kind::object:
return
other.kind() == json::kind::object &&
get_object() == other.get_object();
}
}
//----------------------------------------------------------
//
// key_value_pair
//
//----------------------------------------------------------
// empty keys point here
BOOST_JSON_REQUIRE_CONST_INIT
char const
key_value_pair::empty_[1] = { 0 };
key_value_pair::
key_value_pair(
pilfered<json::value> key,
pilfered<json::value> value) noexcept
: value_(value)
{
std::size_t len;
key_ = access::release_key(key.get(), len);
len_ = static_cast<std::uint32_t>(len);
}
key_value_pair::
key_value_pair(
key_value_pair const& other,
storage_ptr sp)
: value_(other.value_, std::move(sp))
{
auto p = reinterpret_cast<
char*>(value_.storage()->
allocate(other.len_ + 1,
alignof(char)));
std::memcpy(
p, other.key_, other.len_);
len_ = other.len_;
p[len_] = 0;
key_ = p;
}
//----------------------------------------------------------
namespace detail
{
std::size_t
hash_value_impl( value const& jv ) noexcept
{
std::size_t seed = 0;
kind const k = jv.kind();
boost::hash_combine( seed, k != kind::int64 ? k : kind::uint64 );
visit( value_hasher{seed}, jv );
return seed;
}
} // namespace detail
} // namespace json
} // namespace boost
//----------------------------------------------------------
//
// std::hash specialization
//
//----------------------------------------------------------
std::size_t
std::hash<::boost::json::value>::operator()(
::boost::json::value const& jv) const noexcept
{
return ::boost::hash< ::boost::json::value >()( jv );
}
//----------------------------------------------------------
#endif
+58
View File
@@ -0,0 +1,58 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_VALUE_REF_HPP
#define BOOST_JSON_IMPL_VALUE_REF_HPP
namespace boost {
namespace json {
template<class T>
value
value_ref::
from_builtin(
void const* p,
storage_ptr sp) noexcept
{
return value(
*reinterpret_cast<
T const*>(p),
std::move(sp));
}
template<class T>
value
value_ref::
from_const(
void const* p,
storage_ptr sp)
{
return value(
*reinterpret_cast<
T const*>(p),
std::move(sp));
}
template<class T>
value
value_ref::
from_rvalue(
void* p,
storage_ptr sp)
{
return value(
std::move(
*reinterpret_cast<T*>(p)),
std::move(sp));
}
} // namespace json
} // namespace boost
#endif
+189
View File
@@ -0,0 +1,189 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_VALUE_REF_IPP
#define BOOST_JSON_IMPL_VALUE_REF_IPP
#include <boost/json/value_ref.hpp>
#include <boost/json/array.hpp>
#include <boost/json/value.hpp>
namespace boost {
namespace json {
value_ref::
operator
value() const
{
return make_value({});
}
value
value_ref::
from_init_list(
void const* p,
storage_ptr sp)
{
return make_value(
*reinterpret_cast<
init_list const*>(p),
std::move(sp));
}
bool
value_ref::
is_key_value_pair() const noexcept
{
if(what_ != what::ini)
return false;
if(arg_.init_list_.size() != 2)
return false;
auto const& e =
*arg_.init_list_.begin();
if( e.what_ != what::str &&
e.what_ != what::strfunc)
return false;
return true;
}
bool
value_ref::
maybe_object(
std::initializer_list<
value_ref> init) noexcept
{
for(auto const& e : init)
if(! e.is_key_value_pair())
return false;
return true;
}
string_view
value_ref::
get_string() const noexcept
{
BOOST_ASSERT(
what_ == what::str ||
what_ == what::strfunc);
if (what_ == what::strfunc)
return *static_cast<const string*>(f_.p);
return arg_.str_;
}
value
value_ref::
make_value(
storage_ptr sp) const
{
switch(what_)
{
default:
case what::str:
return string(
arg_.str_,
std::move(sp));
case what::ini:
return make_value(
arg_.init_list_,
std::move(sp));
case what::func:
return f_.f(f_.p,
std::move(sp));
case what::strfunc:
return f_.f(f_.p,
std::move(sp));
case what::cfunc:
return cf_.f(cf_.p,
std::move(sp));
}
}
value
value_ref::
make_value(
std::initializer_list<
value_ref> init,
storage_ptr sp)
{
if(maybe_object(init))
return make_object(
init, std::move(sp));
return make_array(
init, std::move(sp));
}
object
value_ref::
make_object(
std::initializer_list<value_ref> init,
storage_ptr sp)
{
object obj(std::move(sp));
obj.reserve(init.size());
for(auto const& e : init)
obj.emplace(
e.arg_.init_list_.begin()[0].get_string(),
e.arg_.init_list_.begin()[1].make_value(
obj.storage()));
return obj;
}
array
value_ref::
make_array(
std::initializer_list<
value_ref> init,
storage_ptr sp)
{
array arr(std::move(sp));
arr.reserve(init.size());
for(auto const& e : init)
arr.emplace_back(
e.make_value(
arr.storage()));
return arr;
}
void
value_ref::
write_array(
value* dest,
std::initializer_list<
value_ref> init,
storage_ptr const& sp)
{
struct undo
{
value* const base;
value* pos;
~undo()
{
if(pos)
while(pos > base)
(--pos)->~value();
}
};
undo u{dest, dest};
for(auto const& e : init)
{
::new(u.pos) value(
e.make_value(sp));
++u.pos;
}
u.pos = nullptr;
}
} // namespace json
} // namespace boost
#endif
+476
View File
@@ -0,0 +1,476 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_VALUE_STACK_IPP
#define BOOST_JSON_IMPL_VALUE_STACK_IPP
#include <boost/json/value_stack.hpp>
#include <cstring>
#include <stdexcept>
#include <utility>
namespace boost {
namespace json {
//--------------------------------------
value_stack::
stack::
~stack()
{
clear();
if( begin_ != temp_ &&
begin_ != nullptr)
sp_->deallocate(
begin_,
(end_ - begin_) *
sizeof(value));
}
value_stack::
stack::
stack(
storage_ptr sp,
void* temp,
std::size_t size) noexcept
: sp_(std::move(sp))
, temp_(temp)
{
if(size >= min_size_ *
sizeof(value))
{
begin_ = reinterpret_cast<
value*>(temp);
top_ = begin_;
end_ = begin_ +
size / sizeof(value);
}
else
{
begin_ = nullptr;
top_ = nullptr;
end_ = nullptr;
}
}
void
value_stack::
stack::
run_dtors(bool b) noexcept
{
run_dtors_ = b;
}
std::size_t
value_stack::
stack::
size() const noexcept
{
return top_ - begin_;
}
bool
value_stack::
stack::
has_chars()
{
return chars_ != 0;
}
//--------------------------------------
// destroy the values but
// not the stack allocation.
void
value_stack::
stack::
clear() noexcept
{
if(top_ != begin_)
{
if(run_dtors_)
for(auto it = top_;
it-- != begin_;)
it->~value();
top_ = begin_;
}
chars_ = 0;
}
void
value_stack::
stack::
maybe_grow()
{
if(top_ >= end_)
grow_one();
}
// make room for at least one more value
void
value_stack::
stack::
grow_one()
{
BOOST_ASSERT(chars_ == 0);
std::size_t const capacity =
end_ - begin_;
std::size_t new_cap = min_size_;
// VFALCO check overflow here
while(new_cap < capacity + 1)
new_cap <<= 1;
auto const begin =
reinterpret_cast<value*>(
sp_->allocate(
new_cap * sizeof(value)));
std::size_t const cur_size = top_ - begin_;
if(begin_)
{
std::memcpy(
reinterpret_cast<char*>(begin),
reinterpret_cast<char*>(begin_),
size() * sizeof(value));
if(begin_ != temp_)
sp_->deallocate(begin_,
capacity * sizeof(value));
}
// book-keeping
top_ = begin + cur_size;
end_ = begin + new_cap;
begin_ = begin;
}
// make room for nchars additional characters.
void
value_stack::
stack::
grow(std::size_t nchars)
{
// needed capacity in values
std::size_t const needed =
size() +
1 +
((chars_ + nchars +
sizeof(value) - 1) /
sizeof(value));
std::size_t const capacity =
end_ - begin_;
BOOST_ASSERT(
needed > capacity);
std::size_t new_cap = min_size_;
// VFALCO check overflow here
while(new_cap < needed)
new_cap <<= 1;
auto const begin =
reinterpret_cast<value*>(
sp_->allocate(
new_cap * sizeof(value)));
std::size_t const cur_size = top_ - begin_;
if(begin_)
{
std::size_t amount =
size() * sizeof(value);
if(chars_ > 0)
amount += sizeof(value) + chars_;
std::memcpy(
reinterpret_cast<char*>(begin),
reinterpret_cast<char*>(begin_),
amount);
if(begin_ != temp_)
sp_->deallocate(begin_,
capacity * sizeof(value));
}
// book-keeping
top_ = begin + cur_size;
end_ = begin + new_cap;
begin_ = begin;
}
//--------------------------------------
void
value_stack::
stack::
append(string_view s)
{
std::size_t const bytes_avail =
reinterpret_cast<
char const*>(end_) -
reinterpret_cast<
char const*>(top_);
// make sure there is room for
// pushing one more value without
// clobbering the string.
if(sizeof(value) + chars_ +
s.size() > bytes_avail)
grow(s.size());
// copy the new piece
std::memcpy(
reinterpret_cast<char*>(
top_ + 1) + chars_,
s.data(), s.size());
chars_ += s.size();
// ensure a pushed value cannot
// clobber the released string.
BOOST_ASSERT(
reinterpret_cast<char*>(
top_ + 1) + chars_ <=
reinterpret_cast<char*>(
end_));
}
string_view
value_stack::
stack::
release_string() noexcept
{
// ensure a pushed value cannot
// clobber the released string.
BOOST_ASSERT(
reinterpret_cast<char*>(
top_ + 1) + chars_ <=
reinterpret_cast<char*>(
end_));
auto const n = chars_;
chars_ = 0;
return { reinterpret_cast<
char const*>(top_ + 1), n };
}
// transfer ownership of the top n
// elements of the stack to the caller
value*
value_stack::
stack::
release(std::size_t n) noexcept
{
BOOST_ASSERT(n <= size());
BOOST_ASSERT(chars_ == 0);
top_ -= n;
return top_;
}
template<class... Args>
value&
value_stack::
stack::
push(Args&&... args)
{
BOOST_ASSERT(chars_ == 0);
if(top_ >= end_)
grow_one();
value& jv = detail::access::
construct_value(top_,
std::forward<Args>(args)...);
++top_;
return jv;
}
template<class Unchecked>
void
value_stack::
stack::
exchange(Unchecked&& u)
{
BOOST_ASSERT(chars_ == 0);
union U
{
value v;
U() {}
~U() {}
} jv;
// construct value on the stack
// to avoid clobbering top_[0],
// which belongs to `u`.
detail::access::
construct_value(
&jv.v, std::move(u));
std::memcpy(
reinterpret_cast<
char*>(top_),
&jv.v, sizeof(value));
++top_;
}
//----------------------------------------------------------
value_stack::
~value_stack()
{
// default dtor is here so the
// definition goes in the library
// instead of the caller's TU.
}
value_stack::
value_stack(
storage_ptr sp,
unsigned char* temp_buffer,
std::size_t temp_size) noexcept
: st_(
std::move(sp),
temp_buffer,
temp_size)
{
}
void
value_stack::
reset(storage_ptr sp) noexcept
{
st_.clear();
sp_.~storage_ptr();
::new(&sp_) storage_ptr(
pilfer(sp));
// `stack` needs this
// to clean up correctly
st_.run_dtors(
! sp_.is_not_shared_and_deallocate_is_trivial());
}
value
value_stack::
release() noexcept
{
// This means the caller did not
// cause a single top level element
// to be produced.
BOOST_ASSERT(st_.size() == 1);
// give up shared ownership
sp_ = {};
return pilfer(*st_.release(1));
}
//----------------------------------------------------------
void
value_stack::
push_array(std::size_t n)
{
// we already have room if n > 0
if(BOOST_JSON_UNLIKELY(n == 0))
st_.maybe_grow();
detail::unchecked_array ua(
st_.release(n), n, sp_);
st_.exchange(std::move(ua));
}
void
value_stack::
push_object(std::size_t n)
{
// we already have room if n > 0
if(BOOST_JSON_UNLIKELY(n == 0))
st_.maybe_grow();
detail::unchecked_object uo(
st_.release(n * 2), n, sp_);
st_.exchange(std::move(uo));
}
void
value_stack::
push_chars(
string_view s)
{
st_.append(s);
}
void
value_stack::
push_key(
string_view s)
{
if(! st_.has_chars())
{
st_.push(detail::key_t{}, s, sp_);
return;
}
auto part = st_.release_string();
st_.push(detail::key_t{}, part, s, sp_);
}
void
value_stack::
push_string(
string_view s)
{
if(! st_.has_chars())
{
// fast path
st_.push(s, sp_);
return;
}
// VFALCO We could add a special
// private ctor to string that just
// creates uninitialized space,
// to reduce member function calls.
auto part = st_.release_string();
auto& str = st_.push(
string_kind, sp_).get_string();
str.reserve(
part.size() + s.size());
std::memcpy(
str.data(),
part.data(), part.size());
std::memcpy(
str.data() + part.size(),
s.data(), s.size());
str.grow(part.size() + s.size());
}
void
value_stack::
push_int64(
int64_t i)
{
st_.push(i, sp_);
}
void
value_stack::
push_uint64(
uint64_t u)
{
st_.push(u, sp_);
}
void
value_stack::
push_double(
double d)
{
st_.push(d, sp_);
}
void
value_stack::
push_bool(
bool b)
{
st_.push(b, sp_);
}
void
value_stack::
push_null()
{
st_.push(nullptr, sp_);
}
} // namespace json
} // namespace boost
#endif
+61
View File
@@ -0,0 +1,61 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_VISIT_HPP
#define BOOST_JSON_IMPL_VISIT_HPP
namespace boost {
namespace json {
template<class Visitor>
auto
visit(
Visitor&& v,
value& jv) -> decltype(
std::declval<Visitor>()(nullptr))
{
switch(jv.kind())
{
default: // unreachable()?
case kind::null: return std::forward<Visitor>(v)(nullptr);
case kind::bool_: return std::forward<Visitor>(v)(jv.get_bool());
case kind::int64: return std::forward<Visitor>(v)(jv.get_int64());
case kind::uint64: return std::forward<Visitor>(v)(jv.get_uint64());
case kind::double_: return std::forward<Visitor>(v)(jv.get_double());
case kind::string: return std::forward<Visitor>(v)(jv.get_string());
case kind::array: return std::forward<Visitor>(v)(jv.get_array());
case kind::object: return std::forward<Visitor>(v)(jv.get_object());
}
}
template<class Visitor>
auto
visit(
Visitor&& v,
value const& jv) -> decltype(
std::declval<Visitor>()(nullptr))
{
switch (jv.kind())
{
default: // unreachable()?
case kind::null: return std::forward<Visitor>(v)(nullptr);
case kind::bool_: return std::forward<Visitor>(v)(jv.get_bool());
case kind::int64: return std::forward<Visitor>(v)(jv.get_int64());
case kind::uint64: return std::forward<Visitor>(v)(jv.get_uint64());
case kind::double_: return std::forward<Visitor>(v)(jv.get_double());
case kind::string: return std::forward<Visitor>(v)(jv.get_string());
case kind::array: return std::forward<Visitor>(v)(jv.get_array());
case kind::object: return std::forward<Visitor>(v)(jv.get_object());
}
}
} // namespace json
} // namespace boost
#endif
+109
View File
@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="boost::json::storage_ptr">
<DisplayString Condition="i_==0">default</DisplayString>
<DisplayString Condition="(i_&amp;3)==1"> shared</DisplayString>
<DisplayString Condition="(i_&amp;3)==2"> trivial</DisplayString>
<DisplayString Condition="(i_&amp;3)==3"> shared, trivial</DisplayString>
<DisplayString>non-owning</DisplayString>
<Expand>
<Item Name="[ refs ]" Condition="(i_&amp;1)==1">((shared_resource*)(i_&amp;~3))->refs</Item>
<Item Name="[ resource ]" Condition="(i_&amp;2)==1">(shared_resource*)(i_&amp;~3)</Item>
<Item Name="[ resource ]" Condition="(i_&amp;2)!=1">(memory_resource*)(i_&amp;~3)</Item>
</Expand>
</Type>
<Type Name="boost::json::detail::shared_resource_impl&lt;*&gt;">
<DisplayString>$T1*</DisplayString>
<Expand>
<Item Name="[ refs ]">refs</Item>
</Expand>
</Type>
<Type Name="boost::json::monotonic_resource">
<DisplayString>monotonic_resource</DisplayString>
<Expand>
<Item Name="[ free ]">head_->n</Item>
</Expand>
</Type>
<Type Name="boost::json::static_resource">
<DisplayString>static_resource</DisplayString>
<Expand>
<Item Name="[ free ]">n_</Item>
</Expand>
</Type>
<Type Name="boost::json::value">
<DisplayString Condition="sca_.k==kind::null">null</DisplayString>
<DisplayString Condition="sca_.k==kind::bool_">{sca_.b}</DisplayString>
<DisplayString Condition="sca_.k==kind::int64">{sca_.i}</DisplayString>
<DisplayString Condition="sca_.k==kind::uint64">{sca_.u}u</DisplayString>
<DisplayString Condition="sca_.k==kind::double_">{sca_.d}</DisplayString>
<DisplayString Condition="sca_.k==kind::string">{((char*)(str_.impl_.p_.t+1)),[str_.impl_.p_.t->size]s}</DisplayString>
<DisplayString Condition="sca_.k==kind::string+64">{((char*)(str_.impl_.k_.s)),[str_.impl_.k_.n]s}:</DisplayString>
<DisplayString Condition="sca_.k==kind::string+128">{str_.impl_.s_.buf,[detail::string_impl::sbo_chars_-str_.impl_.s_.buf[detail::string_impl::sbo_chars_]]s}</DisplayString>
<DisplayString Condition="sca_.k==kind::array">array [{arr_.t_->size}]</DisplayString>
<DisplayString Condition="sca_.k==kind::object">object [{obj_.t_->size}]</DisplayString>
<Expand>
<ExpandedItem Condition="sca_.k==kind::string">str_</ExpandedItem>
<ExpandedItem Condition="sca_.k==kind::string+64">str_</ExpandedItem>
<ExpandedItem Condition="sca_.k==kind::string+128">str_</ExpandedItem>
<ExpandedItem Condition="sca_.k==kind::array">arr_</ExpandedItem>
<ExpandedItem Condition="sca_.k==kind::object">obj_</ExpandedItem>
</Expand>
</Type>
<Type Name="boost::json::string">
<DisplayString Condition="impl_.s_.k==kind::string">{((char*)(impl_.p_.t+1)),[impl_.p_.t->size]s}</DisplayString>
<DisplayString Condition="impl_.s_.k==kind::string+64">{((char*)(impl_.k_.s)),[impl_.k_.n]s}:</DisplayString>
<DisplayString Condition="impl_.s_.k==kind::string+128">{impl_.s_.buf,[detail::string_impl::sbo_chars_-impl_.s_.buf[detail::string_impl::sbo_chars_]]s}</DisplayString>
<Expand>
<!-- VFALCO Need to handle key string here -->
<Item Name="[size]">impl_.s_.k==kind::string?impl_.p_.t->size:detail::string_impl::sbo_chars_-impl_.s_.buf[detail::string_impl::sbo_chars_]</Item>
<Item Name="[capacity]">impl_.s_.k==kind::string?impl_.p_.t->capacity:detail::string_impl::sbo_chars_</Item>
<Item Name="[storage]">sp_</Item>
</Expand>
</Type>
<Type Name="boost::json::array">
<DisplayString>array [{t_->size}]</DisplayString>
<Expand>
<ArrayItems>
<Size>t_->size</Size>
<ValuePointer>((value*)(t_+1))</ValuePointer>
</ArrayItems>
<Item Name="[capacity]">t_->capacity</Item>
<Item Name="[storage]">sp_</Item>
</Expand>
</Type>
<Type Name="boost::json::object">
<DisplayString>object [{t_->size}]</DisplayString>
<Expand>
<ArrayItems>
<Size>t_->size</Size>
<ValuePointer>(boost::json::key_value_pair*)(t_+1)</ValuePointer>
</ArrayItems>
<Item Name="[capacity]">t_->capacity</Item>
<Item Name="[storage]">sp_</Item>
</Expand>
</Type>
<Type Name="boost::json::key_value_pair">
<DisplayString Condition="value_.sca_.k==kind::null">{{ {key_,[len_]s}, null }}</DisplayString>
<DisplayString Condition="value_.sca_.k==kind::bool_">{{ {key_,[len_]s}, {value_.sca_.b} }}</DisplayString>
<DisplayString Condition="value_.sca_.k==kind::int64">{{ {key_,[len_]s}, {value_.sca_.i} }}</DisplayString>
<DisplayString Condition="value_.sca_.k==kind::uint64">{{ {key_,[len_]s}, {value_.sca_.u} }}</DisplayString>
<DisplayString Condition="value_.sca_.k==kind::double_">{{ {key_,[len_]s}, {value_.sca_.d} }}</DisplayString>
<DisplayString Condition="value_.sca_.k==kind::string">{{ {key_,[len_]s}, {((char*)(value_.str_.impl_.p_.t+1)),[value_.str_.impl_.p_.t->size]s} }}</DisplayString>
<DisplayString Condition="value_.sca_.k==kind::string+128">{{ {key_,[len_]s}, {value_.str_.impl_.s_.buf,[detail::string_impl::sbo_chars_-value_.str_.impl_.s_.buf[detail::string_impl::sbo_chars_]]s} }}</DisplayString>
<DisplayString Condition="value_.sca_.k==kind::array">{{ {key_,[len_]s}, array [{value_.arr_.t_->size}] }}</DisplayString>
<DisplayString Condition="value_.sca_.k==kind::object">{{ {key_,[len_]s}, object [{value_.obj_.t_->size}] }}</DisplayString>
<Expand>
<ExpandedItem>&amp;this->value_</ExpandedItem>
</Expand>
</Type>
</AutoVisualizer>
+165
View File
@@ -0,0 +1,165 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_KIND_HPP
#define BOOST_JSON_KIND_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/string_view.hpp>
#include <iosfwd>
namespace boost {
namespace json {
/** Constants for identifying the type of a value
These values are returned from @ref value::kind
*/
// Order matters
enum class kind : unsigned char
{
/// The null value.
null,
/// A `bool`.
bool_,
/// A `std::int64_t`
int64,
/// A `std::uint64_t`
uint64,
/// A `double`.
double_,
/// A @ref string.
string,
/// An @ref array.
array,
/// An @ref object.
object
};
/** Return a string representing a kind.
This provides a human-readable string
representing a @ref kind. This may be
useful for diagnostics.
@returns The string.
@param k The kind.
*/
BOOST_JSON_DECL
string_view
to_string(kind k) noexcept;
/** Format a kind to an output stream.
This allows a @ref kind to be formatted as
a string, typically for diagnostics.
@returns The output stream.
@param os The output stream to format to.
@param k The kind to format.
*/
BOOST_JSON_DECL
std::ostream&
operator<<(std::ostream& os, kind k);
/** A tag type used to select a @ref value constructor overload.
The library provides the constant @ref array_kind
which may be used to select the @ref value constructor
that creates an empty @ref array.
@see @ref array_kind
*/
struct array_kind_t
{
};
/** A tag type used to select a @ref value constructor overload.
The library provides the constant @ref object_kind
which may be used to select the @ref value constructor
that creates an empty @ref object.
@see @ref object_kind
*/
struct object_kind_t
{
};
/** A tag type used to select a @ref value constructor overload.
The library provides the constant @ref string_kind
which may be used to select the @ref value constructor
that creates an empty @ref string.
@see @ref string_kind
*/
struct string_kind_t
{
};
/** A constant used to select a @ref value constructor overload.
The library provides this constant to allow efficient
construction of a @ref value containing an empty @ref array.
@par Example
@code
storage_ptr sp;
value jv( array_kind, sp ); // sp is an optional parameter
@endcode
@see @ref array_kind_t
*/
BOOST_JSON_INLINE_VARIABLE(array_kind, array_kind_t);
/** A constant used to select a @ref value constructor overload.
The library provides this constant to allow efficient
construction of a @ref value containing an empty @ref object.
@par Example
@code
storage_ptr sp;
value jv( object_kind, sp ); // sp is an optional parameter
@endcode
@see @ref object_kind_t
*/
BOOST_JSON_INLINE_VARIABLE(object_kind, object_kind_t);
/** A constant used to select a @ref value constructor overload.
The library provides this constant to allow efficient
construction of a @ref value containing an empty @ref string.
@par Example
@code
storage_ptr sp;
value jv( string_kind, sp ); // sp is an optional parameter
@endcode
@see @ref string_kind_t
*/
BOOST_JSON_INLINE_VARIABLE(string_kind, string_kind_t);
} // namespace json
} // namespace boost
#endif
+108
View File
@@ -0,0 +1,108 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_MEMORY_RESOURCE_HPP
#define BOOST_JSON_MEMORY_RESOURCE_HPP
#include <boost/json/detail/config.hpp>
#include <boost/container/pmr/memory_resource.hpp>
#include <boost/container/pmr/polymorphic_allocator.hpp>
namespace boost {
namespace json {
#ifdef BOOST_JSON_DOCS
/** The type of memory resource used by the library.
Alias for `boost::container::pmr::memory_resource`.
*/
class memory_resource
{
};
/** The type of polymorphic allocator used by the library.
Alias template for `boost::container::pmr::polymorphic_allocator`.
*/
template<class T>
class polymorphic_allocator;
// VFALCO Bug: doc toolchain won't make this a ref
//using memory_resource = __see_below__;
#else
using memory_resource = boost::container::pmr::memory_resource;
template<class T>
using polymorphic_allocator =
boost::container::pmr::polymorphic_allocator<T>;
#endif
/** Return true if a memory resource's deallocate function has no effect.
This metafunction may be specialized to indicate to
the library that calls to the `deallocate` function of
a @ref memory_resource have no effect. The implementation
will elide such calls when it is safe to do so. By default,
the implementation assumes that all memory resources
require a call to `deallocate` for each memory region
obtained by calling `allocate`.
@par Example
This example specializes the metafuction for `my_resource`,
to indicate that calls to deallocate have no effect:
@code
// Forward-declaration for a user-defined memory resource
struct my_resource;
// It is necessary to specialize the template from
// inside the namespace in which it is declared:
namespace boost {
namespace json {
template<>
struct is_deallocate_trivial< my_resource >
{
static constexpr bool value = true;
};
} // namespace json
} // namespace boost
@endcode
It is usually not necessary for users to check this trait.
Instead, they can call @ref storage_ptr::is_deallocate_trivial
to determine if the pointed-to memory resource has a trivial
deallocate function.
@see
@ref memory_resource,
@ref storage_ptr
*/
template<class T>
struct is_deallocate_trivial
{
/** A bool equal to true if calls to `T::do_deallocate` have no effect.
The primary template sets `value` to false.
*/
static constexpr bool value = false;
};
} // namespace json
} // namespace boost
#endif
+354
View File
@@ -0,0 +1,354 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_MONOTONIC_RESOURCE_HPP
#define BOOST_JSON_MONOTONIC_RESOURCE_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/memory_resource.hpp>
#include <boost/json/storage_ptr.hpp>
#include <cstddef>
#include <utility>
namespace boost {
namespace json {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4251) // class needs to have dll-interface to be used by clients of class
#pragma warning(disable: 4275) // non dll-interface class used as base for dll-interface class
#endif
//----------------------------------------------------------
/** A dynamically allocating resource with a trivial deallocate
This memory resource is a special-purpose resource
that releases allocated memory only when the resource
is destroyed (or when @ref release is called).
It has a trivial deallocate function; that is, the
metafunction @ref is_deallocate_trivial returns `true`.
\n
The resource can be constructed with an initial buffer.
If there is no initial buffer, or if the buffer is
exhausted, subsequent dynamic allocations are made from
the system heap. The size of buffers obtained in this
fashion follow a geometric progression.
\n
The purpose of this resource is to optimize the use
case for performing many allocations, followed by
deallocating everything at once. This is precisely the
pattern of memory allocation which occurs when parsing:
allocation is performed for each parsed element, and
when the the resulting @ref value is no longer needed,
the entire structure is destroyed. However, it is not
suited for modifying the value after parsing is
complete; reallocations waste memory, since the
older buffer is not reclaimed until the resource
is destroyed.
@par Example
This parses a JSON text into a value which uses a local
stack buffer, then prints the result.
@code
unsigned char buf[ 4000 ];
monotonic_resource mr( buf );
// Parse the string, using our memory resource
auto const jv = parse( "[1,2,3]", &mr );
// Print the JSON
std::cout << jv;
@endcode
@note The total amount of memory dynamically
allocated is monotonically increasing; That is,
it never decreases.
@par Thread Safety
Members of the same instance may not be
called concurrently.
@see
https://en.wikipedia.org/wiki/Region-based_memory_management
*/
class
BOOST_JSON_DECL
BOOST_SYMBOL_VISIBLE
monotonic_resource final
: public memory_resource
{
struct block;
struct block_base
{
void* p;
std::size_t avail;
std::size_t size;
block_base* next;
};
block_base buffer_;
block_base* head_ = &buffer_;
std::size_t next_size_ = 1024;
storage_ptr upstream_;
static constexpr std::size_t min_size_ = 1024;
inline static constexpr std::size_t max_size();
inline static std::size_t round_pow2(
std::size_t n) noexcept;
inline static std::size_t next_pow2(
std::size_t n) noexcept;
public:
/// Copy constructor (deleted)
monotonic_resource(
monotonic_resource const&) = delete;
/// Copy assignment (deleted)
monotonic_resource& operator=(
monotonic_resource const&) = delete;
/** Destructor
Deallocates all the memory owned by this resource.
@par Effects
@code
this->release();
@endcode
@par Complexity
Linear in the number of deallocations performed.
@par Exception Safety
No-throw guarantee.
*/
~monotonic_resource();
/** Constructor
This constructs the resource and indicates
that the first internal dynamic allocation
shall be at least `initial_size` bytes.
\n
This constructor is guaranteed not to perform
any dynamic allocations.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param initial_size The size of the first
internal dynamic allocation. If this is lower
than the implementation-defined lower limit, then
the lower limit is used instead.
@param upstream An optional upstream memory resource
to use for performing internal dynamic allocations.
If this parameter is omitted, the default resource
is used.
*/
explicit
monotonic_resource(
std::size_t initial_size = 1024,
storage_ptr upstream = {}) noexcept;
/** Constructor
This constructs the resource and indicates that
subsequent allocations should use the specified
caller-owned buffer.
When this buffer is exhausted, dynamic allocations
from the upstream resource are made.
\n
This constructor is guaranteed not to perform
any dynamic allocations.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param buffer The buffer to use.
Ownership is not transferred; the caller is
responsible for ensuring that the lifetime of
the buffer extends until the resource is destroyed.
@param size The number of valid bytes pointed
to by `buffer`.
@param upstream An optional upstream memory resource
to use for performing internal dynamic allocations.
If this parameter is omitted, the default resource
is used.
*/
/** @{ */
monotonic_resource(
unsigned char* buffer,
std::size_t size,
storage_ptr upstream = {}) noexcept;
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
monotonic_resource(
std::byte* buffer,
std::size_t size,
storage_ptr upstream) noexcept
: monotonic_resource(reinterpret_cast<
unsigned char*>(buffer), size,
std::move(upstream))
{
}
#endif
/** @} */
/** Constructor
This constructs the resource and indicates that
subsequent allocations should use the specified
caller-owned buffer.
When this buffer is exhausted, dynamic allocations
from the upstream resource are made.
\n
This constructor is guaranteed not to perform
any dynamic allocations.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param buffer The buffer to use.
Ownership is not transferred; the caller is
responsible for ensuring that the lifetime of
the buffer extends until the resource is destroyed.
@param upstream An optional upstream memory resource
to use for performing internal dynamic allocations.
If this parameter is omitted, the default resource
is used.
*/
/** @{ */
template<std::size_t N>
explicit
monotonic_resource(
unsigned char(&buffer)[N],
storage_ptr upstream = {}) noexcept
: monotonic_resource(&buffer[0],
N, std::move(upstream))
{
}
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
template<std::size_t N>
explicit
monotonic_resource(
std::byte(&buffer)[N],
storage_ptr upstream = {}) noexcept
: monotonic_resource(&buffer[0],
N, std::move(upstream))
{
}
#endif
/** @} */
#ifndef BOOST_JSON_DOCS
// Safety net for accidental buffer overflows
template<std::size_t N>
monotonic_resource(
unsigned char(&buffer)[N],
std::size_t n,
storage_ptr upstream = {}) noexcept
: monotonic_resource(&buffer[0],
n, std::move(upstream))
{
// If this goes off, check your parameters
// closely, chances are you passed an array
// thinking it was a pointer.
BOOST_ASSERT(n <= N);
}
#ifdef __cpp_lib_byte
// Safety net for accidental buffer overflows
template<std::size_t N>
monotonic_resource(
std::byte(&buffer)[N],
std::size_t n,
storage_ptr upstream = {}) noexcept
: monotonic_resource(&buffer[0],
n, std::move(upstream))
{
// If this goes off, check your parameters
// closely, chances are you passed an array
// thinking it was a pointer.
BOOST_ASSERT(n <= N);
}
#endif
#endif
/** Release all allocated memory.
This function deallocates all allocated memory.
If an initial buffer was provided upon construction,
then all of the bytes will be available again for
allocation. Allocated memory is deallocated even
if deallocate has not been called for some of
the allocated blocks.
@par Complexity
Linear in the number of deallocations performed.
@par Exception Safety
No-throw guarantee.
*/
void
release() noexcept;
protected:
#ifndef BOOST_JSON_DOCS
void*
do_allocate(
std::size_t n,
std::size_t align) override;
void
do_deallocate(
void* p,
std::size_t n,
std::size_t align) override;
bool
do_is_equal(
memory_resource const& mr) const noexcept override;
#endif
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
template<>
struct is_deallocate_trivial<
monotonic_resource>
{
static constexpr bool value = true;
};
} // namespace json
} // namespace boost
#endif
+37
View File
@@ -0,0 +1,37 @@
//
// Copyright (c) 2020 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_NULL_RESOURCE_HPP
#define BOOST_JSON_NULL_RESOURCE_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/memory_resource.hpp>
namespace boost {
namespace json {
/** Return a pointer to the null resource.
This memory resource always throws the exception
`std::bad_alloc` in calls to `allocate`.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
BOOST_JSON_DECL
memory_resource*
get_null_resource() noexcept;
} // namespace json
} // namespace boost
#endif
+1686
View File
File diff suppressed because it is too large Load Diff
+214
View File
@@ -0,0 +1,214 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_PARSE_HPP
#define BOOST_JSON_PARSE_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/error.hpp>
#include <boost/json/parse_options.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/json/string_view.hpp>
#include <boost/json/value.hpp>
namespace boost {
namespace json {
/** Return parsed JSON as a @ref value.
This function parses an entire string in one
step to produce a complete JSON object, returned
as a @ref value. If the buffer does not contain a
complete serialized JSON, an error occurs. In this
case the returned value will be null, using the
[default memory resource].
@par Complexity
Linear in `s.size()`.
@par Exception Safety
Strong guarantee.
Calls to `memory_resource::allocate` may throw.
@return A value representing the parsed JSON,
or a null if any error occurred.
@param s The string to parse.
@param ec Set to the error, if any occurred.
@param sp The memory resource that the new value and all
of its elements will use. If this parameter is omitted,
the [default memory resource] is used.
@param opt The options for the parser. If this parameter
is omitted, the parser will accept only standard JSON.
@see
@ref parse_options,
@ref stream_parser.
[default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource
*/
/** @{ */
BOOST_JSON_DECL
value
parse(
string_view s,
error_code& ec,
storage_ptr sp = {},
parse_options const& opt = {});
BOOST_JSON_DECL
value
parse(
string_view s,
std::error_code& ec,
storage_ptr sp = {},
parse_options const& opt = {});
/** @} */
/** Return parsed JSON as a @ref value.
This function parses an entire string in one
step to produce a complete JSON object, returned
as a @ref value. If the buffer does not contain a
complete serialized JSON, an exception is thrown.
@par Complexity
Linear in `s.size()`.
@par Exception Safety
Strong guarantee.
Calls to `memory_resource::allocate` may throw.
@return A value representing the parsed
JSON upon success.
@param s The string to parse.
@param sp The memory resource that the new value and all
of its elements will use. If this parameter is omitted,
the [default memory resource] is used.
@param opt The options for the parser. If this parameter
is omitted, the parser will accept only standard JSON.
@throw system_error Thrown on failure.
@see
@ref parse_options,
@ref stream_parser.
[default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource
*/
BOOST_JSON_DECL
value
parse(
string_view s,
storage_ptr sp = {},
parse_options const& opt = {});
/** Return parsed JSON as a @ref value.
This function reads data from an input stream and parses it to produce a
complete JSON entity, returned as a @ref value. If the stream does not
contain a complete serialized JSON, or contains extra non-whitespace data,
an error occurs. In this case the returned value will be `null`, using the
[default memory resource].
@par Complexity
Linear in the size of consumed input.
@par Exception Safety
Basic guarantee.
Calls to `memory_resource::allocate` may throw.
The stream may throw as described by
[`std::ios::exceptions`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions).
@return A value representing the parsed JSON,
or a `null` if any error occurred.
@param is The stream to read from.
@param ec Set to the error, if any occurred.
@param sp The memory resource that the new value and all of its elements
will use. If this parameter is omitted, the [default memory resource]
is used.
@param opt The options for the parser. If this parameter is omitted, the
parser will accept only standard JSON.
@see @ref parse_options, @ref stream_parser, @ref value::operator>>.
[default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource
*/
/** @{ */
BOOST_JSON_DECL
value
parse(
std::istream& is,
error_code& ec,
storage_ptr sp = {},
parse_options const& opt = {});
BOOST_JSON_DECL
value
parse(
std::istream& is,
std::error_code& ec,
storage_ptr sp = {},
parse_options const& opt = {});
/** @} */
/** Return parsed JSON as a @ref value.
This function reads data from an input stream and parses it to produce a
complete JSON entity, returned as a @ref value. If the stream does not
contain a complete serialized JSON, or contains extra non-whitespace data,
an exception is thrown.
@par Complexity
Linear in the size of consumed input.
@par Exception Safety
Basic guarantee.
Throws @ref system_error on failed parse.
Calls to `memory_resource::allocate` may throw.
The stream may throw as described by
[`std::ios::exceptions`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions).
@return A value representing the parsed JSON upon success.
@param is The stream to read from.
@param sp The memory resource that the new value and all of its elements
will use. If this parameter is omitted, the [default memory resource]
is used.
@param opt The options for the parser. If this parameter is omitted, the
parser will accept only standard JSON.
@see @ref parse_options, @ref stream_parser, @ref value::operator>>.
[default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource
*/
BOOST_JSON_DECL
value
parse(
std::istream& is,
storage_ptr sp = {},
parse_options const& opt = {});
} // namespace json
} // namespace boost
#endif
+216
View File
@@ -0,0 +1,216 @@
//
// Copyright (c) 2021 Peter Dimov
// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_PARSE_INTO_HPP
#define BOOST_JSON_PARSE_INTO_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/basic_parser.hpp>
#include <boost/json/string_view.hpp>
#include <boost/json/system_error.hpp>
#include <boost/json/detail/parse_into.hpp>
namespace boost {
namespace json {
/** `basic_parser` that parses into a given type
This is an alias template for @ref basic_parser instantiations that use
a dedicated handler that parses directly into an object provided by the
user instead of creating a @ref value.
Objects of type `parser_for<T>` have constructor signature equivalent to
`parser_for( parse_options const&, T& )`.
@tparam T the type to parse into. This type must be
[*DefaultConstructible*](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
*/
template< class T >
using parser_for =
#ifndef BOOST_JSON_DOCS
basic_parser<detail::into_handler<T>>;
#else
__see_below__;
#endif
/** Parse a JSON text into a user-defined object.
This function parses an entire string in one step and fills an object
provided by the user. If the buffer does not contain a complete serialized
JSON text, an error occurs. In this case `v` may be partially filled.
The function supports default constructible types satisfying
<a href="https://en.cppreference.com/w/cpp/named_req/SequenceContainer"><em>SequenceContainer</em></a>,
arrays, arithmetic types, `bool`, `std::tuple`, `std::pair`,
`std::optional`, `std::nullptr_t`, and structs and enums described using
Boost.Describe.
@par Complexity
Linear in `sv.size()`.
@par Exception Safety
Basic guarantee.
Calls to `memory_resource::allocate` may throw.
@param v The type to parse into.
@param sv The string to parse.
@param ec Set to the error, if any occurred.
@param opt The options for the parser. If this parameter
is omitted, the parser will accept only standard JSON.
*/
/** @{ */
template<class V>
void
parse_into(
V& v,
string_view sv,
error_code& ec,
parse_options const& opt = {} );
template<class V>
void
parse_into(
V& v,
string_view sv,
std::error_code& ec,
parse_options const& opt = {} );
/** @} */
/** Parse a JSON text into a user-defined object.
This function parses an entire string in one step and fills an object
provided by the user. If the buffer does not contain a complete serialized
JSON text, an exception is thrown. In this case `v` may be
partially filled.
The function supports default constructible types satisfying
<a href="https://en.cppreference.com/w/cpp/named_req/SequenceContainer"><em>SequenceContainer</em></a>,
arrays, arithmetic types, `bool`, `std::tuple`, `std::pair`,
`std::optional`, `std::nullptr_t`, and structs and enums described using
Boost.Describe.
@par Complexity
Linear in `sv.size()`.
@par Exception Safety
Basic guarantee.
Throws @ref system_error on failed parse.
Calls to `memory_resource::allocate` may throw.
@param v The type to parse into.
@param sv The string to parse.
@param opt The options for the parser. If this parameter
is omitted, the parser will accept only standard JSON.
*/
template<class V>
void
parse_into(
V& v,
string_view sv,
parse_options const& opt = {} );
/** Parse a JSON text into a user-defined object.
This function reads data from an input stream and fills an object provided
by the user. If the buffer does not contain a complete serialized JSON
text, or contains extra non-whitespace data, an error occurs. In this case
`v` may be partially filled.
The function supports default constructible types satisfying
<a href="https://en.cppreference.com/w/cpp/named_req/SequenceContainer"><em>SequenceContainer</em></a>,
arrays, arithmetic types, `bool`, `std::tuple`, `std::pair`,
`std::optional`, `std::nullptr_t`, and structs and enums described using
Boost.Describe.
@par Complexity
Linear in the size of consumed input.
@par Exception Safety
Basic guarantee.
Calls to `memory_resource::allocate` may throw.
The stream may throw as described by
[`std::ios::exceptions`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions).
@param v The type to parse into.
@param is The stream to read from.
@param ec Set to the error, if any occurred.
@param opt The options for the parser. If this parameter
is omitted, the parser will accept only standard JSON.
*/
/** @{ */
template<class V>
void
parse_into(
V& v,
std::istream& is,
error_code& ec,
parse_options const& opt = {} );
template<class V>
void
parse_into(
V& v,
std::istream& is,
std::error_code& ec,
parse_options const& opt = {} );
/** @} */
/** Parse a JSON text into a user-defined object.
This function reads data from an input stream and fills an object provided
by the user. If the buffer does not contain a complete serialized JSON
text, or contains extra non-whitespace data, an exception is thrown. In
this case `v` may be partially filled.
The function supports default constructible types satisfying
<a href="https://en.cppreference.com/w/cpp/named_req/SequenceContainer"><em>SequenceContainer</em></a>,
arrays, arithmetic types, `bool`, `std::tuple`, `std::pair`,
`std::optional`, `std::nullptr_t`, and structs and enums described using
Boost.Describe.
@par Complexity
Linear in the size of consumed input.
@par Exception Safety
Basic guarantee.
Throws @ref system_error on failed parse.
Calls to `memory_resource::allocate` may throw.
The stream may throw as described by
[`std::ios::exceptions`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions).
@param v The type to parse into.
@param is The stream to read from.
@param opt The options for the parser. If this parameter
is omitted, the parser will accept only standard JSON.
*/
template<class V>
void
parse_into(
V& v,
std::istream& is,
parse_options const& opt = {} );
} // namespace boost
} // namespace json
#include <boost/json/impl/parse_into.hpp>
#endif
+160
View File
@@ -0,0 +1,160 @@
//
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_PARSE_OPTIONS_HPP
#define BOOST_JSON_PARSE_OPTIONS_HPP
#include <boost/json/detail/config.hpp>
#include <iosfwd>
namespace boost {
namespace json {
/** Enumeration of number parsing modes
These values are used to select the way to parse numbers.
@see
@ref parse_options,
@ref basic_parser,
@ref parser.
*/
enum class number_precision : unsigned char
{
/// Fast, but potentially less precise mode.
imprecise,
/// Slower, but precise mode.
precise,
/// The fastest mode, that only validates encountered numbers without
/// parsing them.
none,
};
/** Parser options
This structure is used for specifying
maximum parsing depth, and whether
to allow various non-standard extensions.
Default-constructed options set maximum
parsing depth to 32 and specify that only
standard JSON is allowed,
@see
@ref basic_parser,
@ref parser.
*/
struct parse_options
{
/** Maximum nesting level of arrays and objects.
This specifies the maximum number of nested
structures allowed while parsing a JSON text. If
this limit is exceeded during a parse, an
error is returned.
@see
@ref basic_parser,
@ref stream_parser.
*/
std::size_t max_depth = 32;
/** Number pasing mode
This selects the way to parse numbers. The default is to parse them
fast, but with possible slight imprecision for floating point numbers
with larger mantissas. Users can also choose to parse numbers slower
but with full precision. Or to not parse them at all, and only validate
numbers. The latter mode is useful for @ref basic_parser instantiations
that wish to treat numbers in a custom way.
@see
@ref basic_parser,
@ref stream_parser.
*/
number_precision numbers = number_precision::imprecise;
/** Non-standard extension option
Allow C and C++ style comments to appear
anywhere that whitespace is permissible.
@see
@ref basic_parser,
@ref stream_parser.
*/
bool allow_comments = false;
/** Non-standard extension option
Allow a trailing comma to appear after
the last element of any array or object.
@see
@ref basic_parser,
@ref stream_parser.
*/
bool allow_trailing_commas = false;
/** Non-standard extension option
Allow invalid UTF-8 sequnces to appear
in keys and strings.
@note This increases parsing performance.
@see
@ref basic_parser,
@ref stream_parser.
*/
bool allow_invalid_utf8 = false;
/** Non-standard extension option
Allow `Infinity`, `-Infinity`, and `NaN` JSON literals. These values
are produced by some popular JSON implementations for positive
infinity, negative infinity and NaN special numbers respectively.
@see
@ref basic_parser,
@ref stream_parser.
*/
bool allow_infinity_and_nan = false;
/** Set JSON parse options on input stream.
The function stores parse options in the private storage of the stream. If
the stream fails to allocate necessary private storage, `badbit` will be
set on it.
@return Reference to `is`.
@par Complexity
Amortized constant (due to potential memory allocation by the stream).
@par Exception Safety
Strong guarantee.
The stream may throw as configured by
[`std::ios::exceptions`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions).
@param is The input stream.
@param opts The options to store.
*/
BOOST_JSON_DECL
friend
std::istream&
operator>>( std::istream& is, parse_options const& opts );
};
} // namespace json
} // namespace boost
#endif
+829
View File
@@ -0,0 +1,829 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_PARSER_HPP
#define BOOST_JSON_PARSER_HPP
#include <boost/json/detail/config.hpp>
#include <boost/json/basic_parser.hpp>
#include <boost/json/storage_ptr.hpp>
#include <boost/json/value.hpp>
#include <boost/json/detail/handler.hpp>
#include <type_traits>
#include <cstddef>
namespace boost {
namespace json {
//----------------------------------------------------------
/** A DOM parser for JSON contained in a single buffer.
This class is used to parse a JSON text contained in a
single character buffer, into a @ref value container.
@par Usage
To use the parser first construct it, then optionally
call @ref reset to specify a @ref storage_ptr to use
for the resulting @ref value. Then call @ref write
to parse a character buffer containing a complete
JSON text. If the parse is successful, call @ref release
to take ownership of the value:
@code
parser p; // construct a parser
size_t n = p.write( "[1,2,3]" ); // parse a complete JSON text
assert( n == 7 ); // all characters consumed
value jv = p.release(); // take ownership of the value
@endcode
@par Extra Data
When the character buffer provided as input contains
additional data that is not part of the complete
JSON text, an error is returned. The @ref write_some
function is an alternative which allows the parse
to finish early, without consuming all the characters
in the buffer. This allows parsing of a buffer
containing multiple individual JSON texts or containing
different protocol data:
@code
parser p; // construct a parser
size_t n = p.write_some( "[1,2,3] null" ); // parse a complete JSON text
assert( n == 8 ); // only some characters consumed
value jv = p.release(); // take ownership of the value
@endcode
@par Temporary Storage
The parser may dynamically allocate temporary
storage as needed to accommodate the nesting level
of the JSON text being parsed. Temporary storage is
first obtained from an optional, caller-owned
buffer specified upon construction. When that
is exhausted, the next allocation uses the
@ref memory_resource passed to the constructor; if
no such argument is specified, the default memory
resource is used. Temporary storage is freed only
when the parser is destroyed; The performance of
parsing multiple JSON texts may be improved by reusing
the same parser instance.
\n
It is important to note that the @ref memory_resource
supplied upon construction is used for temporary
storage only, and not for allocating the elements
which make up the parsed value. That other memory
resource is optionally supplied in each call
to @ref reset.
@par Duplicate Keys
If there are object elements with duplicate keys;
that is, if multiple elements in an object have
keys that compare equal, only the last equivalent
element will be inserted.
@par Non-Standard JSON
The @ref parse_options structure optionally
provided upon construction is used to customize
some parameters of the parser, including which
non-standard JSON extensions should be allowed.
A default-constructed parse options allows only
standard JSON.
@par Thread Safety
Distinct instances may be accessed concurrently.
Non-const member functions of a shared instance
may not be called concurrently with any other
member functions of that instance.
@see
@ref parse,
@ref parse_options,
@ref stream_parser.
*/
class parser
{
basic_parser<detail::handler> p_;
public:
/// Copy constructor (deleted)
parser(
parser const&) = delete;
/// Copy assignment (deleted)
parser& operator=(
parser const&) = delete;
/** Destructor.
All dynamically allocated memory, including
any incomplete parsing results, is freed.
@par Complexity
Linear in the size of partial results
@par Exception Safety
No-throw guarantee.
*/
~parser() = default;
/** Constructor.
This constructs a new parser which first uses
the caller-owned storage pointed to by `buffer`
for temporary storage, falling back to the memory
resource `sp` if needed. The parser will use the
specified parsing options.
\n
The parsed value will use the default memory
resource for storage. To use a different resource,
call @ref reset after construction.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param sp The memory resource to use for
temporary storage after `buffer` is exhausted.
@param opt The parsing options to use.
@param buffer A pointer to valid memory of at least
`size` bytes for the parser to use for temporary storage.
Ownership is not transferred, the caller is responsible
for ensuring the lifetime of the memory pointed to by
`buffer` extends until the parser is destroyed.
@param size The number of valid bytes in `buffer`.
*/
BOOST_JSON_DECL
parser(
storage_ptr sp,
parse_options const& opt,
unsigned char* buffer,
std::size_t size) noexcept;
/** Constructor.
This constructs a new parser which uses the default
memory resource for temporary storage, and accepts
only strict JSON.
\n
The parsed value will use the default memory
resource for storage. To use a different resource,
call @ref reset after construction.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
*/
parser() noexcept
: parser({}, {})
{
}
/** Constructor.
This constructs a new parser which uses the
specified memory resource for temporary storage,
and is configured to use the specified parsing
options.
\n
The parsed value will use the default memory
resource for storage. To use a different resource,
call @ref reset after construction.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param sp The memory resource to use for temporary storage.
@param opt The parsing options to use.
*/
BOOST_JSON_DECL
parser(
storage_ptr sp,
parse_options const& opt) noexcept;
/** Constructor.
This constructs a new parser which uses the
specified memory resource for temporary storage,
and accepts only strict JSON.
\n
The parsed value will use the default memory
resource for storage. To use a different resource,
call @ref reset after construction.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param sp The memory resource to use for temporary storage.
*/
explicit
parser(storage_ptr sp) noexcept
: parser(std::move(sp), {})
{
}
/** Constructor.
This constructs a new parser which first uses the
caller-owned storage `buffer` for temporary storage,
falling back to the memory resource `sp` if needed.
The parser will use the specified parsing options.
\n
The parsed value will use the default memory
resource for storage. To use a different resource,
call @ref reset after construction.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param sp The memory resource to use for
temporary storage after `buffer` is exhausted.
@param opt The parsing options to use.
@param buffer A buffer for the parser to use for
temporary storage. Ownership is not transferred,
the caller is responsible for ensuring the lifetime
of `buffer` extends until the parser is destroyed.
*/
template<std::size_t N>
parser(
storage_ptr sp,
parse_options const& opt,
unsigned char(&buffer)[N]) noexcept
: parser(std::move(sp),
opt, &buffer[0], N)
{
}
#if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
/** Constructor.
This constructs a new parser which first uses
the caller-owned storage pointed to by `buffer`
for temporary storage, falling back to the memory
resource `sp` if needed. The parser will use the
specified parsing options.
\n
The parsed value will use the default memory
resource for storage. To use a different resource,
call @ref reset after construction.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param sp The memory resource to use for
temporary storage after `buffer` is exhausted.
@param opt The parsing options to use.
@param buffer A pointer to valid memory of at least
`size` bytes for the parser to use for temporary storage.
Ownership is not transferred, the caller is responsible
for ensuring the lifetime of the memory pointed to by
`buffer` extends until the parser is destroyed.
@param size The number of valid bytes in `buffer`.
*/
parser(
storage_ptr sp,
parse_options const& opt,
std::byte* buffer,
std::size_t size) noexcept
: parser(sp, opt, reinterpret_cast<
unsigned char*>(buffer), size)
{
}
/** Constructor.
This constructs a new parser which first uses the
caller-owned storage `buffer` for temporary storage,
falling back to the memory resource `sp` if needed.
The parser will use the specified parsing options.
\n
The parsed value will use the default memory
resource for storage. To use a different resource,
call @ref reset after construction.
@par Complexity
Constant.
@par Exception Safety
No-throw guarantee.
@param sp The memory resource to use for
temporary storage after `buffer` is exhausted.
@param opt The parsing options to use.
@param buffer A buffer for the parser to use for
temporary storage. Ownership is not transferred,
the caller is responsible for ensuring the lifetime
of `buffer` extends until the parser is destroyed.
*/
template<std::size_t N>
parser(
storage_ptr sp,
parse_options const& opt,
std::byte(&buffer)[N]) noexcept
: parser(std::move(sp),
opt, &buffer[0], N)
{
}
#endif
#ifndef BOOST_JSON_DOCS
// Safety net for accidental buffer overflows
template<std::size_t N>
parser(
storage_ptr sp,
parse_options const& opt,
unsigned char(&buffer)[N],
std::size_t n) noexcept
: parser(std::move(sp),
opt, &buffer[0], n)
{
// If this goes off, check your parameters
// closely, chances are you passed an array
// thinking it was a pointer.
BOOST_ASSERT(n <= N);
}
#ifdef __cpp_lib_byte
// Safety net for accidental buffer overflows
template<std::size_t N>
parser(
storage_ptr sp,
parse_options const& opt,
std::byte(&buffer)[N], std::size_t n) noexcept
: parser(std::move(sp),
opt, &buffer[0], n)
{
// If this goes off, check your parameters
// closely, chances are you passed an array
// thinking it was a pointer.
BOOST_ASSERT(n <= N);
}
#endif
#endif
/** Reset the parser for a new JSON text.
This function is used to reset the parser to
prepare it for parsing a new complete JSON text.
Any previous partial results are destroyed.
@par Complexity
Constant or linear in the size of any previous
partial parsing results.
@par Exception Safety
No-throw guarantee.
@param sp A pointer to the @ref memory_resource
to use for the resulting @ref value. The parser
will acquire shared ownership.
*/
BOOST_JSON_DECL
void
reset(storage_ptr sp = {}) noexcept;
/** Parse a buffer containing a complete JSON text.
This function parses a complete JSON text contained
in the specified character buffer. Additional
characters past the end of the complete JSON text
are ignored. The function returns the actual
number of characters parsed, which may be less
than the size of the input. This allows parsing
of a buffer containing multiple individual JSON texts
or containing different protocol data:
@par Example
@code
parser p; // construct a parser
size_t n = p.write_some( "[1,2,3] null" ); // parse a complete JSON text
assert( n == 8 ); // only some characters consumed
value jv = p.release(); // take ownership of the value
@endcode
@par Complexity
Linear in `size`.
@par Exception Safety
Basic guarantee.
Calls to `memory_resource::allocate` may throw.
Upon error or exception, subsequent calls will
fail until @ref reset is called to parse a new JSON text.
@return The number of characters consumed from
the buffer.
@param data A pointer to a buffer of `size`
characters to parse.
@param size The number of characters pointed to
by `data`.
@param ec Set to the error, if any occurred.
*/
/** @{ */
BOOST_JSON_DECL
std::size_t
write_some(
char const* data,
std::size_t size,
error_code& ec);
BOOST_JSON_DECL
std::size_t
write_some(
char const* data,
std::size_t size,
std::error_code& ec);
/** @} */
/** Parse a buffer containing a complete JSON text.
This function parses a complete JSON text contained
in the specified character buffer. Additional
characters past the end of the complete JSON text
are ignored. The function returns the actual
number of characters parsed, which may be less
than the size of the input. This allows parsing
of a buffer containing multiple individual JSON texts
or containing different protocol data:
@par Example
@code
parser p; // construct a parser
size_t n = p.write_some( "[1,2,3] null" ); // parse a complete JSON text
assert( n == 8 ); // only some characters consumed
value jv = p.release(); // take ownership of the value
@endcode
@par Complexity
Linear in `size`.
@par Exception Safety
Basic guarantee.
Calls to `memory_resource::allocate` may throw.
Upon error or exception, subsequent calls will
fail until @ref reset is called to parse a new JSON text.
@return The number of characters consumed from
the buffer.
@param data A pointer to a buffer of `size`
characters to parse.
@param size The number of characters pointed to
by `data`.
@throw system_error Thrown on error.
*/
BOOST_JSON_DECL
std::size_t
write_some(
char const* data,
std::size_t size);
/** Parse a buffer containing a complete JSON text.
This function parses a complete JSON text contained
in the specified character buffer. Additional
characters past the end of the complete JSON text
are ignored. The function returns the actual
number of characters parsed, which may be less
than the size of the input. This allows parsing
of a buffer containing multiple individual JSON texts
or containing different protocol data:
@par Example
@code
parser p; // construct a parser
size_t n = p.write_some( "[1,2,3] null" ); // parse a complete JSON text
assert( n == 8 ); // only some characters consumed
value jv = p.release(); // take ownership of the value
@endcode
@par Complexity
Linear in `size`.
@par Exception Safety
Basic guarantee.
Calls to `memory_resource::allocate` may throw.
Upon error or exception, subsequent calls will
fail until @ref reset is called to parse a new JSON text.
@return The number of characters consumed from
the buffer.
@param s The character string to parse.
@param ec Set to the error, if any occurred.
*/
/** @{ */
std::size_t
write_some(
string_view s,
error_code& ec)
{
return write_some(
s.data(), s.size(), ec);
}
std::size_t
write_some(
string_view s,
std::error_code& ec)
{
return write_some(
s.data(), s.size(), ec);
}
/** @} */
/** Parse a buffer containing a complete JSON text.
This function parses a complete JSON text contained
in the specified character buffer. Additional
characters past the end of the complete JSON text
are ignored. The function returns the actual
number of characters parsed, which may be less
than the size of the input. This allows parsing
of a buffer containing multiple individual JSON texts
or containing different protocol data:
@par Example
@code
parser p; // construct a parser
size_t n = p.write_some( "[1,2,3] null" ); // parse a complete JSON text
assert( n == 8 ); // only some characters consumed
value jv = p.release(); // take ownership of the value
@endcode
@par Complexity
Linear in `size`.
@par Exception Safety
Basic guarantee.
Calls to `memory_resource::allocate` may throw.
Upon error or exception, subsequent calls will
fail until @ref reset is called to parse a new JSON text.
@return The number of characters consumed from
the buffer.
@param s The character string to parse.
@throw system_error Thrown on error.
*/
std::size_t
write_some(
string_view s)
{
return write_some(
s.data(), s.size());
}
/** Parse a buffer containing a complete JSON text.
This function parses a complete JSON text contained
in the specified character buffer. The entire
buffer must be consumed; if there are additional
characters past the end of the complete JSON text,
the parse fails and an error is returned.
@par Example
@code
parser p; // construct a parser
size_t n = p.write( "[1,2,3]" ); // parse a complete JSON text
assert( n == 7 ); // all characters consumed
value jv = p.release(); // take ownership of the value
@endcode
@par Complexity
Linear in `size`.
@par Exception Safety
Basic guarantee.
Calls to `memory_resource::allocate` may throw.
Upon error or exception, subsequent calls will
fail until @ref reset is called to parse a new JSON text.
@return The number of characters consumed from
the buffer.
@param data A pointer to a buffer of `size`
characters to parse.
@param size The number of characters pointed to
by `data`.
@param ec Set to the error, if any occurred.
*/
/** @{ */
BOOST_JSON_DECL
std::size_t
write(
char const* data,
std::size_t size,
error_code& ec);
BOOST_JSON_DECL
std::size_t
write(
char const* data,
std::size_t size,
std::error_code& ec);
/** @} */
/** Parse a buffer containing a complete JSON text.
This function parses a complete JSON text contained
in the specified character buffer. The entire
buffer must be consumed; if there are additional
characters past the end of the complete JSON text,
the parse fails and an error is returned.
@par Example
@code
parser p; // construct a parser
size_t n = p.write( "[1,2,3]" ); // parse a complete JSON text
assert( n == 7 ); // all characters consumed
value jv = p.release(); // take ownership of the value
@endcode
@par Complexity
Linear in `size`.
@par Exception Safety
Basic guarantee.
Calls to `memory_resource::allocate` may throw.
Upon error or exception, subsequent calls will
fail until @ref reset is called to parse a new JSON text.
@return The number of characters consumed from
the buffer.
@param data A pointer to a buffer of `size`
characters to parse.
@param size The number of characters pointed to
by `data`.
@throw system_error Thrown on error.
*/
BOOST_JSON_DECL
std::size_t
write(
char const* data,
std::size_t size);
/** Parse a buffer containing a complete JSON text.
This function parses a complete JSON text contained
in the specified character buffer. The entire
buffer must be consumed; if there are additional
characters past the end of the complete JSON text,
the parse fails and an error is returned.
@par Example
@code
parser p; // construct a parser
size_t n = p.write( "[1,2,3]" ); // parse a complete JSON text
assert( n == 7 ); // all characters consumed
value jv = p.release(); // take ownership of the value
@endcode
@par Complexity
Linear in `size`.
@par Exception Safety
Basic guarantee.
Calls to `memory_resource::allocate` may throw.
Upon error or exception, subsequent calls will
fail until @ref reset is called to parse a new JSON text.
@return The number of characters consumed from
the buffer.
@param s The character string to parse.
@param ec Set to the error, if any occurred.
*/
/** @{ */
std::size_t
write(
string_view s,
error_code& ec)
{
return write(
s.data(), s.size(), ec);
}
std::size_t
write(
string_view s,
std::error_code& ec)
{
return write(
s.data(), s.size(), ec);
}
/** @} */
/** Parse a buffer containing a complete JSON text.
This function parses a complete JSON text contained
in the specified character buffer. The entire
buffer must be consumed; if there are additional
characters past the end of the complete JSON text,
the parse fails and an error is returned.
@par Example
@code
parser p; // construct a parser
size_t n = p.write( "[1,2,3]" ); // parse a complete JSON text
assert( n == 7 ); // all characters consumed
value jv = p.release(); // take ownership of the value
@endcode
@par Complexity
Linear in `size`.
@par Exception Safety
Basic guarantee.
Calls to `memory_resource::allocate` may throw.
Upon error or exception, subsequent calls will
fail until @ref reset is called to parse a new JSON text.
@return The number of characters consumed from
the buffer.
@param s The character string to parse.
@throw system_error Thrown on error.
*/
std::size_t
write(
string_view s)
{
return write(
s.data(), s.size());
}
/** Return the parsed JSON text as a @ref value.
This returns the parsed value, or throws
an exception if the parsing is incomplete or
failed. It is necessary to call @ref reset
after calling this function in order to parse
another JSON text.
@par Complexity
Constant.
@return The parsed value. Ownership of this
value is transferred to the caller.
@throw system_error Thrown on failure.
*/
BOOST_JSON_DECL
value
release();
};
} // namespace json
} // namespace boost
#endif
+217
View File
@@ -0,0 +1,217 @@
//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_PILFER_HPP
#define BOOST_JSON_PILFER_HPP
#include <boost/json/detail/config.hpp>
#include <type_traits>
#include <utility>
/*
Implements "pilfering" from P0308R0
@see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0308r0.html
*/
namespace boost {
namespace json {
/** Tag wrapper to specify pilfer-construction.
This wrapper is used to specify a pilfer constructor
overload.
@par Example
A pilfer constructor accepts a single argument
of type @ref pilfered and throws nothing:
@code
struct T
{
T( pilfered<T> ) noexcept;
};
@endcode
@note
The constructor should not be marked explicit.
@see @ref pilfer, @ref is_pilfer_constructible,
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0308r0.html">
Valueless Variants Considered Harmful</a>
*/
template<class T>
class pilfered
{
T& t_;
public:
/** Constructor
Construct the wrapper from `t`.
@param t The pilferable object. Ownership
is not transferred.
*/
explicit
constexpr
pilfered(T&& t) noexcept
: t_(t)
{
}
/** Return a reference to the pilferable object.
This returns a reference to the wrapped object.
*/
constexpr T&
get() const noexcept
{
return t_;
}
/** Return a pointer to the pilferable object.
This returns a pointer to the wrapped object.
*/
constexpr T*
operator->() const noexcept
{
//return std::addressof(t_);
return reinterpret_cast<T*>(
const_cast<char *>(
&reinterpret_cast<
const volatile char &>(t_)));
}
};
#ifndef BOOST_JSON_DOCS
// VFALCO Renamed this to work around an msvc bug
namespace detail_pilfer {
template<class>
struct not_pilfered
{
};
} // detail_pilfer
#endif
/** Metafunction returning `true` if `T` is <em>PilferConstructible</em>
If `T` can be pilfer constructed, this metafunction is
equal to `std::true_type`. Otherwise it is equal to
`std::false_type`.
@see @ref pilfer, @ref pilfered,
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0308r0.html">
Valueless Variants Considered Harmful</a>
*/
template<class T>
struct is_pilfer_constructible
#ifndef BOOST_JSON_DOCS
: std::integral_constant<bool,
std::is_nothrow_move_constructible<T>::value ||
(
std::is_nothrow_constructible<
T, pilfered<T> >::value &&
! std::is_nothrow_constructible<
T, detail_pilfer::not_pilfered<T> >::value
)>
#endif
{
};
/** Indicate that an object `t` may be pilfered from.
A <em>pilfer</em> operation is the construction
of a new object of type `T` from an existing
object `t`. After the construction, the only
valid operation on the pilfered-from object is
destruction. This permits optimizations beyond
those available for a move-construction, as the
pilfered-from object is not required to be in
a "usable" state.
\n
This is used similarly to `std::move`.
@par Example
A pilfer constructor accepts a single argument
of type @ref pilfered and throws nothing:
@code
struct T
{
T( pilfered<T> ) noexcept;
};
@endcode
Pilfer construction is performed using @ref pilfer :
@code
{
T t1; // default construction
T t2( pilfer( t1 ) ); // pilfer-construct from t1
// At this point, t1 may only be destroyed
}
@endcode
@see @ref pilfered, @ref is_pilfer_constructible,
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0308r0.html">
Valueless Variants Considered Harmful</a>
*/
template<class T>
auto
pilfer(T&& t) noexcept ->
typename std::conditional<
std::is_nothrow_constructible<
typename std::remove_reference<T>::type,
pilfered<typename
std::remove_reference<T>::type> >::value &&
! std::is_nothrow_constructible<
typename std::remove_reference<T>::type,
detail_pilfer::not_pilfered<typename
std::remove_reference<T>::type> >::value,
pilfered<typename std::remove_reference<T>::type>,
typename std::remove_reference<T>::type&&
>::type
{
using U =
typename std::remove_reference<T>::type;
static_assert(
is_pilfer_constructible<U>::value, "");
return typename std::conditional<
std::is_nothrow_constructible<
U, pilfered<U> >::value &&
! std::is_nothrow_constructible<
U, detail_pilfer::not_pilfered<U> >::value,
pilfered<U>, U&&
>::type(std::move(t));
}
/*
template<class T>
void
relocate(T* dest, T& src) noexcept
{
static_assert(
is_pilfer_constructible<T>::value, "");
::new(dest) T(pilfer(src));
src.~T();
}
*/
} // json
} // boost
#endif

Some files were not shown because too many files have changed in this diff Show More