123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869 |
- /*
- * Copyright (c)2013-2020 ZeroTier, Inc.
- *
- * Use of this software is governed by the Business Source License included
- * in the LICENSE.TXT file in the project's root directory.
- *
- * Change Date: 2025-01-01
- *
- * On the date above, in accordance with the Business Source License, use
- * of this software will be governed by version 2.0 of the Apache License.
- */
- /****/
- #ifndef ZT_BUF_HPP
- #define ZT_BUF_HPP
- #include "Constants.hpp"
- #include "Utils.hpp"
- #include "SharedPtr.hpp"
- #include "Mutex.hpp"
- #include "TriviallyCopyable.hpp"
- #include "FCV.hpp"
- #include <stdexcept>
- #include <utility>
- #include <algorithm>
- #include <new>
- // Buffers are 16384 bytes in size because this is the smallest size that can hold any packet
- // and is a power of two. It needs to be a power of two because masking is significantly faster
- // than integer division modulus.
- #define ZT_BUF_MEM_SIZE 0x00004000
- #define ZT_BUF_MEM_MASK 0x00003fffU
- // Sanity limit on maximum buffer pool size
- #define ZT_BUF_MAX_POOL_SIZE 1024
- namespace ZeroTier {
- /**
- * Buffer and methods for branch-free bounds-checked data assembly and parsing
- *
- * This implements an extremely fast buffer for packet assembly and parsing that avoids
- * branching whenever possible. To be safe it must be used correctly!
- *
- * The read methods are prefixed by 'r', and write methods with 'w'. All methods take
- * an iterator, which is just an int that should be initialized to 0 (or whatever starting
- * position is desired). All read methods will advance the iterator regardless of outcome.
- *
- * Read and write methods fail silently in the event of overflow. They do not corrupt or
- * access memory outside the bounds of Buf, but will otherwise produce undefined results.
- *
- * IT IS THE RESPONSIBILITY OF THE USER of this class to use the readOverflow() and
- * writeOverflow() static methods to check the iterator for overflow after each series
- * of reads and writes and BEFORE ANY PARSING or other decisions are made on the basis
- * of the data obtained from a buffer. Failure to do so can result in bugs due
- * to parsing and branching on undefined or corrupt data.
- *
- * ^^ THIS IS VERY IMPORTANT ^^
- *
- * A typical packet assembly consists of repeated calls to the write methods followed by
- * a check to writeOverflow() before final packet armoring and transport. A typical packet
- * disassembly and parsing consists of a series of read calls to obtain the packet's
- * fields followed by a call to readOverflow() to check that these fields are valid. The
- * packet is discarded if readOverflow() returns true. Some packet parsers may make
- * additional reads and in this case readOverflow() must be checked after each set of
- * reads to ensure that overflow did not occur.
- *
- * Buf uses a lock-free pool for extremely fast allocation and deallocation.
- *
- * Buf can optionally take a template parameter that will be placed in the 'data'
- * union as 'fields.' This must be a basic plain data type and must be no larger than
- * ZT_BUF_MEM_SIZE. It's typically a packed struct.
- *
- * Buf instances with different template parameters can freely be cast to one another
- * as there is no actual difference in size or layout.
- *
- * @tparam U Type to overlap with data bytes in data union (can't be larger than ZT_BUF_MEM_SIZE)
- */
- class Buf
- {
- friend class SharedPtr< Buf >;
- public:
- // New and delete operators that allocate Buf instances from a shared lock-free memory pool.
- static void *operator new(std::size_t sz);
- static void operator delete(void *ptr);
- /**
- * Raw data held in buffer
- *
- * The additional eight bytes should not be used and should be considered undefined.
- * They exist to allow reads and writes of integer types to silently overflow if a
- * read or write is performed at the end of the buffer.
- */
- uint8_t unsafeData[ZT_BUF_MEM_SIZE + 8];
- /**
- * Free all instances of Buf in shared pool.
- *
- * New buffers will be created and the pool repopulated if get() is called
- * and outstanding buffers will still be returned to the pool. This just
- * frees buffers currently held in reserve.
- */
- static void freePool() noexcept;
- /**
- * @return Number of Buf objects currently allocated via pool mechanism
- */
- static long poolAllocated() noexcept;
- /**
- * Slice is almost exactly like the built-in slice data structure in Go
- */
- struct Slice : TriviallyCopyable
- {
- ZT_INLINE Slice(const SharedPtr< Buf > &b_, const unsigned int s_, const unsigned int e_) noexcept: b(b_), s(s_), e(e_)
- {}
- ZT_INLINE Slice() noexcept: b(), s(0), e(0)
- {}
- ZT_INLINE operator bool() const noexcept
- { return (b); }
- ZT_INLINE unsigned int size() const noexcept
- { return (e - s); }
- ZT_INLINE void zero() noexcept
- {
- b.zero();
- s = 0;
- e = 0;
- }
- /**
- * Buffer holding slice data
- */
- SharedPtr< Buf > b;
- /**
- * Index of start of data in slice
- */
- unsigned int s;
- /**
- * Index of end of data in slice (make sure it's greater than or equal to 's'!)
- */
- unsigned int e;
- };
- /**
- * A vector of slices making up a packet that might span more than one buffer.
- */
- class PacketVector : public ZeroTier::FCV< Slice, ZT_MAX_PACKET_FRAGMENTS >
- {
- public:
- ZT_INLINE PacketVector() : ZeroTier::FCV< Slice, ZT_MAX_PACKET_FRAGMENTS >()
- {}
- ZT_INLINE unsigned int totalSize() const noexcept
- {
- unsigned int size = 0;
- for (PacketVector::const_iterator s(begin()); s != end(); ++s)
- size += s->e - s->s;
- return size;
- }
- /**
- * Merge this packet vector into a single destination buffer
- *
- * @param b Destination buffer
- * @return Size of data in destination or -1 on error
- */
- ZT_INLINE int mergeCopy(Buf &b) const noexcept
- {
- unsigned int size = 0;
- for (PacketVector::const_iterator s(begin()); s != end(); ++s) {
- const unsigned int start = s->s;
- const unsigned int rem = s->e - start;
- if (likely((size + rem) <= ZT_BUF_MEM_SIZE)) {
- Utils::copy(b.unsafeData + size, s->b->unsafeData + start, rem);
- size += rem;
- } else {
- return -1;
- }
- }
- return (int)size;
- }
- /**
- * Merge this packet vector into a single destination buffer with an arbitrary copy function
- *
- * This can be used to e.g. simultaneously merge and decrypt a packet.
- *
- * @param b Destination buffer
- * @param simpleCopyBefore Don't start using copyFunction until this index (0 to always use)
- * @param copyFunction Function to invoke with memcpy-like arguments: (dest, source, size)
- * @tparam F Type of copyFunction (typically inferred)
- * @return Size of data in destination or -1 on error
- */
- template< typename F >
- ZT_INLINE int mergeMap(Buf &b, const unsigned int simpleCopyBefore, F copyFunction) const noexcept
- {
- unsigned int size = 0;
- for (PacketVector::const_iterator s(begin()); s != end(); ++s) {
- unsigned int start = s->s;
- unsigned int rem = s->e - start;
- if (likely((size + rem) <= ZT_BUF_MEM_SIZE)) {
- if (size < simpleCopyBefore) {
- unsigned int sc = simpleCopyBefore - size;
- if (unlikely(sc > rem))
- sc = rem;
- Utils::copy(b.unsafeData + size, s->b->unsafeData + start, sc);
- start += sc;
- rem -= sc;
- }
- if (likely(rem > 0)) {
- copyFunction(b.unsafeData + size, s->b->unsafeData + start, rem);
- size += rem;
- }
- } else {
- return -1;
- }
- }
- return (int)size;
- }
- };
- /**
- * Create a new uninitialized buffer with undefined contents (use clear() to zero if needed)
- */
- ZT_INLINE Buf() noexcept: __nextInPool(0), __refCount(0)
- {}
- /**
- * Create a new buffer and copy data into it
- */
- ZT_INLINE Buf(const void *const data, const unsigned int len) noexcept:
- __refCount(0)
- {
- Utils::copy(unsafeData, data, len);
- }
- ZT_INLINE Buf(const Buf &b2) noexcept:
- __nextInPool(0),
- __refCount(0)
- {
- Utils::copy< ZT_BUF_MEM_SIZE >(unsafeData, b2.unsafeData);
- }
- ZT_INLINE Buf &operator=(const Buf &b2) noexcept
- {
- if (this != &b2)
- Utils::copy< ZT_BUF_MEM_SIZE >(unsafeData, b2.unsafeData);
- return *this;
- }
- /**
- * Check for overflow beyond the size of the buffer
- *
- * This is used to check for overflow when writing. It returns true if the iterator
- * has passed beyond the capacity of the buffer.
- *
- * @param ii Iterator to check
- * @return True if iterator has read past the size of the buffer
- */
- static ZT_INLINE bool writeOverflow(const int &ii) noexcept
- { return ((ii - ZT_BUF_MEM_SIZE) > 0); }
- /**
- * Check for overflow beyond the size of the data that should be in the buffer
- *
- * This is used to check for overflow when reading, with the second argument being the
- * size of the meaningful data actually present in the buffer.
- *
- * @param ii Iterator to check
- * @param size Size of data that should be in buffer
- * @return True if iterator has read past the size of the data
- */
- static ZT_INLINE bool readOverflow(const int &ii, const unsigned int size) noexcept
- { return ((ii - (int)size) > 0); }
- /**
- * Set all memory to zero
- */
- ZT_INLINE void clear() noexcept
- {
- Utils::zero< ZT_BUF_MEM_SIZE >(unsafeData);
- }
- /**
- * Read a byte
- *
- * @param ii Index value-result parameter (incremented by 1)
- * @return Byte (undefined on overflow)
- */
- ZT_INLINE uint8_t rI8(int &ii) const noexcept
- {
- const int s = ii++;
- return unsafeData[(unsigned int)s & ZT_BUF_MEM_MASK];
- }
- /**
- * Read a 16-bit integer
- *
- * @param ii Index value-result parameter (incremented by 2)
- * @return Integer (undefined on overflow)
- */
- ZT_INLINE uint16_t rI16(int &ii) const noexcept
- {
- const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
- ii += 2;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- return (
- ((uint16_t)unsafeData[s] << 8U) |
- (uint16_t)unsafeData[s + 1]);
- #else
- return Utils::ntoh(*reinterpret_cast<const uint16_t *>(unsafeData + s));
- #endif
- }
- /**
- * Read a 32-bit integer
- *
- * @param ii Index value-result parameter (incremented by 4)
- * @return Integer (undefined on overflow)
- */
- ZT_INLINE uint32_t rI32(int &ii) const noexcept
- {
- const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
- ii += 4;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- return (
- ((uint32_t)unsafeData[s] << 24U) |
- ((uint32_t)unsafeData[s + 1] << 16U) |
- ((uint32_t)unsafeData[s + 2] << 8U) |
- (uint32_t)unsafeData[s + 3]);
- #else
- return Utils::ntoh(*reinterpret_cast<const uint32_t *>(unsafeData + s));
- #endif
- }
- /**
- * Read a 64-bit integer
- *
- * @param ii Index value-result parameter (incremented by 8)
- * @return Integer (undefined on overflow)
- */
- ZT_INLINE uint64_t rI64(int &ii) const noexcept
- {
- const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
- ii += 8;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- return (
- ((uint64_t)unsafeData[s] << 56U) |
- ((uint64_t)unsafeData[s + 1] << 48U) |
- ((uint64_t)unsafeData[s + 2] << 40U) |
- ((uint64_t)unsafeData[s + 3] << 32U) |
- ((uint64_t)unsafeData[s + 4] << 24U) |
- ((uint64_t)unsafeData[s + 5] << 16U) |
- ((uint64_t)unsafeData[s + 6] << 8U) |
- (uint64_t)unsafeData[s + 7]);
- #else
- return Utils::ntoh(*reinterpret_cast<const uint64_t *>(unsafeData + s));
- #endif
- }
- /**
- * Read an object supporting the marshal/unmarshal interface
- *
- * If the return value is negative the object's state is undefined. A return value of
- * zero typically also indicates a problem, though this may depend on the object type.
- *
- * Since objects may be invalid even if there is no overflow, it's important to check
- * the return value of this function in all cases and discard invalid packets as it
- * indicates.
- *
- * @tparam T Object type
- * @param ii Index value-result parameter (incremented by object's size in bytes)
- * @param obj Object to read
- * @return Bytes read or a negative value on unmarshal error (passed from object) or overflow
- */
- template< typename T >
- ZT_INLINE int rO(int &ii, T &obj) const noexcept
- {
- if (likely(ii < ZT_BUF_MEM_SIZE)) {
- int ms = obj.unmarshal(unsafeData + ii, ZT_BUF_MEM_SIZE - ii);
- if (ms > 0)
- ii += ms;
- return ms;
- }
- return -1;
- }
- /**
- * Read a C-style string from the buffer, making a copy and advancing the iterator
- *
- * Use this if the buffer's memory may get changed between reading and processing
- * what is read.
- *
- * @param ii Index value-result parameter (incremented by length of string)
- * @param buf Buffer to receive string
- * @param bufSize Capacity of buffer in bytes
- * @return Pointer to buf or NULL on overflow or error
- */
- ZT_INLINE char *rS(int &ii, char *const buf, const unsigned int bufSize) const noexcept
- {
- const char *const s = (const char *)(unsafeData + ii);
- const int sii = ii;
- while (ii < ZT_BUF_MEM_SIZE) {
- if (unsafeData[ii++] == 0) {
- const int l = ii - sii;
- if (unlikely((unsigned int)l > bufSize))
- return nullptr;
- Utils::copy(buf, s, l);
- return buf;
- }
- }
- return nullptr;
- }
- /**
- * Obtain a pointer to a C-style string in the buffer without copying and advance the iterator
- *
- * The iterator is advanced even if this fails and returns NULL so that readOverflow()
- * will indicate that an overflow occurred. As with other reads the string's contents are
- * undefined if readOverflow() returns true.
- *
- * This version avoids a copy and so is faster if the buffer won't be modified between
- * reading and processing.
- *
- * @param ii Index value-result parameter (incremented by length of string)
- * @return Pointer to null-terminated C-style string or NULL on overflow or error
- */
- ZT_INLINE const char *rSnc(int &ii) const noexcept
- {
- const char *const s = (const char *)(unsafeData + ii);
- while (ii < ZT_BUF_MEM_SIZE) {
- if (unsafeData[ii++] == 0)
- return s;
- }
- return nullptr;
- }
- /**
- * Read a byte array from the buffer, making a copy and advancing the iterator
- *
- * Use this if the buffer's memory may get changed between reading and processing
- * what is read.
- *
- * @param ii Index value-result parameter (incremented by len)
- * @param bytes Buffer to contain data to read
- * @param len Length of buffer
- * @return Pointer to data or NULL on overflow or error
- */
- ZT_INLINE uint8_t *rB(int &ii, void *const bytes, const unsigned int len) const noexcept
- {
- if (likely(((ii += (int)len) <= ZT_BUF_MEM_SIZE))) {
- Utils::copy(bytes, unsafeData + ii, len);
- return reinterpret_cast<uint8_t *>(bytes);
- }
- return nullptr;
- }
- /**
- * Obtain a pointer to a field in the buffer without copying and advance the iterator
- *
- * The iterator is advanced even if this fails and returns NULL so that readOverflow()
- * will indicate that an overflow occurred.
- *
- * This version avoids a copy and so is faster if the buffer won't be modified between
- * reading and processing.
- *
- * @param ii Index value-result parameter (incremented by len)
- * @param len Length of data field to obtain a pointer to
- * @return Pointer to field or NULL on overflow
- */
- ZT_INLINE const uint8_t *rBnc(int &ii, unsigned int len) const noexcept
- {
- const uint8_t *const b = unsafeData + ii;
- return ((ii += (int)len) <= ZT_BUF_MEM_SIZE) ? b : nullptr;
- }
- /**
- * Load a value at an index that is compile time checked against the maximum buffer size
- *
- * @tparam I Static index
- * @return Value
- */
- template< unsigned int I >
- ZT_INLINE uint8_t lI8() const noexcept
- {
- static_assert(I < ZT_BUF_MEM_SIZE, "overflow");
- return unsafeData[I];
- }
- /**
- * Load a value at an index that is compile time checked against the maximum buffer size
- *
- * @tparam I Static index
- * @return Value
- */
- template< unsigned int I >
- ZT_INLINE uint8_t lI16() const noexcept
- {
- static_assert((I + 1) < ZT_BUF_MEM_SIZE, "overflow");
- #ifdef ZT_NO_UNALIGNED_ACCESS
- return (
- ((uint16_t)unsafeData[I] << 8U) |
- (uint16_t)unsafeData[I + 1]);
- #else
- return Utils::ntoh(*reinterpret_cast<const uint16_t *>(unsafeData + I));
- #endif
- }
- /**
- * Load a value at an index that is compile time checked against the maximum buffer size
- *
- * @tparam I Static index
- * @return Value
- */
- template< unsigned int I >
- ZT_INLINE uint8_t lI32() const noexcept
- {
- static_assert((I + 3) < ZT_BUF_MEM_SIZE, "overflow");
- #ifdef ZT_NO_UNALIGNED_ACCESS
- return (
- ((uint32_t)unsafeData[I] << 24U) |
- ((uint32_t)unsafeData[I + 1] << 16U) |
- ((uint32_t)unsafeData[I + 2] << 8U) |
- (uint32_t)unsafeData[I + 3]);
- #else
- return Utils::ntoh(*reinterpret_cast<const uint32_t *>(unsafeData + I));
- #endif
- }
- /**
- * Load a value at an index that is compile time checked against the maximum buffer size
- *
- * @tparam I Static index
- * @return Value
- */
- template< unsigned int I >
- ZT_INLINE uint8_t lI64() const noexcept
- {
- static_assert((I + 7) < ZT_BUF_MEM_SIZE, "overflow");
- #ifdef ZT_NO_UNALIGNED_ACCESS
- return (
- ((uint64_t)unsafeData[I] << 56U) |
- ((uint64_t)unsafeData[I + 1] << 48U) |
- ((uint64_t)unsafeData[I + 2] << 40U) |
- ((uint64_t)unsafeData[I + 3] << 32U) |
- ((uint64_t)unsafeData[I + 4] << 24U) |
- ((uint64_t)unsafeData[I + 5] << 16U) |
- ((uint64_t)unsafeData[I + 6] << 8U) |
- (uint64_t)unsafeData[I + 7]);
- #else
- return Utils::ntoh(*reinterpret_cast<const uint64_t *>(unsafeData + I));
- #endif
- }
- /**
- * Load a value at an index without advancing the index
- *
- * Note that unlike the rI??() methods this does not increment ii and therefore
- * will not necessarily result in a 'true' return from readOverflow(). It does
- * however subject 'ii' to soft bounds masking like the gI??() methods.
- */
- ZT_INLINE uint8_t lI8(const int ii) const noexcept
- {
- return unsafeData[(unsigned int)ii & ZT_BUF_MEM_MASK];
- }
- /**
- * Load a value at an index without advancing the index
- *
- * Note that unlike the rI??() methods this does not increment ii and therefore
- * will not necessarily result in a 'true' return from readOverflow(). It does
- * however subject 'ii' to soft bounds masking like the gI??() methods.
- */
- ZT_INLINE uint16_t lI16(const int ii) const noexcept
- {
- const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- return (
- ((uint16_t)unsafeData[s] << 8U) |
- (uint16_t)unsafeData[s + 1]);
- #else
- return Utils::ntoh(*reinterpret_cast<const uint16_t *>(unsafeData + s));
- #endif
- }
- /**
- * Load a value at an index without advancing the index
- *
- * Note that unlike the rI??() methods this does not increment ii and therefore
- * will not necessarily result in a 'true' return from readOverflow(). It does
- * however subject 'ii' to soft bounds masking like the gI??() methods.
- */
- ZT_INLINE uint32_t lI32(const int ii) const noexcept
- {
- const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- return (
- ((uint32_t)unsafeData[s] << 24U) |
- ((uint32_t)unsafeData[s + 1] << 16U) |
- ((uint32_t)unsafeData[s + 2] << 8U) |
- (uint32_t)unsafeData[s + 3]);
- #else
- return Utils::ntoh(*reinterpret_cast<const uint32_t *>(unsafeData + s));
- #endif
- }
- /**
- * Load a value at an index without advancing the index
- *
- * Note that unlike the rI??() methods this does not increment ii and therefore
- * will not necessarily result in a 'true' return from readOverflow(). It does
- * however subject 'ii' to soft bounds masking like the gI??() methods.
- */
- ZT_INLINE uint8_t lI64(const int ii) const noexcept
- {
- const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- return (
- ((uint64_t)unsafeData[s] << 56U) |
- ((uint64_t)unsafeData[s + 1] << 48U) |
- ((uint64_t)unsafeData[s + 2] << 40U) |
- ((uint64_t)unsafeData[s + 3] << 32U) |
- ((uint64_t)unsafeData[s + 4] << 24U) |
- ((uint64_t)unsafeData[s + 5] << 16U) |
- ((uint64_t)unsafeData[s + 6] << 8U) |
- (uint64_t)unsafeData[s + 7]);
- #else
- return Utils::ntoh(*reinterpret_cast<const uint64_t *>(unsafeData + s));
- #endif
- }
- /**
- * Write a byte
- *
- * @param ii Index value-result parameter (incremented by 1)
- * @param n Byte
- */
- ZT_INLINE void wI8(int &ii, const uint8_t n) noexcept
- {
- const int s = ii++;
- unsafeData[(unsigned int)s & ZT_BUF_MEM_MASK] = n;
- }
- /**
- * Write a 16-bit integer in big-endian byte order
- *
- * @param ii Index value-result parameter (incremented by 2)
- * @param n Integer
- */
- ZT_INLINE void wI16(int &ii, const uint16_t n) noexcept
- {
- const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
- ii += 2;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- unsafeData[s] = (uint8_t)(n >> 8U);
- unsafeData[s + 1] = (uint8_t)n;
- #else
- *reinterpret_cast<uint16_t *>(unsafeData + s) = Utils::hton(n);
- #endif
- }
- /**
- * Write a 32-bit integer in big-endian byte order
- *
- * @param ii Index value-result parameter (incremented by 4)
- * @param n Integer
- */
- ZT_INLINE void wI32(int &ii, const uint32_t n) noexcept
- {
- const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
- ii += 4;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- unsafeData[s] = (uint8_t)(n >> 24U);
- unsafeData[s + 1] = (uint8_t)(n >> 16U);
- unsafeData[s + 2] = (uint8_t)(n >> 8U);
- unsafeData[s + 3] = (uint8_t)n;
- #else
- *reinterpret_cast<uint32_t *>(unsafeData + s) = Utils::hton(n);
- #endif
- }
- /**
- * Write a 64-bit integer in big-endian byte order
- *
- * @param ii Index value-result parameter (incremented by 8)
- * @param n Integer
- */
- ZT_INLINE void wI64(int &ii, const uint64_t n) noexcept
- {
- const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
- ii += 8;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- unsafeData[s] = (uint8_t)(n >> 56U);
- unsafeData[s + 1] = (uint8_t)(n >> 48U);
- unsafeData[s + 2] = (uint8_t)(n >> 40U);
- unsafeData[s + 3] = (uint8_t)(n >> 32U);
- unsafeData[s + 4] = (uint8_t)(n >> 24U);
- unsafeData[s + 5] = (uint8_t)(n >> 16U);
- unsafeData[s + 6] = (uint8_t)(n >> 8U);
- unsafeData[s + 7] = (uint8_t)n;
- #else
- *reinterpret_cast<uint64_t *>(unsafeData + s) = Utils::hton(n);
- #endif
- }
- /**
- * Write an object implementing the marshal interface
- *
- * @tparam T Object type
- * @param ii Index value-result parameter (incremented by size of object)
- * @param t Object to write
- */
- template< typename T >
- ZT_INLINE void wO(int &ii, T &t) noexcept
- {
- const int s = ii;
- if (likely((s + T::marshalSizeMax()) <= ZT_BUF_MEM_SIZE)) {
- int ms = t.marshal(unsafeData + s);
- if (ms > 0)
- ii += ms;
- } else {
- ii += T::marshalSizeMax(); // mark as overflowed even if we didn't do anything
- }
- }
- /**
- * Write a C-style null-terminated string (including the trailing zero)
- *
- * @param ii Index value-result parameter (incremented by length of string)
- * @param s String to write (writes an empty string if this is NULL)
- */
- ZT_INLINE void wS(int &ii, const char *s) noexcept
- {
- if (s) {
- char c;
- do {
- c = *(s++);
- wI8(ii, (uint8_t)c);
- } while (c);
- } else {
- wI8(ii, 0);
- }
- }
- /**
- * Write a byte array
- *
- * @param ii Index value-result parameter (incremented by len)
- * @param bytes Bytes to write
- * @param len Size of data in bytes
- */
- ZT_INLINE void wB(int &ii, const void *const bytes, const unsigned int len) noexcept
- {
- const int s = ii;
- if (likely((ii += (int)len) <= ZT_BUF_MEM_SIZE))
- Utils::copy(unsafeData + s, bytes, len);
- }
- /**
- * Write zeroes
- *
- * @param ii Index value-result parameter (incremented by len)
- * @param len Number of zero bytes to write
- */
- ZT_INLINE void wZ(int &ii, const unsigned int len) noexcept
- {
- const int s = ii;
- if (likely((ii += (int)len) <= ZT_BUF_MEM_SIZE))
- Utils::zero(unsafeData + s, len);
- }
- /**
- * Write secure random bytes
- *
- * @param ii Index value-result parameter (incremented by len)
- * @param len Number of random bytes to write
- */
- ZT_INLINE void wR(int &ii, const unsigned int len) noexcept
- {
- const int s = ii;
- if (likely((ii += (int)len) <= ZT_BUF_MEM_SIZE))
- Utils::getSecureRandom(unsafeData + s, len);
- }
- /**
- * Store a byte without advancing the index
- */
- ZT_INLINE void sI8(const int ii, const uint8_t n) noexcept
- {
- unsafeData[(unsigned int)ii & ZT_BUF_MEM_MASK] = n;
- }
- /**
- * Store an integer without advancing the index
- */
- ZT_INLINE void sI16(const int ii, const uint16_t n) noexcept
- {
- const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- unsafeData[s] = (uint8_t)(n >> 8U);
- unsafeData[s + 1] = (uint8_t)n;
- #else
- *reinterpret_cast<uint16_t *>(unsafeData + s) = Utils::hton(n);
- #endif
- }
- /**
- * Store an integer without advancing the index
- */
- ZT_INLINE void sI32(const int ii, const uint32_t n) noexcept
- {
- const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- unsafeData[s] = (uint8_t)(n >> 24U);
- unsafeData[s + 1] = (uint8_t)(n >> 16U);
- unsafeData[s + 2] = (uint8_t)(n >> 8U);
- unsafeData[s + 3] = (uint8_t)n;
- #else
- *reinterpret_cast<uint32_t *>(unsafeData + s) = Utils::hton(n);
- #endif
- }
- /**
- * Store an integer without advancing the index
- */
- ZT_INLINE void sI64(const int ii, const uint64_t n) noexcept
- {
- const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
- #ifdef ZT_NO_UNALIGNED_ACCESS
- unsafeData[s] = (uint8_t)(n >> 56U);
- unsafeData[s + 1] = (uint8_t)(n >> 48U);
- unsafeData[s + 2] = (uint8_t)(n >> 40U);
- unsafeData[s + 3] = (uint8_t)(n >> 32U);
- unsafeData[s + 4] = (uint8_t)(n >> 24U);
- unsafeData[s + 5] = (uint8_t)(n >> 16U);
- unsafeData[s + 6] = (uint8_t)(n >> 8U);
- unsafeData[s + 7] = (uint8_t)n;
- #else
- *reinterpret_cast<uint64_t *>(unsafeData + s) = Utils::hton(n);
- #endif
- }
- /**
- * @return Capacity of this buffer (usable size of data.bytes)
- */
- static constexpr unsigned int capacity() noexcept
- { return ZT_BUF_MEM_SIZE; }
- private:
- // Next item in free buffer pool linked list if Buf is placed in pool, undefined and unused otherwise
- std::atomic< uintptr_t > __nextInPool;
- // Reference counter for SharedPtr<>
- std::atomic< int > __refCount;
- };
- } // namespace ZeroTier
- #endif
|