Resolves a conflict in include/nlohmann/detail/input/input_adapters.hpp
between this branch's memcpy fast path for get_elements() and develop's
lazy-diagnostics feature (#5234), which added a `begin` iterator member,
`get_consumed_count()`, and `copy_consumed_range()` to the same class.
The two features are orthogonal (lexer.hpp uses the new diagnostics
helpers; binary_reader.hpp uses get_elements()) and both are preserved.
single_include/nlohmann/json.hpp is regenerated from the merged sources.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
`lexer::get()` copied every scanned character into `token_string` on the
whole successful-parse hot path, yet that buffer is consumed only by
`get_token_string()` when rendering the "last read" fragment of a parse
error. On well-formed input the per-byte copy (plus the `unget()` pop)
is pure overhead that is always discarded.
For seekable input adapters - random-access, single-byte iterators such
as those backing `std::string`, `const char*`, and `std::vector<char>` -
the offending token is now reconstructed on demand from the input when
an error is reported, using a saved start offset, and the eager copy is
skipped. Streaming adapters (file, istream, wide-string, and user-defined
adapters) keep the eager copy; the strategy is chosen at compile time via
`input_adapter_supports_seek`, so adapters without the capability are
unaffected.
Error messages are byte-for-byte identical across all adapters, verified
by a new parity regression test. Microbenchmark (4 MB mixed JSON, parsed
from a std::string): ~149 -> ~160 MB/s, about +8%.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
std::vector::resize(n) (and string_t's resize) is required to make
size() exactly n; only capacity() is permitted to overshoot. The
destination-size assert in get_bytes should reflect that exact guarantee
rather than a weaker `>=`.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
get_elements() only receives a raw pointer and cannot know the size of
its destination, so the destination-size guarantee is asserted at the
caller that owns the buffer: after resizing `result`, assert it has room
for `wanted` bytes at `old_size`. Together with the `copied <= wanted`
assert at the memcpy, this makes the "destination is large enough"
invariant explicit on both sides of the call.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the "buffers are large enough" reasoning explicit: before the copy,
assert that `copied` fits both the caller-provided destination (`wanted`
bytes) and the remaining input range (`available` bytes). These hold by
construction (`copied = min(wanted, available)`) but were previously only
implied; the asserts document the invariant and would catch a future
regression that breaks it.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Flawfinder flags the bare identifier `read` as the POSIX read() syscall
(CWE-120/CWE-20), producing several false-positive code-scanning alerts.
Renaming the local variable removes them; the byte-copy itself is
unchanged and remains bounds-checked (get_elements caps the copy to the
bytes available). This is a pure rename with identical semantics.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- get_bytes(): replace `len -= static_cast<NumberType>(wanted)` with an
explicit `len = static_cast<NumberType>(len - ...)`. The compound
assignment promoted the operands to int and narrowed back, which GCC
rejects under -Werror=arith-conversion when NumberType is narrower than
int (e.g. UBJSON's int16 length). This also fixes the CodeQL build.
- get_bytes(): add JSON_ASSERT(read == wanted) documenting that
get_elements() never returns more than requested (per review).
- iterator_input_adapter: de-duplicate the iterator_is_contiguous
definition with the #if inside the initializer (per review).
- Add a comment explaining why the memcpy source is &*current (needed for
non-pointer contiguous iterators), and drop the redundant parentheses.
- Run astyle on the new unit test so the amalgamation/format check passes.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The binary reader read CBOR/MessagePack/BSON/UBJSON strings and byte
arrays one byte at a time via get()/push_back(), and the iterator input
adapter's get_elements() fallback was itself a per-byte loop, so even
contiguous inputs never benefited from a block copy.
Two changes:
1. iterator_input_adapter::get_elements() gains a contiguous fast path
that copies the whole requested range with std::memcpy. Contiguity is
detected via std::is_pointer (all standards) and, in C++20,
std::contiguous_iterator (so std::vector/std::string iterators also
qualify). Non-contiguous iterators keep the element-by-element loop.
2. get_string()/get_binary() now share get_bytes(), which reads into the
result in bounded chunks through get_elements() instead of byte by
byte. Capping the chunk size preserves the deliberate "do not
reserve(len) for an untrusted length" DoS protection while turning the
inner loop into block copies. The min(chunk_size, len) computation is
width-safe so narrow length types (e.g. MessagePack ext-8's uint8_t)
cannot truncate chunk_size to zero.
Microbenchmark (2 MiB string + 2 MiB blob + 2000x1 KiB strings, Apple
clang, -O2):
C++20 from_cbor(vector) 20.1 ms -> 1.0 ms (~20x)
from_cbor(pointer) 18.9 ms -> 1.0 ms (~19x)
C++17 from_cbor(pointer) 18.7 ms -> 1.0 ms (~19x, memcpy)
from_cbor(vector) 18.7 ms -> 4.0 ms (~4.6x, tight loop)
Behavior is unchanged: truncated input still throws parse_error.110 at
the same byte offset, and all binary-format unit tests pass.
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(cbor): reject negative ints overflowing int64
CBOR encodes negative integers as "-1 - n" where n is uint64_t. When
n > INT64_MAX, casting to int64_t caused undefined behavior and silent
data corruption. Large negative values were incorrectly parsed as
positive integers (e.g., -9223372036854775809 became 9223372036854775807).
Add bounds check for to reject values that exceed int64_t
representable range, returning parse_error instead of silently
corrupting data.
Added regression test cases to verify.
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
* chore: clarify tests
Add test for "n=0" case (result=-1) to cover the smallest magnitude
boundary. Update comments to explain CBOR 0x3B encoding and why
"result=0" is not possible. Clarify that n is an unsigned integer
in the formula "result = -1 - n" to help understanding the tests.
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
* fix(cbor): extend overflow checks for other types
Extend negative integer overflow detection to all CBOR negative
integer cases (0x38, 0x39, 0x3A) for consistency with the existing
0x3B check.
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
---------
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
Adds pre-multiplication overflow detection to catch cases where dimension
products would exceed size_t max. The previous check only detected when
overflow resulted in exactly 0 or SIZE_MAX, missing other cases.
Retains the original post-multiplication check for backward compatibility.
Adds tests verifying overflow detection with dimensions (2^32+1)×(2^32),
which previously overflowed silently to 2^32.
This prevents custom SAX handlers from receiving incorrect array sizes
that could lead to buffer overflows.
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
* Add implementation to retrieve start and end positions of json during parse
* Add more unit tests and add start/stop parsing for arrays
* Add raw value for all types
* Add more tests and fix compiler warning
* Amalgamate
* Fix CLang GCC warnings
* Fix error in build
* Style using astyle 3.1
* Fix whitespace changes
* revert
* more whitespace reverts
* Address PR comments
* Fix failing issues
* More whitespace reverts
* Address remaining PR comments
* Address comments
* Switch to using custom base class instead of default basic_json
* Adding a basic using for a json using the new base class. Also address PR comments and fix CI failures
* Address decltype comments
* Diagnostic positions macro (#4)
Co-authored-by: Sush Shringarputale <sushring@linux.microsoft.com>
* Fix missed include deletion
* Add docs and address other PR comments (#5)
* Add docs and address other PR comments
---------
Co-authored-by: Sush Shringarputale <sushring@linux.microsoft.com>
* Address new PR comments and fix CI tests for documentation
* Update documentation based on feedback (#6)
---------
Co-authored-by: Sush Shringarputale <sushring@linux.microsoft.com>
* Address std::size_t and other comments
* Fix new CI issues
* Fix lcov
* Improve lcov case with update to handle_diagnostic_positions call for discarded values
* Fix indentation of LCOV_EXCL_STOP comments
* fix amalgamation astyle issue
---------
Co-authored-by: Sush Shringarputale <sushring@linux.microsoft.com>
* multibyte binary reader
* wide_string_input_adapter fallback to get_character
Update input_adapters.hpp
* Update json.hpp
* Add from msgpack test
* Test for broken msgpack with stream, address some warnings
* Reading binary number from wchar as an error, address warnings
* Not casting float to int, it violates strict aliasing rule
* fix: integer parsed as float when EINTR set in errno
* chore: make amalgamate
* chore: make pretty
---------
Co-authored-by: Stuart Gorman <Stuart.Gorman@kallipr.com>
* Possible fix for #4485
Throw's an exception when i is nullptr,
also added a testcase for this scenario though most likely in the wrong test file.cpp
* quick cleanup
* Fix compile issues
* moved tests around, changed exceptions, removed a possibly unneeded include
* add back include <memory> for testing something
* Ninja doesn't like not having a \n, at end of file, adding it back
* update input_adapter file to deal with empty/null file ptr.
* ran make pretty
* added test for inputadapter
* ran make amalgamate
* Update tests/src/unit-deserialization.cpp
Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com>
* Update tests/src/unit-deserialization.cpp
Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com>
* Update input adapters.hpp with new includes
* fix unabigious use of _, (there was a double declare)
* did the amalagamate
* rm duplicate includes
* make amalgamate again
* reorder
* amalgamate
* moved it above
* amalgamate
---------
Co-authored-by: Jordan <jordan-hoang@users.noreply.github.com>
Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com>