summaryrefslogtreecommitdiff
path: root/node/Buffer.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'node/Buffer.hpp')
-rw-r--r--node/Buffer.hpp398
1 files changed, 398 insertions, 0 deletions
diff --git a/node/Buffer.hpp b/node/Buffer.hpp
new file mode 100644
index 00000000..d3603d38
--- /dev/null
+++ b/node/Buffer.hpp
@@ -0,0 +1,398 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_BUFFER_HPP
+#define _ZT_BUFFER_HPP
+
+#include <stdexcept>
+#include <string>
+#include <algorithm>
+#include <utility>
+#include <string.h>
+#include <stdint.h>
+#include "Utils.hpp"
+
+#ifdef __GNUC__
+#define ZT_VAR_MAY_ALIAS __attribute__((__may_alias__))
+#else
+#define ZT_VAR_MAY_ALIAS
+#endif
+
+namespace ZeroTier {
+
+/**
+ * A variable length but statically allocated buffer
+ *
+ * Bounds-checking is done everywhere, since this is used in security
+ * critical code. This supports construction and assignment from buffers
+ * of differing capacities, provided the data actually in them fits.
+ * It throws std::out_of_range on any boundary violation.
+ *
+ * The at(), append(), etc. methods encode integers larger than 8-bit in
+ * big-endian (network) byte order.
+ *
+ * @tparam C Total capacity
+ */
+template<unsigned int C>
+class Buffer
+{
+ // I love me!
+ template <unsigned int C2> friend class Buffer;
+
+public:
+ // STL container idioms
+ typedef unsigned char value_type;
+ typedef unsigned char * pointer;
+ typedef const unsigned char * const_pointer;
+ typedef unsigned char & reference;
+ typedef const unsigned char & const_reference;
+ typedef unsigned char * iterator;
+ typedef const unsigned char * const_iterator;
+ typedef unsigned int size_type;
+ typedef int difference_type;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+ inline iterator begin() { return _b; }
+ inline iterator end() { return (_b + _l); }
+ inline const_iterator begin() const { return _b; }
+ inline const_iterator end() const { return (_b + _l); }
+ inline reverse_iterator rbegin() { return reverse_iterator(begin()); }
+ inline reverse_iterator rend() { return reverse_iterator(end()); }
+ inline const_reverse_iterator rbegin() const { return const_reverse_iterator(begin()); }
+ inline const_reverse_iterator rend() const { return const_reverse_iterator(end()); }
+
+ Buffer()
+ throw() :
+ _l(0)
+ {
+ }
+
+ Buffer(unsigned int l)
+ throw(std::out_of_range)
+ {
+ if (l > C)
+ throw std::out_of_range("Buffer: construct with size larger than capacity");
+ _l = l;
+ }
+
+ template<unsigned int C2>
+ Buffer(const Buffer<C2> &b)
+ throw(std::out_of_range)
+ {
+ *this = b;
+ }
+
+ Buffer(const void *b,unsigned int l)
+ throw(std::out_of_range)
+ {
+ copyFrom(b,l);
+ }
+
+ Buffer(const std::string &s)
+ throw(std::out_of_range)
+ {
+ copyFrom(s.data(),s.length());
+ }
+
+ template<unsigned int C2>
+ inline Buffer &operator=(const Buffer<C2> &b)
+ throw(std::out_of_range)
+ {
+ if (b._l > C)
+ throw std::out_of_range("Buffer: assignment from buffer larger than capacity");
+ memcpy(this,&b,sizeof(_l) + b._l); // one memcpy for all fields
+ return *this;
+ }
+
+ inline Buffer &operator=(const std::string &s)
+ throw(std::out_of_range)
+ {
+ copyFrom(s.data(),s.length());
+ return *this;
+ }
+
+ inline void copyFrom(const void *b,unsigned int l)
+ throw(std::out_of_range)
+ {
+ if (l > C)
+ throw std::out_of_range("Buffer: set from C array larger than capacity");
+ _l = l;
+ memcpy(_b,b,l);
+ }
+
+ unsigned char operator[](const unsigned int i) const
+ throw(std::out_of_range)
+ {
+ if (i >= _l)
+ throw std::out_of_range("Buffer: [] beyond end of data");
+ return (unsigned char)_b[i];
+ }
+
+ unsigned char &operator[](const unsigned int i)
+ throw(std::out_of_range)
+ {
+ if (i >= _l)
+ throw std::out_of_range("Buffer: [] beyond end of data");
+ return ((unsigned char *)_b)[i];
+ }
+
+ unsigned char *data() throw() { return (unsigned char *)_b; }
+ const unsigned char *data() const throw() { return (const unsigned char *)_b; }
+
+ /**
+ * Safe way to get a pointer to a field from data() with bounds checking
+ *
+ * @param i Index of field in buffer
+ * @param l Length of field in bytes
+ * @return Pointer to field data
+ * @throws std::out_of_range Field extends beyond data size
+ */
+ unsigned char *field(unsigned int i,unsigned int l)
+ throw(std::out_of_range)
+ {
+ if ((i + l) > _l)
+ throw std::out_of_range("Buffer: field() beyond end of data");
+ return (unsigned char *)(_b + i);
+ }
+ const unsigned char *field(unsigned int i,unsigned int l) const
+ throw(std::out_of_range)
+ {
+ if ((i + l) > _l)
+ throw std::out_of_range("Buffer: field() beyond end of data");
+ return (const unsigned char *)(_b + i);
+ }
+
+ /**
+ * Place a primitive integer value at a given position
+ *
+ * @param i Index to place value
+ * @param v Value
+ * @tparam T Integer type (e.g. uint16_t, int64_t)
+ */
+ template<typename T>
+ inline void setAt(unsigned int i,const T v)
+ throw(std::out_of_range)
+ {
+ if ((i + sizeof(T)) > _l)
+ throw std::out_of_range("Buffer: set() beyond end of data");
+ T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T *>(_b + i);
+ *p = Utils::hton(v);
+ }
+
+ /**
+ * Get a primitive integer value at a given position
+ *
+ * This behaves like set() in reverse.
+ *
+ * @param i Index to get integer
+ * @tparam T Integer type (e.g. uint16_t, int64_t)
+ * @return Integer value
+ */
+ template<typename T>
+ inline T at(unsigned int i) const
+ throw(std::out_of_range)
+ {
+ if ((i + sizeof(T)) > _l)
+ throw std::out_of_range("Buffer: at() beyond end of data");
+ const T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<const T *>(_b + i);
+ return Utils::ntoh(*p);
+ }
+
+ /**
+ * Append an integer type to this buffer
+ *
+ * @param v Value to append
+ * @tparam T Integer type (e.g. uint16_t, int64_t)
+ * @throws std::out_of_range Attempt to append beyond capacity
+ */
+ template<typename T>
+ inline void append(const T v)
+ throw(std::out_of_range)
+ {
+ if ((_l + sizeof(T)) > C)
+ throw std::out_of_range("Buffer: append beyond capacity");
+ T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T *>(_b + _l);
+ *p = Utils::hton(v);
+ _l += sizeof(T);
+ }
+
+ /**
+ * Append a C-array of bytes
+ *
+ * @param b Data
+ * @param l Length
+ * @throws std::out_of_range Attempt to append beyond capacity
+ */
+ inline void append(const void *b,unsigned int l)
+ throw(std::out_of_range)
+ {
+ if ((_l + l) > C)
+ throw std::out_of_range("Buffer: append beyond capacity");
+ memcpy(_b + _l,b,l);
+ _l += l;
+ }
+
+ /**
+ * Append a string
+ *
+ * @param s String to append
+ * @throws std::out_of_range Attempt to append beyond capacity
+ */
+ inline void append(const std::string &s)
+ throw(std::out_of_range)
+ {
+ append(s.data(),s.length());
+ }
+
+ /**
+ * Append a buffer
+ *
+ * @param b Buffer to append
+ * @tparam C2 Capacity of second buffer (typically inferred)
+ * @throws std::out_of_range Attempt to append beyond capacity
+ */
+ template<unsigned int C2>
+ inline void append(const Buffer<C2> &b)
+ throw(std::out_of_range)
+ {
+ append(b._b,b._l);
+ }
+
+ /**
+ * Increment size by a given number of bytes
+ *
+ * The contents of new space are undefined.
+ *
+ * @param i Bytes to increment
+ * @throws std::out_of_range Capacity exceeded
+ */
+ inline void addSize(unsigned int i)
+ throw(std::out_of_range)
+ {
+ if ((i + _l) > C)
+ throw std::out_of_range("Buffer: setSize to larger than capacity");
+ _l += i;
+ }
+
+ /**
+ * Set size of data in buffer
+ *
+ * The contents of new space are undefined.
+ *
+ * @param i New size
+ * @throws std::out_of_range Size larger than capacity
+ */
+ inline void setSize(const unsigned int i)
+ throw(std::out_of_range)
+ {
+ if (i > C)
+ throw std::out_of_range("Buffer: setSize to larger than capacity");
+ _l = i;
+ }
+
+ /**
+ * Set buffer data length to zero
+ */
+ inline void clear()
+ throw()
+ {
+ _l = 0;
+ }
+
+ /**
+ * Zero buffer up to size()
+ */
+ inline void zero()
+ throw()
+ {
+ memset(_b,0,_l);
+ }
+
+ /**
+ * Zero unused capacity area
+ */
+ inline void zeroUnused()
+ throw()
+ {
+ memset(_b + _l,0,C - _l);
+ }
+
+ /**
+ * @return Size of data in buffer
+ */
+ inline unsigned int size() const throw() { return _l; }
+
+ /**
+ * @return Capacity of buffer
+ */
+ inline unsigned int capacity() const throw() { return C; }
+
+ template<unsigned int C2>
+ inline bool operator==(const Buffer<C2> &b) const
+ throw()
+ {
+ return ((_l == b._l)&&(!memcmp(_b,b._b,_l)));
+ }
+ template<unsigned int C2>
+ inline bool operator!=(const Buffer<C2> &b) const
+ throw()
+ {
+ return ((_l != b._l)||(memcmp(_b,b._b,_l)));
+ }
+ template<unsigned int C2>
+ inline bool operator<(const Buffer<C2> &b) const
+ throw()
+ {
+ return (memcmp(_b,b._b,std::min(_l,b._l)) < 0);
+ }
+ template<unsigned int C2>
+ inline bool operator>(const Buffer<C2> &b) const
+ throw()
+ {
+ return (b < *this);
+ }
+ template<unsigned int C2>
+ inline bool operator<=(const Buffer<C2> &b) const
+ throw()
+ {
+ return !(b < *this);
+ }
+ template<unsigned int C2>
+ inline bool operator>=(const Buffer<C2> &b) const
+ throw()
+ {
+ return !(*this < b);
+ }
+
+protected:
+ unsigned int _l;
+ char ZT_VAR_MAY_ALIAS _b[C];
+};
+
+} // namespace ZeroTier
+
+#endif