275 lines
34 KiB
HTML
275 lines
34 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
|
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
|
|
<meta name="generator" content="Doxygen 1.9.5"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
<title>MPack: Using the Expect API</title>
|
|
<link href="tabs.css" rel="stylesheet" type="text/css"/>
|
|
<script type="text/javascript" src="jquery.js"></script>
|
|
<script type="text/javascript" src="dynsections.js"></script>
|
|
<link href="search/search.css" rel="stylesheet" type="text/css"/>
|
|
<script type="text/javascript" src="search/searchdata.js"></script>
|
|
<script type="text/javascript" src="search/search.js"></script>
|
|
<link href="doxygen.css" rel="stylesheet" type="text/css" />
|
|
<link href="doxygen-mpack-css.css" rel="stylesheet" type="text/css"/>
|
|
</head>
|
|
<body>
|
|
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
|
<div id="titlearea">
|
|
<table cellspacing="0" cellpadding="0">
|
|
<tbody>
|
|
<tr id="projectrow">
|
|
<td id="projectalign">
|
|
<div id="projectname">MPack<span id="projectnumber"> 1.1.1</span>
|
|
</div>
|
|
<div id="projectbrief">A C encoding/decoding library for the MessagePack serialization format.</div>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<!-- end header part -->
|
|
<!-- Generated by Doxygen 1.9.5 -->
|
|
<script type="text/javascript">
|
|
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */
|
|
var searchBox = new SearchBox("searchBox", "search/",'.html');
|
|
/* @license-end */
|
|
</script>
|
|
<script type="text/javascript" src="menudata.js"></script>
|
|
<script type="text/javascript" src="menu.js"></script>
|
|
<script type="text/javascript">
|
|
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */
|
|
$(function() {
|
|
initMenu('',true,false,'search.php','Search');
|
|
$(document).ready(function() { init_search(); });
|
|
});
|
|
/* @license-end */
|
|
</script>
|
|
<div id="main-nav"></div>
|
|
<!-- window showing the filter options -->
|
|
<div id="MSearchSelectWindow"
|
|
onmouseover="return searchBox.OnSearchSelectShow()"
|
|
onmouseout="return searchBox.OnSearchSelectHide()"
|
|
onkeydown="return searchBox.OnSearchSelectKey(event)">
|
|
</div>
|
|
|
|
<!-- iframe showing the search results (closed by default) -->
|
|
<div id="MSearchResultsWindow">
|
|
<div id="MSearchResults">
|
|
<div class="SRPage">
|
|
<div id="SRIndex">
|
|
<div id="SRResults"></div>
|
|
<div class="SRStatus" id="Loading">Loading...</div>
|
|
<div class="SRStatus" id="Searching">Searching...</div>
|
|
<div class="SRStatus" id="NoMatches">No Matches</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- top -->
|
|
<div><div class="header">
|
|
<div class="headertitle"><div class="title">Using the Expect API </div></div>
|
|
</div><!--header-->
|
|
<div class="contents">
|
|
<div class="textblock"><p >The Expect API is used to imperatively parse data of a fixed (hardcoded) schema. It is most useful when parsing very large MessagePack files, parsing in memory-constrained environments, or generating parsing code from a schema. The API is similar to <a href="https://github.com/camgunz/cmp">CMP</a>, but has many helper functions especially for map keys and expected value ranges. Some of these will be covered below.</p>
|
|
<p >Check out the <a class="el" href="md_docs_reader.html">Reader API</a> guide first for information on setting up a reader and reading strings.</p>
|
|
<p >If you are not writing code for an embedded device or generating parsing code from a schema, you should not follow this guide. You should most likely be using the <a class="el" href="md_docs_node.html">Node API</a> instead.</p>
|
|
<h1><a class="anchor" id="autotoc_md15"></a>
|
|
A simple example</h1>
|
|
<p >Suppose we have data that we know will have the following schema:</p>
|
|
<div class="fragment"><div class="line">an array containing three elements</div>
|
|
<div class="line"> a UTF-8 string of at most 127 characters</div>
|
|
<div class="line"> a UTF-8 string of at most 127 characters</div>
|
|
<div class="line"> an array containing up to ten elements</div>
|
|
<div class="line"> where all elements are ints</div>
|
|
</div><!-- fragment --><p >For example, we could have the following bytes in a MessagePack file called <code>example.mp</code>:</p>
|
|
<div class="fragment"><div class="line">93 # an array containing three elements</div>
|
|
<div class="line"> a5 68 65 6c 6c 6f # "hello"</div>
|
|
<div class="line"> a6 77 6f 72 6c 64 21 # "world!"</div>
|
|
<div class="line"> 94 # an array containing four elements</div>
|
|
<div class="line"> 01 # 1</div>
|
|
<div class="line"> 02 # 2</div>
|
|
<div class="line"> 03 # 3</div>
|
|
<div class="line"> 04 # 4</div>
|
|
</div><!-- fragment --><p >In JSON this would look like this:</p>
|
|
<div class="fragment"><div class="line">[</div>
|
|
<div class="line"> "hello",</div>
|
|
<div class="line"> "world!",</div>
|
|
<div class="line"> [</div>
|
|
<div class="line"> 1,</div>
|
|
<div class="line"> 2,</div>
|
|
<div class="line"> 3,</div>
|
|
<div class="line"> 4</div>
|
|
<div class="line"> ]</div>
|
|
<div class="line">]</div>
|
|
</div><!-- fragment --><p >You can use <a href="https://github.com/ludocode/msgpack-tools">msgpack-tools</a> with the above JSON to generate <code>example.mp</code>. The below code demonstrates reading this data from a file using the Expect API:</p>
|
|
<div class="fragment"><div class="line"><span class="preprocessor">#include "mpack.h"</span></div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="keywordtype">int</span> main(<span class="keywordtype">void</span>) {</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> <span class="comment">// Initialize a reader from a file</span></div>
|
|
<div class="line"> <a class="code hl_typedef" href="group__reader.html#gaee791c36a15344b9e57edae160c86615">mpack_reader_t</a> reader;</div>
|
|
<div class="line"> <a class="code hl_function" href="group__reader.html#ga957b5be20debe9f3b81b629478bda0f5">mpack_reader_init_file</a>(&reader, <span class="stringliteral">"example.mp"</span>);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> <span class="comment">// The top-level array must have exactly three elements</span></div>
|
|
<div class="line"> <a class="code hl_function" href="group__expect.html#ga799bb820a49f5a8a70e604466f8ab9b5">mpack_expect_array_match</a>(&reader, 3);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> <span class="comment">// The first two elements are short strings</span></div>
|
|
<div class="line"> <span class="keywordtype">char</span> first[128];</div>
|
|
<div class="line"> <span class="keywordtype">char</span> second[128];</div>
|
|
<div class="line"> <a class="code hl_function" href="group__expect.html#ga62be63032f3c84181eeed4609f0dddb4">mpack_expect_utf8_cstr</a>(&reader, first, <span class="keyword">sizeof</span>(first));</div>
|
|
<div class="line"> <a class="code hl_function" href="group__expect.html#ga62be63032f3c84181eeed4609f0dddb4">mpack_expect_utf8_cstr</a>(&reader, second, <span class="keyword">sizeof</span>(second));</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> <span class="comment">// Next we have an array of up to ten ints</span></div>
|
|
<div class="line"> int32_t numbers[10];</div>
|
|
<div class="line"> <span class="keywordtype">size_t</span> count = <a class="code hl_function" href="group__expect.html#ga3e1599675ca83c5c03af8de220b9a826">mpack_expect_array_max</a>(&reader, <span class="keyword">sizeof</span>(numbers) / <span class="keyword">sizeof</span>(numbers[0]));</div>
|
|
<div class="line"> <span class="keywordflow">for</span> (<span class="keywordtype">size_t</span> i = 0; i < count; ++i)</div>
|
|
<div class="line"> numbers[i] = <a class="code hl_function" href="group__expect.html#gaddca7f73951f581f0ddf03f6755322cf">mpack_expect_i32</a>(&reader);</div>
|
|
<div class="line"> <a class="code hl_function" href="group__reader.html#gaddae460657b9a26207ed34352bff0b06">mpack_done_array</a>(&reader);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> <span class="comment">// Done reading the top-level array</span></div>
|
|
<div class="line"> <a class="code hl_function" href="group__reader.html#gaddae460657b9a26207ed34352bff0b06">mpack_done_array</a>(&reader);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> <span class="comment">// Clean up and handle errors</span></div>
|
|
<div class="line"> <a class="code hl_enumeration" href="group__common.html#ga9d9f282ca4183ab5190e09d04c1f74c4">mpack_error_t</a> error = <a class="code hl_function" href="group__reader.html#gac04666405e21eea6e8819182571f0d20">mpack_reader_destroy</a>(&reader);</div>
|
|
<div class="line"> <span class="keywordflow">if</span> (error != <a class="code hl_enumvalue" href="group__common.html#gga9d9f282ca4183ab5190e09d04c1f74c4a642a07519ef145fc9dd1068230c4a661">mpack_ok</a>) {</div>
|
|
<div class="line"> fprintf(stderr, <span class="stringliteral">"Error %i occurred reading data!\n"</span>, (<span class="keywordtype">int</span>)error);</div>
|
|
<div class="line"> <span class="keywordflow">return</span> EXIT_FAILURE;</div>
|
|
<div class="line"> }</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> <span class="comment">// We now know the data was parsed correctly and can safely</span></div>
|
|
<div class="line"> <span class="comment">// be used. The strings are null-terminated and valid UTF-8,</span></div>
|
|
<div class="line"> <span class="comment">// the array contained at most ten elements, and the numbers</span></div>
|
|
<div class="line"> <span class="comment">// are all within the range of an int32_t.</span></div>
|
|
<div class="line"> printf(<span class="stringliteral">"%s\n"</span>, first);</div>
|
|
<div class="line"> printf(<span class="stringliteral">"%s\n"</span>, second);</div>
|
|
<div class="line"> <span class="keywordflow">for</span> (<span class="keywordtype">size_t</span> i = 0; i < count; ++i)</div>
|
|
<div class="line"> printf(<span class="stringliteral">"%i "</span>, numbers[i]);</div>
|
|
<div class="line"> printf(<span class="stringliteral">"\n"</span>);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> <span class="keywordflow">return</span> EXIT_SUCCESS;</div>
|
|
<div class="line">}</div>
|
|
<div class="ttc" id="agroup__common_html_ga9d9f282ca4183ab5190e09d04c1f74c4"><div class="ttname"><a href="group__common.html#ga9d9f282ca4183ab5190e09d04c1f74c4">mpack_error_t</a></div><div class="ttdeci">mpack_error_t</div><div class="ttdoc">Error states for MPack objects.</div><div class="ttdef"><b>Definition:</b> mpack-common.h:161</div></div>
|
|
<div class="ttc" id="agroup__common_html_gga9d9f282ca4183ab5190e09d04c1f74c4a642a07519ef145fc9dd1068230c4a661"><div class="ttname"><a href="group__common.html#gga9d9f282ca4183ab5190e09d04c1f74c4a642a07519ef145fc9dd1068230c4a661">mpack_ok</a></div><div class="ttdeci">@ mpack_ok</div><div class="ttdoc">No error.</div><div class="ttdef"><b>Definition:</b> mpack-common.h:162</div></div>
|
|
<div class="ttc" id="agroup__expect_html_ga3e1599675ca83c5c03af8de220b9a826"><div class="ttname"><a href="group__expect.html#ga3e1599675ca83c5c03af8de220b9a826">mpack_expect_array_max</a></div><div class="ttdeci">uint32_t mpack_expect_array_max(mpack_reader_t *reader, uint32_t max_count)</div><div class="ttdoc">Reads the start of an array with a number of elements at most max_count, returning its element count.</div><div class="ttdef"><b>Definition:</b> mpack-expect.h:801</div></div>
|
|
<div class="ttc" id="agroup__expect_html_ga62be63032f3c84181eeed4609f0dddb4"><div class="ttname"><a href="group__expect.html#ga62be63032f3c84181eeed4609f0dddb4">mpack_expect_utf8_cstr</a></div><div class="ttdeci">void mpack_expect_utf8_cstr(mpack_reader_t *reader, char *buf, size_t size)</div><div class="ttdoc">Reads a string into the given buffer, ensures it is a valid UTF-8 string without NUL characters,...</div></div>
|
|
<div class="ttc" id="agroup__expect_html_ga799bb820a49f5a8a70e604466f8ab9b5"><div class="ttname"><a href="group__expect.html#ga799bb820a49f5a8a70e604466f8ab9b5">mpack_expect_array_match</a></div><div class="ttdeci">void mpack_expect_array_match(mpack_reader_t *reader, uint32_t count)</div><div class="ttdoc">Reads the start of an array of the exact size given.</div></div>
|
|
<div class="ttc" id="agroup__expect_html_gaddca7f73951f581f0ddf03f6755322cf"><div class="ttname"><a href="group__expect.html#gaddca7f73951f581f0ddf03f6755322cf">mpack_expect_i32</a></div><div class="ttdeci">int32_t mpack_expect_i32(mpack_reader_t *reader)</div><div class="ttdoc">Reads a 32-bit signed integer.</div></div>
|
|
<div class="ttc" id="agroup__reader_html_ga957b5be20debe9f3b81b629478bda0f5"><div class="ttname"><a href="group__reader.html#ga957b5be20debe9f3b81b629478bda0f5">mpack_reader_init_file</a></div><div class="ttdeci">void mpack_reader_init_file(mpack_reader_t *reader, const char *filename)</div><div class="ttdoc">Deprecated.</div><div class="ttdef"><b>Definition:</b> mpack-reader.h:217</div></div>
|
|
<div class="ttc" id="agroup__reader_html_gac04666405e21eea6e8819182571f0d20"><div class="ttname"><a href="group__reader.html#gac04666405e21eea6e8819182571f0d20">mpack_reader_destroy</a></div><div class="ttdeci">mpack_error_t mpack_reader_destroy(mpack_reader_t *reader)</div><div class="ttdoc">Cleans up the MPack reader, ensuring that all compound elements have been completely read.</div></div>
|
|
<div class="ttc" id="agroup__reader_html_gaddae460657b9a26207ed34352bff0b06"><div class="ttname"><a href="group__reader.html#gaddae460657b9a26207ed34352bff0b06">mpack_done_array</a></div><div class="ttdeci">void mpack_done_array(mpack_reader_t *reader)</div><div class="ttdoc">Finishes reading an array.</div><div class="ttdef"><b>Definition:</b> mpack-reader.h:761</div></div>
|
|
<div class="ttc" id="agroup__reader_html_gaee791c36a15344b9e57edae160c86615"><div class="ttname"><a href="group__reader.html#gaee791c36a15344b9e57edae160c86615">mpack_reader_t</a></div><div class="ttdeci">struct mpack_reader_t mpack_reader_t</div><div class="ttdoc">A buffered MessagePack decoder.</div><div class="ttdef"><b>Definition:</b> mpack-reader.h:84</div></div>
|
|
</div><!-- fragment --><p >With the file given above, this example will print:</p>
|
|
<div class="fragment"><div class="line">hello</div>
|
|
<div class="line">world!</div>
|
|
<div class="line">1 2 3 4 </div>
|
|
</div><!-- fragment --><p >Note that there is only a single error check in this example. In fact each call to the reader is checking for errors and storing any error in the reader. These could be errors from reading data from the file, from invalid or corrupt MessagePack, or from not matching our expected types or ranges. On any call to the reader, if the reader was already in error or an error occurs during the call, a safe value is returned.</p>
|
|
<p >For example the <code><a class="el" href="group__expect.html#ga3e1599675ca83c5c03af8de220b9a826" title="Reads the start of an array with a number of elements at most max_count, returning its element count.">mpack_expect_array_max()</a></code> call above will return zero if the element is not an array, if it has more than ten elements, if the MessagePack data is corrupt, or even if the file does not exist. The <code><a class="el" href="group__expect.html#ga62be63032f3c84181eeed4609f0dddb4" title="Reads a string into the given buffer, ensures it is a valid UTF-8 string without NUL characters,...">mpack_expect_utf8_cstr()</a></code> calls will also place a null-terminator at the start of the given buffer if any error occurs just in case the data is used without an error check. The error check can be performed later at a more convenient time.</p>
|
|
<h1><a class="anchor" id="autotoc_md16"></a>
|
|
Maps</h1>
|
|
<p >Maps can be more complicated to read because you usually want to safely handle keys being re-ordered. MessagePack itself does not specify whether maps can be re-ordered, so if you are sticking only to MessagePack implementations that preserve ordering, it may not be strictly necessary to handle this. (MPack always preserves map key ordering.) However many MessagePack implementations will ignore the order of map keys in the original data, especially in scripting languages where the data will be parsed into or encoded from an unordered map or dict. If you plan to interoperate with them, you will need to allow keys to be re-ordered.</p>
|
|
<p >Suppose we expect to receive a map containing two key/value pairs: a key called "compact" with a boolean value, and a key called "schema" with an int value. The example on the <a href="http://msgpack.org/">MessagePack homepage</a> fits this schema, which looks like this in JSON:</p>
|
|
<div class="fragment"><div class="line">{</div>
|
|
<div class="line"> "compact": true,</div>
|
|
<div class="line"> "schema": 0</div>
|
|
<div class="line">}</div>
|
|
</div><!-- fragment --><p >If we also expect the key called "compact" to always come first, then parsing this is straightforward:</p>
|
|
<div class="fragment"><div class="line"><a class="code hl_function" href="group__expect.html#gab66c8c170be1b394b397ccc041d6c133">mpack_expect_map_match</a>(&reader, 2);</div>
|
|
<div class="line"><a class="code hl_function" href="group__expect.html#ga39666f6c01ad79fcd04b94e6d647bcdf">mpack_expect_cstr_match</a>(&reader, <span class="stringliteral">"compact"</span>);</div>
|
|
<div class="line"><span class="keywordtype">bool</span> compact = <a class="code hl_function" href="group__expect.html#gaeafa42ca3ae974494f127eb4b56ed8ae">mpack_expect_bool</a>(&reader);</div>
|
|
<div class="line"><a class="code hl_function" href="group__expect.html#ga39666f6c01ad79fcd04b94e6d647bcdf">mpack_expect_cstr_match</a>(&reader, <span class="stringliteral">"schema"</span>);</div>
|
|
<div class="line"><span class="keywordtype">int</span> schema = <a class="code hl_function" href="group__expect.html#gae81b9d03f80e49501c9b9a695489315f">mpack_expect_int</a>(&reader);</div>
|
|
<div class="line"><a class="code hl_function" href="group__reader.html#ga86165fc780e7adef09f4b45aee54842a">mpack_done_map</a>(&reader);</div>
|
|
<div class="ttc" id="agroup__expect_html_ga39666f6c01ad79fcd04b94e6d647bcdf"><div class="ttname"><a href="group__expect.html#ga39666f6c01ad79fcd04b94e6d647bcdf">mpack_expect_cstr_match</a></div><div class="ttdeci">void mpack_expect_cstr_match(mpack_reader_t *reader, const char *cstr)</div><div class="ttdoc">Reads a string, ensuring it exactly matches the given null-terminated string.</div><div class="ttdef"><b>Definition:</b> mpack-expect.h:1079</div></div>
|
|
<div class="ttc" id="agroup__expect_html_gab66c8c170be1b394b397ccc041d6c133"><div class="ttname"><a href="group__expect.html#gab66c8c170be1b394b397ccc041d6c133">mpack_expect_map_match</a></div><div class="ttdeci">void mpack_expect_map_match(mpack_reader_t *reader, uint32_t count)</div><div class="ttdoc">Reads the start of a map of the exact size given.</div></div>
|
|
<div class="ttc" id="agroup__expect_html_gae81b9d03f80e49501c9b9a695489315f"><div class="ttname"><a href="group__expect.html#gae81b9d03f80e49501c9b9a695489315f">mpack_expect_int</a></div><div class="ttdeci">int mpack_expect_int(mpack_reader_t *reader)</div><div class="ttdoc">Reads a signed int.</div><div class="ttdef"><b>Definition:</b> mpack-expect.h:528</div></div>
|
|
<div class="ttc" id="agroup__expect_html_gaeafa42ca3ae974494f127eb4b56ed8ae"><div class="ttname"><a href="group__expect.html#gaeafa42ca3ae974494f127eb4b56ed8ae">mpack_expect_bool</a></div><div class="ttdeci">bool mpack_expect_bool(mpack_reader_t *reader)</div><div class="ttdoc">Reads a boolean.</div></div>
|
|
<div class="ttc" id="agroup__reader_html_ga86165fc780e7adef09f4b45aee54842a"><div class="ttname"><a href="group__reader.html#ga86165fc780e7adef09f4b45aee54842a">mpack_done_map</a></div><div class="ttdeci">void mpack_done_map(mpack_reader_t *reader)</div><div class="ttdoc">Finishes reading a map.</div><div class="ttdef"><b>Definition:</b> mpack-reader.h:772</div></div>
|
|
</div><!-- fragment --><p >If we expect the "schema" key to be optional, but always after "compact", then parsing this is longer but still straightforward:</p>
|
|
<div class="fragment"><div class="line"><span class="keywordtype">size_t</span> count = <a class="code hl_function" href="group__expect.html#ga36d86c85877f94aa7b493aaa739c8849">mpack_expect_map_max</a>(&reader, 2);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><a class="code hl_function" href="group__expect.html#ga39666f6c01ad79fcd04b94e6d647bcdf">mpack_expect_cstr_match</a>(&reader, <span class="stringliteral">"compact"</span>);</div>
|
|
<div class="line"><span class="keywordtype">bool</span> compact = <a class="code hl_function" href="group__expect.html#gaeafa42ca3ae974494f127eb4b56ed8ae">mpack_expect_bool</a>(&reader);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="keywordtype">bool</span> has_schema = <span class="keyword">false</span>;</div>
|
|
<div class="line"><span class="keywordtype">int</span> schema = -1;</div>
|
|
<div class="line"><span class="keywordflow">if</span> (count == 0) {</div>
|
|
<div class="line"> <a class="code hl_function" href="group__expect.html#ga39666f6c01ad79fcd04b94e6d647bcdf">mpack_expect_cstr_match</a>(&reader, <span class="stringliteral">"schema"</span>);</div>
|
|
<div class="line"> schema = <a class="code hl_function" href="group__expect.html#gae81b9d03f80e49501c9b9a695489315f">mpack_expect_int</a>(&reader);</div>
|
|
<div class="line">}</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><a class="code hl_function" href="group__reader.html#ga86165fc780e7adef09f4b45aee54842a">mpack_done_map</a>(&reader);</div>
|
|
<div class="ttc" id="agroup__expect_html_ga36d86c85877f94aa7b493aaa739c8849"><div class="ttname"><a href="group__expect.html#ga36d86c85877f94aa7b493aaa739c8849">mpack_expect_map_max</a></div><div class="ttdeci">uint32_t mpack_expect_map_max(mpack_reader_t *reader, uint32_t max_count)</div><div class="ttdoc">Reads the start of a map with a number of elements at most max_count, returning its element count.</div><div class="ttdef"><b>Definition:</b> mpack-expect.h:691</div></div>
|
|
</div><!-- fragment --><p >If however we want to allow keys to be re-ordered, then parsing this can become a lot more verbose. You need to switch on the key, but you also need to track whether each key has been used to prevent duplicate keys and ensure that required keys were found. Using the <code><a class="el" href="group__expect.html#ga94489d03628c1fb1a3d0ac6971600fe8" title="Reads a string into the given buffer, ensures it has no null bytes, and adds a null-terminator at the...">mpack_expect_cstr()</a></code> directly for keys, this would look like this:</p>
|
|
<div class="fragment"><div class="line"><span class="keywordtype">bool</span> has_compact = <span class="keyword">false</span>;</div>
|
|
<div class="line"><span class="keywordtype">bool</span> compact = <span class="keyword">false</span>;</div>
|
|
<div class="line"><span class="keywordtype">bool</span> has_schema = <span class="keyword">false</span>;</div>
|
|
<div class="line"><span class="keywordtype">int</span> schema = -1;</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="keywordflow">for</span> (<span class="keywordtype">size_t</span> i = <a class="code hl_function" href="group__expect.html#ga36d86c85877f94aa7b493aaa739c8849">mpack_expect_map_max</a>(&reader, 100); i > 0 && <a class="code hl_function" href="group__reader.html#ga79050efd2a581e8216f58d4946e7abc2">mpack_reader_error</a>(&reader) == <a class="code hl_enumvalue" href="group__common.html#gga9d9f282ca4183ab5190e09d04c1f74c4a642a07519ef145fc9dd1068230c4a661">mpack_ok</a>; --i) {</div>
|
|
<div class="line"> <span class="keywordtype">char</span> key[20];</div>
|
|
<div class="line"> <a class="code hl_function" href="group__expect.html#ga94489d03628c1fb1a3d0ac6971600fe8">mpack_expect_cstr</a>(&reader, key, <span class="keyword">sizeof</span>(key));</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> <span class="keywordflow">if</span> (strcmp(key, <span class="stringliteral">"compact"</span>) == 0) {</div>
|
|
<div class="line"> <span class="keywordflow">if</span> (has_compact)</div>
|
|
<div class="line"> mpack_flag_error(&reader, <a class="code hl_enumvalue" href="group__common.html#gga9d9f282ca4183ab5190e09d04c1f74c4ae53cbed8fcc42915d71ae37d121b22e8">mpack_error_data</a>); <span class="comment">// duplicate key</span></div>
|
|
<div class="line"> has_compact = <span class="keyword">true</span>;</div>
|
|
<div class="line"> compact = <a class="code hl_function" href="group__expect.html#gaeafa42ca3ae974494f127eb4b56ed8ae">mpack_expect_bool</a>(&reader);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> } <span class="keywordflow">else</span> <span class="keywordflow">if</span> (strcmp(key, <span class="stringliteral">"schema"</span>) == 0) {</div>
|
|
<div class="line"> <span class="keywordflow">if</span> (has_schema)</div>
|
|
<div class="line"> mpack_flag_error(&reader, <a class="code hl_enumvalue" href="group__common.html#gga9d9f282ca4183ab5190e09d04c1f74c4ae53cbed8fcc42915d71ae37d121b22e8">mpack_error_data</a>); <span class="comment">// duplicate key</span></div>
|
|
<div class="line"> has_schema = <span class="keyword">true</span>;</div>
|
|
<div class="line"> schema = <a class="code hl_function" href="group__expect.html#gae81b9d03f80e49501c9b9a695489315f">mpack_expect_int</a>(&reader);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> } <span class="keywordflow">else</span> {</div>
|
|
<div class="line"> <a class="code hl_function" href="group__reader.html#ga438bb2b85fbbd06cd8f10d5c8079427e">mpack_discard</a>(&reader);</div>
|
|
<div class="line"> }</div>
|
|
<div class="line"> </div>
|
|
<div class="line">}</div>
|
|
<div class="line"><a class="code hl_function" href="group__reader.html#ga86165fc780e7adef09f4b45aee54842a">mpack_done_map</a>(&reader);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// compact is not optional</span></div>
|
|
<div class="line"><span class="keywordflow">if</span> (!has_compact)</div>
|
|
<div class="line"> <a class="code hl_function" href="group__reader.html#ga5c45c2e0592f16ae671cd509d8d8c512">mpack_reader_flag_error</a>(&reader, <a class="code hl_enumvalue" href="group__common.html#gga9d9f282ca4183ab5190e09d04c1f74c4ae53cbed8fcc42915d71ae37d121b22e8">mpack_error_data</a>);</div>
|
|
<div class="ttc" id="agroup__common_html_gga9d9f282ca4183ab5190e09d04c1f74c4ae53cbed8fcc42915d71ae37d121b22e8"><div class="ttname"><a href="group__common.html#gga9d9f282ca4183ab5190e09d04c1f74c4ae53cbed8fcc42915d71ae37d121b22e8">mpack_error_data</a></div><div class="ttdeci">@ mpack_error_data</div><div class="ttdoc">The contained data is not valid.</div><div class="ttdef"><b>Definition:</b> mpack-common.h:170</div></div>
|
|
<div class="ttc" id="agroup__expect_html_ga94489d03628c1fb1a3d0ac6971600fe8"><div class="ttname"><a href="group__expect.html#ga94489d03628c1fb1a3d0ac6971600fe8">mpack_expect_cstr</a></div><div class="ttdeci">void mpack_expect_cstr(mpack_reader_t *reader, char *buf, size_t size)</div><div class="ttdoc">Reads a string into the given buffer, ensures it has no null bytes, and adds a null-terminator at the...</div></div>
|
|
<div class="ttc" id="agroup__reader_html_ga438bb2b85fbbd06cd8f10d5c8079427e"><div class="ttname"><a href="group__reader.html#ga438bb2b85fbbd06cd8f10d5c8079427e">mpack_discard</a></div><div class="ttdeci">void mpack_discard(mpack_reader_t *reader)</div><div class="ttdoc">Reads and discards the next object.</div></div>
|
|
<div class="ttc" id="agroup__reader_html_ga5c45c2e0592f16ae671cd509d8d8c512"><div class="ttname"><a href="group__reader.html#ga5c45c2e0592f16ae671cd509d8d8c512">mpack_reader_flag_error</a></div><div class="ttdeci">void mpack_reader_flag_error(mpack_reader_t *reader, mpack_error_t error)</div><div class="ttdoc">Places the reader in the given error state, calling the error callback if one is set.</div></div>
|
|
<div class="ttc" id="agroup__reader_html_ga79050efd2a581e8216f58d4946e7abc2"><div class="ttname"><a href="group__reader.html#ga79050efd2a581e8216f58d4946e7abc2">mpack_reader_error</a></div><div class="ttdeci">mpack_error_t mpack_reader_error(mpack_reader_t *reader)</div><div class="ttdoc">Queries the error state of the MPack reader.</div><div class="ttdef"><b>Definition:</b> mpack-reader.h:391</div></div>
|
|
</div><!-- fragment --><p >This is obviously way too verbose. In order to simplify this code, MPack includes an Expect function called <code><a class="el" href="group__expect.html#gada27a479e6ad56faaa14528d1a3dfb26" title="Expects a string map key matching one of the strings in the given key list, marking it as found in th...">mpack_expect_key_cstr()</a></code> to switch on string keys. This function should be passed an array of key strings and an array of bool flags storing whether each key was found. It will find the key in the given string array, check for duplicate keys, and return the index of the found key (or the key count if it is unrecognized or if an error occurs.) You would use it with an <code>enum</code> and a <code>switch</code>, like this:</p>
|
|
<div class="fragment"><div class="line"><span class="keyword">enum</span> key_names {KEY_COMPACT, KEY_SCHEMA, KEY_COUNT};</div>
|
|
<div class="line"><span class="keyword">const</span> <span class="keywordtype">char</span>* keys[] = {<span class="stringliteral">"compact"</span> , <span class="stringliteral">"schema"</span> };</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="keywordtype">bool</span> found[KEY_COUNT] = {0};</div>
|
|
<div class="line"><span class="keywordtype">bool</span> compact = <span class="keyword">false</span>;</div>
|
|
<div class="line"><span class="keywordtype">int</span> schema = -1;</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="keywordtype">size_t</span> i = <a class="code hl_function" href="group__expect.html#ga36d86c85877f94aa7b493aaa739c8849">mpack_expect_map_max</a>(&reader, 100); <span class="comment">// critical check!</span></div>
|
|
<div class="line"><span class="keywordflow">for</span> (; i > 0 && <a class="code hl_function" href="group__reader.html#ga79050efd2a581e8216f58d4946e7abc2">mpack_reader_error</a>(&reader) == <a class="code hl_enumvalue" href="group__common.html#gga9d9f282ca4183ab5190e09d04c1f74c4a642a07519ef145fc9dd1068230c4a661">mpack_ok</a>; --i) { <span class="comment">// critical check!</span></div>
|
|
<div class="line"> <span class="keywordflow">switch</span> (<a class="code hl_function" href="group__expect.html#gada27a479e6ad56faaa14528d1a3dfb26">mpack_expect_key_cstr</a>(&reader, keys, found, KEY_COUNT)) {</div>
|
|
<div class="line"> <span class="keywordflow">case</span> KEY_COMPACT: compact = <a class="code hl_function" href="group__expect.html#gaeafa42ca3ae974494f127eb4b56ed8ae">mpack_expect_bool</a>(&reader); <span class="keywordflow">break</span>;</div>
|
|
<div class="line"> <span class="keywordflow">case</span> KEY_SCHEMA: schema = <a class="code hl_function" href="group__expect.html#gae81b9d03f80e49501c9b9a695489315f">mpack_expect_int</a>(&reader); <span class="keywordflow">break</span>;</div>
|
|
<div class="line"> <span class="keywordflow">default</span>: <a class="code hl_function" href="group__reader.html#ga438bb2b85fbbd06cd8f10d5c8079427e">mpack_discard</a>(&reader); <span class="keywordflow">break</span>;</div>
|
|
<div class="line"> }</div>
|
|
<div class="line">}</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// compact is not optional</span></div>
|
|
<div class="line"><span class="keywordflow">if</span> (!found[KEY_COMPACT])</div>
|
|
<div class="line"> <a class="code hl_function" href="group__reader.html#ga5c45c2e0592f16ae671cd509d8d8c512">mpack_reader_flag_error</a>(&reader, <a class="code hl_enumvalue" href="group__common.html#gga9d9f282ca4183ab5190e09d04c1f74c4ae53cbed8fcc42915d71ae37d121b22e8">mpack_error_data</a>);</div>
|
|
<div class="ttc" id="agroup__expect_html_gada27a479e6ad56faaa14528d1a3dfb26"><div class="ttname"><a href="group__expect.html#gada27a479e6ad56faaa14528d1a3dfb26">mpack_expect_key_cstr</a></div><div class="ttdeci">size_t mpack_expect_key_cstr(mpack_reader_t *reader, const char *keys[], bool found[], size_t count)</div><div class="ttdoc">Expects a string map key matching one of the strings in the given key list, marking it as found in th...</div></div>
|
|
</div><!-- fragment --><p >In the above examples, the call to <code>mpack_discard(&reader);</code> skips over the value for unrecognized keys, allowing the format to be extensible and providing forwards-compatibility. If you want to forbid unrecognized keys, you can flag an error (e.g. <code>mpack_reader_flag_error(&reader, mpack_error_data);</code>) instead of discarding the value.</p>
|
|
<p >WARNING: See above the importance of using a reasonable limit on <code><a class="el" href="group__expect.html#ga36d86c85877f94aa7b493aaa739c8849" title="Reads the start of a map with a number of elements at most max_count, returning its element count.">mpack_expect_map_max()</a></code>, and of checking for errors in each iteration of the loop. If we were to leave these out, an attacker could craft a message declaring an array of a billion elements, forcing this code into a very long loop. We specify a size of 100 here as an arbitrary limit that leaves enough space for the schema to grow in the future. If you forbid unrecognized keys, you could specify the key count as the limit.</p>
|
|
<p >Unlike JSON, MessagePack supports any type as a map key, so the enum integer values can themselves be used as keys. This reduces message size at some expense of debuggability (losing some of the value of a schemaless format.) There is a simpler function <code><a class="el" href="group__expect.html#ga78f80d9fabe961d661eabe95732c2d59" title="Expects an unsigned integer map key between 0 and count-1, marking it as found in the given bool arra...">mpack_expect_key_uint()</a></code> which can be used to switch on small non-negative enum values directly.</p>
|
|
<p >On the surface this doesn't appear much shorter than the previous code, but it becomes much nicer when you have many possible keys in a map. Of course if at all possible you should consider using the <a class="el" href="md_docs_node.html">Node API</a> which is much less error-prone and will handle all of this for you. </p>
|
|
</div></div><!-- contents -->
|
|
</div><!-- PageDoc -->
|
|
<!-- start footer part -->
|
|
<hr class="footer"/><address class="footer"><small>
|
|
Generated by <a href="https://www.doxygen.org/index.html"><img class="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.9.5
|
|
</small></address>
|
|
</body>
|
|
</html>
|