This commit is contained in:
nlohmann committed 2026-11-15 18:44:48 +00:00
1 parent 9f62284716
commit 70feeb496c
447 files changed
+1643 -876

No files matched your search

@@ -0,0 +1,85 @@
<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="JSON for Modern C++ is a header-only C++11 library that makes JSON a first-class data type."><meta name=author content="Niels Lohmann"><link href="https://json.nlohmann.me/api/macros/json_ubjson_valueless_elements_per_byte/" rel="canonical"><link href=../json_strict_nul_handling/ rel=prev><link href=../json_use_global_udls/ rel=next><link rel=icon href=../../../assets/images/favicon.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.8.0"><title>JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE - JSON for Modern C++</title><link rel=stylesheet href=../../../assets/stylesheets/main.57a0d79d.min.css><link rel=stylesheet href=../../../assets/stylesheets/palette.fb2fffe2.min.css><link rel=stylesheet href=../../../css/custom.css><link rel=stylesheet href=../../../css/brand.css><link rel=stylesheet href=../../../css/fig-sax.css><link rel=stylesheet href=../../../css/fig-jsonview.css><link rel=stylesheet href=../../../css/fig-exceptions.css><link rel=stylesheet href=../../../css/fig-pointer.css><link rel=stylesheet href=../../../css/fig-charts.css><link rel=stylesheet href=../../../css/fig-integration.css><link rel=stylesheet href=../../../css/fig-macros.css><script>__md_scope=new URL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#json_ubjson_valueless_elements_per_byte class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../../.. title="JSON for Modern C++" class="md-header__button md-logo" aria-label="JSON for Modern C++" data-md-component=logo> <img src=../../../images/wordmark-header.svg alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> JSON for Modern C++ </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE </span> </div> </div> </div> <form class=md-header__option data-md-component=palette> <input class=md-option data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme=default data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to dark mode" type=radio name=__palette id=__palette_0> <label class="md-header__button md-icon" title="Switch to dark mode" for=__palette_1 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 8a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4m0 10a6 6 0 0 1-6-6 6 6 0 0 1 6-6 6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> <input class=md-option data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme=slate data-md-color-primary=custom data-md-color-accent=custom aria-label="Switch to light mode" type=radio name=__palette id=__palette_1> <label class="md-header__button md-icon" title="Switch to light mode" for=__palette_0 hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M12 18c-.89 0-1.74-.2-2.5-.55C11.56 16.5 13 14.42 13 12s-1.44-4.5-3.5-5.45C10.26 6.2 11.11 6 12 6a6 6 0 0 1 6 6 6 6 0 0 1-6 6m8-9.31V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12z"/></svg> </label> </form> <script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script> <label cLine truncated
</code></pre></div> <p><a href=../../basic_json/from_ubjson/ ><code>from_ubjson</code></a> and <a href=../../basic_json/sax_parse/ ><code>sax_parse</code></a> refuse an input in which the optimized arrays of type <code>Z</code> (null), <code>T</code> (true), and <code>F</code> (false) together have more than 1,048,576 elements plus <code>JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE</code> for every byte read so far, with <a href=../../../home/exceptions/#jsonexceptionout_of_range408><code>out_of_range.408</code></a>. The macro sets that number. It is the elements per byte that the input is allowed to announce without paying for them: an element of such an array is a single marker that is implied by the array's <code>$Z#n</code> header, so a few bytes can announce a million values (see <a href=../../../features/parsing/untrusted_input/ >Parsing Untrusted Input</a>).</p> <p>The macro only matters for input that was not written by this library 3.13.0 or later: <a href=../../basic_json/to_ubjson/ ><code>to_ubjson</code></a> never writes more than the base of 1,048,576 elements in typed arrays of these types, and a reader accepts at least that many whatever the macro is. Its main use is reading files written by version 3.12.0 and earlier, which did not limit them (see <a href=#reading-files-written-by-version-3120-and-earlier>below</a>).</p> <h2 id=default-definition>Default definition<a class=headerlink href=#default-definition title="Permanent link">&para;</a></h2> <p>The default value is <code>4</code>.</p> <div class=highlight><pre><span></span><code><span class=cp>#define JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE 4</span>
</code></pre></div> <h2 id=notes>Notes<a class=headerlink href=#notes title="Permanent link">&para;</a></h2> <div class="admonition info"> <p class=admonition-title>Allowed values</p> <p>The value must be a <strong>positive integer</strong> (a value of <code>0</code> would not allow any element beyond the base, and a negative or fractional rate has no meaning). Any other value is a compile-time error with a message that names the macro:</p> <div class=highlight><pre><span></span><code>error: static assertion failed: JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE must be a positive integer
error: static assertion failed: JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE must be an integer (not bool or a character type)
error: static assertion failed: JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE must not be wider than std::uintmax_t
</code></pre></div> <p>The first is reported for <code>0</code> and negative values, the second for values such as <code>4.5</code>, <code>1e3</code>, <code>true</code>, or a character of any character type (<code>'a'</code>, <code>L'a'</code>, <code>u8'a'</code>, ...), and the third for an integer type that is wider than <code class=highlight><span class=n>std</span><span class=o>::</span><span class=kt>uintmax_t</span></code> (for example <code>unsigned __int128</code>). A value that is larger than the largest <code class=highlight><span class=n>std</span><span class=o>::</span><span class=kt>size_t</span></code> is treated as that largest value, so that a project can use one spelling, for example <code>0xFFFFFFFFFFFFFFFFu</code>, for 32-bit and 64-bit targets.</p> </div> <div class="admonition info"> <p class=admonition-title>Large values: the limit is switched off</p> <p>The limit is the base plus the rate times the bytes read. It saturates at the largest <code class=highlight><span class=n>std</span><span class=o>::</span><span class=kt>size_t</span></code> instead of overflowing, for any rate and any input size (also with a 32-bit <code class=highlight><span class=n>std</span><span class=o>::</span><span class=kt>size_t</span></code>), so a large value never makes the limit smaller. A rate of <strong>at least 131,072</strong> (<code>1048576 / 8</code>) effectively switches the limit for the whole input off: the shortest header of an array of more than 32,767 elements takes 8 bytes (<code>$Z#l</code> and four bytes of the count, inside an optimized array of arrays; 9 bytes, <code>[$Z#l</code>, at the top level), and a shorter header (<code>$Z#I</code> and two bytes) can announce at most 32,767. No array can then announce more than the rate times the bytes of its own header. With a rate of 131,071 that is not so: rows of 1,048,576 elements in 8 bytes each gain 8 elements per row, and a table of 278,527 such rows (2.2 MB) is the shortest input that is refused.</p> </div> <div class="admonition info"> <p class=admonition-title>What the macro does not change</p> <ul> <li>The limit of <strong>one array</strong> stays at 1,048,576 elements (<a href=../../../home/exceptions/#jsonexceptionout_of_range408><code>out_of_range.408</code></a>, "excessive array size"), whatever the macro is. A value that switches the whole-input limit off does not allow a single array of 1,048,577 elements.</li> <li>The <strong>base</strong> of 1,048,576 elements is a fixed constant, the same as the limit of one array. An array that was accepted on its own before the whole-input limit existed is therefore still accepted at the start of an input.</li> <li>The <strong>writer</strong> is not affected. <a href=../../basic_json/to_ubjson/ ><code>to_ubjson</code></a> follows the base only: it writes a typed array of <code>Z</code>, <code>T</code>, or <code>F</code> only while the arrays written so far and this one stay within 1,048,576 elements, and writes the elements one by one otherwise. That is within the limit of every reader, because the limit is never below the base.</li> <li><strong>Objects</strong> and arrays of other types are not counted: every member of an object has a key, and every element of another type takes at least a byte of input.</li> <li><a href=../../basic_json/from_bjdata/ ><code>from_bjdata</code></a> is not affected: <abbr title="Binary JData">BJData</abbr> has no optimized arrays of these types.</li> </ul> </div> <div class="admonition info"> <p class=admonition-title>Memory</p> <p>A value takes 16 bytes in a <abbr title="Document Object Model">DOM</abbr>, so every element that the limit lets pass costs memory. Beyond the first 1,048,576 elements (about 33 MB: 16 bytes per element, and a vector that grows by reallocation needs the old and the new storage at once), a malicious input of <code>n</code> bytes can take <strong>about <code>20 * K * n</code> bytes</strong> of memory in a <abbr title="Document Object Model">DOM</abbr>, where <code>K</code> is the value of the macro. The measurements below read inputs that are at the limit, with containers of <code>9 * K</code> elements (<code>[$Z#l</code> and a count, 9 bytes each), with <code>from_ubjson</code> on macOS, arm64, compiled with <code>-O2</code>. The numbers on other systems differ by the allocator; the 16 bytes per element are the lower bound.</p> <table> <thead> <tr> <th style="text-align: right;"><code>K</code></th> <th style="text-align: right;">bytes of memory per byte of input</th> <th style="text-align: right;">memory for 1 MB of input</th> </tr> </thead> <tbody> <tr> <td style="text-align: right;">1</td> <td style="text-align: right;">25</td> <td style="text-align: right;">25 MB</td> </tr> <tr> <td style="text-align: right;">4</td> <td style="text-align: right;">81</td> <td style="text-align: right;">81 MB</td> </tr> <tr> <td style="text-align: right;">16</td> <td style="text-align: right;">312</td> <td style="text-align: right;">312 MB</td> </tr>Line truncated
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;iostream&gt;</span>
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;vector&gt;</span>
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;nlohmann/json.hpp&gt;</span>
<span class=k>using</span><span class=w> </span><span class=n>json</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json</span><span class=p>;</span>
<span class=c1>// the bytes version 3.12.0 wrote for a table of 1100 rows of 1000 null values</span>
<span class=c1>// with to_ubjson(table, true, true): `[$[#I` and the number of rows, then</span>
<span class=c1>// `$Z#I` and the number of elements for every row</span>
<span class=n>std</span><span class=o>::</span><span class=n>vector</span><span class=o>&lt;</span><span class=n>std</span><span class=o>::</span><span class=kt>uint8_t</span><span class=o>&gt;</span><span class=w> </span><span class=n>table_written_by_3_12_0</span><span class=p>()</span>
<span class=p>{</span>
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>vector</span><span class=o>&lt;</span><span class=n>std</span><span class=o>::</span><span class=kt>uint8_t</span><span class=o>&gt;</span><span class=w> </span><span class=n>bytes</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=p>{</span><span class=sc>'['</span><span class=p>,</span><span class=w> </span><span class=sc>'$'</span><span class=p>,</span><span class=w> </span><span class=sc>'['</span><span class=p>,</span><span class=w> </span><span class=sc>'#'</span><span class=p>,</span><span class=w> </span><span class=sc>'I'</span><span class=p>,</span><span class=w> </span><span class=mi>1100</span><span class=w> </span><span class=o>&gt;&gt;</span><span class=w> </span><span class=mi>8</span><span class=p>,</span><span class=w> </span><span class=mi>1100</span><span class=w> </span><span class=o>&amp;</span><span class=w> </span><span class=mh>0xFF</span><span class=p>};</span>
<span class=w> </span><span class=k>for</span><span class=w> </span><span class=p>(</span><span class=kt>int</span><span class=w> </span><span class=n>row</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=mi>0</span><span class=p>;</span><span class=w> </span><span class=n>row</span><span class=w> </span><span class=o>&lt;</span><span class=w> </span><span class=mi>1100</span><span class=p>;</span><span class=w> </span><span class=o>++</span><span class=n>row</span><span class=p>)</span>
<span class=w> </span><span class=p>{</span>
<span class=w> </span><span class=n>bytes</span><span class=p>.</span><span class=n>insert</span><span class=p>(</span><span class=n>bytes</span><span class=p>.</span><span class=n>end</span><span class=p>(),</span><span class=w> </span><span class=p>{</span><span class=sc>'$'</span><span class=p>,</span><span class=w> </span><span class=sc>'Z'</span><span class=p>,</span><span class=w> </span><span class=sc>'#'</span><span class=p>,</span><span class=w> </span><span class=sc>'I'</span><span class=p>,</span><span class=w> </span><span class=mi>1000</span><span class=w> </span><span class=o>&gt;&gt;</span><span class=w> </span><span class=mi>8</span><span class=p>,</span><span class=w> </span><span class=mi>1000</span><span class=w> </span><span class=o>&amp;</span><span class=w> </span><span class=mh>0xFF</span><span class=p>});</span>
<span class=w> </span><span class=p>}</span>
<span class=w> </span><span class=k>return</span><span class=w> </span><span class=n>bytes</span><span class=p>;</span>
<span class=p>}</span>
<span class=kt>int</span><span class=w> </span><span class=n>main</span><span class=p>()</span>
<span class=p>{</span>
<span class=w> </span><span class=k>try</span>
<span class=w> </span><span class=p>{</span>
<span class=w> </span><span class=c1>// 6607 bytes announce 1,100,000 values, more than 1,048,576 plus 4 for</span>
<span class=w> </span><span class=c1>// every byte read (the limit is exceeded in row 1075)</span>
<span class=w> </span><span class=n>json</span><span class=w> </span><span class=n>table</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json</span><span class=o>::</span><span class=n>from_ubjson</span><span class=p>(</span><span class=n>table_written_by_3_12_0</span><span class=p>());</span>
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o>&lt;&lt;</span><span class=w> </span><span class=n>table</span><span class=p>.</span><span class=n>size</span><span class=p>()</span><span class=w> </span><span class=o>&lt;&lt;</span><span class=w> </span><span class=s>" rows</span><span class=se>\n</span><span class=s>"</span><span class=p>;</span>
<span class=w> </span><span class=p>}</span>
<span class=w> </span><span class=k>catch</span><span class=w> </span><span class=p>(</span><span class=k>const</span><span class=w> </span><span class=n>json</span><span class=o>::</span><span class=n>out_of_range</span><span class=o>&amp;</span><span class=w> </span><span class=n>e</span><span class=p>)</span>
<span class=w> </span><span class=p>{</span>
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o>&lt;&lt;</span><span class=w> </span><span class=n>e</span><span class=p>.</span><span class=n>what</span><span class=p>()</span><span class=w> </span><span class=o>&lt;&lt;</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
<span class=w> </span><span class=p>}</span>
<span class=p>}</span>
</code></pre></div> <p>Output:</p> <div class=highlight><pre><span></span><code>[json.exception.out_of_range.408] syntax error while parsing UBJSON size: excessive number of elements without payload
</code></pre></div> </details> <details class=example> <summary>Example: raised limit</summary> <p>With 16 elements per byte the same table is read. The limit of one array still applies.</p> <div class=highlight><pre><span></span><code><span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;cstdint&gt;</span>
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;iostream&gt;</span>
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;vector&gt;</span>
<span class=c1>// accept 16 values per byte of input instead of 4; define the macro before the</span>
<span class=c1>// first inclusion of the header, and to the same value in every translation unit</span>
<span class=cp>#define JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE 16</span>
<span class=cp>#include</span><span class=w> </span><span class=cpf>&lt;nlohmann/json.hpp&gt;</span>
<span class=k>using</span><span class=w> </span><span class=n>json</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>nlohmann</span><span class=o>::</span><span class=n>json</span><span class=p>;</span>
<span class=c1>// the bytes version 3.12.0 wrote for a table of 1100 rows of 1000 null values</span>
<span class=c1>// with to_ubjson(table, true, true): `[$[#I` and the number of rows, then</span>
<span class=c1>// `$Z#I` and the number of elements for every row</span>
<span class=n>std</span><span class=o>::</span><span class=n>vector</span><span class=o>&lt;</span><span class=n>std</span><span class=o>::</span><span class=kt>uint8_t</span><span class=o>&gt;</span><span class=w> </span><span class=n>table_written_by_3_12_0</span><span class=p>()</span>
<span class=p>{</span>
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>vector</span><span class=o>&lt;</span><span class=n>std</span><span class=o>::</span><span class=kt>uint8_t</span><span class=o>&gt;</span><span class=w> </span><span class=n>bytes</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=p>{</span><span class=sc>'['</span><span class=p>,</span><span class=w> </span><span class=sc>'$'</span><span class=p>,</span><span class=w> </span><span class=sc>'['</span><span class=p>,</span><span class=w> </span><span class=sc>'#'</span><span class=p>,</span><span class=w> </span><span class=sc>'I'</span><span class=p>,</span><span class=w> </span><span class=mi>1100</span><span class=w> </span><span class=o>&gt;&gt;</span><span class=w> </span><span class=mi>8</span><span class=p>,</span><span class=w> </span><span class=mi>1100</span><span class=w> </span><span class=o>&amp;</span><span class=w> </span><span class=mh>0xFF</span><span class=p>};</span>
<span class=w> </span><span class=k>for</span><span class=w> </span><span class=p>(</span><span class=kt>int</span><span class=w> </span><span class=n>row</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=mi>0</span><span class=p>;</span><span class=w> </span><span class=n>row</span><span class=w> </span><span class=o>&lt;</span><span class=w> </span><span class=mi>1100</span><span class=p>;</span><span class=w> </span><span class=o>++</span><span class=n>row</span><span class=p>)</span>
<span class=w> </span><span class=p>{</span>
<span class=w> </span><span class=n>bytes</span><span class=p>.</span><span class=n>insert</span><span class=p>(</span><span class=n>bytes</span><span class=p>.</span><span class=n>end</span><span class=p>(),</span><span class=w> </span><span class=p>{</span><span class=sc>'$'</span><span class=p>,</span><span class=w> </span><span class=sc>'Z'</span><span class=p>,</span><span class=w> </span><span class=sc>'#'</span><span class=p>,</span><span class=w> </span><span class=sc>'I'</span><span class=p>,</span><span class=w> </span><span class=mi>1000</span><span class=w> </span><span class=o>&gt;&gt;</span><span class=w> </span><span class=mi>8</span><span class=p>,</span><span class=w> </span><span class=mi>1000</span><span class=w> </span><span class=o>&amp;</span><span class=w> </span><span class=mh>0xFF</span><span class=p>});</span>
<span class=w> </span><span class=p>}</span>
<span class=w> </span><span class=k>return</span><span class=w> </span><span class=n>bytes</span><span class=p>;</span>
<span class=p>}</span>
<span class=kt>int</span><span class=w> </span><span class=n>main</span><span class=p>()</span>
<span class=p>{</span>
<span class=w> </span><span class=c1>// 6607 bytes announce 1,100,000 values, which is below 1,048,576 plus 16</span>
<span class=w> </span><span class=c1>// for every byte read</span>
<span class=w> </span><span class=n>json</span><span class=w> </span><span class=n>table</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json</span><span class=o>::</span><span class=n>from_ubjson</span><span class=p>(</span><span class=n>table_written_by_3_12_0</span><span class=p>());</span>
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o>&lt;&lt;</span><span class=w> </span><span class=n>table</span><span class=p>.</span><span class=n>size</span><span class=p>()</span><span class=w> </span><span class=o>&lt;&lt;</span><span class=w> </span><span class=s>" rows of "</span><span class=w> </span><span class=o>&lt;&lt;</span><span class=w> </span><span class=n>table</span><span class=p>[</span><span class=mi>0</span><span class=p>].</span><span class=n>size</span><span class=p>()</span><span class=w> </span><span class=o>&lt;&lt;</span><span class=w> </span><span class=s>" values</span><span class=se>\n</span><span class=s>"</span><span class=p>;</span>
<span class=w> </span><span class=c1>// a single container is still limited to 1,048,576 elements</span>
<span class=w> </span><span class=k>const</span><span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>vector</span><span class=o>&lt;</span><span class=n>std</span><span class=o>::</span><span class=kt>uint8_t</span><span class=o>&gt;</span><span class=w> </span><span class=n>too_long</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=p>{</span><span class=sc>'['</span><span class=p>,</span><span class=w> </span><span class=sc>'$'</span><span class=p>,</span><span class=w> </span><span class=sc>'Z'</span><span class=p>,</span><span class=w> </span><span class=sc>'#'</span><span class=p>,</span><span class=w> </span><span class=sc>'l'</span><span class=p>,</span><span class=w> </span><span class=mh>0x00</span><span class=p>,</span><span class=w> </span><span class=mh>0x10</span><span class=p>,</span><span class=w> </span><span class=mh>0x00</span><span class=p>,</span><span class=w> </span><span class=mh>0x01</span><span class=p>};</span>
<span class=w> </span><span class=k>try</span>
<span class=w> </span><span class=p>{</span>
<span class=w> </span><span class=n>json</span><span class=w> </span><span class=n>j</span><span class=w> </span><span class=o>=</span><span class=w> </span><span class=n>json</span><span class=o>::</span><span class=n>from_ubjson</span><span class=p>(</span><span class=n>too_long</span><span class=p>);</span>
<span class=w> </span><span class=p>}</span>
<span class=w> </span><span class=k>catch</span><span class=w> </span><span class=p>(</span><span class=k>const</span><span class=w> </span><span class=n>json</span><span class=o>::</span><span class=n>out_of_range</span><span class=o>&amp;</span><span class=w> </span><span class=n>e</span><span class=p>)</span>
<span class=w> </span><span class=p>{</span>
<span class=w> </span><span class=n>std</span><span class=o>::</span><span class=n>cout</span><span class=w> </span><span class=o>&lt;&lt;</span><span class=w> </span><span class=n>e</span><span class=p>.</span><span class=n>what</span><span class=p>()</span><span class=w> </span><span class=o>&lt;&lt;</span><span class=w> </span><span class=sc>'\n'</span><span class=p>;</span>
<span class=w> </span><span class=p>}</span>
<span class=p>}</span>
</code></pre></div> <p>Output:</p> <div class=highlight><pre><span></span><code>1100 rows of 1000 values
[json.exception.out_of_range.408] syntax error while parsing UBJSON size: excessive array size
</code></pre></div> </details> <h2 id=see-also>See also<a class=headerlink href=#see-also title="Permanent link">&para;</a></h2> <ul> <li><a href=../../basic_json/from_ubjson/ ><code>from_ubjson</code></a> - create a <abbr title="JavaScript Object Notation">JSON</abbr> value from a <abbr title="Universal Binary JSON">UBJSON</abbr> serialization</li> <li><a href=../../basic_json/to_ubjson/ ><code>to_ubjson</code></a> - create a <abbr title="Universal Binary JSON">UBJSON</abbr> serialization of a <abbr title="JavaScript Object Notation">JSON</abbr> value</li> <li><a href=../../basic_json/sax_parse/ ><code>sax_parse</code></a> - generate <abbr title="Simple API for XML">SAX</abbr> events</li> <li><a href=../../../features/binary_formats/ubjson/ ><abbr title="Universal Binary JSON">UBJSON</abbr></a> - the format and its limits in the library</li> <li><a href=../../../features/parsing/untrusted_input/ >Parsing Untrusted Input</a> - limits and checks for input you do not control</li> <li><a href=../../../home/exceptions/#jsonexceptionout_of_range408><code>out_of_range.408</code></a> - the exception that reports the limit</li> </ul> <h2 id=version-history>Version history<a class=headerlink href=#version-history title="Permanent link">&para;</a></h2> <ul> <li>Added in version 3.13.0 <span class=unreleased-version title="Not part of a release yet; the latest release is 3.12.0.">unreleased</span>.</li> </ul> <!-- https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary --> <aside class=md-source-file> <span class=md-source-file__fact> <span class=md-icon title="Last update"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M21 13.1c-.1 0-.3.1-.4.2l-1 1 2.1 2.1 1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8-6.1 6V23h2.1l6.1-6.1zM12.5 7v5.2l4 2.4-1 1L11 13V7zM11 21.9c-5.1-.5-9-4.8-9-9.9C2 6.5 6.5 2 12 2c5.3 0 9.6 4.1 10 9.3-.3-.1-.6-.2-1-.2s-.7.1-1 .2C19.6 7.2 16.2 4 12 4c-4.4 0-8 3.6-8 8 0 4.1 3.1 7.5 7.1 7.9l-.1.2z"/></svg> </span> <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date" title="November 15, 2026 16:02:05 UTC">November 15, 2026</span> </span> </aside> </article> </div> <script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> <button type=button class="md-top md-icon" data-md-component=top hidden> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg> Back to top </button> </main> <footer class=md-footer> <nav class="md-footer__inner md-grid" aria-label=Footer> <a href=../json_strict_nul_handling/ class="md-footer__link md-footer__link--prev" aria-label="Previous: JSON_STRICT_NUL_HANDLING"> <div class="md-footer__button md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg> </div> <div class=md-footer__title> <span class=md-footer__direction> Previous </span> <div class=md-ellipsis> JSON_STRICT_NUL_HANDLING </div> </div> </a> <a href=../json_use_global_udls/ class="md-footer__link md-footer__link--next" aria-label="Next: JSON_USE_GLOBAL_UDLS"> <div class=md-footer__title> <span class=md-footer__direction> Next </span> <div class=md-ellipsis> JSON_USE_GLOBAL_UDLS </div> </div> <div class="md-footer__button md-icon"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg> </div> </a> </nav> <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class=md-copyright> <div class=md-copyright__highlight> Copyright &copy; 2013-2026 Niels Lohmann </div> </div> <div class=md-social> <a href="https://github.com/nlohmann" target="_blank" rel="noopener" title="github.com" class="md-social__link"> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1Line truncated
@@ -0,0 +1,191 @@
# JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE
```
#define JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE /* value */
```
[`from_ubjson`](https://json.nlohmann.me/api/basic_json/from_ubjson/index.md) and [`sax_parse`](https://json.nlohmann.me/api/basic_json/sax_parse/index.md) refuse an input in which the optimized arrays of type `Z` (null), `T` (true), and `F` (false) together have more than 1,048,576 elements plus `JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE` for every byte read so far, with [`out_of_range.408`](https://json.nlohmann.me/home/exceptions/#jsonexceptionout_of_range408). The macro sets that number. It is the elements per byte that the input is allowed to announce without paying for them: an element of such an array is a single marker that is implied by the array's `$Z#n` header, so a few bytes can announce a million values (see [Parsing Untrusted Input](https://json.nlohmann.me/features/parsing/untrusted_input/index.md)).
The macro only matters for input that was not written by this library 3.13.0 or later: [`to_ubjson`](https://json.nlohmann.me/api/basic_json/to_ubjson/index.md) never writes more than the base of 1,048,576 elements in typed arrays of these types, and a reader accepts at least that many whatever the macro is. Its main use is reading files written by version 3.12.0 and earlier, which did not limit them (see [below](#reading-files-written-by-version-3120-and-earlier)).
## Default definition
The default value is `4`.
```
#define JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE 4
```
## Notes
Allowed values
The value must be a **positive integer** (a value of `0` would not allow any element beyond the base, and a negative or fractional rate has no meaning). Any other value is a compile-time error with a message that names the macro:
```
error: static assertion failed: JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE must be a positive integer
error: static assertion failed: JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE must be an integer (not bool or a character type)
error: static assertion failed: JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE must not be wider than std::uintmax_t
```
The first is reported for `0` and negative values, the second for values such as `4.5`, `1e3`, `true`, or a character of any character type (`'a'`, `L'a'`, `u8'a'`, ...), and the third for an integer type that is wider than `std::uintmax_t` (for example `unsigned __int128`). A value that is larger than the largest `std::size_t` is treated as that largest value, so that a project can use one spelling, for example `0xFFFFFFFFFFFFFFFFu`, for 32-bit and 64-bit targets.
Large values: the limit is switched off
The limit is the base plus the rate times the bytes read. It saturates at the largest `std::size_t` instead of overflowing, for any rate and any input size (also with a 32-bit `std::size_t`), so a large value never makes the limit smaller. A rate of **at least 131,072** (`1048576 / 8`) effectively switches the limit for the whole input off: the shortest header of an array of more than 32,767 elements takes 8 bytes (`$Z#l` and four bytes of the count, inside an optimized array of arrays; 9 bytes, `[$Z#l`, at the top level), and a shorter header (`$Z#I` and two bytes) can announce at most 32,767. No array can then announce more than the rate times the bytes of its own header. With a rate of 131,071 that is not so: rows of 1,048,576 elements in 8 bytes each gain 8 elements per row, and a table of 278,527 such rows (2.2 MB) is the shortest input that is refused.
What the macro does not change
- The limit of **one array** stays at 1,048,576 elements ([`out_of_range.408`](https://json.nlohmann.me/home/exceptions/#jsonexceptionout_of_range408), "excessive array size"), whatever the macro is. A value that switches the whole-input limit off does not allow a single array of 1,048,577 elements.
- The **base** of 1,048,576 elements is a fixed constant, the same as the limit of one array. An array that was accepted on its own before the whole-input limit existed is therefore still accepted at the start of an input.
- The **writer** is not affected. [`to_ubjson`](https://json.nlohmann.me/api/basic_json/to_ubjson/index.md) follows the base only: it writes a typed array of `Z`, `T`, or `F` only while the arrays written so far and this one stay within 1,048,576 elements, and writes the elements one by one otherwise. That is within the limit of every reader, because the limit is never below the base.
- **Objects** and arrays of other types are not counted: every member of an object has a key, and every element of another type takes at least a byte of input.
- [`from_bjdata`](https://json.nlohmann.me/api/basic_json/from_bjdata/index.md) is not affected: BJData has no optimized arrays of these types.
Memory
A value takes 16 bytes in a DOM, so every element that the limit lets pass costs memory. Beyond the first 1,048,576 elements (about 33 MB: 16 bytes per element, and a vector that grows by reallocation needs the old and the new storage at once), a malicious input of `n` bytes can take **about `20 * K * n` bytes** of memory in a DOM, where `K` is the value of the macro. The measurements below read inputs that are at the limit, with containers of `9 * K` elements (`[$Z#l` and a count, 9 bytes each), with `from_ubjson` on macOS, arm64, compiled with `-O2`. The numbers on other systems differ by the allocator; the 16 bytes per element are the lower bound.
| `K` | bytes of memory per byte of input | memory for 1 MB of input |
| --- | --------------------------------- | ------------------------ |
| 1 | 25 | 25 MB |
| 4 | 81 | 81 MB |
| 16 | 312 | 312 MB |
| 64 | 1,226 | 1.2 GB |
| 256 | 5,493 | 5.5 GB |
A SAX handler receives the same number of events, and no memory beyond what it keeps. Choose the smallest value that accepts your files, not a large one, if the input comes from an untrusted source: a value of 131,072 or more switches the limit of the whole input off, so that every 8 bytes of input can announce another 1,048,576 values, the limit of one array.
Define it before the include, in every translation unit
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no effect.
The value is compiled into the member functions of the binary reader, which are inline, but it is **not** encoded in the [namespace](https://json.nlohmann.me/features/namespace/index.md) of the library (there is no ABI tag for it). It must have the same value in every translation unit of a program (for example by defining it on the command line or in a common header); translation units with different values violate the One Definition Rule, which is undefined behavior (no diagnostic is required). In practice, which value a read uses depends on which functions the compiler inlined and which out-of-line copy of each remaining function the linker kept, so a function compiled with one value can call a function compiled with the other, and a single parse can mix both values.
### Reading files written by version 3.12.0 and earlier
Version 3.12.0 wrote an array of `Z`, `T`, or `F` of any length in the optimized form, and a table of equal rows as an optimized array of optimized arrays: a header of `[$[#` and the number of rows (`i` or `U` and 1 byte, `I` and 2 bytes, or `l` and 4 bytes: `h` = 6, 7, or 9 bytes in all for up to 255, 32,767, or more rows), then for every row `$Z#` and its count: 5 bytes for up to 255 elements (`U`), 6 bytes for up to 32,767 (`I`), and 8 bytes above (`l`). Every row announces `N` elements for the `b` bytes of its header, so the limit grows by `K * b` and the number of elements by `N` per row.
- A table is never refused if its rows have at most `K * b` elements each: at most 5 `K` for rows of up to 255 elements (20 at the default of `K = 4`), at most 6 `K` for rows of 256 to 32,767 (rows of 1,000 need `K >= 167`), and at most 8 `K` above.
- Otherwise the first `(1,048,576 + h * K) / (N - K * b)` rows are accepted, rounded down (`h` as above; 7 for the numbers here). For the default, a table of rows of 1,000 `null` values is read up to row 1,074 (the input of the first example below has 1,100); for `K = 1`, up to row 1,054; for `K = 16`, up to row 1,160.
- In general, the smallest value that accepts an input is the largest of `(the elements of the arrays so far - 1,048,576) / (the bytes read when the header of the last of them has been read)` over all arrays, rounded up. For 1,100 rows of 1,000 elements that is `(1,100,000 - 1,048,576) / 6,607 = 7.8`, so `K = 8`.
To read such a file, define the macro, before the include (or with `-DJSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE=8`), to the smallest value that accepts the file, and see the table above for what that value allows an untrusted input to cost. A file can be converted once: read it with the larger value and write it again with `to_ubjson`, which writes no typed array that a reader with the default value would refuse, so that the result does not need the macro.
## Examples
Example: default limit
The table of 1,100 rows of 1,000 `null` values (6,607 bytes) that version 3.12.0 wrote announces 1,100,000 values, which is more than 1,048,576 plus 4 for every byte read.
```
#include <cstdint>
#include <iostream>
#include <vector>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// the bytes version 3.12.0 wrote for a table of 1100 rows of 1000 null values
// with to_ubjson(table, true, true): `[$[#I` and the number of rows, then
// `$Z#I` and the number of elements for every row
std::vector<std::uint8_t> table_written_by_3_12_0()
{
std::vector<std::uint8_t> bytes = {'[', '$', '[', '#', 'I', 1100 >> 8, 1100 & 0xFF};
for (int row = 0; row < 1100; ++row)
{
bytes.insert(bytes.end(), {'$', 'Z', '#', 'I', 1000 >> 8, 1000 & 0xFF});
}
return bytes;
}
int main()
{
try
{
// 6607 bytes announce 1,100,000 values, more than 1,048,576 plus 4 for
// every byte read (the limit is exceeded in row 1075)
json table = json::from_ubjson(table_written_by_3_12_0());
std::cout << table.size() << " rows\n";
}
catch (const json::out_of_range& e)
{
std::cout << e.what() << '\n';
}
}
```
Output:
```
[json.exception.out_of_range.408] syntax error while parsing UBJSON size: excessive number of elements without payload
```
Example: raised limit
With 16 elements per byte the same table is read. The limit of one array still applies.
```
#include <cstdint>
#include <iostream>
#include <vector>
// accept 16 values per byte of input instead of 4; define the macro before the
// first inclusion of the header, and to the same value in every translation unit
#define JSON_UBJSON_VALUELESS_ELEMENTS_PER_BYTE 16
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// the bytes version 3.12.0 wrote for a table of 1100 rows of 1000 null values
// with to_ubjson(table, true, true): `[$[#I` and the number of rows, then
// `$Z#I` and the number of elements for every row
std::vector<std::uint8_t> table_written_by_3_12_0()
{
std::vector<std::uint8_t> bytes = {'[', '$', '[', '#', 'I', 1100 >> 8, 1100 & 0xFF};
for (int row = 0; row < 1100; ++row)
{
bytes.insert(bytes.end(), {'$', 'Z', '#', 'I', 1000 >> 8, 1000 & 0xFF});
}
return bytes;
}
int main()
{
// 6607 bytes announce 1,100,000 values, which is below 1,048,576 plus 16
// for every byte read
json table = json::from_ubjson(table_written_by_3_12_0());
std::cout << table.size() << " rows of " << table[0].size() << " values\n";
// a single container is still limited to 1,048,576 elements
const std::vector<std::uint8_t> too_long = {'[', '$', 'Z', '#', 'l', 0x00, 0x10, 0x00, 0x01};
try
{
json j = json::from_ubjson(too_long);
}
catch (const json::out_of_range& e)
{
std::cout << e.what() << '\n';
}
}
```
Output:
```
1100 rows of 1000 values
[json.exception.out_of_range.408] syntax error while parsing UBJSON size: excessive array size
```
## See also
- [`from_ubjson`](https://json.nlohmann.me/api/basic_json/from_ubjson/index.md) - create a JSON value from a UBJSON serialization
- [`to_ubjson`](https://json.nlohmann.me/api/basic_json/to_ubjson/index.md) - create a UBJSON serialization of a JSON value
- [`sax_parse`](https://json.nlohmann.me/api/basic_json/sax_parse/index.md) - generate SAX events
- [UBJSON](https://json.nlohmann.me/features/binary_formats/ubjson/index.md) - the format and its limits in the library
- [Parsing Untrusted Input](https://json.nlohmann.me/features/parsing/untrusted_input/index.md) - limits and checks for input you do not control
- [`out_of_range.408`](https://json.nlohmann.me/home/exceptions/#jsonexceptionout_of_range408) - the exception that reports the limit
## Version history
- Added in version 3.13.0 unreleased.