Compare commits

..
Author SHA1 Message Date
Claude cf14cbdd02 Deduplicate UBJSON/BJData length-type error message
The "expected length type specification" parse error was built with an
identical if/else block in both get_ubjson_string() and
get_ubjson_size_value(), each branching on the input format to pick the
marker list. Extract a single unexpected_length_type_message() helper that
selects the markers via a ternary and appends the caller-supplied infix,
removing four near-duplicate message strings and centralizing the marker
list. Error messages are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tv6SnfYbcx8y3y4PrECcto
2026-07-22 21:03:03 +00:00
5 changed files with 368 additions and 750 deletions
+22 -22
View File
@@ -1869,6 +1869,24 @@ class binary_reader
return true;
}
/*!
@brief create the error message for a missing/invalid length-type marker
Used both when reading a UBJSON/BJData string and when reading an optimized
container size. The accepted markers depend on the input format, and the
size variant appends " after '#'" via @a infix.
@param[in] last_token the offending byte as returned by get_token_string()
@param[in] infix extra context inserted after the marker list
@return the formatted error message
*/
std::string unexpected_length_type_message(const std::string& last_token, const char* infix) const
{
return concat("expected length type specification (",
input_format == input_format_t::bjdata ? "U, i, u, I, m, l, M, L" : "U, i, I, l, L",
")", infix, "; last byte: 0x", last_token);
}
/*!
@brief reads a UBJSON string
@@ -1961,17 +1979,8 @@ class binary_reader
break;
}
auto last_token = get_token_string();
std::string message;
if (input_format != input_format_t::bjdata)
{
message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
}
else
{
message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
}
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format, unexpected_length_type_message(last_token, ""), "string"), nullptr));
}
/*!
@@ -2250,17 +2259,8 @@ class binary_reader
break;
}
auto last_token = get_token_string();
std::string message;
if (input_format != input_format_t::bjdata)
{
message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
}
else
{
message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
}
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
exception_message(input_format, unexpected_length_type_message(last_token, " after '#'"), "size"), nullptr));
}
/*!
File diff suppressed because it is too large Load Diff
@@ -13,7 +13,6 @@
#include <iterator> // back_inserter
#include <memory> // shared_ptr, make_shared
#include <string> // basic_string
#include <utility> // move
#include <vector> // vector
#ifndef JSON_NO_IO
@@ -119,72 +118,6 @@ class output_string_adapter : public output_adapter_protocol<CharType>
StringType& str;
};
/// @brief non-virtual output sink writing into a std::vector
///
/// Unlike output_vector_adapter, this sink is not part of the virtual
/// output_adapter_protocol hierarchy: it is passed to binary_writer by value as
/// a template parameter, so write_character()/write_characters() are ordinary
/// (inlinable) calls with no vtable lookup and no shared_ptr. It is used for the
/// common `to_cbor`/`to_msgpack`/... into a std::vector.
template<typename CharType, typename AllocatorType = std::allocator<CharType>>
class output_vector_sink
{
public:
explicit output_vector_sink(std::vector<CharType, AllocatorType>& vec) noexcept
: v(vec)
{}
void write_character(CharType c)
{
v.push_back(c);
}
// no JSON_HEDLEY_NON_NULL here: binary_writer legitimately passes a null
// pointer with length 0 for empty strings/binary values. Appending an empty
// range is a no-op; the type-erased path tolerates this via the (unattributed)
// virtual base, and the concrete sink must do the same.
void write_characters(const CharType* s, std::size_t length)
{
v.insert(v.end(), s, s + length);
}
private:
std::vector<CharType, AllocatorType>& v;
};
/// @brief output sink forwarding to a type-erased output adapter
///
/// Wraps the polymorphic output_adapter_t so the same binary_writer template can
/// also target arbitrary adapters (output streams, strings, user-provided
/// adapters) via the `output_adapter`-based overloads. Each write still goes
/// through one virtual call, exactly as before; only the concrete sinks above
/// avoid it.
template<typename CharType>
class output_adapter_sink
{
public:
explicit output_adapter_sink(output_adapter_t<CharType> adapter)
: oa(std::move(adapter))
{
JSON_ASSERT(oa);
}
void write_character(CharType c)
{
oa->write_character(c);
}
// no JSON_HEDLEY_NON_NULL: forwards (null, 0) for empty payloads, exactly as
// the type-erased path already did before this sink existed
void write_characters(const CharType* s, std::size_t length)
{
oa->write_characters(s, length);
}
private:
output_adapter_t<CharType> oa = nullptr;
};
template<typename CharType, typename StringType = std::basic_string<CharType>>
class output_adapter
{
+6 -16
View File
@@ -140,7 +140,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
friend ::nlohmann::detail::serializer<basic_json>;
template<typename BasicJsonType>
friend class ::nlohmann::detail::iter_impl;
template<typename BasicJsonType, typename CharType, typename OutputSinkType>
template<typename BasicJsonType, typename CharType>
friend class ::nlohmann::detail::binary_writer;
template<typename BasicJsonType, typename InputType, typename SAX>
friend class ::nlohmann::detail::binary_reader;
@@ -4327,9 +4327,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static std::vector<std::uint8_t> to_cbor(const basic_json& j)
{
std::vector<std::uint8_t> result;
result.reserve(detail::binary_reserve_hint(j));
detail::binary_writer<basic_json, std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_cbor(j);
to_cbor(j, result);
return result;
}
@@ -4352,9 +4350,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static std::vector<std::uint8_t> to_msgpack(const basic_json& j)
{
std::vector<std::uint8_t> result;
result.reserve(detail::binary_reserve_hint(j));
detail::binary_writer<basic_json, std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_msgpack(j);
to_msgpack(j, result);
return result;
}
@@ -4379,9 +4375,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bool use_type = false)
{
std::vector<std::uint8_t> result;
result.reserve(detail::binary_reserve_hint(j));
detail::binary_writer<basic_json, std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_ubjson(j, use_size, use_type);
to_ubjson(j, result, use_size, use_type);
return result;
}
@@ -4409,9 +4403,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
const bjdata_version_t version = bjdata_version_t::draft2)
{
std::vector<std::uint8_t> result;
result.reserve(detail::binary_reserve_hint(j));
detail::binary_writer<basic_json, std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_ubjson(j, use_size, use_type, true, true, version);
to_bjdata(j, result, use_size, use_type, version);
return result;
}
@@ -4438,9 +4430,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static std::vector<std::uint8_t> to_bson(const basic_json& j)
{
std::vector<std::uint8_t> result;
result.reserve(detail::binary_reserve_hint(j));
detail::binary_writer<basic_json, std::uint8_t, detail::output_vector_sink<std::uint8_t>>(
detail::output_vector_sink<std::uint8_t>(result)).write_bson(j);
to_bson(j, result);
return result;
}
File diff suppressed because it is too large Load Diff