/*============================================================================ Copyright (c) 2016-2018, The Linux Foundation. Copyright (c) 2018-2020, Laurence Lundblade. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * Neither the name of The Linux Foundation nor the names of its contributors, nor the name "Laurence Lundblade" may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. =============================================================================*/ /*============================================================================ FILE: UsefulBuf.h DESCRIPTION: General purpose input and output buffers EDIT HISTORY FOR FILE: This section contains comments describing changes made to the module. Notice that changes are listed in reverse chronological order. when who what, where, why -------- ---- -------------------------------------------------- 1/25/2020 llundblade Add some casts so static anlyzers don't complain. 5/21/2019 llundblade #define configs for efficient endianness handling. 5/16/2019 llundblade Add UsefulOutBuf_IsBufferNULL(). 3/23/2019 llundblade Big documentation & style update. No interface change. 3/6/2019 llundblade Add UsefulBuf_IsValue() 12/17/2018 llundblade Remove const from UsefulBuf and UsefulBufC .len 12/13/2018 llundblade Documentation improvements 09/18/2018 llundblade Cleaner distinction between UsefulBuf and UsefulBufC. 02/02/18 llundbla Full support for integers in and out; fix pointer alignment bug. Incompatible change: integers in/out are now in network byte order. 08/12/17 llundbla Added UsefulOutBuf_AtStart and UsefulBuf_Find 06/27/17 llundbla Fix UsefulBuf_Compare() bug. Only affected comparison for < or > for unequal length buffers. Added UsefulBuf_Set() function. 05/30/17 llundbla Functions for NULL UsefulBufs and const / unconst 11/13/16 llundbla Initial Version. =============================================================================*/ #ifndef _UsefulBuf_h #define _UsefulBuf_h /* Configuration Options This code is designed so it will work correctly and completely by default. No configuration is necessary to make it work. None of the following #defines need to be enabled. The code works and is very portable with them all turned off. All configuration options (USEFULBUF_CONFIG_XXX) 1) Reduce code size 2) Improve efficiency 3) Both of the above The efficiency improvements are not large, so the main reason really is to reduce code size. */ /* Endianness Configuration By default, UsefulBuf does not need to know what the endianness of the device is. All the code will run correctly on either big or little endian CPUs. Here's the recipe for configuring the endianness-related #defines to use more efficient CPU/OS/compiler dependent features to reduce code size. Note these only affect the integer arrays (tagged arrays) feature of QCBOR. All other endianness handling in QCBOR is integrated with code that also handles alignment and preferred encoding. The first option is to not define anything. This will work fine on with all CPU's, OS's and compilers. The code for encoding integers will be a little larger and slower. If your CPU is big-endian then define USEFULBUF_CONFIG_BIG_ENDIAN. This will give the most efficient code for big-endian CPUs. It will be small and efficient because there will be no byte swapping. Try defining USEFULBUF_CONFIG_HTON. This will work on most CPU's, OS's and compilers, but not all. On big-endian CPUs this should give the most efficient code, the same as USEFULBUF_CONFIG_BIG_ENDIAN does. On little-endian CPUs it should call the system-defined byte swapping method which is presumably implemented efficiently. In some cases, this will be a dedicated byte swap instruction like Intel's bswap. If USEFULBUF_CONFIG_HTON works and you know your CPU is little-endian, it is also good to define USEFULBUF_CONFIG_LITTLE_ENDIAN. if USEFULBUF_CONFIG_HTON doesn't work and you know your system is little-endian, try defining both USEFULBUF_CONFIG_LITTLE_ENDIAN and USEFULBUF_CONFIG_BSWAP. This should call the most efficient system-defined byte swap method. However, note https://hardwarebug.org/2010/01/14/beware-the-builtins/. Perhaps this is fixed now. Often hton() and ntoh() will call the built-in __builtin_bswapXX()() function, so this size issue could affect USEFULBUF_CONFIG_HTON. Last, run the tests. They must all pass. These #define config options affect the inline implementation of UsefulOutBuf_InsertUint64() and UsefulInputBuf_GetUint64(). They also affect the 16-, 32-bit, float and double versions of these instructions. Since they are inline, they size effect is not in the UsefulBuf object code, but in the calling code. Summary: USEFULBUF_CONFIG_BIG_ENDIAN -- Force configuration to big-endian. USEFULBUF_CONFIG_LITTLE_ENDIAN -- Force to little-endian. USEFULBUF_CONFIG_HTON -- Use hton(), htonl(), ntohl()... to handle big and little-endian with system option. USEFULBUF_CONFIG_BSWAP -- With USEFULBUF_CONFIG_LITTLE_ENDIAN, use __builtin_bswapXX(). */ #if defined(USEFULBUF_CONFIG_BIG_ENDIAN) && defined(USEFULBUF_CONFIG_LITTLE_ENDIAN) #error "Cannot define both USEFULBUF_CONFIG_BIG_ENDIAN and USEFULBUF_CONFIG_LITTLE_ENDIAN" #endif #include // for uint8_t, uint16_t.... #include // for strlen, memcpy, memmove, memset #include // for size_t #ifdef USEFULBUF_CONFIG_HTON #include // for htons, htonl, htonll, ntohs... #endif #ifdef __cplusplus extern "C" { #endif /** @file UsefulBuf.h The goal of this code is to make buffer and pointer manipulation easier and safer when working with binary data. The @ref UsefulBuf, @ref UsefulOutBuf and @ref UsefulInputBuf structures are used to represent buffers rather than ad hoc pointers and lengths. With these it will often be possible to write code that does little or no direct pointer manipulation for copying and formatting data. For example, the QCBOR encoder was written using these and has no less pointer manipulation. While it is true that object code using these functions will be a little larger and slower than a white-knuckle clever use of pointers might be, but not by that much or enough to have an effect for most use cases. For security-oriented code this is highly worthwhile. Clarity, simplicity, reviewability and are more important. There are some extra sanity and double checks in this code to help catch coding errors and simple memory corruption. They are helpful, but not a substitute for proper code review, input validation and such. This code consists of a lot of inline functions and a few that are not. It should not generate very much object code, especially with the optimizer turned up to @c -Os or @c -O3. */ /** @ref UsefulBufC and @ref UsefulBuf are simple data structures to hold a pointer and length for binary data. In C99 this data structure can be passed on the stack making a lot of code cleaner than carrying around a pointer and length as two parameters. This is also conducive to secure coding practice as the length is always carried with the pointer and the convention for handling a pointer and a length is clear. While it might be possible to write buffer and pointer code more efficiently in some use cases, the thought is that unless there is an extreme need for performance (e.g., you are building a gigabit-per-second IP router), it is probably better to have cleaner code you can be most certain about the security of. The non-const @ref UsefulBuf is usually used to refer a buffer to be filled in. The length is the size of the buffer. The const @ref UsefulBufC is usually used to refer to some data that has been filled in. The length is amount of valid data pointed to. A common use is to pass a @ref UsefulBuf to a function, the function fills it in, the function returns a @ref UsefulBufC. The pointer is the same in both. A @ref UsefulBuf is null, it has no value, when @c ptr in it is @c NULL. There are utility functions for the following: - Initializing - Create initialized const @ref UsefulBufC from compiler literals - Create initialized const @ref UsefulBufC from NULL-terminated string - Make an empty @ref UsefulBuf on the stack - Checking whether a @ref UsefulBuf is null, empty or both - Copying, copying with offset, copying head or tail - Comparing and finding substrings See also @ref UsefulOutBuf. It is a richer structure that has both the size of the valid data and the size of the buffer. @ref UsefulBuf is only 16 or 8 bytes on a 64- or 32-bit machine so it can go on the stack and be a function parameter or return value. Another way to look at it is this. C has the NULL-terminated string as a means for handling text strings, but no means or convention for binary strings. Other languages do have such means, Rust, an efficient compiled language, for example. @ref UsefulBuf is kind of like the Useful Pot Pooh gave Eeyore on his birthday. Eeyore's balloon fits beautifully, "it goes in and out like anything". */ typedef struct q_useful_buf_c { const void *ptr; size_t len; } UsefulBufC; /** This non-const @ref UsefulBuf is typically used for some allocated memory that is to be filled in. The @c len is the amount of memory, not the length of the valid data in the buffer. */ typedef struct q_useful_buf { void *ptr; size_t len; } UsefulBuf; /** A null @ref UsefulBufC is one that has no value in the same way a @c NULL pointer has no value. A @ref UsefulBufC is @c NULL when the @c ptr field is @c NULL. It doesn't matter what @c len is. See UsefulBuf_IsEmpty() for the distinction between null and empty. */ #define NULLUsefulBufC ((UsefulBufC) {NULL, 0}) /** A null @ref UsefulBuf is one that has no memory associated the same way @c NULL points to nothing. It does not matter what @c len is. */ #define NULLUsefulBuf ((UsefulBuf) {NULL, 0}) /** @brief Check if a @ref UsefulBuf is @ref NULLUsefulBuf or not. @param[in] UB The UsefulBuf to check. @return 1 if it is @ref NULLUsefulBuf, 0 if not. */ static inline int UsefulBuf_IsNULL(UsefulBuf UB); /** @brief Check if a @ref UsefulBufC is @ref NULLUsefulBufC or not. @param[in] UB The @ref UsefulBufC to check. @return 1 if it is @c NULLUsefulBufC, 0 if not. */ static inline int UsefulBuf_IsNULLC(UsefulBufC UB); /** @brief Check if a @ref UsefulBuf is empty or not. @param[in] UB The @ref UsefulBuf to check. @return 1 if it is empty, 0 if not. An "empty" @ref UsefulBuf is one that has a value and can be considered to be set, but that value is of zero length. It is empty when @c len is zero. It doesn't matter what the @c ptr is. A lot of uses will not need to clearly distinguish a @c NULL @ref UsefulBuf from an empty one and can have the @c ptr @c NULL and the @c len 0. However if a use of @ref UsefulBuf needs to make a distinction then @c ptr should not be @c NULL when the @ref UsefulBuf is considered empty, but not @c NULL. */ static inline int UsefulBuf_IsEmpty(UsefulBuf UB); /** @brief Check if a @ref UsefulBufC is empty or not. @param[in] UB The @ref UsefulBufC to check. @return 1 if it is empty, 0 if not. */ static inline int UsefulBuf_IsEmptyC(UsefulBufC UB); /** @brief Check if a @ref UsefulBuf is @ref NULLUsefulBuf or empty. @param[in] UB The @ref UsefulBuf to check. @return 1 if it is either @ref NULLUsefulBuf or empty, 0 if not. */ static inline int UsefulBuf_IsNULLOrEmpty(UsefulBuf UB); /** @brief Check if a @ref UsefulBufC is @ref NULLUsefulBufC or empty. @param[in] UB The @ref UsefulBufC to check. @return 1 if it is either @ref NULLUsefulBufC or empty, 0 if not. */ static inline int UsefulBuf_IsNULLOrEmptyC(UsefulBufC UB); /** @brief Convert a non-const @ref UsefulBuf to a const @ref UsefulBufC. @param[in] UB The @ref UsefulBuf to convert. @return A @ref UsefulBufC struct. */ static inline UsefulBufC UsefulBuf_Const(const UsefulBuf UB); /** @brief Convert a const @ref UsefulBufC to a non-const @ref UsefulBuf. @param[in] UBC The @ref UsefulBuf to convert. @return A non-const @ref UsefulBuf struct. */ static inline UsefulBuf UsefulBuf_Unconst(const UsefulBufC UBC); /** Convert a literal string to a @ref UsefulBufC. @c szString must be a literal string that @c sizeof() works on. This is better for literal strings than UsefulBuf_FromSZ() because it generates less code. It will not work on non-literal strings. The terminating \0 (NULL) is NOT included in the length! */ #define UsefulBuf_FROM_SZ_LITERAL(szString) \ ((UsefulBufC) {(szString), sizeof(szString)-1}) /** Convert a literal byte array to a @ref UsefulBufC. @c pBytes must be a literal string that @c sizeof() works on. It will not work on non-literal arrays. */ #define UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pBytes) \ ((UsefulBufC) {(pBytes), sizeof(pBytes)}) /** Make an automatic variable named @c name of type @ref UsefulBuf and point it to a stack variable of the given @c size. */ #define UsefulBuf_MAKE_STACK_UB(name, size) \ uint8_t __pBuf##name[(size)];\ UsefulBuf name = {__pBuf##name , sizeof( __pBuf##name )} /** Make a byte array in to a @ref UsefulBuf. This is usually used on stack variables or static variables. Also see @ref UsefulBuf_MAKE_STACK_UB. */ #define UsefulBuf_FROM_BYTE_ARRAY(pBytes) \ ((UsefulBuf) {(pBytes), sizeof(pBytes)}) /** @brief Convert a NULL-terminated string to a @ref UsefulBufC. @param[in] szString The string to convert. @return A @ref UsefulBufC struct. @c UsefulBufC.ptr points to the string so its lifetime must be maintained. The terminating \0 (NULL) is NOT included in the length. */ static inline UsefulBufC UsefulBuf_FromSZ(const char *szString); /** @brief Copy one @ref UsefulBuf into another at an offset. @param[in] Dest Destination buffer to copy into. @param[in] uOffset The byte offset in @c Dest at which to copy to. @param[in] Src The bytes to copy. @return Pointer and length of the copy or @ref NULLUsefulBufC. This fails and returns @ref NULLUsefulBufC if @c offset is beyond the size of @c Dest. This fails and returns @ref NULLUsefulBufC if the @c Src length plus @c uOffset is greater than the length of @c Dest. The results are undefined if @c Dest and @c Src overlap. This assumes that there is valid data in @c Dest up to @c uOffset. The @ref UsefulBufC returned starts at the beginning of @c Dest and goes to @c Src.len @c + @c uOffset. */ UsefulBufC UsefulBuf_CopyOffset(UsefulBuf Dest, size_t uOffset, const UsefulBufC Src); /** @brief Copy one @ref UsefulBuf into another. @param[in] Dest The destination buffer to copy into. @param[out] Src The source to copy from. @return Filled in @ref UsefulBufC on success, @ref NULLUsefulBufC on failure. This fails if @c Src.len is greater than @c Dest.len. Note that like @c memcpy(), the pointers are not checked and this will crash rather than return @ref NULLUsefulBufC if they are @c NULL or invalid. The results are undefined if @c Dest and @c Src overlap. */ static inline UsefulBufC UsefulBuf_Copy(UsefulBuf Dest, const UsefulBufC Src); /** @brief Set all bytes in a @ref UsefulBuf to a value, for example to 0. @param[in] pDest The destination buffer to copy into. @param[in] value The value to set the bytes to. Note that like @c memset(), the pointer in @c pDest is not checked and this will crash if @c NULL or invalid. */ static inline UsefulBufC UsefulBuf_Set(UsefulBuf pDest, uint8_t value); /** @brief Copy a pointer into a @ref UsefulBuf. @param[in,out] Dest The destination buffer to copy into. @param[in] ptr The source to copy from. @param[in] uLen Length of the source; amount to copy. @return 0 on success, 1 on failure. This fails and returns @ref NULLUsefulBufC if @c uLen is greater than @c pDest->len. Note that like @c memcpy(), the pointers are not checked and this will crash, rather than return 1 if they are @c NULL or invalid. */ static inline UsefulBufC UsefulBuf_CopyPtr(UsefulBuf Dest, const void *ptr, size_t uLen); /** @brief Returns a truncation of a @ref UsefulBufC. @param[in] UB The buffer to get the head of. @param[in] uAmount The number of bytes in the head. @return A @ref UsefulBufC that is the head of UB. */ static inline UsefulBufC UsefulBuf_Head(UsefulBufC UB, size_t uAmount); /** @brief Returns bytes from the end of a @ref UsefulBufC. @param[in] UB The buffer to get the tail of. @param[in] uAmount The offset from the start where the tail is to begin. @return A @ref UsefulBufC that is the tail of @c UB or @ref NULLUsefulBufC if @c uAmount is greater than the length of the @ref UsefulBufC. If @c UB.ptr is @c NULL, but @c UB.len is not zero, then the result will be a @ref UsefulBufC with a @c NULL @c ptr and @c len with the length of the tail. */ static inline UsefulBufC UsefulBuf_Tail(UsefulBufC UB, size_t uAmount); /** @brief Compare one @ref UsefulBufC to another. @param[in] UB1 The first buffer to compare. @param[in] UB2 The second buffer to compare. @return 0, positive or negative value. Returns a negative value if @c UB1 if is less than @c UB2. @c UB1 is less than @c UB2 if it is shorter or the first byte that is not the same is less. Returns 0 if the inputs are the same. Returns a positive value if @c UB2 is less than @c UB1. All that is of significance is that the result is positive, negative or 0. (This doesn't return the difference between the first non-matching byte like @c memcmp() ). */ int UsefulBuf_Compare(const UsefulBufC UB1, const UsefulBufC UB2); /** @brief Find first byte that is not a particular byte value. @param[in] UB The destination buffer for byte comparison. @param[in] uValue The byte value to compare to. @return Offset of first byte that isn't @c uValue or @c SIZE_MAX if all bytes are @c uValue. Note that unlike most comparison functions, 0 does not indicate a successful comparison, so the test for match is: UsefulBuf_IsValue(...) == SIZE_MAX If @c UB is null or empty, there is no match and 0 is returned. */ size_t UsefulBuf_IsValue(const UsefulBufC UB, uint8_t uValue); /** @brief Find one @ref UsefulBufC in another. @param[in] BytesToSearch Buffer to search through. @param[in] BytesToFind Buffer with bytes to be found. @return Position of found bytes or @c SIZE_MAX if not found. */ size_t UsefulBuf_FindBytes(UsefulBufC BytesToSearch, UsefulBufC BytesToFind); #if 1 // NOT_DEPRECATED /** Deprecated macro; use @ref UsefulBuf_FROM_SZ_LITERAL instead */ #define SZLiteralToUsefulBufC(szString) \ ((UsefulBufC) {(szString), sizeof(szString)-1}) /** Deprecated macro; use UsefulBuf_MAKE_STACK_UB instead */ #define MakeUsefulBufOnStack(name, size) \ uint8_t __pBuf##name[(size)];\ UsefulBuf name = {__pBuf##name , sizeof( __pBuf##name )} /** Deprecated macro; use @ref UsefulBuf_FROM_BYTE_ARRAY_LITERAL instead */ #define ByteArrayLiteralToUsefulBufC(pBytes) \ ((UsefulBufC) {(pBytes), sizeof(pBytes)}) /** Deprecated function; use UsefulBuf_Unconst() instead */ static inline UsefulBuf UsefulBufC_Unconst(const UsefulBufC UBC) { return (UsefulBuf){(void *)UBC.ptr, UBC.len}; } #endif /** @brief Copy a @c float to a @c uint32_t. @param[in] f Float value to copy. @return A @c uint32_t with the float bits. Convenience function to avoid type punning, compiler warnings and such. The optimizer usually reduces this to a simple assignment. This is a crusty corner of C. */ static inline uint32_t UsefulBufUtil_CopyFloatToUint32(float f); /** @brief Copy a @c double to a @c uint64_t. @param[in] d Double value to copy. @return A @c uint64_t with the double bits. Convenience function to avoid type punning, compiler warnings and such. The optimizer usually reduces this to a simple assignment. This is a crusty corner of C. */ static inline uint64_t UsefulBufUtil_CopyDoubleToUint64(double d); /** @brief Copy a @c uint32_t to a @c float. @param[in] u32 Integer value to copy. @return The value as a @c float. Convenience function to avoid type punning, compiler warnings and such. The optimizer usually reduces this to a simple assignment. This is a crusty corner of C. */ static inline float UsefulBufUtil_CopyUint32ToFloat(uint32_t u32); /** @brief Copy a @c uint64_t to a @c double. @param[in] u64 Integer value to copy. @return The value as a @c double. Convenience function to avoid type punning, compiler warnings and such. The optimizer usually reduces this to a simple assignment. This is a crusty corner of C. */ static inline double UsefulBufUtil_CopyUint64ToDouble(uint64_t u64); /** UsefulOutBuf is a structure and functions (an object) for serializing data into a buffer when encoding a network protocol or writing data to file. The main idea is that all the pointer manipulation is performed by @ref UsefulOutBuf functions so the caller doesn't have to do any pointer manipulation. The pointer manipulation is centralized. This code will have been reviewed and written carefully so it spares the caller of much of this work and results in safer code with less work. The @ref UsefulOutBuf methods that add data to the output buffer always check the length and will never write off the end of the output buffer. If an attempt to add data that will not fit is made, an internal error flag will be set and further attempts to add data will not do anything. There is no way to ever write off the end of that buffer when calling the @c UsefulOutBuf_AddXxx() and @c UsefulOutBuf_InsertXxx() functions. The functions to add data do not return an error. The working model is that all calls to add data are made without an error check. Errors are just checked for once after all the data has been added before the and before serialized data is to be used. This makes the calling code cleaner. There is a utility function to get the error status anytime along the way for a special circumstance. There are functions to see how much room is left and see if some data will fit too, but their use is generally not necessary. The general call flow is: - Initialize by calling @ref UsefulOutBuf_Init(). The output buffer given to it can be from the heap, stack or otherwise. @ref UsefulOutBuf_MakeOnStack is a convenience macro that makes a buffer on the stack and initializes it. - Call methods like UsefulOutBuf_InsertString(), UsefulOutBuf_AppendUint32() and UsefulOutBuf_InsertUsefulBuf() to output data. The append calls add data to the end of the valid data. The insert calls take a position argument. - Call UsefulOutBuf_OutUBuf() or UsefulOutBuf_CopyOut() to see there were no errors and to get the serialized output bytes. @ref UsefulOutBuf can be used in a size calculation mode to calculate the size of output that would be generated. This is useful to calculate the size of a buffer that is to be allocated to hold the output. To use @ref UsefulOutBuf in this mode, call UsefulOutBuf_Init() with the @c Storage @ref UsefulBuf as @c (UsefulBuf){NULL,MAX_UINT32}. Then call all the Insert and Add functions. No attempt will made to actually copy data, so only the lengths have to be valid for these calls. Methods like UsefulOutBuf_InsertUint64() always output in network bytes order (big endian). The possible errors are: - The @ref UsefulOutBuf was not initialized or was corrupted. - An attempt was made to add data that will not fit. - An attempt was made to insert data at a position beyond the end of the buffer. - An attempt was made to insert data at a position beyond the valid data in the buffer. Some inexpensive simple sanity checks are performed before every data addition to guard against use of an uninitialized or corrupted UsefulOutBuf. This has been used to create a CBOR encoder. The CBOR encoder has almost no pointer manipulation in it, is easier to read, and easier to review. A @ref UsefulOutBuf is small and can go on the stack: - 32 bytes (27 bytes plus alignment padding) on a 64-bit machine - 16 bytes (15 bytes plus alignment padding) on a 32-bit machines */ typedef struct useful_out_buf { // PRIVATE DATA STRUCTURE UsefulBuf UB; // Memory that is being output to size_t data_len; // length of the data uint16_t magic; // Used to detect corruption and lack of initialization uint8_t err; } UsefulOutBuf; /** @brief Initialize and supply the actual output buffer. @param[out] pUOutBuf The @ref UsefulOutBuf to initialize. @param[in] Storage Buffer to output into. Initializes the @ref UsefulOutBuf with storage. Sets the current position to the beginning of the buffer clears the error. This must be called before the @ref UsefulOutBuf is used. */ void UsefulOutBuf_Init(UsefulOutBuf *pUOutBuf, UsefulBuf Storage); /** Convenience macro to make a @ref UsefulOutBuf on the stack and initialize it with a stack buffer of the given size. The variable will be named @c name. */ #define UsefulOutBuf_MakeOnStack(name, size) \ uint8_t __pBuf##name[(size)];\ UsefulOutBuf name;\ UsefulOutBuf_Init(&(name), (UsefulBuf){__pBuf##name, (size)}); /** @brief Reset a @ref UsefulOutBuf for re use @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf This sets the amount of data in the output buffer to none and clears the error state. The output buffer is still the same one and size as from the UsefulOutBuf_Init() call. This doesn't zero the data, just resets to 0 bytes of valid data. */ static inline void UsefulOutBuf_Reset(UsefulOutBuf *pUOutBuf); /** @brief Returns position of end of data in the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @return position of end of data. On a freshly initialized @ref UsefulOutBuf with no data added, this will return 0. After 10 bytes have been added, it will return 10 and so on. Generally callers will not need this function for most uses of @ref UsefulOutBuf. */ static inline size_t UsefulOutBuf_GetEndPosition(UsefulOutBuf *pUOutBuf); /** @brief Returns whether any data has been added to the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @return 1 if output position is at start. */ static inline int UsefulOutBuf_AtStart(UsefulOutBuf *pUOutBuf); /** @brief Inserts bytes into the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] NewData The bytes to insert. @param[in] uPos Index in output buffer at which to insert. @c NewData is the pointer and length for the bytes to be added to the output buffer. There must be room in the output buffer for all of @c NewData or an error will occur. The insertion point must be between 0 and the current valid data. If not, an error will occur. Appending data to the output buffer is achieved by inserting at the end of the valid data. This can be retrieved by calling UsefulOutBuf_GetEndPosition(). When insertion is performed, the bytes between the insertion point and the end of data previously added to the output buffer are slid to the right to make room for the new data. Overlapping buffers are OK. @c NewData can point to data in the output buffer. If an error occurs an error state is set in the @ref UsefulOutBuf. No error is returned. All subsequent attempts to add data will do nothing. The intended use is that all additions are made without checking for an error. The error will be taken into account when UsefulOutBuf_OutUBuf() returns @c NullUsefulBufC. UsefulOutBuf_GetError() can also be called to check for an error. */ void UsefulOutBuf_InsertUsefulBuf(UsefulOutBuf *pUOutBuf, UsefulBufC NewData, size_t uPos); /** @brief Insert a data buffer into the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] pBytes Pointer to the bytes to insert @param[in] uLen Length of the bytes to insert @param[in] uPos Index in output buffer at which to insert See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with the difference being a pointer and length is passed in rather than an @ref UsefulBufC. */ static inline void UsefulOutBuf_InsertData(UsefulOutBuf *pUOutBuf, const void *pBytes, size_t uLen, size_t uPos); /** @brief Insert a NULL-terminated string into the UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] szString NULL-terminated string to insert. @param[in] uPos Index in output buffer at which to insert. */ static inline void UsefulOutBuf_InsertString(UsefulOutBuf *pUOutBuf, const char *szString, size_t uPos); /** @brief Insert a byte into the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the UsefulOutBuf. @param[in] byte Bytes to insert. @param[in] uPos Index in output buffer at which to insert. See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with the difference being a single byte is to be inserted. */ static inline void UsefulOutBuf_InsertByte(UsefulOutBuf *pUOutBuf, uint8_t byte, size_t uPos); /** @brief Insert a 16-bit integer into the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] uInteger16 Integer to insert. @param[in] uPos Index in output buffer at which to insert. See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with the difference being a two-byte integer is to be inserted. The integer will be inserted in network byte order (big endian). */ static inline void UsefulOutBuf_InsertUint16(UsefulOutBuf *pUOutBuf, uint16_t uInteger16, size_t uPos); /** @brief Insert a 32-bit integer into the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] uInteger32 Integer to insert. @param[in] uPos Index in output buffer at which to insert. See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with the difference being a four-byte integer is to be inserted. The integer will be inserted in network byte order (big endian). */ static inline void UsefulOutBuf_InsertUint32(UsefulOutBuf *pUOutBuf, uint32_t uInteger32, size_t uPos); /** @brief Insert a 64-bit integer into the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] uInteger64 Integer to insert. @param[in] uPos Index in output buffer at which to insert. See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with the difference being an eight-byte integer is to be inserted. The integer will be inserted in network byte order (big endian). */ static inline void UsefulOutBuf_InsertUint64(UsefulOutBuf *pUOutBuf, uint64_t uInteger64, size_t uPos); /** @brief Insert a @c float into the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] f @c float to insert. @param[in] uPos Index in output buffer at which to insert. See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with the difference being a @c float is to be inserted. The @c float will be inserted in network byte order (big endian). */ static inline void UsefulOutBuf_InsertFloat(UsefulOutBuf *pUOutBuf, float f, size_t uPos); /** @brief Insert a @c double into the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] d @c double to insert. @param[in] uPos Index in output buffer at which to insert. See UsefulOutBuf_InsertUsefulBuf() for details. This is the same with the difference being a @c double is to be inserted. The @c double will be inserted in network byte order (big endian). */ static inline void UsefulOutBuf_InsertDouble(UsefulOutBuf *pUOutBuf, double d, size_t uPos); /** @brief Append a @ref UsefulBuf into the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] NewData The @ref UsefulBuf with the bytes to append. See UsefulOutBuf_InsertUsefulBuf() for details. This does the same with the insertion point at the end of the valid data. */ static inline void UsefulOutBuf_AppendUsefulBuf(UsefulOutBuf *pUOutBuf, UsefulBufC NewData); /** @brief Append bytes to the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] pBytes Pointer to bytes to append. @param[in] uLen Length of @c pBytes to append. See UsefulOutBuf_InsertData() for details. This does the same with the insertion point at the end of the valid data. */ static inline void UsefulOutBuf_AppendData(UsefulOutBuf *pUOutBuf, const void *pBytes, size_t uLen); /** @brief Append a NULL-terminated string to the @ref UsefulOutBuf @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] szString NULL-terminated string to append. */ static inline void UsefulOutBuf_AppendString(UsefulOutBuf *pUOutBuf, const char *szString); /** @brief Append a byte to the @ref UsefulOutBuf @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] byte Bytes to append. See UsefulOutBuf_InsertByte() for details. This does the same with the insertion point at the end of the valid data. */ static inline void UsefulOutBuf_AppendByte(UsefulOutBuf *pUOutBuf, uint8_t byte); /** @brief Append an integer to the @ref UsefulOutBuf @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] uInteger16 Integer to append. See UsefulOutBuf_InsertUint16() for details. This does the same with the insertion point at the end of the valid data. The integer will be appended in network byte order (big endian). */ static inline void UsefulOutBuf_AppendUint16(UsefulOutBuf *pUOutBuf, uint16_t uInteger16); /** @brief Append an integer to the @ref UsefulOutBuf @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] uInteger32 Integer to append. See UsefulOutBuf_InsertUint32() for details. This does the same with the insertion point at the end of the valid data. The integer will be appended in network byte order (big endian). */ static inline void UsefulOutBuf_AppendUint32(UsefulOutBuf *pUOutBuf, uint32_t uInteger32); /** @brief Append an integer to the @ref UsefulOutBuf @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] uInteger64 Integer to append. See UsefulOutBuf_InsertUint64() for details. This does the same with the insertion point at the end of the valid data. The integer will be appended in network byte order (big endian). */ static inline void UsefulOutBuf_AppendUint64(UsefulOutBuf *pUOutBuf, uint64_t uInteger64); /** @brief Append a @c float to the @ref UsefulOutBuf @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] f @c float to append. See UsefulOutBuf_InsertFloat() for details. This does the same with the insertion point at the end of the valid data. The float will be appended in network byte order (big endian). */ static inline void UsefulOutBuf_AppendFloat(UsefulOutBuf *pUOutBuf, float f); /** @brief Append a @c double to the @ref UsefulOutBuf @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[in] d @c double to append. See UsefulOutBuf_InsertDouble() for details. This does the same with the insertion point at the end of the valid data. The double will be appended in network byte order (big endian). */ static inline void UsefulOutBuf_AppendDouble(UsefulOutBuf *pUOutBuf, double d); /** @brief Returns the current error status. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @return 0 if all OK, 1 on error. This is the error status since the call to either UsefulOutBuf_Reset() of UsefulOutBuf_Init(). Once it goes into error state it will stay until one of those functions is called. Possible error conditions are: - bytes to be inserted will not fit - insertion point is out of buffer or past valid data - current position is off end of buffer (probably corrupted or uninitialized) - detect corruption / uninitialized by bad magic number */ static inline int UsefulOutBuf_GetError(UsefulOutBuf *pUOutBuf); /** @brief Returns number of bytes unused used in the output buffer. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @return Number of unused bytes or zero. Because of the error handling strategy and checks in UsefulOutBuf_InsertUsefulBuf() it is usually not necessary to use this. */ static inline size_t UsefulOutBuf_RoomLeft(UsefulOutBuf *pUOutBuf); /** @brief Returns 1 if some number of bytes will fit in the @ref UsefulOutBuf. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf @param[in] uLen Number of bytes for which to check @return 1 if @c uLen bytes will fit, 0 if not. Because of the error handling strategy and checks in UsefulOutBuf_InsertUsefulBuf() it is usually not necessary to use this. */ static inline int UsefulOutBuf_WillItFit(UsefulOutBuf *pUOutBuf, size_t uLen); /** @brief Returns 1 if buffer given to UsefulOutBuf_Init() was @c NULL. @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf @return 1 if buffer given to UsefulOutBuf_Init() was @c NULL. Giving a @c NULL output buffer to UsefulOutBuf_Init() is used when just calculating the length of the encoded data. */ static inline int UsefulOutBuf_IsBufferNULL(UsefulOutBuf *pUOutBuf); /** @brief Returns the resulting valid data in a UsefulOutBuf @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @return The valid data in @ref UsefulOutBuf or @ref NULLUsefulBufC if there was an error adding data. The storage for the returned data is the @c Storage parameter passed to UsefulOutBuf_Init(). See also UsefulOutBuf_CopyOut(). This can be called anytime and many times to get intermediate results. It doesn't change the data or reset the current position so you can keep adding data. */ UsefulBufC UsefulOutBuf_OutUBuf(UsefulOutBuf *pUOutBuf); /** @brief Copies the valid data into a supplied buffer @param[in] pUOutBuf Pointer to the @ref UsefulOutBuf. @param[out] Dest The destination buffer to copy into. @return Pointer and length of copied data or @c NULLUsefulBufC if it will not fit in the @c Dest buffer. This is the same as UsefulOutBuf_OutUBuf() except it copies the data to @c Dest. */ UsefulBufC UsefulOutBuf_CopyOut(UsefulOutBuf *pUOutBuf, UsefulBuf Dest); /** @ref UsefulInputBuf is the counterpart to @ref UsefulOutBuf and is for parsing data read or received. Initialize it with the data from the network. Then use the functions here to get data chunks of various types. A position cursor is maintained internally. As long as the functions here are used, there will never be a reference off the end of the given buffer. This is true even if they care called incorrectly, an attempt is made to seek of the end of the buffer, etc. This makes it easier to write safe and correct code. For example, the QCBOR decoder implementation is safer and easier to review through its use of @ref UsefulInputBuf. @ref UsefulInputBuf maintains an internal error state. The intended use is that data chunks can be fetched without error checking until the end. Once data has been requested off the end of the buffer, the error state is entered. In the error state the @c UsefulInputBuf_GetXxxx() functions return 0, or @c NULL or @ref NULLUsefulBufC. As long as null are not dereferenced, the error check can be put off until the end, simplifying the calling code. The integer and float parsing expects network byte order (big endian). Network byte order is what is used by TCP/IP, CBOR and most internet protocols. Lots of inline functions are used to keep code size down. The code optimizer, particularly with the @c -Os or @c -O3, also reduces code size a lot. The only non-inline code is UsefulInputBuf_GetBytes() which is less than 100 bytes so use of @ref UsefulInputBuf doesn't add much code for all the messy hard-to-get right issues with parsing in C that is solves. The parse context size is: - 64-bit machine: 16 + 8 + 2 + 1 (5 bytes padding to align) = 32 bytes - 32-bit machine: 8 + 4 + 2 + 1 (1 byte padding to align) = 16 bytes */ typedef struct useful_input_buf { // PRIVATE DATA STRUCTURE UsefulBufC UB; // Data being parsed size_t cursor; // Current offset in data being parse uint16_t magic; // Check for corrupted or uninitialized UsefulInputBuf uint8_t err; // Set request goes off end or magic number is bad } UsefulInputBuf; #define UIB_MAGIC (0xB00F) /** @brief Initialize the UsefulInputBuf structure before use. @param[in] pUInBuf Pointer to the UsefulInputBuf instance. @param[in] UB The data to parse. */ static inline void UsefulInputBuf_Init(UsefulInputBuf *pUInBuf, UsefulBufC UB); /** @brief Returns current position in input buffer. @param[in] pUInBuf Pointer to the UsefulInputBuf. @return Integer position of the cursor. The position that the next bytes will be returned from. */ static size_t UsefulInputBuf_Tell(UsefulInputBuf *pUInBuf); /** @brief Sets the current position in input buffer. @param[in] pUInBuf Pointer to the UsefulInputBuf. @param[in] uPos Position to set to. If the position is off the end of the input buffer, the error state is entered, and all functions will do nothing. Seeking to a valid position in the buffer will not reset the error state. Only re initialization will do that. */ static void UsefulInputBuf_Seek(UsefulInputBuf *pUInBuf, size_t uPos); /** @brief Returns the number of bytes from the cursor to the end of the buffer, the unconsumed bytes. @param[in] pUInBuf Pointer to the UsefulInputBuf. @return Number of bytes unconsumed or 0 on error. This is a critical function for input length validation. Returns 0 if the cursor it invalid or corruption of the structure is detected. */ static size_t UsefulInputBuf_BytesUnconsumed(UsefulInputBuf *pUInBuf); /** @brief Check if there are any unconsumed bytes. @param[in] pUInBuf Pointer to the UsefulInputBuf. @param[in] uLen Number of bytes to check availability for. @return 1 if @c uLen bytes are available after the cursor, and 0 if not. */ static int UsefulInputBuf_BytesAvailable(UsefulInputBuf *pUInBuf, size_t uLen); /** @brief Get pointer to bytes out of the input buffer. @param[in] pUInBuf Pointer to the UsefulInputBuf. @param[in] uNum Number of bytes to get. @return Pointer to bytes. This consumes @c uNum bytes from the input buffer. It returns a pointer to the start of the @c uNum bytes. If there are not @c uNum bytes in the input buffer, @c NULL will be returned and an error will be set. It advances the current position by @c uNum bytes. */ const void * UsefulInputBuf_GetBytes(UsefulInputBuf *pUInBuf, size_t uNum); /** @brief Get @ref UsefulBuf out of the input buffer. @param[in] pUInBuf Pointer to the UsefulInputBuf. @param[in] uNum Number of bytes to get. @return A @ref UsefulBufC with ptr and length of bytes consumed. This consumes @c uNum bytes from the input buffer and returns the pointer and length for them as a @ref UsefulBufC. The length returned will always be @c uNum. If there are not @c uNum bytes in the input buffer, @ref NULLUsefulBufC will be returned and the error state is set. It advances the current position by @c uNum bytes. */ static inline UsefulBufC UsefulInputBuf_GetUsefulBuf(UsefulInputBuf *pUInBuf, size_t uNum); /** @brief Get a byte out of the input buffer. @param[in] pUInBuf Pointer to the @ref UsefulInputBuf. @return The byte. This consumes 1 byte from the input buffer. It returns the byte. If there is not 1 byte in the buffer, 0 will be returned for the byte and an error set internally. You must check the error at some point to know whether the 0 was the real value or just returned in error, but you may not have to do that right away. Check the error state with UsefulInputBuf_GetError(). You can also know you are in the error state if UsefulInputBuf_GetBytes() returns @c NULL or the @c ptr from UsefulInputBuf_GetUsefulBuf() is @c NULL. It advances the current position by 1 byte. */ static inline uint8_t UsefulInputBuf_GetByte(UsefulInputBuf *pUInBuf); /** @brief Get a @c uint16_t out of the input buffer. @param[in] pUInBuf Pointer to the UsefulInputBuf. @return The @c uint16_t. See UsefulInputBuf_GetByte(). This works the same, except it returns a @c uint16_t and two bytes are consumed. The input bytes must be in network order (big endian). */ static inline uint16_t UsefulInputBuf_GetUint16(UsefulInputBuf *pUInBuf); /** @brief Get a uint32_t out of the input buffer. @param[in] pUInBuf Pointer to the UsefulInputBuf. @return The @c uint32_t. See UsefulInputBuf_GetByte(). This works the same, except it returns a @c uint32_t and four bytes are consumed. The input bytes must be in network order (big endian). */ static uint32_t UsefulInputBuf_GetUint32(UsefulInputBuf *pUInBuf); /** @brief Get a uint64_t out of the input buffer. @param[in] pUInBuf Pointer to the UsefulInputBuf. @return The uint64_t. See UsefulInputBuf_GetByte(). This works the same, except it returns a @c uint64_t and eight bytes are consumed. The input bytes must be in network order (big endian). */ static uint64_t UsefulInputBuf_GetUint64(UsefulInputBuf *pUInBuf); /** @brief Get a float out of the input buffer. @param[in] pUInBuf Pointer to the UsefulInputBuf. @return The float. See UsefulInputBuf_GetByte(). This works the same, except it returns a float and four bytes are consumed. The input bytes must be in network order (big endian). */ static float UsefulInputBuf_GetFloat(UsefulInputBuf *pUInBuf); /** @brief Get a double out of the input buffer. @param[in] pUInBuf Pointer to the UsefulInputBuf. @return The double. See UsefulInputBuf_GetByte(). This works the same, except it returns a double and eight bytes are consumed. The input bytes must be in network order (big endian). */ static double UsefulInputBuf_GetDouble(UsefulInputBuf *pUInBuf); /** @brief Get the error status. @param[in] pUInBuf Pointer to the @ref UsefulInputBuf. @return 0 if there is no error, 1 if there is. The error state is entered for one of these reasons: - Attempt to fetch data past the end of the buffer - Attempt to seek to a position past the end of the buffer - Attempt to get data from an uninitialized or corrupt instance of @ref UsefulInputBuf Once in the error state, it can only be cleared by calling UsefulInputBuf_Init(). You may be able to only check the error state at the end after all the UsefulInputBuf_GetXxxx() calls have been made, but if what you get later depends on what you get sooner you cannot. For example, if you get a length or count of following items you will have to check the error. */ static int UsefulInputBuf_GetError(UsefulInputBuf *pUInBuf); /*---------------------------------------------------------- Inline implementations. */ static inline int UsefulBuf_IsNULL(UsefulBuf UB) { return !UB.ptr; } static inline int UsefulBuf_IsNULLC(UsefulBufC UB) { return !UB.ptr; } static inline int UsefulBuf_IsEmpty(UsefulBuf UB) { return !UB.len; } static inline int UsefulBuf_IsEmptyC(UsefulBufC UB) { return !UB.len; } static inline int UsefulBuf_IsNULLOrEmpty(UsefulBuf UB) { return UsefulBuf_IsEmpty(UB) || UsefulBuf_IsNULL(UB); } static inline int UsefulBuf_IsNULLOrEmptyC(UsefulBufC UB) { return UsefulBuf_IsEmptyC(UB) || UsefulBuf_IsNULLC(UB); } static inline UsefulBufC UsefulBuf_Const(const UsefulBuf UB) { return (UsefulBufC){UB.ptr, UB.len}; } static inline UsefulBuf UsefulBuf_Unconst(const UsefulBufC UBC) { return (UsefulBuf){(void *)UBC.ptr, UBC.len}; } static inline UsefulBufC UsefulBuf_FromSZ(const char *szString) { return ((UsefulBufC) {szString, strlen(szString)}); } static inline UsefulBufC UsefulBuf_Copy(UsefulBuf Dest, const UsefulBufC Src) { return UsefulBuf_CopyOffset(Dest, 0, Src); } static inline UsefulBufC UsefulBuf_Set(UsefulBuf pDest, uint8_t value) { memset(pDest.ptr, value, pDest.len); return (UsefulBufC){pDest.ptr, pDest.len}; } static inline UsefulBufC UsefulBuf_CopyPtr(UsefulBuf Dest, const void *ptr, size_t len) { return UsefulBuf_Copy(Dest, (UsefulBufC){ptr, len}); } static inline UsefulBufC UsefulBuf_Head(UsefulBufC UB, size_t uAmount) { if(uAmount > UB.len) { return NULLUsefulBufC; } return (UsefulBufC){UB.ptr, uAmount}; } static inline UsefulBufC UsefulBuf_Tail(UsefulBufC UB, size_t uAmount) { UsefulBufC ReturnValue; if(uAmount > UB.len) { ReturnValue = NULLUsefulBufC; } else if(UB.ptr == NULL) { ReturnValue = (UsefulBufC){NULL, UB.len - uAmount}; } else { ReturnValue = (UsefulBufC){(uint8_t *)UB.ptr + uAmount, UB.len - uAmount}; } return ReturnValue; } static inline uint32_t UsefulBufUtil_CopyFloatToUint32(float f) { uint32_t u32; memcpy(&u32, &f, sizeof(uint32_t)); return u32; } static inline uint64_t UsefulBufUtil_CopyDoubleToUint64(double d) { uint64_t u64; memcpy(&u64, &d, sizeof(uint64_t)); return u64; } static inline double UsefulBufUtil_CopyUint64ToDouble(uint64_t u64) { double d; memcpy(&d, &u64, sizeof(uint64_t)); return d; } static inline float UsefulBufUtil_CopyUint32ToFloat(uint32_t u32) { float f; memcpy(&f, &u32, sizeof(uint32_t)); return f; } static inline void UsefulOutBuf_Reset(UsefulOutBuf *pMe) { pMe->data_len = 0; pMe->err = 0; } static inline size_t UsefulOutBuf_GetEndPosition(UsefulOutBuf *pMe) { return pMe->data_len; } static inline int UsefulOutBuf_AtStart(UsefulOutBuf *pMe) { return 0 == pMe->data_len; } static inline void UsefulOutBuf_InsertData(UsefulOutBuf *pMe, const void *pBytes, size_t uLen, size_t uPos) { UsefulBufC Data = {pBytes, uLen}; UsefulOutBuf_InsertUsefulBuf(pMe, Data, uPos); } static inline void UsefulOutBuf_InsertString(UsefulOutBuf *pMe, const char *szString, size_t uPos) { UsefulOutBuf_InsertUsefulBuf(pMe, (UsefulBufC){szString, strlen(szString)}, uPos); } static inline void UsefulOutBuf_InsertByte(UsefulOutBuf *me, uint8_t byte, size_t uPos) { UsefulOutBuf_InsertData(me, &byte, 1, uPos); } static inline void UsefulOutBuf_InsertUint16(UsefulOutBuf *me, uint16_t uInteger16, size_t uPos) { // See UsefulOutBuf_InsertUint64() for comments on this code const void *pBytes; #if defined(USEFULBUF_CONFIG_BIG_ENDIAN) pBytes = &uInteger16; #elif defined(USEFULBUF_CONFIG_HTON) uint16_t uTmp = htons(uInteger16); pBytes = &uTmp; #elif defined(USEFULBUF_CONFIG_LITTLE_ENDIAN) && defined(USEFULBUF_CONFIG_BSWAP) uint16_t uTmp = __builtin_bswap16(uInteger16); pBytes = &uTmp; #else uint8_t aTmp[2]; aTmp[0] = (uint8_t)((uInteger16 & 0xff00) >> 8); aTmp[1] = (uint8_t)(uInteger16 & 0xff); pBytes = aTmp; #endif UsefulOutBuf_InsertData(me, pBytes, 2, uPos); } static inline void UsefulOutBuf_InsertUint32(UsefulOutBuf *pMe, uint32_t uInteger32, size_t uPos) { // See UsefulOutBuf_InsertUint64() for comments on this code const void *pBytes; #if defined(USEFULBUF_CONFIG_BIG_ENDIAN) pBytes = &uInteger32; #elif defined(USEFULBUF_CONFIG_HTON) uint32_t uTmp = htonl(uInteger32); pBytes = &uTmp; #elif defined(USEFULBUF_CONFIG_LITTLE_ENDIAN) && defined(USEFULBUF_CONFIG_BSWAP) uint32_t uTmp = __builtin_bswap32(uInteger32); pBytes = &uTmp; #else uint8_t aTmp[4]; aTmp[0] = (uint8_t)((uInteger32 & 0xff000000) >> 24); aTmp[1] = (uint8_t)((uInteger32 & 0xff0000) >> 16); aTmp[2] = (uint8_t)((uInteger32 & 0xff00) >> 8); aTmp[3] = (uint8_t)(uInteger32 & 0xff); pBytes = aTmp; #endif UsefulOutBuf_InsertData(pMe, pBytes, 4, uPos); } static inline void UsefulOutBuf_InsertUint64(UsefulOutBuf *pMe, uint64_t uInteger64, size_t uPos) { const void *pBytes; #if defined(USEFULBUF_CONFIG_BIG_ENDIAN) // We have been told explicitly we are running on a big-endian // machine. Network byte order is big endian, so just copy. There // is no issue with alignment here because uInter64 is always // aligned (and it doesn't matter if pBytes is aligned). pBytes = &uInteger64; #elif defined(USEFULBUF_CONFIG_HTON) // Use system function to handle big- and little-endian. This works // on both big- and little-endian machines, but hton() is not // always available or in a standard place so it is not used by // default. With some compilers and CPUs the code for this is very // compact through use of a special swap instruction and on // big-endian machines hton() will reduce to nothing. uint64_t uTmp = htonll(uInteger64); pBytes = &uTmp; #elif defined(USEFULBUF_CONFIG_LITTLE_ENDIAN) && defined(USEFULBUF_CONFIG_BSWAP) // Use built-in function for byte swapping. This usually compiles // to an efficient special byte swap instruction. Unlike hton() it // does not do this conditionally on the CPU endianness, so this // code is also conditional on USEFULBUF_CONFIG_LITTLE_ENDIAN uint64_t uTmp = __builtin_bswap64(uInteger64); pBytes = &uTmp; #else // Default which works on every CPU with no dependency on anything // from the CPU, compiler, libraries or OS. This always works, but // it is usually a little larger and slower than hton(). uint8_t aTmp[8]; aTmp[0] = (uint8_t)((uInteger64 & 0xff00000000000000) >> 56); aTmp[1] = (uint8_t)((uInteger64 & 0xff000000000000) >> 48); aTmp[2] = (uint8_t)((uInteger64 & 0xff0000000000) >> 40); aTmp[3] = (uint8_t)((uInteger64 & 0xff00000000) >> 32); aTmp[4] = (uint8_t)((uInteger64 & 0xff000000) >> 24); aTmp[5] = (uint8_t)((uInteger64 & 0xff0000) >> 16); aTmp[6] = (uint8_t)((uInteger64 & 0xff00) >> 8); aTmp[7] = (uint8_t)(uInteger64 & 0xff); pBytes = aTmp; #endif // Do the insert UsefulOutBuf_InsertData(pMe, pBytes, sizeof(uint64_t), uPos); } static inline void UsefulOutBuf_InsertFloat(UsefulOutBuf *pMe, float f, size_t uPos) { UsefulOutBuf_InsertUint32(pMe, UsefulBufUtil_CopyFloatToUint32(f), uPos); } static inline void UsefulOutBuf_InsertDouble(UsefulOutBuf *pMe, double d, size_t uPos) { UsefulOutBuf_InsertUint64(pMe, UsefulBufUtil_CopyDoubleToUint64(d), uPos); } static inline void UsefulOutBuf_AppendUsefulBuf(UsefulOutBuf *pMe, UsefulBufC NewData) { // An append is just a insert at the end UsefulOutBuf_InsertUsefulBuf(pMe, NewData, UsefulOutBuf_GetEndPosition(pMe)); } static inline void UsefulOutBuf_AppendData(UsefulOutBuf *pMe, const void *pBytes, size_t uLen) { UsefulBufC Data = {pBytes, uLen}; UsefulOutBuf_AppendUsefulBuf(pMe, Data); } static inline void UsefulOutBuf_AppendString(UsefulOutBuf *pMe, const char *szString) { UsefulOutBuf_AppendUsefulBuf(pMe, (UsefulBufC){szString, strlen(szString)}); } static inline void UsefulOutBuf_AppendByte(UsefulOutBuf *pMe, uint8_t byte) { UsefulOutBuf_AppendData(pMe, &byte, 1); } static inline void UsefulOutBuf_AppendUint16(UsefulOutBuf *pMe, uint16_t uInteger16) { UsefulOutBuf_InsertUint16(pMe, uInteger16, UsefulOutBuf_GetEndPosition(pMe)); } static inline void UsefulOutBuf_AppendUint32(UsefulOutBuf *pMe, uint32_t uInteger32) { UsefulOutBuf_InsertUint32(pMe, uInteger32, UsefulOutBuf_GetEndPosition(pMe)); } static inline void UsefulOutBuf_AppendUint64(UsefulOutBuf *pMe, uint64_t uInteger64) { UsefulOutBuf_InsertUint64(pMe, uInteger64, UsefulOutBuf_GetEndPosition(pMe)); } static inline void UsefulOutBuf_AppendFloat(UsefulOutBuf *pMe, float f) { UsefulOutBuf_InsertFloat(pMe, f, UsefulOutBuf_GetEndPosition(pMe)); } static inline void UsefulOutBuf_AppendDouble(UsefulOutBuf *pMe, double d) { UsefulOutBuf_InsertDouble(pMe, d, UsefulOutBuf_GetEndPosition(pMe)); } static inline int UsefulOutBuf_GetError(UsefulOutBuf *pMe) { return pMe->err; } static inline size_t UsefulOutBuf_RoomLeft(UsefulOutBuf *pMe) { return pMe->UB.len - pMe->data_len; } static inline int UsefulOutBuf_WillItFit(UsefulOutBuf *pMe, size_t uLen) { return uLen <= UsefulOutBuf_RoomLeft(pMe); } static inline int UsefulOutBuf_IsBufferNULL(UsefulOutBuf *pMe) { return pMe->UB.ptr == NULL; } static inline void UsefulInputBuf_Init(UsefulInputBuf *pMe, UsefulBufC UB) { pMe->cursor = 0; pMe->err = 0; pMe->magic = UIB_MAGIC; pMe->UB = UB; } static inline size_t UsefulInputBuf_Tell(UsefulInputBuf *pMe) { return pMe->cursor; } static inline void UsefulInputBuf_Seek(UsefulInputBuf *pMe, size_t uPos) { if(uPos > pMe->UB.len) { pMe->err = 1; } else { pMe->cursor = uPos; } } static inline size_t UsefulInputBuf_BytesUnconsumed(UsefulInputBuf *pMe) { // Code Reviewers: THIS FUNCTION DOES POINTER MATH // Magic number is messed up. Either the structure got overwritten // or was never initialized. if(pMe->magic != UIB_MAGIC) { return 0; } // The cursor is off the end of the input buffer given. // Presuming there are no bugs in this code, this should never happen. // If it so, the struct was corrupted. The check is retained as // as a defense in case there is a bug in this code or the struct is // corrupted. if(pMe->cursor > pMe->UB.len) { return 0; } // subtraction can't go neative because of check above return pMe->UB.len - pMe->cursor; } static inline int UsefulInputBuf_BytesAvailable(UsefulInputBuf *pMe, size_t uLen) { return UsefulInputBuf_BytesUnconsumed(pMe) >= uLen ? 1 : 0; } static inline UsefulBufC UsefulInputBuf_GetUsefulBuf(UsefulInputBuf *pMe, size_t uNum) { const void *pResult = UsefulInputBuf_GetBytes(pMe, uNum); if(!pResult) { return NULLUsefulBufC; } else { return (UsefulBufC){pResult, uNum}; } } static inline uint8_t UsefulInputBuf_GetByte(UsefulInputBuf *pMe) { const void *pResult = UsefulInputBuf_GetBytes(pMe, sizeof(uint8_t)); // The ternery operator is subject to integer promotion, because the // operands are smaller than int, so cast back to uint8_t is needed // to be completely explicit about types (for static analyzers) return (uint8_t)(pResult ? *(uint8_t *)pResult : 0); } static inline uint16_t UsefulInputBuf_GetUint16(UsefulInputBuf *pMe) { const uint8_t *pResult = (const uint8_t *)UsefulInputBuf_GetBytes(pMe, sizeof(uint16_t)); if(!pResult) { return 0; } // See UsefulInputBuf_GetUint64() for comments on this code #if defined(USEFULBUF_CONFIG_BIG_ENDIAN) || defined(USEFULBUF_CONFIG_HTON) || defined(USEFULBUF_CONFIG_BSWAP) uint16_t uTmp; memcpy(&uTmp, pResult, sizeof(uint16_t)); #if defined(USEFULBUF_CONFIG_BIG_ENDIAN) return uTmp; #elif defined(USEFULBUF_CONFIG_HTON) return ntohs(uTmp); #else return __builtin_bswap16(uTmp); #endif #else // The operations here are subject to integer promotion because the // operands are smaller than int. They will be promoted to unsigned // int for the shift and addition. The cast back to uint16_t is is needed // to be completely explicit about types (for static analyzers) return (uint16_t)((pResult[0] << 8) + pResult[1]); #endif } static inline uint32_t UsefulInputBuf_GetUint32(UsefulInputBuf *pMe) { const uint8_t *pResult = (const uint8_t *)UsefulInputBuf_GetBytes(pMe, sizeof(uint32_t)); if(!pResult) { return 0; } // See UsefulInputBuf_GetUint64() for comments on this code #if defined(USEFULBUF_CONFIG_BIG_ENDIAN) || defined(USEFULBUF_CONFIG_HTON) || defined(USEFULBUF_CONFIG_BSWAP) uint32_t uTmp; memcpy(&uTmp, pResult, sizeof(uint32_t)); #if defined(USEFULBUF_CONFIG_BIG_ENDIAN) return uTmp; #elif defined(USEFULBUF_CONFIG_HTON) return ntohl(uTmp); #else return __builtin_bswap32(uTmp); #endif #else return ((uint32_t)pResult[0]<<24) + ((uint32_t)pResult[1]<<16) + ((uint32_t)pResult[2]<<8) + (uint32_t)pResult[3]; #endif } static inline uint64_t UsefulInputBuf_GetUint64(UsefulInputBuf *pMe) { const uint8_t *pResult = (const uint8_t *)UsefulInputBuf_GetBytes(pMe, sizeof(uint64_t)); if(!pResult) { return 0; } #if defined(USEFULBUF_CONFIG_BIG_ENDIAN) || defined(USEFULBUF_CONFIG_HTON) || defined(USEFULBUF_CONFIG_BSWAP) // pResult will probably not be aligned. This memcpy() moves the // bytes into a temp variable safely for CPUs that can or can't do // unaligned memory access. Many compilers will optimize the // memcpy() into a simple move instruction. uint64_t uTmp; memcpy(&uTmp, pResult, sizeof(uint64_t)); #if defined(USEFULBUF_CONFIG_BIG_ENDIAN) // We have been told expliclity this is a big-endian CPU. Since // network byte order is big-endian, there is nothing to do. return uTmp; #elif defined(USEFULBUF_CONFIG_HTON) // We have been told to use ntoh(), the system function to handle // big- and little-endian. This works on both big- and // little-endian machines, but ntoh() is not always available or in // a standard place so it is not used by default. On some CPUs the // code for this is very compact through use of a special swap // instruction. return ntohll(uTmp); #else // Little-endian (since it is not USEFULBUF_CONFIG_BIG_ENDIAN) and // USEFULBUF_CONFIG_BSWAP (since it is not USEFULBUF_CONFIG_HTON). // __builtin_bswap64() and friends are not conditional on CPU // endianness so this must only be used on little-endian machines. return __builtin_bswap64(uTmp); #endif #else // This is the default code that works on every CPU and every // endianness with no dependency on ntoh(). This works on CPUs // that either allow or do not allow unaligned access. It will // always work, but usually is a little less efficient than ntoh(). return ((uint64_t)pResult[0]<<56) + ((uint64_t)pResult[1]<<48) + ((uint64_t)pResult[2]<<40) + ((uint64_t)pResult[3]<<32) + ((uint64_t)pResult[4]<<24) + ((uint64_t)pResult[5]<<16) + ((uint64_t)pResult[6]<<8) + (uint64_t)pResult[7]; #endif } static inline float UsefulInputBuf_GetFloat(UsefulInputBuf *pMe) { uint32_t uResult = UsefulInputBuf_GetUint32(pMe); return uResult ? UsefulBufUtil_CopyUint32ToFloat(uResult) : 0; } static inline double UsefulInputBuf_GetDouble(UsefulInputBuf *pMe) { uint64_t uResult = UsefulInputBuf_GetUint64(pMe); return uResult ? UsefulBufUtil_CopyUint64ToDouble(uResult) : 0; } static inline int UsefulInputBuf_GetError(UsefulInputBuf *pMe) { return pMe->err; } #ifdef __cplusplus } #endif #endif // _UsefulBuf_h