mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 06:14:59 +01:00
Pinning the bytes around the invalid one to 0x80 lost coverage: with error_handler_t::ignore/replace, the serializer re-reads the invalid byte and decodes the following bytes, and its decoder distinguishes the continuation classes 0x80-0x8F, 0x90-0x9F, and 0xA0-0xBF, as do the lexer's range checks. Iterate those positions over the first and last byte of each class within the valid range instead (utils::utf8_continuation_bytes). The invalid byte still takes all 256 values. Defining JSON_TEST_UTF8_EXHAUSTIVE restores the full Cartesian product, with the same assertion counts as before #5418. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
102 lines
3.5 KiB
C++
102 lines
3.5 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
// | | |__ | | | | | | version 3.12.0
|
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
//
|
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#pragma once
|
|
|
|
#include <array> // array
|
|
#include <cstdint> // uint8_t
|
|
#include <cstddef> // size_t
|
|
#include <fstream> // ifstream, istreambuf_iterator, ios
|
|
#include <vector> // vector
|
|
|
|
namespace utils
|
|
{
|
|
|
|
// Some tests intentionally discard the [[nodiscard]]/JSON_HEDLEY_WARN_UNUSED_RESULT
|
|
// return value of a call they only make to exercise its side effects (e.g. checking
|
|
// that it does not throw). A plain (void) cast on the call expression does not
|
|
// suppress GCC's warning for functions using the GNU __attribute__((warn_unused_result))
|
|
// form (as opposed to the C++17 [[nodiscard]] attribute) -- passing the value into an
|
|
// ordinary function call does.
|
|
template<typename T>
|
|
inline void ignore_return_value(T&& /*unused*/) noexcept {}
|
|
|
|
// Advance i toward last (inclusive) by stride, always visiting last.
|
|
// stride 7 is coprime to 256, so every low-byte residue is still hit.
|
|
template<typename T>
|
|
T next_integer_sample(T i, T last, T stride)
|
|
{
|
|
if (i >= last)
|
|
{
|
|
return static_cast<T>(last + 1);
|
|
}
|
|
if (stride > 0 && i > static_cast<T>(last - stride))
|
|
{
|
|
return last;
|
|
}
|
|
const T n = static_cast<T>(i + stride);
|
|
return n < last ? n : last;
|
|
}
|
|
|
|
// UTF-8 continuation bytes in [lo, hi] that stand in for all of them in the
|
|
// ill-formed UTF-8 tests. Both the lexer's range checks and the serializer's
|
|
// decoder (detail::decode) only distinguish the classes 0x80..0x8F, 0x90..0x9F,
|
|
// and 0xA0..0xBF, so the first and last byte of each class within [lo, hi]
|
|
// exercise every behavior while a test sweeps another byte position through
|
|
// all 256 values (#5418). Define JSON_TEST_UTF8_EXHAUSTIVE to get every byte.
|
|
inline std::vector<int> utf8_continuation_bytes(int lo, int hi)
|
|
{
|
|
std::vector<int> result;
|
|
#ifdef JSON_TEST_UTF8_EXHAUSTIVE
|
|
for (int byte = lo; byte <= hi; ++byte)
|
|
{
|
|
result.push_back(byte);
|
|
}
|
|
#else
|
|
static const std::array<int, 6> class_ends = {{0x80, 0x8F, 0x90, 0x9F, 0xA0, 0xBF}};
|
|
for (const int byte : class_ends)
|
|
{
|
|
if (lo <= byte && byte <= hi)
|
|
{
|
|
result.push_back(byte);
|
|
}
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
inline std::vector<std::uint8_t> read_binary_file(const std::string& filename)
|
|
{
|
|
std::ifstream file(filename, std::ios::binary);
|
|
file.unsetf(std::ios::skipws);
|
|
|
|
file.seekg(0, std::ios::end);
|
|
const auto size = file.tellg();
|
|
file.seekg(0, std::ios::beg);
|
|
|
|
std::vector<std::uint8_t> byte_vector;
|
|
byte_vector.reserve(static_cast<std::size_t>(size));
|
|
byte_vector.insert(byte_vector.begin(), std::istream_iterator<std::uint8_t>(file), std::istream_iterator<std::uint8_t>());
|
|
return byte_vector;
|
|
}
|
|
|
|
// sentinel for istreambuf_iterator; compares != true until EOF is reached
|
|
// lets tests read a file directly via the new iterator+sentinel overloads
|
|
// instead of buffering the whole file into a vector first.
|
|
// Only the iterator-first direction (it != sentinel) is ever evaluated by
|
|
// the library's parse loop, so no reversed-order overload is needed.
|
|
struct istreambuf_sentinel
|
|
{
|
|
friend bool operator!=(const std::istreambuf_iterator<char>& it, const istreambuf_sentinel& /*unused*/) noexcept
|
|
{
|
|
return it != std::istreambuf_iterator<char>();
|
|
}
|
|
};
|
|
|
|
} // namespace utils
|