mirror of
https://github.com/nlohmann/json.git
synced 2026-09-07 16:57:59 +00:00
get_ubjson_array() and get_ubjson_object() read their elements by calling back into the value reader, which called them again for a nested container, so the native call stack grew with the nesting depth of the input. '[' alone opens a container, so half a million of them crashes the process before the input runs out (#5104). The optimized forms reach the same path through a size or type annotation, and in plain UBJSON '[' and '{' are permitted as the type of an optimized container, so "[$[#i\x01" repeated nests just as deeply at six bytes a level. Both readers now only open their container, and parse_ubjson_internal() loops: it closes the containers that have ended, claims the next element of the innermost one, reads its key when it is an object, and works out the marker of the value to read next. That last part is where the formats differ, and the loop follows what the four element loops used to do: - a sized, typed container gives its elements no marker of their own - a sized, untyped container reads one for each element - a container that ends at a marker has the byte already, from the test against ']' or '}'; for an object it is the first byte of the key The ND-array wrapper and the 'B' binary shortcut stay as they are. Both read a complete value rather than opening a container, and their elements are always scalars: BJData does not permit '[' or '{' as an optimized type, which is also why only plain UBJSON needed the type-marker case above. A container of no-ops keeps its behaviour of holding no elements while still announcing its declared size to the SAX parser, by opening it and then setting its count to zero. unit-ubjson and unit-bjdata pass unchanged, 1.39 million assertions between them, and a behaviour comparison against the previous commit over every container form -- sized, unsized, typed, untyped, empty, no-op, ND-array, binary, and the forms nested inside one another -- gives identical values, error codes, messages and byte offsets. 500,000 levels of each vector now report a parse error instead of crashing, and a well-formed 100,000-level value is read to completion. The driver costs about 3 % on parsing 60,000 small objects and one array of a million integers, for the reason given in the previous commit; reading the frame once per element rather than per branch halved what it cost before. Signed-off-by: Niels Lohmann <mail@nlohmann.me>