From cf85e58da042632bd1def8ecbd24a970f3757986 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 17 Dec 2018 03:12:55 +0100 Subject: [PATCH] Add the PackMessage library. --- src/packmsg.h | 1921 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1921 insertions(+) create mode 100644 src/packmsg.h diff --git a/src/packmsg.h b/src/packmsg.h new file mode 100644 index 00000000..d7b25bbd --- /dev/null +++ b/src/packmsg.h @@ -0,0 +1,1921 @@ +#pragma once + +/* + SPDX-License-Identifier: BSD-3-Clause + + packmsg.h -- Little-endian MessagePack implementation, optimized for speed + Copyright (C) 2018 Guus Sliepen + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +/** \mainpage PackMessage, a safe and fast header-only C library for little-endian MessagePack encoding and decoding. + * + * This library can encode and decode MessagePack objects, however it differs in one important point + * from the official MessagePack specification: PackMessage stores all values in little-endian format. + * PackMessage offers a simple streaming API for encoding and decoding. + * + * PackMessage is *safe*: + * + * * Reads from and writes to buffers are always bounds checked. + * * String, binary and extension data can be read into buffers allocated by PackMessage using simple API calls. + * * Any error will result in null values and pointers being returned, and/or application-allocated buffers for strings will be zero-terminated, so there is no undefined state. + * * Once an encoding/decoding error occurs, all subsequent operations on the same buffer will also fail. + * * The API is designed to follow the principle of least surprise, and makes it hard to use in a wrong way. + * + * PackMessage is *fast*: + * + * * Values are stored in little-endian format, since virtually all mainstream processors are little-endian, or they can switch between endianness and are probably running an operating system that has configured it to be little-endian. This saves the overhead of converting to and from big-endian format. + * * No memory allocation is done unless requested. + * * The application can get const pointers to string, binary and extension data pointing into the input buffer if desired, avoiding copies. + * * The application does not have to check for errors after for every operation; it can be done once after encoding/decoding a buffer if desired. + * * The library is header-only, allowing the compiler to inline all functions and better optimize your application. + * + * ## API overview + * + * For encoding, a struct packmsg_output variable must be initialized + * with a pointer to the start of an output buffer, and its size. + * Elements can then be encoded using packmsg_add_*() functions. + * When all desired elements have been added, the length of the encoded message + * can be retrieved using the packmsg_output_size() function. + * + * For decoding, a struct packmsg_input variable must be initialized + * with a const pointer to the start of an input buffer, and its size. + * Elements can then be decoded using packmsg_get_*() functions. + * If the type of elements in a message is not known up front, then + * the type of the next element can be queried using packmsg_get_type() + * or packmsg_is_*() functions. To check that the complete message has been decoded + * correctly, the function packmsg_done() can be called. + * + * ## Example code + * + * @ref example.c + * + * \example example.c + * + * This is an example of how to encode and decode the equivalent of the JSON object `{"compact": true, "schema": 0}` + * using PackMessage. + */ + +/* Buffer iterators + * ================ + */ + +/** \brief Iterator for PackMessage output. + * + * This is an iterator that has to be initialized with a pointer to + * an output buffer that is allocated by the application, + * and the length of that buffer. A pointer to it is passed to all + * packmsg_add_*() functions. + */ +typedef struct packmsg_output { + uint8_t *ptr; /**< A pointer into a buffer. */ + ptrdiff_t len; /**< The remaining length of the buffer, or -1 in case of errors. */ +} packmsg_output_t; + +/** \brief Iterator for PackMessage input. + * + * This is an iterator that has to be initialized with a pointer to + * an input buffer that is allocated by the application, + * and the length of that buffer. A pointer to it is passed to all + * packmsg_get_*() functions. + */ +typedef struct packmsg_input { + const uint8_t *ptr; /**< A pointer into a buffer. */ + ptrdiff_t len; /**< The remaining length of the buffer, or -1 in case of errors. */ +} packmsg_input_t; + +/* Checks + * ====== + */ + +/** \brief Check if the PackMessage output buffer is in a valid state. + * \memberof packmsg_output + * + * This function checks if all operations performed on the output buffer so far + * have all completed succesfully, and the buffer contains a valid PackMessage message. + * + * \param buf A pointer to an output buffer iterator. + * + * \return True if all write operations performed on the output buffer so far have completed successfully, + * false if any error has occurred. + */ +static inline bool packmsg_output_ok(const struct packmsg_output *buf) { + assert(buf); + + return likely(buf->len >= 0); +} + +/** \brief Calculate the amount of bytes written to the output buffer. + * \memberof packmsg_output + * + * This function calculates the amount of bytes written to the output buffer + * based on the current position of the output iterator, and a pointer to the start of the buffer. + * + * \param buf A pointer to an output buffer iterator. + * \param start A pointer to the start of the output buffer. + * + * \return The total amount of bytes written to the output buffer, + * or 0 if any error has occurred. + */ +static inline size_t packmsg_output_size(const struct packmsg_output *buf, const uint8_t *start) { + if(likely(packmsg_output_ok(buf))) { + return buf->ptr - start; + } else { + return 0; + } +} + +/** \brief Check if the PackMessage input buffer is in a valid state. + * \memberof packmsg_input + * + * This function checks if all operations performed on the input buffer so far + * have all completed succesfully, and the buffer contains a valid PackMessage message. + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if all read operations performed on the input buffer so far have completed successfully, + * false if any error has occurred. + */ +static inline bool packmsg_input_ok(const struct packmsg_input *buf) { + assert(buf); + + return likely(buf->len >= 0); +} + +/** \brief Check if the PackMessage input buffer has been read completely. + * \memberof packmsg_input + * + * This function checks if all data in the input buffer has been consumed + * by input operations. This function should always be called after the last + * input operation, when one expects the whole buffer to have been read. + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the whole input buffer has been read successfully, + * false if there is still data remaining in the input buffer, + * or if any error has occurred. + */ +static inline bool packmsg_done(const struct packmsg_input *buf) { + assert(buf); + + return buf->len == 0; +} + +/* Invalidation functions + * ====================== + */ + +/** \brief Invalidate an output iterator. + * \memberof packmsg_output + * + * This function invalidates an output iterator. This signals that an error occurred, + * and prevents further output to be written. + * + * \param buf A pointer to an output buffer iterator. + */ +static inline void packmsg_output_invalidate(struct packmsg_output *buf) { + buf->len = -1; +} + +/** \brief Invalidate an input iterator. + * \memberof packmsg_input + * + * This function invalidates an input iterator. This signals that an error occurred, + * and prevents further input to be read. + * + * \param buf A pointer to an input buffer iterator. + */ +static inline void packmsg_input_invalidate(struct packmsg_input *buf) { + buf->len = -1; +} + +/* Encoding functions + * ================== + */ + +/** \brief Internal function, do not use. */ +static inline void packmsg_write_hdr_(struct packmsg_output *buf, uint8_t hdr) { + assert(buf); + assert(buf->ptr); + + if(likely(buf->len > 0)) { + *buf->ptr = hdr; + buf->ptr++; + buf->len--; + } else { + packmsg_output_invalidate(buf); + } +} + +/** \brief Internal function, do not use. */ +static inline void packmsg_write_data_(struct packmsg_output *buf, const void *data, uint32_t dlen) { + assert(buf); + assert(buf->ptr); + assert(data); + + if(likely(buf->len >= dlen)) { + memcpy(buf->ptr, data, dlen); + buf->ptr += dlen; + buf->len -= dlen; + } else { + packmsg_output_invalidate(buf); + } +} + +/** \brief Internal function, do not use. */ +static inline void packmsg_write_hdrdata_(struct packmsg_output *buf, uint8_t hdr, const void *data, uint32_t dlen) { + assert(buf); + assert(buf->ptr); + assert(data); + + if(likely(buf->len > dlen)) { + *buf->ptr = hdr; + buf->ptr++; + buf->len--; + + memcpy(buf->ptr, data, dlen); + buf->ptr += dlen; + buf->len -= dlen; + } else { + packmsg_output_invalidate(buf); + } +} + +/** \brief Add a NIL to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + */ +static inline void packmsg_add_nil(struct packmsg_output *buf) { + packmsg_write_hdr_(buf, 0xc0); +} + +/** \brief Add a boolean value to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param val The value to add. + */ +static inline void packmsg_add_bool(struct packmsg_output *buf, bool val) { + packmsg_write_hdr_(buf, val ? 0xc3 : 0xc2); +} + +/** \brief Add an int8 value to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param val The value to add. + */ +static inline void packmsg_add_int8(struct packmsg_output *buf, int8_t val) { + if(val >= -32) { // fixint + packmsg_write_hdr_(buf, val); + } else { // TODO: negative fixint + packmsg_write_hdrdata_(buf, 0xd0, &val, 1); + } +} + +/** \brief Add an int16 value to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param val The value to add. + */ +static inline void packmsg_add_int16(struct packmsg_output *buf, int16_t val) { + if((int8_t) val != val) { + packmsg_write_hdrdata_(buf, 0xd1, &val, 2); + } else { + packmsg_add_int8(buf, val); + } +} + +/** \brief Add an int32 value to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param val The value to add. + */ +static inline void packmsg_add_int32(struct packmsg_output *buf, int32_t val) { + if((int16_t) val != val) { + packmsg_write_hdrdata_(buf, 0xd2, &val, 4); + } else { + packmsg_add_int16(buf, val); + } +} + +/** \brief Add an int64 value to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param val The value to add. + */ +static inline void packmsg_add_int64(struct packmsg_output *buf, int64_t val) { + if((int32_t) val != val) { + packmsg_write_hdrdata_(buf, 0xd3, &val, 8); + } else { + packmsg_add_int32(buf, val); + } +} + +/** \brief Add a uint8 value to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param val The value to add. + */ +static inline void packmsg_add_uint8(struct packmsg_output *buf, uint8_t val) { + if(val < 0x80) { // fixint + packmsg_write_hdr_(buf, val); + } else { + packmsg_write_hdrdata_(buf, 0xcc, &val, 1); + } +} + +/** \brief Add a uint16 value to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param val The value to add. + */ +static inline void packmsg_add_uint16(struct packmsg_output *buf, uint16_t val) { + if(val & 0xff00) { + packmsg_write_hdrdata_(buf, 0xcd, &val, 2); + } else { + packmsg_add_uint8(buf, val); + } +} + +/** \brief Add a int32 value to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param val The value to add. + */ +static inline void packmsg_add_uint32(struct packmsg_output *buf, uint32_t val) { + if(val & 0xffff0000) { + packmsg_write_hdrdata_(buf, 0xce, &val, 4); + } else { + packmsg_add_uint16(buf, val); + } +} + +/** \brief Add a int64 value to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param val The value to add. + */ +static inline void packmsg_add_uint64(struct packmsg_output *buf, uint64_t val) { + if(val & 0xffffffff00000000) { + packmsg_write_hdrdata_(buf, 0xcf, &val, 8); + } else { + packmsg_add_uint32(buf, val); + } +} + +/** \brief Add a float value to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param val The value to add. + */ +static inline void packmsg_add_float(struct packmsg_output *buf, float val) { + packmsg_write_hdrdata_(buf, 0xca, &val, 4); +} + +/** \brief Add a double value to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param val The value to add. + */ +static inline void packmsg_add_double(struct packmsg_output *buf, double val) { + packmsg_write_hdrdata_(buf, 0xcb, &val, 8); +} + +/** \brief Add a string to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param str The string to add. This must be a NUL-terminated string. + */ +static inline void packmsg_add_str(struct packmsg_output *buf, const char *str) { + size_t slen = strlen(str); + + if(slen < 32) { + packmsg_write_hdr_(buf, 0xa0 | (uint8_t) slen); + } else if(slen <= 0xff) { + packmsg_write_hdrdata_(buf, 0xd9, &slen, 1); + } else if(slen <= 0xffff) { + packmsg_write_hdrdata_(buf, 0xda, &slen, 2); + } else if(slen <= 0xffffffff) { + packmsg_write_hdrdata_(buf, 0xdb, &slen, 4); + } else { + packmsg_output_invalidate(buf); + return; + } + + packmsg_write_data_(buf, str, slen); +} + +/** \brief Add binary data to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param data A pointer to the data to add. + * \param dlen The length of the data in bytes. + */ +static inline void packmsg_add_bin(struct packmsg_output *buf, const void *data, uint32_t dlen) { + if(dlen <= 0xff) { + packmsg_write_hdrdata_(buf, 0xc4, &dlen, 1); + } else if(dlen <= 0xffff) { + packmsg_write_hdrdata_(buf, 0xc5, &dlen, 2); + } else if(dlen <= 0xffffffff) { + packmsg_write_hdrdata_(buf, 0xc6, &dlen, 4); + } else { + packmsg_output_invalidate(buf); + return; + } + + packmsg_write_data_(buf, data, dlen); +} + +/** \brief Add extension data to the output. + * \memberof packmsg_output + * + * \param buf A pointer to an output buffer iterator. + * \param type The extension type. Values between 0 and 127 are application specific, + * values between -1 and -128 are reserved for future extensions. + * \param data A pointer to the data to add. + * \param dlen The length of the data in bytes. + */ +static inline void packmsg_add_ext(struct packmsg_output *buf, int8_t type, const void *data, uint32_t dlen) { + if(dlen <= 0xff) { + if(dlen == 16) { + packmsg_write_hdrdata_(buf, 0xd8, &type, 1); + } else if(dlen == 8) { + packmsg_write_hdrdata_(buf, 0xd7, &type, 1); + } else if(dlen == 4) { + packmsg_write_hdrdata_(buf, 0xd6, &type, 1); + } else if(dlen == 2) { + packmsg_write_hdrdata_(buf, 0xd5, &type, 1); + } else if(dlen == 1) { + packmsg_write_hdrdata_(buf, 0xd4, &type, 1); + } else { + packmsg_write_hdrdata_(buf, 0xc7, &dlen, 1); + packmsg_write_data_(buf, &type, 1); + } + } else if(dlen <= 0xffff) { + packmsg_write_hdrdata_(buf, 0xc8, &dlen, 2); + packmsg_write_data_(buf, &type, 1); + } else if(dlen <= 0xffffffff) { + packmsg_write_hdrdata_(buf, 0xc9, &dlen, 4); + packmsg_write_data_(buf, &type, 1); + } else { + packmsg_output_invalidate(buf); + return; + } + + packmsg_write_data_(buf, data, dlen); +} + +/** \brief Add a map header to the output. + * \memberof packmsg_output + * + * This function only adds an an indicator that the next 2 * count elements + * are a sequence of key-value pairs that make up the contents of the map. + * These key-value pairs have to be added by the application using regular + * packmsg_add_*() calls. + * + * \param buf A pointer to an output buffer iterator. + * \param count The number of elements in the map. + */ +static inline void packmsg_add_map(struct packmsg_output *buf, uint32_t count) { + if(count <= 0xf) { + packmsg_write_hdr_(buf, 0x80 | (uint8_t) count); + } else if(count <= 0xffff) { + packmsg_write_hdrdata_(buf, 0xde, &count, 2); + } else { + packmsg_write_hdrdata_(buf, 0xdf, &count, 4); + } +} + +/** \brief Add an array header to the output. + * \memberof packmsg_output + * + * This function only adds an an indicator that the next count elements + * are a sequence of elements that make up the contents of the array. + * These elements have to be added by the application using regular + * packmsg_add_*() calls. + * + * \param buf A pointer to an output buffer iterator. + * \param count The number of elements in the array. + */ +static inline void packmsg_add_array(struct packmsg_output *buf, uint32_t count) { + if(count <= 0xf) { + packmsg_write_hdr_(buf, 0x90 | (uint8_t) count); + } else if(count <= 0xffff) { + packmsg_write_hdrdata_(buf, 0xdc, &count, 2); + } else { + packmsg_write_hdrdata_(buf, 0xdd, &count, 4); + } +} + +/* Decoding functions + * ================== + */ + +/** \brief Internal function, do not use. */ +static inline uint8_t packmsg_read_hdr_(struct packmsg_input *buf) { + assert(buf); + assert(buf->ptr); + + if(likely(buf->len > 0)) { + uint8_t hdr = *buf->ptr; + buf->ptr++; + buf->len--; + return hdr; + } else { + packmsg_input_invalidate(buf); + return 0xc1; + } +} + +/** \brief Internal function, do not use. */ +static inline void packmsg_read_data_(struct packmsg_input *buf, void *data, uint32_t dlen) { + assert(buf); + assert(buf->ptr); + assert(data); + + if(likely(buf->len >= dlen)) { + memcpy(data, buf->ptr, dlen); + buf->ptr += dlen; + buf->len -= dlen; + } else { + packmsg_input_invalidate(buf); + } +} + +/** \brief Internal function, do not use. */ +static inline uint8_t packmsg_peek_hdr_(const struct packmsg_input *buf) { + assert(buf); + assert(buf->ptr); + + if(likely(buf->len > 0)) { + return *buf->ptr; + } else { + return 0xc1; + } +} + +/** \brief Get a NIL from the input. + * \memberof packmsg_input + * + * This function does not return anything, but will invalidate the input interator + * if no NIL was succesfully consumed from the input. + * + * \param buf A pointer to an input buffer iterator. + */ +static inline void packmsg_get_nil(struct packmsg_input *buf) { + if(packmsg_read_hdr_(buf) != 0xc0) { + packmsg_input_invalidate(buf); + } +} + + +/** \brief Get a boolean value from the input. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * \return The boolean value that was read from the input, + * or false in case of an error. + */ +static inline bool packmsg_get_bool(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if(hdr == 0xc2) { + return false; + } else if(hdr == 0xc3) { + return true; + } else { + packmsg_input_invalidate(buf); + return false; + } +} + +/** \brief Get an int8 value from the input. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * \return The int8 value that was read from the input, + * or 0 in case of an error. + */ +static inline int8_t packmsg_get_int8(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if(hdr < 0x80 || hdr >= 0xe0) { + return (int8_t)hdr; + } else if(hdr == 0xd0) { + return packmsg_read_hdr_(buf); + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/** \brief Get an int16 value from the input. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * \return The int16 value that was read from the input, + * or 0 in case of an error. + */ +static inline int16_t packmsg_get_int16(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if(hdr < 0x80 || hdr >= 0xe0) { + return (int8_t)hdr; + } else if(hdr == 0xd0) { + return (int8_t) packmsg_read_hdr_(buf); + } else if(hdr == 0xd1) { + int16_t val = 0; + packmsg_read_data_(buf, &val, 2); + return val; + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/** \brief Get an int32 value from the input. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * \return The int32 value that was read from the input, + * or 0 in case of an error. + */ +static inline int32_t packmsg_get_int32(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if(hdr < 0x80 || hdr >= 0xe0) { + return (int8_t)hdr; + } else if(hdr == 0xd0) { + return (int8_t) packmsg_read_hdr_(buf); + } else if(hdr == 0xd1) { + int16_t val = 0; + packmsg_read_data_(buf, &val, 2); + return val; + } else if(hdr == 0xd2) { + int32_t val = 0; + packmsg_read_data_(buf, &val, 4); + return val; + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/** \brief Get an int64 value from the input. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * \return The int64 value that was read from the input, + * or 0 in case of an error. + */ +static inline int64_t packmsg_get_int64(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if(hdr < 0x80 || hdr >= 0xe0) { + return (int8_t)hdr; + } else if(hdr == 0xd0) { + return (int8_t) packmsg_read_hdr_(buf); + } else if(hdr == 0xd1) { + int16_t val = 0; + packmsg_read_data_(buf, &val, 2); + return val; + } else if(hdr == 0xd2) { + int32_t val = 0; + packmsg_read_data_(buf, &val, 4); + return val; + } else if(hdr == 0xd3) { + int64_t val = 0; + packmsg_read_data_(buf, &val, 8); + return val; + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/** \brief Get an uint8 value from the input. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * \return The uint8 value that was read from the input, + * or 0 in case of an error. + */ +static inline uint8_t packmsg_get_uint8(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if(hdr < 0x80) { + return hdr; + } else if(hdr == 0xcc) { + return packmsg_read_hdr_(buf); + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/** \brief Get an uint16 value from the input. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * \return The uint16 value that was read from the input, + * or 0 in case of an error. + */ +static inline uint16_t packmsg_get_uint16(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if(hdr < 0x80) { + return hdr; + } else if(hdr == 0xcc) { + return packmsg_read_hdr_(buf); + } else if(hdr == 0xcd) { + uint16_t val = 0; + packmsg_read_data_(buf, &val, 2); + return val; + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/** \brief Get an uint32 value from the input. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * \return The uint32 value that was read from the input, + * or 0 in case of an error. + */ +static inline uint32_t packmsg_get_uint32(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if(hdr < 0x80) { + return hdr; + } else if(hdr == 0xcc) { + return packmsg_read_hdr_(buf); + } else if(hdr == 0xcd) { + uint16_t val = 0; + packmsg_read_data_(buf, &val, 2); + return val; + } else if(hdr == 0xce) { + uint32_t val = 0; + packmsg_read_data_(buf, &val, 4); + return val; + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/** \brief Get an uint64 value from the input. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * \return The uint64 value that was read from the input, + * or 0 in case of an error. + */ +static inline uint64_t packmsg_get_uint64(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if(hdr < 0x80) { + return hdr; + } else if(hdr == 0xcc) { + return packmsg_read_hdr_(buf); + } else if(hdr == 0xcd) { + uint16_t val = 0; + packmsg_read_data_(buf, &val, 2); + return val; + } else if(hdr == 0xce) { + uint32_t val = 0; + packmsg_read_data_(buf, &val, 4); + return val; + } else if(hdr == 0xcf) { + uint64_t val = 0; + packmsg_read_data_(buf, &val, 8); + return val; + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/** \brief Get a float value from the input. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * \return The float value that was read from the input, + * or 0 in case of an error. + */ +static inline float packmsg_get_float(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if(hdr == 0xca) { + float val; + packmsg_read_data_(buf, &val, 4); + return val; + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/** \brief Get a double value from the input. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * \return The float value that was read from the input, + * or 0 in case of an error. + */ +static inline double packmsg_get_double(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if(hdr == 0xcb) { + double val; + packmsg_read_data_(buf, &val, 8); + return val; + } else if(hdr == 0xca) { + float val; + packmsg_read_data_(buf, &val, 4); + return val; + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/** \brief Get a raw pointer to a string from the input. + * \memberof packmsg_input + * + * This function returns the size of a string and a pointer into the input buffer itself, + * to a string that is *not NUL-terminated!* This function avoids making a copy of the string, + * but the application must take care to not read more than the returned number of bytes. + * + * \param buf A pointer to an input buffer iterator. + * \param[out] str A pointer to a const char pointer that will be set to the start of the string, + * or will be set to NULL in case of an error. + * \return The size of the string in bytes, + * or 0 in case of an error. + */ +static inline uint32_t packmsg_get_str_raw(struct packmsg_input *buf, const char **str) { + assert(str); + + uint8_t hdr = packmsg_read_hdr_(buf); + uint32_t slen = 0; + + if((hdr & 0xe0) == 0xa0) { + slen = hdr & 0x1f; + } else if(hdr == 0xd9) { + packmsg_read_data_(buf, &slen, 1); + } else if(hdr == 0xda) { + packmsg_read_data_(buf, &slen, 2); + } else if(hdr == 0xdb) { + packmsg_read_data_(buf, &slen, 4); + } else { + packmsg_input_invalidate(buf); + *str = NULL; + return 0; + } + + if(likely(buf->len >= slen)) { + *str = (const char *)buf->ptr; + buf->ptr += slen; + buf->len -= slen; + return slen; + } else { + packmsg_input_invalidate(buf); + *str = NULL; + return 0; + } +} + +/** \brief Copy a string from the input into a newly allocated buffer. + * \memberof packmsg_input + * + * This function copies a string from the input into a buffer allocated by the library + * using malloc(). The copy will be NUL-terminated. + * The application is responsible for freeing the memory of the buffer using free(). + * + * \param buf A pointer to an input buffer iterator. + * + * \return A pointer to the newly allocated buffer containing a NUL-terminated string, + * or NULL in case of an error. + */ +static inline char *packmsg_get_str_dup(struct packmsg_input *buf) { + const char *str; + uint32_t slen = packmsg_get_str_raw(buf, &str); + + if(likely(packmsg_input_ok(buf))) { + char *dup = (char *)malloc((size_t) slen + 1); + + if(likely(dup)) { + memcpy(dup, str, slen); + dup[slen] = 0; + return dup; + } else { + packmsg_input_invalidate(buf); + return NULL; + } + } else { + return NULL; + } +} + +/** \brief Copy a string from the input into another buffer. + * \memberof packmsg_input + * + * This function copies a string from the input another buffer provided by the application. + * The buffer must be long enough to hold the complete string plus a terminating NUL-byte. + * If the buffer is not long enough, or another error occured, + * a single NUL-byte will be written to the start of the buffer (if its size is at least one byte). + * + * \param buf A pointer to an input buffer iterator. + * \param data A pointer to a buffer allocated by the application. + * \param dlen The size of the buffer pointed to by data. + * + * \return The size of the string in bytes, + * or 0 in case of an error. + */ +static inline uint32_t packmsg_get_str_copy(struct packmsg_input *buf, void *data, uint32_t dlen) { + assert(data); + + const char *str; + uint32_t slen = packmsg_get_str_raw(buf, &str); + + if(likely(packmsg_input_ok(buf))) { + if(likely(slen < dlen)) { + memcpy(data, str, slen); + ((char *)data)[slen] = 0; + return slen; + } else { + if(dlen) { + *(char *)data = 0; + } + + packmsg_input_invalidate(buf); + return 0; + } + } else { + if(dlen) { + *(char *)data = 0; + } + + return 0; + } +} + +/** \brief Get a raw pointer to binary data from the input. + * \memberof packmsg_input + * + * This function returns the size of the binary data and a pointer into the input buffer itself. + * This function avoids making a copy of the binary data, + * but the application must take care to not read more than the returned number of bytes. + * + * \param buf A pointer to an input buffer iterator. + * \param[out] data A pointer to a const void pointer that will be set to the start of the data, + * or will be set to NULL in case of an error. + * \return The size of the data in bytes, + * or 0 in case of an error. + */ +static inline uint32_t packmsg_get_bin_raw(struct packmsg_input *buf, const void **data) { + assert(data); + + uint8_t hdr = packmsg_read_hdr_(buf); + uint32_t dlen = 0; + + if(hdr == 0xc4) { + packmsg_read_data_(buf, &dlen, 1); + } else if(hdr == 0xc5) { + packmsg_read_data_(buf, &dlen, 2); + } else if(hdr == 0xc6) { + packmsg_read_data_(buf, &dlen, 4); + } else { + packmsg_input_invalidate(buf); + *data = NULL; + return 0; + } + + if(likely(buf->len >= dlen)) { + *data = buf->ptr; + buf->ptr += dlen; + buf->len -= dlen; + return dlen; + } else { + packmsg_input_invalidate(buf); + *data = NULL; + return 0; + } +} + +/** \brief Copy binary data from the input into a newly allocated buffer. + * \memberof packmsg_input + * + * This function copies binary data from the input into a buffer allocated by the library + * using malloc(). + * The application is responsible for freeing the memory of the buffer using free(). + * + * \param buf A pointer to an input buffer iterator. + * \param[out] dlen A pointer to an uint32_t that will be set to the size of the binary data. + * + * \return A pointer to the newly allocated buffer containing the binary data, + * or NULL in case of an error. + */ +static inline void *packmsg_get_bin_dup(struct packmsg_input *buf, uint32_t *dlen) { + const void *data; + *dlen = packmsg_get_bin_raw(buf, &data); + + if(likely(packmsg_input_ok(buf))) { + char *dup = (char *)malloc(*dlen); + + if(likely(dup)) { + memcpy(dup, data, *dlen); + return dup; + } else { + *dlen = 0; + packmsg_input_invalidate(buf); + return NULL; + } + } else { + return NULL; + } +} + +/** \brief Copy binary data from the input into another buffer. + * \memberof packmsg_input + * + * This function copies binary data from the input another buffer provided by the application. + * The buffer must be long enough to hold all the binary data. + * + * \param buf A pointer to an input buffer iterator. + * \param rawbuf A pointer to a buffer allocated by the application. + * \param rlen The size of the buffer pointed to by data. + * + * \return The size of the binary data in bytes, + * or 0 in case of an error. + */ +static inline uint32_t packmsg_get_bin_copy(struct packmsg_input *buf, void *rawbuf, uint32_t rlen) { + assert(rawbuf); + + const void *data; + uint32_t dlen = packmsg_get_bin_raw(buf, &data); + + if(likely(packmsg_input_ok(buf))) { + if(likely(dlen <= rlen)) { + memcpy(rawbuf, data, dlen); + return dlen; + } else { + packmsg_input_invalidate(buf); + return 0; + } + } else { + return 0; + } +} + +/** \brief Get a raw pointer to extension data from the input. + * \memberof packmsg_input + * + * This function returns the type of the extension, the size of the data + * and a pointer into the input buffer itself. + * This function avoids making a copy of the binary data, + * but the application must take care to not read more than the returned number of bytes. + * + * \param buf A pointer to an input buffer iterator. + * \param[out] type A pointer to an int8_t that will be set to the type of the extension. + * or will be set to 0 in case of an error. + * \param[out] data A pointer to a const void pointer that will be set to the start of the data, + * or will be set to NULL in case of an error. + * + * \return The size of the data in bytes, + * or 0 in case of an error. + */ +static inline uint32_t packmsg_get_ext_raw(struct packmsg_input *buf, int8_t *type, const void **data) { + assert(type); + assert(data); + + uint8_t hdr = packmsg_read_hdr_(buf); + uint32_t dlen = 0; + + if(hdr == 0xc7) { + packmsg_read_data_(buf, &dlen, 1); + } else if(hdr == 0xc8) { + packmsg_read_data_(buf, &dlen, 2); + } else if(hdr == 0xc9) { + packmsg_read_data_(buf, &dlen, 4); + } else if(hdr >= 0xd4 && hdr <= 0xd8) { + dlen = 1 << (hdr - 0xd4); + } else { + packmsg_input_invalidate(buf); + *type = 0; + *data = NULL; + return 0; + } + + *type = packmsg_read_hdr_(buf); + + if(likely(buf->len >= dlen)) { + *data = buf->ptr; + buf->ptr += dlen; + buf->len -= dlen; + return dlen; + } else { + packmsg_input_invalidate(buf); + *type = 0; + *data = NULL; + return 0; + } +} + +/** \brief Copy extension data from the input into a newly allocated buffer. + * \memberof packmsg_input + * + * This function copies extension data from the input into a buffer allocated by the library + * using malloc(). + * The application is responsible for freeing the memory of the buffer using free(). + * + * \param buf A pointer to an input buffer iterator. + * \param[out] type A pointer to an int8_t that will be set to the type of the extension. + * or will be set to 0 in case of an error. + * \param[out] dlen A pointer to an uint32_t that will be set to the size of the extension data, + * or will be set to 0 in case of an error. + * + * \return A pointer to the newly allocated buffer containing the extension data, + * or NULL in case of an error. + */ +static inline void *packmsg_get_ext_dup(struct packmsg_input *buf, int8_t *type, uint32_t *dlen) { + assert(type); + + const void *data; + *dlen = packmsg_get_ext_raw(buf, type, &data); + + if(likely(packmsg_input_ok(buf))) { + char *dup = (char *)malloc(*dlen); + + if(likely(dup)) { + memcpy(dup, data, *dlen); + return dup; + } else { + *type = 0; + *dlen = 0; + packmsg_input_invalidate(buf); + return NULL; + } + } else { + *type = 0; + *dlen = 0; + return NULL; + } +} + +/** \brief Copy extension data from the input into another buffer. + * \memberof packmsg_input + * + * This function copies extension data from the input another buffer provided by the application. + * The buffer must be long enough to hold all the extension data. + * + * \param buf A pointer to an input buffer iterator. + * \param[out] type A pointer to an int8_t that will be set to the type of the extension. + * or will be set to 0 in case of an error. + * \param rawbuf A pointer to a buffer allocated by the application. + * \param rlen The size of the buffer pointed to by data. + * + * \return The size of the extension data in bytes, + * or 0 in case of an error. + */ +static inline uint32_t packmsg_get_ext_copy(struct packmsg_input *buf, int8_t *type, void *rawbuf, uint32_t rlen) { + assert(type); + assert(rawbuf); + + const void *data; + uint32_t dlen = packmsg_get_ext_raw(buf, type, &data); + + if(likely(packmsg_input_ok(buf))) { + if(likely(dlen <= rlen)) { + memcpy(rawbuf, data, dlen); + return dlen; + } else { + *type = 0; + packmsg_input_invalidate(buf); + return 0; + } + } else { + *type = 0; + return 0; + } +} + +/** \brief Get a map header from the output. + * \memberof packmsg_input + * + * This function only reads a map header, and returns the number of key-value + * pairs in the map. + * These key-value pairs have to be read by the application using regular + * packmsg_get_*() calls. + * + * \param buf A pointer to an input buffer iterator. + * + * \return The number of key-value pairs in the map. + */ +static inline uint32_t packmsg_get_map(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if((hdr & 0xf0) == 0x80) { + return hdr & 0xf; + } else if(hdr == 0xde) { + uint32_t dlen = 0; + packmsg_read_data_(buf, &dlen, 2); + return dlen; + } else if(hdr == 0xdf) { + uint32_t dlen = 0; + packmsg_read_data_(buf, &dlen, 4); + return dlen; + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/** \brief Get an array header from the output. + * \memberof packmsg_input + * + * This function only reads an array header, and returns the number of elements + * in the array. + * These elements have to be read by the application using regular + * packmsg_get_*() calls. + * + * \param buf A pointer to an input buffer iterator. + * + * \return The number of elements in the array. + */ +static inline uint32_t packmsg_get_array(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + + if((hdr & 0xf0) == 0x90) { + return hdr & 0xf; + } else if(hdr == 0xdc) { + uint32_t dlen = 0; + packmsg_read_data_(buf, &dlen, 2); + return dlen; + } else if(hdr == 0xdd) { + uint32_t dlen = 0; + packmsg_read_data_(buf, &dlen, 4); + return dlen; + } else { + packmsg_input_invalidate(buf); + return 0; + } +} + +/* Type checking + * ============= + */ + +/** \brief An enum describing the type of an element in a PackMessage message. + * + * This enum describes the type of an element in a PackMessage message. + * In case of integers and floating point values, the type normally represents + * the smallest type that can succesfully hold the value of the element; + * i.e. an element of type PACKMSG_INT32 can only succesfully be read by + * packmsg_get_int32() or packmsg_get_int64(). However, the converse it not true; + * for an element of type PACKMSG_INT32, there is no guarantee + * that the value is larger than would fit into an int16_t. + * + * PackMessage makes a clear distinction between signed and unsigned integers, + * except in the case of positive fixints (values between 0 and 127 inclusive), + * which can be read as both signed and unsigned. + */ +enum packmsg_type { + PACKMSG_ERROR, /**< An invalid element was found or the input buffer is in an invalid state. */ + PACKMSG_NIL, /**< The next element is a NIL. */ + PACKMSG_BOOL, /**< The next element is a boolean. */ + PACKMSG_POSITIVE_FIXINT, /**< The next element is an integer between 0 and 127 inclusive. */ + PACKMSG_INT8, /**< The next element is a signed integer that fits in an int8_t. */ + PACKMSG_INT16, /**< The next element is a signed integer that fits in an int16_t. */ + PACKMSG_INT32, /**< The next element is a signed integer that fits in an int32_t. */ + PACKMSG_INT64, /**< The next element is a signed integer that fits in an int64_t. */ + PACKMSG_UINT8, /**< The next element is an unsigned integer that fits in an uint8_t. */ + PACKMSG_UINT16, /**< The next element is an unsigned integer that fits in an uint16_t. */ + PACKMSG_UINT32, /**< The next element is an unsigned integer that fits in an uint32_t. */ + PACKMSG_UINT64, /**< The next element is an unsigned integer that fits in an uint64_t. */ + PACKMSG_FLOAT, /**< The next element is a single precision floating point value. */ + PACKMSG_DOUBLE, /**< The next element is a double precision floating point value. */ + PACKMSG_STR, /**< The next element is a string. */ + PACKMSG_BIN, /**< The next element is binary data. */ + PACKMSG_EXT, /**< The next element is extension data. */ + PACKMSG_MAP, /**< The next element is a map header. */ + PACKMSG_ARRAY, /**< The next element is an array header. */ + PACKMSG_DONE, /**< There are no more elements in the input buffer. */ +}; + +/** \brief Checks if the next element is a NIL. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_nil(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_nil(const struct packmsg_input *buf) { + return packmsg_peek_hdr_(buf) == 0xc0; +} + +/** \brief Checks if the next element is a bool. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_nil(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_bool(const struct packmsg_input *buf) { + return (packmsg_peek_hdr_(buf) & 0xfe) == 0xc2; +} + +/** \brief Checks if the next element is a signed integer that fits in an int8_t. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_int8(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_int8(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return hdr < 0x80 || hdr == 0xd0; +} + +/** \brief Checks if the next element is a signed integer that fits in an int16_t. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_int16(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_int16(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return hdr < 0x80 || hdr == 0xd0 || hdr == 0xd1; +} + +/** \brief Checks if the next element is a signed integer that fits in an int32_t. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_int32(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_int32(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return hdr < 0x80 || hdr == 0xd0 || hdr == 0xd1 || hdr == 0xd2; +} + +/** \brief Checks if the next element is a signed integer that fits in an int64_t. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_int64(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_int64(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return hdr < 0x80 || hdr == 0xd0 || hdr == 0xd1 || hdr == 0xd2 || hdr == 0xd3; +} + +/** \brief Checks if the next element is an unsigned integer that fits in an uint8_t. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_uint8(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_uint8(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return hdr < 0x80 || hdr == 0xcc; +} + +/** \brief Checks if the next element is an unsigned integer that fits in an uint16_t. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_uint16(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_uint16(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return hdr < 0x80 || hdr == 0xcc || hdr == 0xcd; +} + +/** \brief Checks if the next element is an unsigned integer that fits in an uint32_t. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_uint32(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_uint32(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return hdr < 0x80 || hdr == 0xcc || hdr == 0xcd || hdr == 0xce; +} + +/** \brief Checks if the next element is an unsigned integer that fits in an uint64_t. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_uint64(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_uint64(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return hdr < 0x80 || hdr == 0xcc || hdr == 0xcd || hdr == 0xce || hdr == 0xcf; +} + +/** \brief Checks if the next element is a single precision floating point value. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_float(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_float(const struct packmsg_input *buf) { + return packmsg_peek_hdr_(buf) == 0xca; +} + +/** \brief Checks if the next element is a single or double precision floating point value. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_double(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_double(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return hdr == 0xcb || hdr == 0xca; +} + +/** \brief Checks if the next element is a string. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_str_*(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_str(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return (hdr & 0xe0) == 0xa0 || hdr == 0xd9 || hdr == 0xda || hdr == 0xdb; +} + +/** \brief Checks if the next element is binary data. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_bin_*(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_bin(const struct packmsg_input *buf) { + return (packmsg_peek_hdr_(buf) & 0xfc) == 0xc4; +} + +/** \brief Checks if the next element is extension data. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_ext_*(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_ext(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return (hdr >= 0xc7 && hdr <= 0xc9) || (hdr >= 0xd4 && hdr <= 0xd8); +} + +/** \brief Checks if the next element is a map header. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_map(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_map(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return (hdr & 0xf0) == 0x80 || hdr == 0xde || hdr == 0xdf; +} + +/** \brief Checks if the next element is an array header. + * \memberof packmsg_input + * + * \param buf A pointer to an input buffer iterator. + * + * \return True if the next element can be read by packmsg_get_array(), + * false if not or if any other error occurred. + */ +static inline bool packmsg_is_array(const struct packmsg_input *buf) { + uint8_t hdr = packmsg_peek_hdr_(buf); + return (hdr & 0xf0) == 0x90 || hdr == 0xdc || hdr == 0xdd; +} + +/** \brief Checks the type of the next element. + * \memberof packmsg_input + * + * This function checks the next element and returns an enum packmsg_type + * that describes the type of the element. If the input buffer was fully consumed + * and there are no more elements left, this function will return PACKMSG_DONE. + * + * \param buf A pointer to an output buffer iterator. + * + * \return The type of the next element, or PACKMSG_DONE if no more elements + * are present in the input buffer, or PACKMSG_ERROR if the next element + * is invalid, or if any other error occurred. + */ +static inline enum packmsg_type packmsg_get_type(const struct packmsg_input *buf) { + if(unlikely(packmsg_done(buf))) { + return PACKMSG_DONE; + } + + uint8_t hdr = packmsg_peek_hdr_(buf); + + switch(hdr >> 4) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + return PACKMSG_POSITIVE_FIXINT; + + case 0x8: + return PACKMSG_MAP; + + case 0x9: + return PACKMSG_ARRAY; + + case 0xa: + case 0xb: + return PACKMSG_STR; + + case 0xc: + switch(hdr & 0xf) { + case 0x0: + return PACKMSG_NIL; + + case 0x1: + return PACKMSG_ERROR; + + case 0x2: + case 0x3: + return PACKMSG_BOOL; + + case 0x4: + case 0x5: + case 0x6: + return PACKMSG_BIN; + + case 0x7: + case 0x8: + case 0x9: + return PACKMSG_EXT; + + case 0xa: + return PACKMSG_FLOAT; + + case 0xb: + return PACKMSG_DOUBLE; + + case 0xc: + return PACKMSG_UINT8; + + case 0xd: + return PACKMSG_UINT16; + + case 0xe: + return PACKMSG_UINT32; + + case 0xf: + return PACKMSG_UINT64; + + default: + return PACKMSG_ERROR; + } + + case 0xd: + switch(hdr & 0xf) { + case 0x0: + return PACKMSG_INT8; + + case 0x1: + return PACKMSG_INT16; + + case 0x2: + return PACKMSG_INT32; + + case 0x3: + return PACKMSG_INT64; + + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + return PACKMSG_EXT; + + case 0x9: + case 0xa: + case 0xb: + return PACKMSG_STR; + + case 0xc: + case 0xd: + return PACKMSG_ARRAY; + + case 0xe: + case 0xf: + return PACKMSG_MAP; + + default: + return PACKMSG_ERROR; + } + + case 0xe: + case 0xf: + return PACKMSG_INT8; + + default: + return PACKMSG_ERROR; + } +} + +/** \brief Skip one element in the input + * \memberof packmsg_input + * + * This function skips the next element in the input. + * If the element is a map or an array, only the map or array header is skipped, + * but not the contents of the map or array. + * + * \param buf A pointer to an output buffer iterator. + */ +static inline void packmsg_skip_element(struct packmsg_input *buf) { + uint8_t hdr = packmsg_read_hdr_(buf); + ptrdiff_t skip = 0; + + switch(hdr >> 4) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + case 0x9: + case 0xa: + case 0xb: + return; + + case 0xc: + switch(hdr & 0xf) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + return; + + case 0x4: + skip = -1; + break; + + case 0x5: + skip = -2; + break; + + case 0x6: + skip = -4; + break; + + case 0x7: + skip = -1; + break; + + case 0x8: + skip = -2; + break; + + case 0x9: + skip = -4; + break; + + case 0xa: + skip = 4; + break; + + case 0xb: + skip = 8; + break; + + case 0xc: + skip = 1; + break; + + case 0xd: + skip = 2; + break; + + case 0xe: + skip = 4; + break; + + case 0xf: + skip = 8; + break; + } + + break; + + case 0xd: + switch(hdr & 0xf) { + case 0x0: + skip = 1; + break; + + case 0x1: + skip = 2; + break; + + case 0x2: + skip = 4; + break; + + case 0x3: + skip = 8; + break; + + case 0x4: + skip = 2; + break; + + case 0x5: + skip = 3; + break; + + case 0x6: + skip = 5; + break; + + case 0x7: + skip = 9; + break; + + case 0x8: + skip = 17; + break; + + case 0x9: + skip = -1; + break; + + case 0xa: + skip = -2; + break; + + case 0xb: + skip = -4; + break; + + case 0xc: + skip = -2; + break; + + case 0xd: + skip = -4; + break; + + case 0xe: + skip = -2; + break; + + case 0xf: + skip = -4; + break; + } + + break; + + case 0xe: + case 0xf: + return; + } + + uint32_t dlen; + + if(skip < 0) { + packmsg_read_data_(buf, &dlen, -skip); + + if(hdr >= 0xc7 && hdr <= 0xc9) { + dlen++; + } + } else { + dlen = skip; + } + + if(likely(buf->len >= dlen)) { + buf->ptr += dlen; + buf->len -= dlen; + } else { + packmsg_input_invalidate(buf); + } +} + +/** \brief Skip one object in the input + * \memberof packmsg_input + * + * This function checks the type of the next element. + * In case it is a scalar value (for example, an int or a string), + * it skips just that scalar. If the next element is a map or an array, + * it will recursively skip as many objects as there are in that map or array. + * + * \param buf A pointer to an output buffer iterator. + */ +static inline void packmsg_skip_object(struct packmsg_input *buf) { + if(packmsg_is_array(buf)) { + uint32_t count = packmsg_get_array(buf); + + while(count-- && buf->len >= 0) { + packmsg_skip_object(buf); + } + } else if(packmsg_is_map(buf)) { + uint32_t count = packmsg_get_map(buf); + + while(count-- && buf->len >= 0) { + packmsg_skip_object(buf); + packmsg_skip_object(buf); + } + } else { + packmsg_skip_object(buf); + } +} + +#undef likely +#undef unlikely + +#ifdef __cplusplus +} +#endif -- 2.39.5