mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-05 20:13:44 +02:00
Take Dictionary behind the barn...
This commit is contained in:
parent
d5b9a54c55
commit
cdc6c42375
11 changed files with 727 additions and 660 deletions
|
@ -34,50 +34,30 @@ public:
|
|||
typedef char * iterator;
|
||||
typedef const char * const_iterator;
|
||||
|
||||
ZT_ALWAYS_INLINE Str() { _l = 0; _s[0] = 0; }
|
||||
ZT_ALWAYS_INLINE Str(const Str &s)
|
||||
{
|
||||
_l = s._l;
|
||||
memcpy(_s,s._s,_l+1);
|
||||
}
|
||||
ZT_ALWAYS_INLINE Str(const char *s)
|
||||
{
|
||||
_l = 0;
|
||||
_s[0] = 0;
|
||||
(*this) << s;
|
||||
}
|
||||
ZT_ALWAYS_INLINE Str(const std::string &s) { *this = s; }
|
||||
ZT_ALWAYS_INLINE Str() { memset(reinterpret_cast<void *>(this),0,sizeof(Str)); }
|
||||
explicit ZT_ALWAYS_INLINE Str(const char *s) { *this = s; }
|
||||
|
||||
ZT_ALWAYS_INLINE Str &operator=(const Str &s)
|
||||
{
|
||||
_l = s._l;
|
||||
memcpy(_s,s._s,_l+1);
|
||||
return *this;
|
||||
}
|
||||
ZT_ALWAYS_INLINE Str &operator=(const char *s)
|
||||
{
|
||||
_l = 0;
|
||||
_s[0] = 0;
|
||||
return ((*this) << s);
|
||||
}
|
||||
ZT_ALWAYS_INLINE Str &operator=(const std::string &s)
|
||||
{
|
||||
if (s.length() > C) {
|
||||
if (s) {
|
||||
unsigned int l = 0;
|
||||
while (l < C) {
|
||||
char c = s[l];
|
||||
if (!c) break;
|
||||
_s[l++] = c;
|
||||
}
|
||||
_s[l] = 0;
|
||||
_l = (uint16_t)l;
|
||||
} else {
|
||||
_l = 0;
|
||||
_s[0] = 0;
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
} else {
|
||||
_l = (uint8_t)s.length();
|
||||
memcpy(_s,s.data(),s.length());
|
||||
_s[s.length()] = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZT_ALWAYS_INLINE char operator[](const unsigned int i) const
|
||||
{
|
||||
if (unlikely(i >= (unsigned int)_l))
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
if (i >= (unsigned int)_l)
|
||||
return 0;
|
||||
return _s[i];
|
||||
}
|
||||
|
||||
|
@ -85,6 +65,7 @@ public:
|
|||
ZT_ALWAYS_INLINE const char *c_str() const { return _s; }
|
||||
ZT_ALWAYS_INLINE unsigned int length() const { return (unsigned int)_l; }
|
||||
ZT_ALWAYS_INLINE bool empty() const { return (_l == 0); }
|
||||
|
||||
ZT_ALWAYS_INLINE iterator begin() { return (iterator)_s; }
|
||||
ZT_ALWAYS_INLINE iterator end() { return (iterator)(_s + (unsigned long)_l); }
|
||||
ZT_ALWAYS_INLINE const_iterator begin() const { return (const_iterator)_s; }
|
||||
|
@ -92,44 +73,27 @@ public:
|
|||
|
||||
ZT_ALWAYS_INLINE Str &operator<<(const char *s)
|
||||
{
|
||||
if (likely(s != (const char *)0)) {
|
||||
unsigned long l = _l;
|
||||
while (*s) {
|
||||
if (unlikely(l >= C)) {
|
||||
_s[C] = 0;
|
||||
_l = C;
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
_s[l++] = *(s++);
|
||||
if (s) {
|
||||
unsigned int l = _l;
|
||||
while (l < C) {
|
||||
char c = s[l];
|
||||
if (!c) break;
|
||||
_s[l++] = c;
|
||||
}
|
||||
_s[l] = 0;
|
||||
_l = (uint8_t)l;
|
||||
_l = (uint16_t)l;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
ZT_ALWAYS_INLINE Str &operator<<(const Str &s) { return ((*this) << s._s); }
|
||||
ZT_ALWAYS_INLINE Str &operator<<(const char c)
|
||||
{
|
||||
if (unlikely(_l >= C)) {
|
||||
_s[C] = 0;
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
if (_l < C) {
|
||||
_s[_l++] = c;
|
||||
_s[_l] = 0;
|
||||
}
|
||||
_s[(unsigned long)(_l++)] = c;
|
||||
_s[(unsigned long)_l] = 0;
|
||||
return *this;
|
||||
}
|
||||
ZT_ALWAYS_INLINE Str &operator<<(const unsigned long n)
|
||||
{
|
||||
char tmp[32];
|
||||
Utils::decimal(n,tmp);
|
||||
return ((*this) << tmp);
|
||||
}
|
||||
ZT_ALWAYS_INLINE Str &operator<<(const unsigned int n)
|
||||
{
|
||||
char tmp[32];
|
||||
Utils::decimal((unsigned long)n,tmp);
|
||||
return ((*this) << tmp);
|
||||
}
|
||||
|
||||
ZT_ALWAYS_INLINE Str &operator<<(const Address &a)
|
||||
{
|
||||
char tmp[32];
|
||||
|
@ -146,27 +110,6 @@ public:
|
|||
return ((*this) << a.toString(tmp));
|
||||
}
|
||||
|
||||
inline Str &append(const char *s,const unsigned int max)
|
||||
{
|
||||
if (likely(s != (const char *)0)) {
|
||||
unsigned long l = _l;
|
||||
unsigned int c = 0;
|
||||
while (*s) {
|
||||
if (c++ >= max) break;
|
||||
if (unlikely(l >= C)) {
|
||||
_s[C] = 0;
|
||||
_l = C;
|
||||
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
|
||||
}
|
||||
_s[l++] = *s;
|
||||
++s;
|
||||
}
|
||||
_s[l] = 0;
|
||||
_l = (uint8_t)l;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZT_ALWAYS_INLINE operator bool() const { return (_l != 0); }
|
||||
|
||||
ZT_ALWAYS_INLINE bool operator==(const Str &s) const { return ((_l == s._l)&&(memcmp(_s,s._s,_l) == 0)); }
|
92
node/Buf.cpp
92
node/Buf.cpp
|
@ -16,9 +16,97 @@
|
|||
namespace ZeroTier {
|
||||
|
||||
#ifdef __GNUC__
|
||||
uintptr_t Buf_pool = 0;
|
||||
uintptr_t _Buf_pool = 0;
|
||||
#else
|
||||
std::atomic<uintptr_t> Buf_pool(0);
|
||||
std::atomic<uintptr_t> _Buf_pool(0);
|
||||
#endif
|
||||
|
||||
void _Buf_release(void *ptr,std::size_t sz)
|
||||
{
|
||||
if (ptr) {
|
||||
uintptr_t bb;
|
||||
const uintptr_t locked = ~((uintptr_t)0);
|
||||
for (;;) {
|
||||
#ifdef __GNUC__
|
||||
bb = __sync_fetch_and_or(&_Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
|
||||
#else
|
||||
bb = s_pool.fetch_or(locked);
|
||||
#endif
|
||||
if (bb != locked)
|
||||
break;
|
||||
}
|
||||
|
||||
((Buf<> *)ptr)->__nextInPool = bb;
|
||||
#ifdef __GNUC__
|
||||
__sync_fetch_and_and(&_Buf_pool,(uintptr_t)ptr);
|
||||
#else
|
||||
s_pool.store((uintptr_t)ptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void *_Buf_get()
|
||||
{
|
||||
uintptr_t bb;
|
||||
const uintptr_t locked = ~((uintptr_t)0);
|
||||
for (;;) {
|
||||
#ifdef __GNUC__
|
||||
bb = __sync_fetch_and_or(&_Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
|
||||
#else
|
||||
bb = s_pool.fetch_or(locked);
|
||||
#endif
|
||||
if (bb != locked)
|
||||
break;
|
||||
}
|
||||
|
||||
Buf<> *b;
|
||||
if (bb == 0) {
|
||||
#ifdef __GNUC__
|
||||
__sync_fetch_and_and(&_Buf_pool,bb);
|
||||
#else
|
||||
s_pool.store(bb);
|
||||
#endif
|
||||
b = (Buf<> *)malloc(sizeof(Buf<>));
|
||||
if (!b)
|
||||
return nullptr;
|
||||
} else {
|
||||
b = (Buf<> *)bb;
|
||||
#ifdef __GNUC__
|
||||
__sync_fetch_and_and(&_Buf_pool,b->__nextInPool);
|
||||
#else
|
||||
s_pool.store(b->__nextInPool);
|
||||
#endif
|
||||
}
|
||||
|
||||
b->__refCount.zero();
|
||||
return (void *)b;
|
||||
}
|
||||
|
||||
void freeBufPool()
|
||||
{
|
||||
uintptr_t bb;
|
||||
const uintptr_t locked = ~((uintptr_t)0);
|
||||
for (;;) {
|
||||
#ifdef __GNUC__
|
||||
bb = __sync_fetch_and_or(&_Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
|
||||
#else
|
||||
bb = s_pool.fetch_or(locked);
|
||||
#endif
|
||||
if (bb != locked)
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
__sync_fetch_and_and(&_Buf_pool,(uintptr_t)0);
|
||||
#else
|
||||
s_pool.store((uintptr_t)0);
|
||||
#endif
|
||||
|
||||
while (bb != 0) {
|
||||
uintptr_t next = ((Buf<> *)bb)->__nextInPool;
|
||||
free((void *)bb);
|
||||
bb = next;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
|
118
node/Buf.hpp
118
node/Buf.hpp
|
@ -23,6 +23,7 @@
|
|||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
|
||||
#ifndef __GNUC__
|
||||
#include <atomic>
|
||||
|
@ -37,10 +38,21 @@
|
|||
namespace ZeroTier {
|
||||
|
||||
#ifdef __GNUC__
|
||||
extern uintptr_t Buf_pool;
|
||||
extern uintptr_t _Buf_pool;
|
||||
#else
|
||||
extern std::atomic<uintptr_t> Buf_pool;
|
||||
extern std::atomic<uintptr_t> _Buf_pool;
|
||||
#endif
|
||||
void _Buf_release(void *ptr,std::size_t sz);
|
||||
void *_Buf_get();
|
||||
|
||||
/**
|
||||
* Free buffers in the 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.
|
||||
*/
|
||||
void freeBufPool();
|
||||
|
||||
/**
|
||||
* Buffer and methods for branch-free bounds-checked data assembly and parsing
|
||||
|
@ -82,10 +94,13 @@ extern std::atomic<uintptr_t> Buf_pool;
|
|||
*
|
||||
* @tparam U Type to overlap with data bytes in data union (can't be larger than ZT_BUF_MEM_SIZE)
|
||||
*/
|
||||
template<typename U = void>
|
||||
template<typename U = int>
|
||||
class Buf
|
||||
{
|
||||
friend class SharedPtr< Buf<U> >;
|
||||
friend void _Buf_release(void *,std::size_t);
|
||||
friend void *_Buf_get();
|
||||
friend void freeBufPool();
|
||||
|
||||
private:
|
||||
// Direct construction isn't allowed; use get().
|
||||
|
@ -95,104 +110,19 @@ private:
|
|||
ZT_ALWAYS_INLINE Buf(const Buf<X> &b) { memcpy(data.bytes,b.data.bytes,ZT_BUF_MEM_SIZE); }
|
||||
|
||||
public:
|
||||
static void operator delete(void *ptr,std::size_t sz)
|
||||
{
|
||||
if (ptr) {
|
||||
uintptr_t bb;
|
||||
const uintptr_t locked = ~((uintptr_t)0);
|
||||
for (;;) {
|
||||
#ifdef __GNUC__
|
||||
bb = __sync_fetch_and_or(&Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
|
||||
#else
|
||||
bb = s_pool.fetch_or(locked);
|
||||
#endif
|
||||
if (bb != locked)
|
||||
break;
|
||||
}
|
||||
|
||||
((Buf *)ptr)->__nextInPool = bb;
|
||||
#ifdef __GNUC__
|
||||
__sync_fetch_and_and(&Buf_pool,(uintptr_t)ptr);
|
||||
#else
|
||||
s_pool.store((uintptr_t)ptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
static void operator delete(void *ptr,std::size_t sz) { _Buf_release(ptr,sz); }
|
||||
|
||||
/**
|
||||
* Get obtains a buffer from the pool or allocates a new buffer if the pool is empty
|
||||
*
|
||||
* @return Buffer
|
||||
* @return Buffer instance
|
||||
*/
|
||||
static ZT_ALWAYS_INLINE SharedPtr< Buf<U> > get()
|
||||
{
|
||||
uintptr_t bb;
|
||||
const uintptr_t locked = ~((uintptr_t)0);
|
||||
for (;;) {
|
||||
#ifdef __GNUC__
|
||||
bb = __sync_fetch_and_or(&Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
|
||||
#else
|
||||
bb = s_pool.fetch_or(locked);
|
||||
#endif
|
||||
if (bb != locked)
|
||||
break;
|
||||
}
|
||||
|
||||
Buf *b;
|
||||
if (bb == 0) {
|
||||
#ifdef __GNUC__
|
||||
__sync_fetch_and_and(&Buf_pool,bb);
|
||||
#else
|
||||
s_pool.store(bb);
|
||||
#endif
|
||||
b = (Buf *)malloc(sizeof(Buf));
|
||||
if (!b)
|
||||
return SharedPtr<Buf>();
|
||||
} else {
|
||||
b = (Buf *)bb;
|
||||
#ifdef __GNUC__
|
||||
__sync_fetch_and_and(&Buf_pool,b->__nextInPool);
|
||||
#else
|
||||
s_pool.store(b->__nextInPool);
|
||||
#endif
|
||||
}
|
||||
|
||||
b->__refCount.zero();
|
||||
return SharedPtr<Buf>(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free buffers in the 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 inline void freePool()
|
||||
{
|
||||
uintptr_t bb;
|
||||
const uintptr_t locked = ~((uintptr_t)0);
|
||||
for (;;) {
|
||||
#ifdef __GNUC__
|
||||
bb = __sync_fetch_and_or(&Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
|
||||
#else
|
||||
bb = s_pool.fetch_or(locked);
|
||||
#endif
|
||||
if (bb != locked)
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
__sync_fetch_and_and(&Buf_pool,(uintptr_t)0);
|
||||
#else
|
||||
s_pool.store((uintptr_t)0);
|
||||
#endif
|
||||
|
||||
while (bb != 0) {
|
||||
uintptr_t next = ((Buf *)bb)->__nextInPool;
|
||||
free((void *)bb);
|
||||
bb = next;
|
||||
}
|
||||
void *const b = _Buf_get();
|
||||
if (b)
|
||||
return SharedPtr<Buf>((Buf *)b);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,7 +38,6 @@ set(core_headers
|
|||
SelfAwareness.hpp
|
||||
SHA512.hpp
|
||||
SharedPtr.hpp
|
||||
Str.hpp
|
||||
Switch.hpp
|
||||
Tag.hpp
|
||||
Topology.hpp
|
||||
|
|
|
@ -231,9 +231,6 @@
|
|||
#define ZT_CRYPTO_ALG_C25519 0
|
||||
#define ZT_CRYPTO_ALG_P384 1
|
||||
|
||||
// Exceptions thrown in core ZT code
|
||||
#define ZT_EXCEPTION_OUT_OF_BOUNDS 100
|
||||
|
||||
/* Ethernet frame types that might be relevant to us */
|
||||
#define ZT_ETHERTYPE_IPV4 0x0800
|
||||
#define ZT_ETHERTYPE_ARP 0x0806
|
||||
|
|
|
@ -15,210 +15,264 @@
|
|||
|
||||
namespace ZeroTier {
|
||||
|
||||
Dictionary::Dictionary(const char *s,unsigned int len)
|
||||
static uint64_t _toKey(const char *k)
|
||||
{
|
||||
for(unsigned int i=0;i<ZT_DICTIONARY_MAX_CAPACITY;++i) {
|
||||
if ((s)&&(i < len)) {
|
||||
if (!(_d[i] = *s))
|
||||
s = (const char *)0;
|
||||
else ++s;
|
||||
} else _d[i] = (char)0;
|
||||
uint64_t n = 0;
|
||||
unsigned int i = 0;
|
||||
for(;;) {
|
||||
char c = k[i];
|
||||
if (!c) break;
|
||||
reinterpret_cast<uint8_t *>(&n)[i & 7U] ^= (uint8_t)c;
|
||||
++i;
|
||||
}
|
||||
_d[ZT_DICTIONARY_MAX_CAPACITY-1] = (char)0;
|
||||
return n;
|
||||
}
|
||||
|
||||
bool Dictionary::load(const char *s)
|
||||
Dictionary::Dictionary()
|
||||
{
|
||||
for(unsigned int i=0;i<ZT_DICTIONARY_MAX_CAPACITY;++i) {
|
||||
if (s) {
|
||||
if (!(_d[i] = *s))
|
||||
s = (const char *)0;
|
||||
else ++s;
|
||||
} else _d[i] = (char)0;
|
||||
}
|
||||
_d[ZT_DICTIONARY_MAX_CAPACITY - 1] = (char)0;
|
||||
return (!s);
|
||||
}
|
||||
|
||||
int Dictionary::get(const char *key,char *dest,unsigned int destlen) const
|
||||
Dictionary::~Dictionary()
|
||||
{
|
||||
const char *p = _d;
|
||||
const char *const eof = p + ZT_DICTIONARY_MAX_CAPACITY;
|
||||
const char *k;
|
||||
bool esc;
|
||||
int j;
|
||||
}
|
||||
|
||||
if (!destlen) // sanity check
|
||||
return -1;
|
||||
std::vector<uint8_t> &Dictionary::operator[](const char *k)
|
||||
{
|
||||
return _t[_toKey(k)];
|
||||
}
|
||||
|
||||
while (*p) {
|
||||
k = key;
|
||||
while ((*k)&&(*p)) {
|
||||
if (*p != *k)
|
||||
break;
|
||||
++k;
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
const std::vector<uint8_t> &Dictionary::operator[](const char *k) const
|
||||
{
|
||||
static const std::vector<uint8_t> emptyEntry;
|
||||
const std::vector<uint8_t> *const e = _t.get(_toKey(k));
|
||||
return (e) ? *e : emptyEntry;
|
||||
}
|
||||
|
||||
if ((!*k)&&(*p == '=')) {
|
||||
j = 0;
|
||||
esc = false;
|
||||
++p;
|
||||
while ((*p != 0)&&(*p != 13)&&(*p != 10)) {
|
||||
if (esc) {
|
||||
esc = false;
|
||||
switch(*p) {
|
||||
case 'r': dest[j++] = 13; break;
|
||||
case 'n': dest[j++] = 10; break;
|
||||
case '0': dest[j++] = (char)0; break;
|
||||
case 'e': dest[j++] = '='; break;
|
||||
default: dest[j++] = *p; break;
|
||||
}
|
||||
if (j == (int)destlen) {
|
||||
dest[j-1] = (char)0;
|
||||
return j-1;
|
||||
}
|
||||
} else if (*p == '\\') {
|
||||
esc = true;
|
||||
} else {
|
||||
dest[j++] = *p;
|
||||
if (j == (int)destlen) {
|
||||
dest[j-1] = (char)0;
|
||||
return j-1;
|
||||
}
|
||||
}
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
dest[j] = (char)0;
|
||||
return j;
|
||||
} else {
|
||||
while ((*p)&&(*p != 13)&&(*p != 10)) {
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (*p) {
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else break;
|
||||
void Dictionary::add(const char *k,bool v)
|
||||
{
|
||||
std::vector<uint8_t> &e = (*this)[k];
|
||||
e.resize(2);
|
||||
e[0] = (uint8_t)(v ? '1' : '0');
|
||||
e[1] = 0;
|
||||
}
|
||||
|
||||
void Dictionary::add(const char *k,uint16_t v)
|
||||
{
|
||||
std::vector<uint8_t> &e = (*this)[k];
|
||||
e.resize(5);
|
||||
Utils::hex(v,(char *)e.data());
|
||||
}
|
||||
|
||||
void Dictionary::add(const char *k,uint32_t v)
|
||||
{
|
||||
std::vector<uint8_t> &e = (*this)[k];
|
||||
e.resize(9);
|
||||
Utils::hex(v,(char *)e.data());
|
||||
}
|
||||
|
||||
void Dictionary::add(const char *k,uint64_t v)
|
||||
{
|
||||
std::vector<uint8_t> &e = (*this)[k];
|
||||
e.resize(17);
|
||||
Utils::hex(v,(char *)e.data());
|
||||
}
|
||||
|
||||
void Dictionary::add(const char *k,const Address &v)
|
||||
{
|
||||
std::vector<uint8_t> &e = (*this)[k];
|
||||
e.resize(11);
|
||||
v.toString((char *)e.data());
|
||||
}
|
||||
|
||||
void Dictionary::add(const char *k,const char *v)
|
||||
{
|
||||
std::vector<uint8_t> &e = (*this)[k];
|
||||
e.clear();
|
||||
if (v) {
|
||||
for(;;) {
|
||||
const uint8_t c = (uint8_t)*(v++);
|
||||
e.push_back(c);
|
||||
if (!c) break;
|
||||
}
|
||||
}
|
||||
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Dictionary::add(const char *key,const char *value,int vlen)
|
||||
void Dictionary::add(const char *k,const void *data,unsigned int len)
|
||||
{
|
||||
for(unsigned int i=0;i<ZT_DICTIONARY_MAX_CAPACITY;++i) {
|
||||
if (!_d[i]) {
|
||||
unsigned int j = i;
|
||||
std::vector<uint8_t> &e = (*this)[k];
|
||||
if (len != 0) {
|
||||
e.assign((const uint8_t *)data,(const uint8_t *)data + len);
|
||||
} else {
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (j > 0) {
|
||||
_d[j++] = (char)10;
|
||||
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char *p = key;
|
||||
while (*p) {
|
||||
_d[j++] = *(p++);
|
||||
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_d[j++] = '=';
|
||||
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
|
||||
_d[i] = (char)0;
|
||||
bool Dictionary::getB(const char *k,bool dfl) const
|
||||
{
|
||||
bool v = dfl;
|
||||
const std::vector<uint8_t> &e = (*this)[k];
|
||||
if (!e.empty()) {
|
||||
switch ((char)e[0]) {
|
||||
case '1':
|
||||
case 't':
|
||||
case 'T':
|
||||
case 'y':
|
||||
case 'Y':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
p = value;
|
||||
int k = 0;
|
||||
while ( ((vlen < 0)&&(*p)) || (k < vlen) ) {
|
||||
switch(*p) {
|
||||
case 0:
|
||||
case 13:
|
||||
case 10:
|
||||
case '\\':
|
||||
case '=':
|
||||
_d[j++] = '\\';
|
||||
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
switch(*p) {
|
||||
case 0: _d[j++] = '0'; break;
|
||||
case 13: _d[j++] = 'r'; break;
|
||||
case 10: _d[j++] = 'n'; break;
|
||||
case '\\': _d[j++] = '\\'; break;
|
||||
case '=': _d[j++] = 'e'; break;
|
||||
}
|
||||
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
uint64_t Dictionary::getUI(const char *k,uint64_t dfl) const
|
||||
{
|
||||
uint8_t tmp[18];
|
||||
uint64_t v = dfl;
|
||||
const std::vector<uint8_t> &e = (*this)[k];
|
||||
if (!e.empty()) {
|
||||
if (e.back() != 0) {
|
||||
const unsigned long sl = e.size();
|
||||
memcpy(tmp,e.data(),(sl > 17) ? 17 : sl);
|
||||
tmp[17] = 0;
|
||||
return Utils::unhex((const char *)tmp);
|
||||
}
|
||||
return Utils::unhex((const char *)e.data());
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void Dictionary::getS(const char *k,char *v,unsigned int cap) const
|
||||
{
|
||||
if (cap == 0) // sanity check
|
||||
return;
|
||||
const std::vector<uint8_t> &e = (*this)[k];
|
||||
unsigned int i = 0;
|
||||
const unsigned int last = cap - 1;
|
||||
for(;;) {
|
||||
if ((i == last)||(i >= (unsigned int)e.size()))
|
||||
break;
|
||||
v[i] = (char)e[i];
|
||||
++i;
|
||||
}
|
||||
v[i] = 0;
|
||||
}
|
||||
|
||||
void Dictionary::clear()
|
||||
{
|
||||
_t.clear();
|
||||
}
|
||||
|
||||
void Dictionary::encode(std::vector<uint8_t> &out) const
|
||||
{
|
||||
uint64_t str[2] = { 0,0 }; // second entry causes all strings to be null-terminated even if 8 chars in length
|
||||
|
||||
out.clear();
|
||||
|
||||
Hashtable< uint64_t,std::vector<uint8_t> >::Iterator ti(const_cast<Dictionary *>(this)->_t);
|
||||
uint64_t *kk = nullptr;
|
||||
std::vector<uint8_t> *vv = nullptr;
|
||||
while (ti.next(kk,vv)) {
|
||||
str[0] = *kk;
|
||||
const char *k = (const char *)str;
|
||||
|
||||
for(;;) {
|
||||
char kc = *(k++);
|
||||
if (!kc) break;
|
||||
if ((kc >= 33)&&(kc <= 126)&&(kc != 61)&&(kc != 92)) // printable ASCII with no spaces, equals, or backslash
|
||||
out.push_back((uint8_t)kc);
|
||||
}
|
||||
|
||||
out.push_back(61); // =
|
||||
|
||||
for(std::vector<uint8_t>::const_iterator i(vv->begin());i!=vv->end();++i) {
|
||||
uint8_t c = *i;
|
||||
switch(c) {
|
||||
case 0:
|
||||
out.push_back(92);
|
||||
out.push_back(48);
|
||||
break;
|
||||
case 10:
|
||||
out.push_back(92);
|
||||
out.push_back(110);
|
||||
break;
|
||||
case 13:
|
||||
out.push_back(92);
|
||||
out.push_back(114);
|
||||
break;
|
||||
case 61:
|
||||
out.push_back(92);
|
||||
out.push_back(101);
|
||||
break;
|
||||
case 92:
|
||||
out.push_back(92);
|
||||
out.push_back(92);
|
||||
break;
|
||||
default:
|
||||
out.push_back(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out.push_back(10);
|
||||
}
|
||||
}
|
||||
|
||||
bool Dictionary::decode(const void *data,unsigned int len)
|
||||
{
|
||||
clear();
|
||||
|
||||
uint64_t k = 0;
|
||||
unsigned int ki = 0;
|
||||
std::vector<uint8_t> *v = nullptr;
|
||||
bool escape = false;
|
||||
for(unsigned int di=0;di<len;++di) {
|
||||
uint8_t c = reinterpret_cast<const uint8_t *>(data)[di];
|
||||
if (!c) break;
|
||||
if (v) {
|
||||
if (escape) {
|
||||
escape = false;
|
||||
switch(c) {
|
||||
case 48:
|
||||
v->push_back(0);
|
||||
break;
|
||||
case 101:
|
||||
v->push_back(61);
|
||||
break;
|
||||
case 110:
|
||||
v->push_back(10);
|
||||
break;
|
||||
case 114:
|
||||
v->push_back(13);
|
||||
break;
|
||||
default:
|
||||
_d[j++] = *p;
|
||||
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
v->push_back(c);
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
++k;
|
||||
} else {
|
||||
if (c == 10) {
|
||||
k = 0;
|
||||
ki = 0;
|
||||
v = nullptr;
|
||||
} else if (c == 92) {
|
||||
escape = true;
|
||||
} else {
|
||||
v->push_back(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((c < 33)||(c > 126)||(c == 92)) {
|
||||
return false;
|
||||
} else if (c == 61) {
|
||||
v = &_t[k];
|
||||
} else {
|
||||
reinterpret_cast<uint8_t *>(&k)[ki & 7U] ^= c;
|
||||
}
|
||||
|
||||
_d[j] = (char)0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Dictionary::add(const char *key,bool value)
|
||||
{
|
||||
return this->add(key,(value) ? "1" : "0",1);
|
||||
}
|
||||
|
||||
bool Dictionary::add(const char *key,uint64_t value)
|
||||
{
|
||||
char tmp[32];
|
||||
return this->add(key,Utils::hex(value,tmp),-1);
|
||||
}
|
||||
|
||||
bool Dictionary::add(const char *key,int64_t value)
|
||||
{
|
||||
char tmp[32];
|
||||
if (value >= 0) {
|
||||
return this->add(key,Utils::hex((uint64_t)value,tmp),-1);
|
||||
} else {
|
||||
tmp[0] = '-';
|
||||
return this->add(key,Utils::hex((uint64_t)(value * -1),tmp+1),-1);
|
||||
}
|
||||
}
|
||||
|
||||
bool Dictionary::add(const char *key,const Address &a)
|
||||
{
|
||||
char tmp[32];
|
||||
return this->add(key,Utils::hex(a.toInt(),tmp),-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
|
|
@ -18,181 +18,157 @@
|
|||
#include "Utils.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Buf.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#define ZT_DICTIONARY_MAX_CAPACITY 65536
|
||||
#include <vector>
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* A small (in code and data) packed key=value store
|
||||
* A simple key-value store for short keys
|
||||
*
|
||||
* This stores data in the form of a compact blob that is sort of human
|
||||
* readable (depending on whether you put binary data in it) and is backward
|
||||
* compatible with older versions. Binary data is escaped such that the
|
||||
* serialized form of a Dictionary is always a valid null-terminated C string.
|
||||
* This data structure is used for network configurations, node meta-data,
|
||||
* and other open-definition protocol objects. It consists of a key-value
|
||||
* store with short (under 8 characters) keys that map to strings, blobs,
|
||||
* or integers with the latter being by convention in hex format.
|
||||
*
|
||||
* Keys are restricted: no binary data, no CR/LF, and no equals (=). If a key
|
||||
* contains these characters it may not be retrievable. This is not checked.
|
||||
*
|
||||
* Lookup is via linear search and will be slow with a lot of keys. It's
|
||||
* designed for small things.
|
||||
*
|
||||
* There is code to test and fuzz this in selftest.cpp. Fuzzing a blob of
|
||||
* pointer tricks like this is important after any modifications.
|
||||
*
|
||||
* This is used for network configurations and for saving some things on disk
|
||||
* in the ZeroTier One service code.
|
||||
*
|
||||
* @tparam C Dictionary max capacity in bytes
|
||||
* If this seems a little odd, it is. It dates back to the very first alpha
|
||||
* versions of ZeroTier and if it were redesigned today we'd use some kind
|
||||
* of simple or standardized binary encoding. Nevertheless it is efficient
|
||||
* and it works so there is no need to change it and break backward
|
||||
* compatibility.
|
||||
*/
|
||||
class Dictionary
|
||||
{
|
||||
public:
|
||||
ZT_ALWAYS_INLINE Dictionary() { _d[0] = 0; }
|
||||
explicit ZT_ALWAYS_INLINE Dictionary(const char *s) { this->load(s); }
|
||||
Dictionary(const char *s,unsigned int len);
|
||||
|
||||
ZT_ALWAYS_INLINE operator bool() const { return (_d[0] != 0); }
|
||||
Dictionary();
|
||||
~Dictionary();
|
||||
|
||||
/**
|
||||
* Load a dictionary from a C-string
|
||||
* Get a reference to a value
|
||||
*
|
||||
* @param s Dictionary in string form
|
||||
* @return False if 's' was longer than our capacity
|
||||
* @param k Key to look up
|
||||
* @return Reference to value
|
||||
*/
|
||||
bool load(const char *s);
|
||||
std::vector<uint8_t> &operator[](const char *k);
|
||||
|
||||
/**
|
||||
* Delete all entries
|
||||
*/
|
||||
ZT_ALWAYS_INLINE void clear() { memset(_d,0,sizeof(_d)); }
|
||||
|
||||
/**
|
||||
* Get an entry
|
||||
* Get a const reference to a value
|
||||
*
|
||||
* Note that to get binary values, dest[] should be at least one more than
|
||||
* the maximum size of the value being retrieved. That's because even if
|
||||
* the data is binary a terminating 0 is still appended to dest[] after it.
|
||||
* @param k Key to look up
|
||||
* @return Reference to value or to empty vector if not found
|
||||
*/
|
||||
const std::vector<uint8_t> &operator[](const char *k) const;
|
||||
|
||||
/**
|
||||
* Add a boolean as '1' or '0'
|
||||
*/
|
||||
void add(const char *k,bool v);
|
||||
|
||||
/**
|
||||
* Add an integer as a hexadecimal string value
|
||||
*/
|
||||
void add(const char *k,uint16_t v);
|
||||
|
||||
/**
|
||||
* Add an integer as a hexadecimal string value
|
||||
*/
|
||||
void add(const char *k,uint32_t v);
|
||||
|
||||
/**
|
||||
* Add an integer as a hexadecimal string value
|
||||
*/
|
||||
void add(const char *k,uint64_t v);
|
||||
|
||||
ZT_ALWAYS_INLINE void add(const char *k,int16_t v) { add(k,(uint16_t)v); }
|
||||
ZT_ALWAYS_INLINE void add(const char *k,int32_t v) { add(k,(uint32_t)v); }
|
||||
ZT_ALWAYS_INLINE void add(const char *k,int64_t v) { add(k,(uint64_t)v); }
|
||||
|
||||
/**
|
||||
* Add an address in 10-digit hex string format
|
||||
*/
|
||||
void add(const char *k,const Address &v);
|
||||
|
||||
/**
|
||||
* Add a C string as a value
|
||||
*/
|
||||
void add(const char *k,const char *v);
|
||||
|
||||
/**
|
||||
* Add a binary blob as a value
|
||||
*/
|
||||
void add(const char *k,const void *data,unsigned int len);
|
||||
|
||||
/**
|
||||
* Get a boolean
|
||||
*
|
||||
* If the key is not found, dest[0] is set to 0 to make dest[] an empty
|
||||
* C string in that case. The dest[] array will *never* be unterminated
|
||||
* after this call.
|
||||
* @param k Key to look up
|
||||
* @param dfl Default value (default: false)
|
||||
* @return Value of key or default if not found
|
||||
*/
|
||||
bool getB(const char *k,bool dfl = false) const;
|
||||
|
||||
/**
|
||||
* Get an integer
|
||||
*
|
||||
* Security note: if 'key' is ever directly based on anything that is not
|
||||
* a hard-code or internally-generated name, it must be checked to ensure
|
||||
* that the buffer is NULL-terminated since key[] does not take a secondary
|
||||
* size parameter. In NetworkConfig all keys are hard-coded strings so this
|
||||
* isn't a problem in the core.
|
||||
* @param k Key to look up
|
||||
* @param dfl Default value (default: 0)
|
||||
* @return Value of key or default if not found
|
||||
*/
|
||||
uint64_t getUI(const char *k,uint64_t dfl = 0) const;
|
||||
|
||||
/**
|
||||
* Get a C string
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dest Destination buffer
|
||||
* @param destlen Size of destination buffer
|
||||
* @return -1 if not found, or actual number of bytes stored in dest[] minus trailing 0
|
||||
*/
|
||||
int get(const char *key,char *dest,unsigned int destlen) const;
|
||||
|
||||
/**
|
||||
* Get a boolean value
|
||||
* If the buffer is too small the string will be truncated, but the
|
||||
* buffer will always end in a terminating null no matter what.
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dfl Default value if not found in dictionary
|
||||
* @return Boolean value of key or 'dfl' if not found
|
||||
* @param k Key to look up
|
||||
* @param v Buffer to hold string
|
||||
* @param cap Maximum size of string (including terminating null)
|
||||
*/
|
||||
ZT_ALWAYS_INLINE bool getB(const char *key,bool dfl = false) const
|
||||
{
|
||||
char tmp[4];
|
||||
if (this->get(key,tmp,sizeof(tmp)) >= 0)
|
||||
return ((*tmp == '1')||(*tmp == 't')||(*tmp == 'T'));
|
||||
return dfl;
|
||||
}
|
||||
void getS(const char *k,char *v,unsigned int cap) const;
|
||||
|
||||
/**
|
||||
* Get an unsigned int64 stored as hex in the dictionary
|
||||
* Erase all entries in dictionary
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @return Number of entries
|
||||
*/
|
||||
ZT_ALWAYS_INLINE unsigned int size() const { return _t.size(); }
|
||||
|
||||
/**
|
||||
* @return True if dictionary is not empty
|
||||
*/
|
||||
ZT_ALWAYS_INLINE bool empty() const { return _t.empty(); }
|
||||
|
||||
/**
|
||||
* Encode to a string in the supplied vector
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dfl Default value or 0 if unspecified
|
||||
* @return Decoded hex UInt value or 'dfl' if not found
|
||||
*/
|
||||
ZT_ALWAYS_INLINE uint64_t getUI(const char *key,uint64_t dfl = 0) const
|
||||
{
|
||||
char tmp[128];
|
||||
if (this->get(key,tmp,sizeof(tmp)) >= 1)
|
||||
return Utils::hexStrToU64(tmp);
|
||||
return dfl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unsigned int64 stored as hex in the dictionary
|
||||
* This does not add a terminating zero. This must be pushed afterwords
|
||||
* if the result is to be handled as a C string.
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dfl Default value or 0 if unspecified
|
||||
* @return Decoded hex UInt value or 'dfl' if not found
|
||||
* @param out String encoded dictionary
|
||||
*/
|
||||
ZT_ALWAYS_INLINE int64_t getI(const char *key,int64_t dfl = 0) const
|
||||
{
|
||||
char tmp[128];
|
||||
if (this->get(key,tmp,sizeof(tmp)) >= 1)
|
||||
return Utils::hexStrTo64(tmp);
|
||||
return dfl;
|
||||
}
|
||||
void encode(std::vector<uint8_t> &out) const;
|
||||
|
||||
/**
|
||||
* Add a new key=value pair
|
||||
* Decode a string encoded dictionary
|
||||
*
|
||||
* If the key is already present this will append another, but the first
|
||||
* will always be returned by get(). This is not checked. If you want to
|
||||
* ensure a key is not present use erase() first.
|
||||
* This will decode up to 'len' but will also abort if it finds a
|
||||
* null/zero as this could be a C string.
|
||||
*
|
||||
* Use the vlen parameter to add binary values. Nulls will be escaped.
|
||||
*
|
||||
* @param key Key -- nulls, CR/LF, and equals (=) are illegal characters
|
||||
* @param value Value to set
|
||||
* @param vlen Length of value in bytes or -1 to treat value[] as a C-string and look for terminating 0
|
||||
* @return True if there was enough room to add this key=value pair
|
||||
* @param data Data to decode
|
||||
* @param len Length of data
|
||||
* @return True if dictionary was formatted correctly and valid, false on error
|
||||
*/
|
||||
bool add(const char *key,const char *value,int vlen = -1);
|
||||
|
||||
/**
|
||||
* Add a boolean as a '1' or a '0'
|
||||
*/
|
||||
bool add(const char *key,bool value);
|
||||
|
||||
/**
|
||||
* Add a 64-bit integer (unsigned) as a hex value
|
||||
*/
|
||||
bool add(const char *key,uint64_t value);
|
||||
|
||||
/**
|
||||
* Add a 64-bit integer (unsigned) as a hex value
|
||||
*/
|
||||
bool add(const char *key,int64_t value);
|
||||
|
||||
/**
|
||||
* Add a 64-bit integer (unsigned) as a hex value
|
||||
*/
|
||||
bool add(const char *key,const Address &a);
|
||||
|
||||
/**
|
||||
* @param key Key to check
|
||||
* @return True if key is present
|
||||
*/
|
||||
ZT_ALWAYS_INLINE bool contains(const char *key) const
|
||||
{
|
||||
char tmp[2];
|
||||
return (this->get(key,tmp,2) >= 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value of C template parameter
|
||||
*/
|
||||
ZT_ALWAYS_INLINE unsigned int capacity() const { return sizeof(_d); }
|
||||
|
||||
ZT_ALWAYS_INLINE const char *data() const { return _d; }
|
||||
bool decode(const void *data,unsigned int len);
|
||||
|
||||
private:
|
||||
char _d[ZT_DICTIONARY_MAX_CAPACITY];
|
||||
Hashtable< uint64_t,std::vector<uint8_t> > _t;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
|
|
@ -35,7 +35,7 @@ private:
|
|||
struct _Bucket
|
||||
{
|
||||
ZT_ALWAYS_INLINE _Bucket(const K &k,const V &v) : k(k),v(v) {}
|
||||
ZT_ALWAYS_INLINE _Bucket(const K &k) : k(k),v() {}
|
||||
explicit ZT_ALWAYS_INLINE _Bucket(const K &k) : k(k),v() {}
|
||||
ZT_ALWAYS_INLINE _Bucket(const _Bucket &b) : k(b.k),v(b.v) {}
|
||||
ZT_ALWAYS_INLINE _Bucket &operator=(const _Bucket &b) { k = b.k; v = b.v; return *this; }
|
||||
_Bucket *next; // must be set manually for each _Bucket
|
||||
|
@ -57,7 +57,7 @@ public:
|
|||
/**
|
||||
* @param ht Hash table to iterate over
|
||||
*/
|
||||
ZT_ALWAYS_INLINE Iterator(Hashtable &ht) :
|
||||
explicit ZT_ALWAYS_INLINE Iterator(Hashtable &ht) :
|
||||
_idx(0),
|
||||
_ht(&ht),
|
||||
_b(ht._t[0])
|
||||
|
@ -95,7 +95,7 @@ public:
|
|||
/**
|
||||
* @param bc Initial capacity in buckets (default: 32, must be nonzero)
|
||||
*/
|
||||
ZT_ALWAYS_INLINE Hashtable(unsigned long bc = 32) :
|
||||
explicit ZT_ALWAYS_INLINE Hashtable(unsigned long bc = 32) :
|
||||
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
|
||||
_bc(bc),
|
||||
_s(0)
|
||||
|
|
|
@ -44,138 +44,105 @@ NetworkConfig::NetworkConfig() :
|
|||
bool NetworkConfig::toDictionary(Dictionary &d,bool includeLegacy) const
|
||||
{
|
||||
uint8_t tmp[16384];
|
||||
std::vector<uint8_t> buf;
|
||||
char tmp2[128];
|
||||
try {
|
||||
d.clear();
|
||||
|
||||
d.clear();
|
||||
d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId);
|
||||
d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp);
|
||||
d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta);
|
||||
d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision);
|
||||
d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo.toString((char *)tmp));
|
||||
d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags);
|
||||
d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit);
|
||||
d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint16_t)this->type);
|
||||
d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name);
|
||||
d.add(ZT_NETWORKCONFIG_DICT_KEY_MTU,this->mtu);
|
||||
|
||||
// Try to put the more human-readable fields first
|
||||
if (this->com)
|
||||
d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,tmp,this->com.marshal(tmp));
|
||||
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo.toString(tmp2))) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MTU,(uint64_t)this->mtu)) return false;
|
||||
|
||||
// Then add binary blobs
|
||||
|
||||
if (this->com) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,(const char *)tmp,this->com.marshal(tmp)))
|
||||
return false;
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
for(unsigned int i=0;i<this->capabilityCount;++i) {
|
||||
int l = this->capabilities[i].marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
buf.insert(buf.end(),tmp,tmp + l);
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,(const char *)buf.data(),(int)buf.size()))
|
||||
return false;
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
for(unsigned int i=0;i<this->tagCount;++i) {
|
||||
int l = this->tags[i].marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
buf.insert(buf.end(),tmp,tmp + l);
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TAGS,(const char *)buf.data(),(int)buf.size()))
|
||||
return false;
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
for(unsigned int i=0;i<this->certificateOfOwnershipCount;++i) {
|
||||
int l = this->certificatesOfOwnership[i].marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
buf.insert(buf.end(),tmp,tmp + l);
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,(const char *)buf.data(),(int)buf.size()))
|
||||
return false;
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
for(unsigned int i=0;i<this->specialistCount;++i) {
|
||||
Utils::storeBigEndian<uint64_t>(tmp,this->specialists[i]);
|
||||
buf.insert(buf.end(),tmp,tmp + 8);
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,(const char *)buf.data(),(int)buf.size()))
|
||||
return false;
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
for(unsigned int i=0;i<this->routeCount;++i) {
|
||||
int l = asInetAddress(this->routes[i].target).marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
buf.insert(buf.end(),tmp,tmp + l);
|
||||
l = asInetAddress(this->routes[i].via).marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
buf.insert(buf.end(),tmp,tmp + l);
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,(const char *)buf.data(),(int)buf.size()))
|
||||
return false;
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
for(unsigned int i=0;i<this->staticIpCount;++i) {
|
||||
int l = this->staticIps[i].marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
buf.insert(buf.end(),tmp,tmp + l);
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,(const char *)buf.data(),(int)buf.size()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->ruleCount) {
|
||||
buf.resize(ruleCount * ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX);
|
||||
int l = Capability::marshalVirtualNetworkRules(buf.data(),rules,ruleCount);
|
||||
if (l > 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,(const char *)buf.data(),l))
|
||||
std::vector<uint8_t> *blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES]);
|
||||
for (unsigned int i = 0; i < this->capabilityCount; ++i) {
|
||||
int l = this->capabilities[i].marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
blob->insert(blob->end(),tmp,tmp + l);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_TAGS]);
|
||||
for (unsigned int i = 0; i < this->tagCount; ++i) {
|
||||
int l = this->tags[i].marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
blob->insert(blob->end(),tmp,tmp + l);
|
||||
}
|
||||
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP]);
|
||||
for (unsigned int i = 0; i < this->certificateOfOwnershipCount; ++i) {
|
||||
int l = this->certificatesOfOwnership[i].marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
blob->insert(blob->end(),tmp,tmp + l);
|
||||
}
|
||||
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS]);
|
||||
for (unsigned int i = 0; i < this->specialistCount; ++i) {
|
||||
Utils::storeBigEndian<uint64_t>(tmp,this->specialists[i]);
|
||||
blob->insert(blob->end(),tmp,tmp + 8);
|
||||
}
|
||||
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_ROUTES]);
|
||||
for (unsigned int i = 0; i < this->routeCount; ++i) {
|
||||
int l = asInetAddress(this->routes[i].target).marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
blob->insert(blob->end(),tmp,tmp + l);
|
||||
l = asInetAddress(this->routes[i].via).marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
blob->insert(blob->end(),tmp,tmp + l);
|
||||
}
|
||||
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS]);
|
||||
for (unsigned int i = 0; i < this->staticIpCount; ++i) {
|
||||
int l = this->staticIps[i].marshal(tmp);
|
||||
if (l < 0)
|
||||
return false;
|
||||
blob->insert(blob->end(),tmp,tmp + l);
|
||||
}
|
||||
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_RULES]);
|
||||
if (this->ruleCount) {
|
||||
blob->resize(ruleCount * ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX);
|
||||
int l = Capability::marshalVirtualNetworkRules(blob->data(),rules,ruleCount);
|
||||
if (l > 0)
|
||||
blob->resize(l);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch ( ... ) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkConfig::fromDictionary(const Dictionary &d)
|
||||
{
|
||||
static const NetworkConfig NIL_NC;
|
||||
ScopedPtr< Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> > tmp(new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>());
|
||||
|
||||
try {
|
||||
*this = NIL_NC;
|
||||
|
||||
// Fields that are always present, new or old
|
||||
this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0);
|
||||
if (!this->networkId)
|
||||
return false;
|
||||
this->timestamp = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,0);
|
||||
if (this->timestamp <= 0)
|
||||
return false;
|
||||
this->credentialTimeMaxDelta = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,0);
|
||||
this->revision = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REVISION,0);
|
||||
this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0);
|
||||
if (!this->issuedTo)
|
||||
return false;
|
||||
this->multicastLimit = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,0);
|
||||
d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name));
|
||||
|
||||
d.getS(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name));
|
||||
this->mtu = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MTU,ZT_DEFAULT_MTU);
|
||||
if (this->mtu < 1280)
|
||||
this->mtu = 1280; // minimum MTU allowed by IPv6 standard and others
|
||||
|
@ -185,87 +152,120 @@ bool NetworkConfig::fromDictionary(const Dictionary &d)
|
|||
if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) {
|
||||
return false;
|
||||
} else {
|
||||
// Otherwise we can use the new fields
|
||||
this->flags = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,0);
|
||||
this->type = (ZT_VirtualNetworkType)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)ZT_NETWORK_TYPE_PRIVATE);
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp))
|
||||
this->com.deserialize(*tmp,0);
|
||||
const std::vector<uint8_t> *blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_COM]);
|
||||
if (!blob->empty()) {
|
||||
if (this->com.unmarshal(blob->data(),(int)(blob->size()) < 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) {
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES]);
|
||||
if (!blob->empty()) {
|
||||
try {
|
||||
unsigned int p = 0;
|
||||
while (p < tmp->size()) {
|
||||
while (p < blob->size()) {
|
||||
Capability cap;
|
||||
p += cap.deserialize(*tmp,p);
|
||||
this->capabilities[this->capabilityCount++] = cap;
|
||||
int l = cap.unmarshal(blob->data() + p,(int)(blob->size() - p));
|
||||
if (l < 0)
|
||||
return false;
|
||||
p += l;
|
||||
if (this->capabilityCount < ZT_MAX_NETWORK_CAPABILITIES)
|
||||
this->capabilities[this->capabilityCount++] = cap;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
std::sort(&(this->capabilities[0]),&(this->capabilities[this->capabilityCount]));
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) {
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_TAGS]);
|
||||
if (!blob->empty()) {
|
||||
try {
|
||||
unsigned int p = 0;
|
||||
while (p < tmp->size()) {
|
||||
while (p < blob->size()) {
|
||||
Tag tag;
|
||||
p += tag.deserialize(*tmp,p);
|
||||
this->tags[this->tagCount++] = tag;
|
||||
int l = tag.unmarshal(blob->data() + p,(int)(blob->size() - p));
|
||||
if (l < 0)
|
||||
return false;
|
||||
p += l;
|
||||
if (this->tagCount < ZT_MAX_NETWORK_TAGS)
|
||||
this->tags[this->tagCount++] = tag;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
std::sort(&(this->tags[0]),&(this->tags[this->tagCount]));
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) {
|
||||
unsigned int p = 0;
|
||||
while (p < tmp->size()) {
|
||||
if (certificateOfOwnershipCount < ZT_MAX_CERTIFICATES_OF_OWNERSHIP)
|
||||
p += certificatesOfOwnership[certificateOfOwnershipCount++].deserialize(*tmp,p);
|
||||
else {
|
||||
CertificateOfOwnership foo;
|
||||
p += foo.deserialize(*tmp,p);
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP]);
|
||||
if (!blob->empty()) {
|
||||
try {
|
||||
unsigned int p = 0;
|
||||
while (p < blob->size()) {
|
||||
CertificateOfOwnership coo;
|
||||
int l = coo.unmarshal(blob->data() + p,(int)(blob->size() - p));
|
||||
if (l < 0)
|
||||
return false;
|
||||
p += l;
|
||||
if (this->certificateOfOwnershipCount < ZT_MAX_CERTIFICATES_OF_OWNERSHIP)
|
||||
this->certificatesOfOwnership[certificateOfOwnershipCount++] = coo;
|
||||
}
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
std::sort(&(this->certificatesOfOwnership[0]),&(this->certificatesOfOwnership[this->certificateOfOwnershipCount]));
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) {
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS]);
|
||||
if (!blob->empty()) {
|
||||
unsigned int p = 0;
|
||||
while ((p + 8) <= tmp->size()) {
|
||||
if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS)
|
||||
this->specialists[this->specialistCount++] = tmp->at<uint64_t>(p);
|
||||
while (((p + 8) <= blob->size())&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) {
|
||||
this->specialists[this->specialistCount++] = Utils::loadBigEndian<uint64_t>(blob->data() + p);
|
||||
p += 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) {
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_ROUTES]);
|
||||
if (!blob->empty()) {
|
||||
unsigned int p = 0;
|
||||
while ((p < tmp->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
|
||||
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(*tmp,p);
|
||||
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(*tmp,p);
|
||||
this->routes[this->routeCount].flags = tmp->at<uint16_t>(p); p += 2;
|
||||
this->routes[this->routeCount].metric = tmp->at<uint16_t>(p); p += 2;
|
||||
while ((p < blob->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
|
||||
int l = asInetAddress(this->routes[this->routeCount].target).unmarshal(blob->data(),(int)(blob->size() - p));
|
||||
if (l < 0)
|
||||
return false;
|
||||
p += l;
|
||||
if (p >= blob->size())
|
||||
return false;
|
||||
l = asInetAddress(this->routes[this->routeCount].via).unmarshal(blob->data(),(int)(blob->size() - p));
|
||||
if (l < 0)
|
||||
return false;
|
||||
p += l;
|
||||
if ((p + 4) > blob->size())
|
||||
return false;
|
||||
this->routes[this->routeCount].flags = Utils::loadBigEndian<uint16_t>(blob->data() + p); p += 2;
|
||||
this->routes[this->routeCount].metric = Utils::loadBigEndian<uint16_t>(blob->data() + p); p += 2;
|
||||
++this->routeCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) {
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS]);
|
||||
if (!blob->empty()) {
|
||||
unsigned int p = 0;
|
||||
while ((p < tmp->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
|
||||
p += this->staticIps[this->staticIpCount++].deserialize(*tmp,p);
|
||||
while ((p < blob->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
|
||||
int l = this->staticIps[this->staticIpCount].unmarshal(blob->data() + p,(int)(blob->size() - p));
|
||||
if (l < 0)
|
||||
return false;
|
||||
p += l;
|
||||
++this->staticIpCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) {
|
||||
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_RULES]);
|
||||
if (!blob->empty()) {
|
||||
this->ruleCount = 0;
|
||||
unsigned int p = 0;
|
||||
Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES);
|
||||
if (Capability::unmarshalVirtualNetworkRules(blob->data(),(int)blob->size(),this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES) < 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch ( ... ) {
|
||||
return false;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkConfig::addSpecialist(const Address &a,const uint64_t f)
|
||||
|
|
|
@ -97,6 +97,85 @@ char *decimal(unsigned long n,char s[24])
|
|||
return s;
|
||||
}
|
||||
|
||||
char *hex(uint8_t i,char s[3])
|
||||
{
|
||||
s[0] = HEXCHARS[(i >> 4U) & 0xfU];
|
||||
s[1] = HEXCHARS[i & 0xfU];
|
||||
s[2] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
char *hex(uint16_t i,char s[5])
|
||||
{
|
||||
s[0] = HEXCHARS[(i >> 12U) & 0xfU];
|
||||
s[1] = HEXCHARS[(i >> 8U) & 0xfU];
|
||||
s[2] = HEXCHARS[(i >> 4U) & 0xfU];
|
||||
s[3] = HEXCHARS[i & 0xfU];
|
||||
s[4] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
char *hex(uint32_t i,char s[9])
|
||||
{
|
||||
s[0] = HEXCHARS[(i >> 28U) & 0xfU];
|
||||
s[1] = HEXCHARS[(i >> 24U) & 0xfU];
|
||||
s[2] = HEXCHARS[(i >> 20U) & 0xfU];
|
||||
s[3] = HEXCHARS[(i >> 16U) & 0xfU];
|
||||
s[4] = HEXCHARS[(i >> 12U) & 0xfU];
|
||||
s[5] = HEXCHARS[(i >> 8U) & 0xfU];
|
||||
s[6] = HEXCHARS[(i >> 4U) & 0xfU];
|
||||
s[7] = HEXCHARS[i & 0xfU];
|
||||
s[8] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
char *hex(uint64_t i,char s[17])
|
||||
{
|
||||
s[0] = HEXCHARS[(i >> 60U) & 0xfU];
|
||||
s[1] = HEXCHARS[(i >> 56U) & 0xfU];
|
||||
s[2] = HEXCHARS[(i >> 52U) & 0xfU];
|
||||
s[3] = HEXCHARS[(i >> 48U) & 0xfU];
|
||||
s[4] = HEXCHARS[(i >> 44U) & 0xfU];
|
||||
s[5] = HEXCHARS[(i >> 40U) & 0xfU];
|
||||
s[6] = HEXCHARS[(i >> 36U) & 0xfU];
|
||||
s[7] = HEXCHARS[(i >> 32U) & 0xfU];
|
||||
s[8] = HEXCHARS[(i >> 28U) & 0xfU];
|
||||
s[9] = HEXCHARS[(i >> 24U) & 0xfU];
|
||||
s[10] = HEXCHARS[(i >> 20U) & 0xfU];
|
||||
s[11] = HEXCHARS[(i >> 16U) & 0xfU];
|
||||
s[12] = HEXCHARS[(i >> 12U) & 0xfU];
|
||||
s[13] = HEXCHARS[(i >> 8U) & 0xfU];
|
||||
s[14] = HEXCHARS[(i >> 4U) & 0xfU];
|
||||
s[15] = HEXCHARS[i & 0xfU];
|
||||
s[16] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
uint64_t unhex(const char *s)
|
||||
{
|
||||
uint64_t n = 0;
|
||||
if (s) {
|
||||
int k = 0;
|
||||
while (k < 16) {
|
||||
char hc = *(s++);
|
||||
if (!hc) break;
|
||||
|
||||
uint8_t c = 0;
|
||||
if ((hc >= 48)&&(hc <= 57))
|
||||
c = hc - 48;
|
||||
else if ((hc >= 97)&&(hc <= 102))
|
||||
c = hc - 87;
|
||||
else if ((hc >= 65)&&(hc <= 70))
|
||||
c = hc - 55;
|
||||
|
||||
n <<= 4U;
|
||||
n |= (uint64_t)c;
|
||||
++k;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
char *hex10(uint64_t i,char s[11])
|
||||
{
|
||||
s[0] = HEXCHARS[(i >> 36U) & 0xfU];
|
||||
|
|
|
@ -85,17 +85,18 @@ char *decimal(unsigned long n,char s[24]);
|
|||
* @param s Buffer to receive hex, must be at least (2*sizeof(i))+1 in size or overflow will occur.
|
||||
* @return Pointer to s containing hex string with trailing zero byte
|
||||
*/
|
||||
template<typename I>
|
||||
static ZT_ALWAYS_INLINE char *hex(I x,char *s)
|
||||
{
|
||||
char *const r = s;
|
||||
for(unsigned int i=0,b=(sizeof(x)*8);i<sizeof(x);++i) {
|
||||
*(s++) = HEXCHARS[(x >> (b -= 4)) & 0xf];
|
||||
*(s++) = HEXCHARS[(x >> (b -= 4)) & 0xf];
|
||||
}
|
||||
*s = (char)0;
|
||||
return r;
|
||||
}
|
||||
char *hex(uint8_t i,char s[3]);
|
||||
char *hex(uint16_t i,char s[5]);
|
||||
char *hex(uint32_t i,char s[9]);
|
||||
char *hex(uint64_t i,char s[17]);
|
||||
|
||||
/**
|
||||
* Decode an unsigned integer in hex format
|
||||
*
|
||||
* @param s String to decode, non-hex chars are ignored
|
||||
* @return Unsigned integer
|
||||
*/
|
||||
uint64_t unhex(const char *s);
|
||||
|
||||
/**
|
||||
* Convert the least significant 40 bits of a uint64_t to hex
|
||||
|
|
Loading…
Add table
Reference in a new issue