// File: pjson.h - written by Rich Geldreich 2012 - License: Unlicense http://unlicense.org/ #ifndef PURPLE_JSON_H #define PURPLE_JSON_H #ifdef WIN32 #pragma once #endif #include #include #include #include // ---- Macros #define PJSON_ASSERT assert #define PJSON_FORCEINLINE __forceinline #define PJSON_PARSE_STATS 0 #define PJSON_DEFAULT_MIN_CHUNK_SIZE 4096 #define PJSON_MAX_CHUNK_GROW_SIZE 8*1024*1024 #define PJSON_DEFAULT_MAX_BYTES_TO_PRESERVE_ACROSS_RESETS 16*1024*1024 #define PJSON_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define PJSON_MAX(a, b) (((a) < (b)) ? (b) : (a)) namespace pjson { // ---- Types typedef unsigned char uint8; typedef unsigned int uint; typedef signed int int32; typedef unsigned int uint32; typedef signed __int64 int64; typedef unsigned __int64 uint64; class document; class value_variant; struct value_variant_data; struct key_value_t; typedef std::vector char_vec_t; typedef std::string string_t; // Memory allocation inline void* pjson_malloc(size_t size) { return malloc(size); } inline void* pjson_realloc(void* p, size_t size) { return realloc(p, size); } inline void pjson_free(void* p) { free(p); } // Misc. Helpers template inline void swap(T& l, T& r) { T temp(l); l = r; r = temp; } inline bool is_power_of_2(uint32 x) { return x && ((x & (x - 1U)) == 0U); } inline uint32 next_pow2(uint32 val) { val--; val |= val >> 16; val |= val >> 8; val |= val >> 4; val |= val >> 2; val |= val >> 1; return val + 1; } inline int pjson_stricmp(const char* p, const char* q) { return _stricmp(p, q); } // ---- Global Arrays // This template utilizes the One Definition Rule to create global arrays in a header. template struct globals_struct { static const uint8 s_str_serialize_flags[256]; static const double s_pow10_table[63]; static const uint8 s_parse_flags[256]; }; typedef globals_struct<> globals; template const uint8 globals_struct::s_str_serialize_flags[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 // 128-255 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 }; template const double globals_struct::s_pow10_table[63] = { 1.e-031,1.e-030,1.e-029,1.e-028,1.e-027,1.e-026,1.e-025,1.e-024,1.e-023,1.e-022,1.e-021,1.e-020,1.e-019,1.e-018,1.e-017,1.e-016, 1.e-015,1.e-014,1.e-013,1.e-012,1.e-011,1.e-010,1.e-009,1.e-008,1.e-007,1.e-006,1.e-005,1.e-004,1.e-003,1.e-002,1.e-001,1.e+000, 1.e+001,1.e+002,1.e+003,1.e+004,1.e+005,1.e+006,1.e+007,1.e+008,1.e+009,1.e+010,1.e+011,1.e+012,1.e+013,1.e+014,1.e+015,1.e+016, 1.e+017,1.e+018,1.e+019,1.e+020,1.e+021,1.e+022,1.e+023,1.e+024,1.e+025,1.e+026,1.e+027,1.e+028,1.e+029,1.e+030,1.e+031 }; // bit 0 (1) - set if: \0 cr lf " \ // bit 1 (2) - set if: \0 cr lf // bit 2 (4) - set if: whitespace // bit 3 (8) - set if: 0-9 // bit 4 (0x10) - set if: 0-9 e E . template const uint8 globals_struct::s_parse_flags[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 4, 4, 7, 4, 4, // 0 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 1 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10,0, // 2 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0x10,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0x10,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 // 128-255 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 }; // ---- Pool Allocator struct pool_allocator { inline pool_allocator(uint initial_size = 0, uint min_chunk_size = PJSON_DEFAULT_MIN_CHUNK_SIZE, size_t max_bytes_to_preserve_across_resets = PJSON_DEFAULT_MAX_BYTES_TO_PRESERVE_ACROSS_RESETS) : m_pActive_chunks(NULL), m_pFree_chunks(NULL), m_total_free_bytes(0), m_initial_size(initial_size), m_min_chunk_size(min_chunk_size), m_cur_grow_size(min_chunk_size), m_max_to_preserve_across_resets(max_bytes_to_preserve_across_resets) { if (initial_size) { m_pActive_chunks = static_cast(pjson_malloc(sizeof(chunk) + initial_size)); m_pActive_chunks->m_pNext = NULL; m_pActive_chunks->m_ofs = 0; m_pActive_chunks->m_size = initial_size; } } inline ~pool_allocator() { clear(); } // Release all active/free chunks void clear() { free_chunk_chain(m_pActive_chunks); m_pActive_chunks = NULL; free_chunk_chain(m_pFree_chunks); m_pFree_chunks = NULL; m_total_free_bytes = 0; m_cur_grow_size = m_min_chunk_size; } inline size_t get_total_free_bytes() const { return m_total_free_bytes; } inline uint get_min_chunk_size() const { return m_min_chunk_size; } inline size_t get_max_bytes_to_preserve_across_resets() const { return m_max_to_preserve_across_resets; } inline void set_min_chunk_size(uint s) { m_min_chunk_size = m_cur_grow_size = s; } inline void set_max_bytes_to_preserve_across_resets(size_t s) { m_max_to_preserve_across_resets = s; } inline uint get_cur_grow_size() const { return m_cur_grow_size; } inline void* Alloc(size_t size) { size = (size + 3) & ~3; if ((!m_pActive_chunks) || ((m_pActive_chunks->m_size - m_pActive_chunks->m_ofs) < size)) { chunk* pNew_chunk = m_pFree_chunks; if ((pNew_chunk) && (pNew_chunk->m_size >= size)) { PJSON_ASSERT(m_total_free_bytes >= pNew_chunk->m_size); m_total_free_bytes -= pNew_chunk->m_size; m_pFree_chunks = pNew_chunk->m_pNext; PJSON_ASSERT(!pNew_chunk->m_ofs); } else { size_t alloc_size = PJSON_MAX(size, m_cur_grow_size); m_cur_grow_size = PJSON_MIN(m_cur_grow_size * 2, PJSON_MAX_CHUNK_GROW_SIZE); pNew_chunk = static_cast(pjson_malloc(sizeof(chunk) + alloc_size)); pNew_chunk->m_size = alloc_size; pNew_chunk->m_ofs = 0; } pNew_chunk->m_pNext = m_pActive_chunks; m_pActive_chunks = pNew_chunk; } void* pRet = (uint8*)m_pActive_chunks + sizeof(chunk) + m_pActive_chunks->m_ofs; m_pActive_chunks->m_ofs += size; PJSON_ASSERT(m_pActive_chunks->m_ofs <= m_pActive_chunks->m_size); return pRet; } inline void* Realloc(void* p, size_t new_size, size_t cur_size) { if (!p) return Alloc(new_size); new_size = (new_size + 3) & ~3; cur_size = (cur_size + 3) & ~3; if (new_size == cur_size) return p; uint8* pTop = (uint8*)m_pActive_chunks + sizeof(chunk) + m_pActive_chunks->m_ofs; if ((static_cast(p) + cur_size) == pTop) { if (new_size > cur_size) { size_t bytes_needed = new_size - cur_size; if ((m_pActive_chunks->m_size - m_pActive_chunks->m_ofs) >= bytes_needed) { m_pActive_chunks->m_ofs += bytes_needed; PJSON_ASSERT(m_pActive_chunks->m_ofs <= m_pActive_chunks->m_size); return p; } } else { PJSON_ASSERT(m_pActive_chunks->m_ofs >= (cur_size - new_size)); m_pActive_chunks->m_ofs -= (cur_size - new_size); return new_size ? p : NULL; } } if (!new_size) return NULL; void* pNew_block = Alloc(new_size); memcpy(pNew_block, p, cur_size); return pNew_block; } // Move all active chunks to the free chunk list, then free any chunks if we're over the preserve limit. inline void reset() { if (!m_pActive_chunks) return; chunk* pCur_active_tail = m_pActive_chunks; size_t total_allocated_bytes = 0; for ( ; ; ) { total_allocated_bytes += pCur_active_tail->m_size; pCur_active_tail->m_ofs = 0; if (!pCur_active_tail->m_pNext) break; pCur_active_tail = pCur_active_tail->m_pNext; } pCur_active_tail->m_pNext = m_pFree_chunks; m_pFree_chunks = m_pActive_chunks; m_pActive_chunks = NULL; m_total_free_bytes += total_allocated_bytes; while (m_total_free_bytes > m_max_to_preserve_across_resets) { PJSON_ASSERT(m_pFree_chunks); chunk* pNext_free = m_pFree_chunks->m_pNext; PJSON_ASSERT(m_total_free_bytes >= m_pFree_chunks->m_size); m_total_free_bytes -= m_pFree_chunks->m_size; pjson_free(m_pFree_chunks); m_pFree_chunks = pNext_free; } m_cur_grow_size = m_min_chunk_size; } struct stats_t { size_t m_total_allocated; uint m_num_active_chunks; size_t m_num_active_bytes_allocated; size_t m_num_active_bytes_avail; size_t m_max_active_chunk_size; uint m_num_free_chunks; size_t m_num_free_chunk_bytes_avail; size_t m_max_free_chunk_size; }; inline void get_stats(stats_t& s) const { memset(&s, 0, sizeof(s)); chunk* pChunk = m_pActive_chunks; while (pChunk) { s.m_num_active_chunks++; s.m_total_allocated += pChunk->m_size; s.m_num_active_bytes_allocated += pChunk->m_ofs; s.m_num_active_bytes_avail += (pChunk->m_size - pChunk->m_ofs); s.m_max_active_chunk_size = PJSON_MAX(s.m_max_active_chunk_size, pChunk->m_size); pChunk = pChunk->m_pNext; } pChunk = m_pFree_chunks; while (pChunk) { s.m_num_free_chunks++; s.m_total_allocated += pChunk->m_size; PJSON_ASSERT(!pChunk->m_ofs); s.m_num_free_chunk_bytes_avail += pChunk->m_size; s.m_max_free_chunk_size = PJSON_MAX(s.m_max_free_chunk_size, pChunk->m_size); pChunk = pChunk->m_pNext; } PJSON_ASSERT(s.m_num_free_chunk_bytes_avail == m_total_free_bytes); } private: pool_allocator(const pool_allocator&); pool_allocator& operator= (const pool_allocator&); struct chunk { chunk* m_pNext; size_t m_size; size_t m_ofs; }; chunk* m_pActive_chunks; chunk* m_pFree_chunks; size_t m_total_free_bytes; uint m_initial_size; uint m_min_chunk_size; size_t m_max_to_preserve_across_resets; uint m_cur_grow_size; inline void free_chunk_chain(chunk* pChunk) { while (pChunk) { chunk* pNext_chunk = pChunk->m_pNext; pjson_free(pChunk); pChunk = pNext_chunk; } } }; // ---- Simple vector (growable array) template struct simple_vector_default_copy_construction_policy { inline static void copy_construct(void *pDst, const T& init, pool_allocator& alloc) { alloc; new (pDst) T(init); } inline static void assign(void *pDst, const T& src, pool_allocator& alloc) { alloc; *static_cast(pDst) = src; } }; template struct simple_vector_allocator_copy_construction_policy { inline static void copy_construct(void *pDst, const T& init, pool_allocator& alloc) { alloc; new (pDst) T(init, alloc); } inline static void assign(void *pDst, const T& src, pool_allocator& alloc) { static_cast(pDst)->assign(src, alloc); } }; template inline T* construct(T* p) { return new (static_cast(p)) T; } template inline void construct_array(T* p, uint n) { T* q = p + n; for ( ; p != q; ++p) new (static_cast(p)) T; } template struct elemental_vector { typedef T value_type; typedef T& reference; typedef const T& const_reference; typedef T* pointer; typedef const T* const_pointer; T* m_p; uint32 m_size; }; template > struct simple_vector : elemental_vector { typedef elemental_vector base; inline simple_vector() { construct(); } inline simple_vector(const simple_vector& other, pool_allocator& alloc) { construct(other, alloc); } // Manual constructor methods inline void construct() { base::m_p = NULL; base::m_size = 0; } inline void construct(uint size, pool_allocator& alloc) { construct(); enlarge(size, alloc, false); } inline void construct(const T* p, uint size, pool_allocator& alloc) { base::m_size = size; base::m_p = NULL; if (size) { uint num_bytes = sizeof(T) * size; base::m_p = static_cast(alloc.Alloc(num_bytes)); if (UseConstructor) { T* pDst = base::m_p; T* pDst_end = pDst + size; const T* pSrc = p; while (pDst != pDst_end) ConstructionPolicy::copy_construct(pDst++, *pSrc++, alloc); } else memcpy(base::m_p, p, num_bytes); } } inline void construct(const simple_vector& other, pool_allocator& alloc) { construct(other.m_p, other.m_size, alloc); } inline uint size() const { return base::m_size; } inline uint size_in_bytes() const { return base::m_size * sizeof(T); } inline const T& operator[] (uint i) const { PJSON_ASSERT(i < base::m_size); return base::m_p[i]; } inline T& operator[] (uint i) { PJSON_ASSERT(i < base::m_size); return base::m_p[i]; } inline const T* get_ptr() const { return base::m_p; } inline T* get_ptr() { return base::m_p; } inline const T* get_ptr(const T* pDef) const { return base::m_p ? base::m_p : pDef; } inline T* get_ptr(T* pDef) { return base::m_p ? base::m_p : pDef; } inline void clear() { base::m_p = NULL; base::m_size = 0; } inline void resize(uint new_size, pool_allocator& alloc) { if (new_size > base::m_size) { grow(new_size, alloc); if (UseConstructor) construct_array(base::m_p + base::m_size, new_size - base::m_size); } base::m_size = new_size; } inline void shrink(uint new_size) { base::m_size = new_size; } inline T* enlarge_no_construct(uint n, pool_allocator& alloc) { PJSON_ASSERT(n); uint cur_size = base::m_size, new_size = base::m_size + n; grow(new_size, alloc); base::m_size = new_size; return base::m_p + cur_size; } inline T* enlarge(uint n, pool_allocator& alloc) { T* p = enlarge_no_construct(n, alloc); if (UseConstructor) construct_array(p, n); return p; } inline void push_back(const T& obj, pool_allocator& alloc) { PJSON_ASSERT(!base::m_p || (&obj < base::m_p) || (&obj >= (base::m_p + base::m_size))); grow(base::m_size + 1, alloc); if (UseConstructor) ConstructionPolicy::copy_construct(base::m_p + base::m_size, obj, alloc); else memcpy(base::m_p + base::m_size, &obj, sizeof(T)); base::m_size++; } inline void push_back(const T* p, uint n, pool_allocator& alloc) { PJSON_ASSERT(!base::m_p || ((p + n) <= base::m_p) || (p >= (base::m_p + base::m_size))); T* pDst = enlarge_no_construct(n, alloc); if (UseConstructor) { T* pDst_end = pDst + n; const T* pSrc = p; while (pDst != pDst_end) ConstructionPolicy::copy_construct(pDst, *pSrc++, alloc); } else memcpy(pDst, p, sizeof(T) * n); } inline void assign(const T* p, uint n, pool_allocator& alloc) { PJSON_ASSERT(!base::m_p || ((p + n) <= base::m_p) || (p >= (base::m_p + base::m_size))); const uint num_to_assign = PJSON_MIN(base::m_size, n); if (num_to_assign) { if (UseConstructor) { for (uint i = 0; i < num_to_assign; ++i) ConstructionPolicy::assign(&base::m_p[i], p[i], alloc); } else memcpy(base::m_p, p, sizeof(T) * num_to_assign); } if (n > base::m_size) push_back(p + num_to_assign, n - num_to_assign, alloc); else shrink(n); } inline void assign(const simple_vector& other, pool_allocator& alloc) { assign(other.m_p, other.m_size, alloc); } inline void erase(uint start, uint n) { PJSON_ASSERT((start + n) <= base::m_size); if ((!n) || ((start + n) > base::m_size)) return; const uint num_to_move = base::m_size - (start + n); T* pDst = base::m_p + start; memmove(pDst, base::m_p + start + n, num_to_move * sizeof(T)); base::m_size -= n; } inline void swap(simple_vector& other) { pjson::swap(base::m_p, other.m_p); pjson::swap(base::m_size, other.m_size); } inline void grow(uint new_size, pool_allocator& alloc) { if (new_size > base::m_size) base::m_p = static_cast(alloc.Realloc(base::m_p, sizeof(T) * new_size, base::m_size * sizeof(T))); } }; enum json_value_type_t { cJSONValueTypeNull = 0, cJSONValueTypeBool, cJSONValueTypeInt, cJSONValueTypeDouble, // All types that follow require storage. Do not change the relative order of these types. cJSONValueTypeString, cJSONValueTypeArray, cJSONValueTypeObject, }; // ---- struct value_variant_data typedef simple_vector string_vec_t; typedef simple_vector > key_value_vec_t; typedef simple_vector > value_variant_vec_t; #pragma pack(push, 4) struct value_variant_data { union json_value_data_t { elemental_vector m_object; elemental_vector m_array; elemental_vector m_string; int64 m_nVal; double m_flVal; }; json_value_data_t m_data; json_value_type_t m_type; inline const string_vec_t& get_string() const { return (const string_vec_t&)m_data.m_string; } inline string_vec_t& get_string() { return (string_vec_t&)m_data.m_string; } inline const char* get_string_ptr() const { return get_string().get_ptr(""); } inline const value_variant_vec_t& get_array() const { return (const value_variant_vec_t&)m_data.m_array; } inline value_variant_vec_t& get_array() { return (value_variant_vec_t&)m_data.m_array; } inline const key_value_vec_t& get_object() const { return (const key_value_vec_t&)m_data.m_object; } inline key_value_vec_t& get_object() { return (key_value_vec_t&)m_data.m_object; } }; // ---- struct key_value_t struct key_value_t { inline key_value_t() { } inline key_value_t(const key_value_t& other, pool_allocator& alloc); inline void assign(const key_value_t& src, pool_allocator& alloc); inline const string_vec_t& get_key() const { return m_key; } inline string_vec_t& get_key() { return m_key; } inline const value_variant& get_value() const { return (const value_variant&)m_value_data; } inline value_variant& get_value() { return (value_variant&)m_value_data; } string_vec_t m_key; value_variant_data m_value_data; }; #pragma pack(pop) // ---- class char_vector_print_helper class char_vector_print_helper { char_vector_print_helper(const char_vector_print_helper&); char_vector_print_helper& operator= (const char_vector_print_helper&); public: inline char_vector_print_helper(char_vec_t& buf) : m_buf(buf) { } inline void resize(size_t new_size) { m_buf.resize(new_size); } inline size_t size() const { return m_buf.size(); } inline char* get_ptr() const { return &m_buf[0]; } inline const char_vec_t& get_buf() const { return m_buf; } inline char_vec_t& get_buf() { return m_buf; } inline void puts(const char* pStr, size_t l) { m_buf.insert(m_buf.end(), pStr, pStr + l); } inline void print_tabs(size_t n) { m_buf.insert(m_buf.end(), n, '\t'); } inline void print_char(char c) { m_buf.push_back(c); } void print_escaped(const string_vec_t& str) { const char* pStr = str.m_p; uint len = str.m_size; len; static const char* s_to_hex = "0123456789abcdef"; print_char('\"'); while (*pStr) { uint8 c = *pStr++; if ((c >= ' ') && (c != '\"') && (c != '\\')) print_char(c); else { print_char('\\'); switch (c) { case '\b': print_char('b'); break; case '\r': print_char('r'); break; case '\t': print_char('t'); break; case '\f': print_char('f'); break; case '\n': print_char('n'); break; case '\\': print_char('\\'); break; case '\"': print_char('\"'); break; default: puts("u00", 3); print_char(s_to_hex[c >> 4]); print_char(s_to_hex[c & 0xF]); break; } } } print_char('\"'); } private: char_vec_t& m_buf; }; // ---- class char_buf_print_helper class char_buf_print_helper { char_buf_print_helper(const char_buf_print_helper&); char_buf_print_helper& operator= (const char_buf_print_helper&); public: inline char_buf_print_helper(char* pBuf, size_t buf_size) : m_pDst(pBuf), m_pStart(pBuf), m_pEnd(pBuf + buf_size) { } inline void resize(size_t new_size) { PJSON_ASSERT(new_size <= (size_t)(m_pEnd - m_pStart)); m_pDst = m_pStart + new_size; } inline size_t size() const { return m_pDst - m_pStart; } inline char* get_ptr() const { return m_pStart; } inline void puts(const char* pStr, size_t l) { memcpy(m_pDst, pStr, l = PJSON_MIN(l, (size_t)(m_pEnd - m_pDst))); m_pDst += l; } inline void print_tabs(size_t n) { n = PJSON_MIN(n, (size_t)(m_pEnd - m_pDst)); memset(m_pDst, '\t', n); m_pDst += n; } inline void print_char(char c) { if (m_pDst < m_pEnd) *m_pDst++ = c; } void print_escaped(const string_vec_t& str) { static const char* s_to_hex = "0123456789abcdef"; const char* pStr = str.m_p; char* pDst = m_pDst; char* pEnd = m_pEnd; uint len = str.m_size; // If len!=0, it includes the terminating null, so this expression is conservative. if (static_cast(pEnd - pDst) < (len + 2)) { m_pDst = pEnd; return; } *pDst++ = '\"'; uint8 c = 0; if (pStr) c = pStr[0]; while (!globals::s_str_serialize_flags[c]) { pDst[0] = c; c = pStr[1]; if (globals::s_str_serialize_flags[c]) { ++pStr, ++pDst; break; } pDst[1] = c; c = pStr[2]; if (globals::s_str_serialize_flags[c]) { pStr += 2, pDst += 2; break; } pDst[2] = c; c = pStr[3]; if (globals::s_str_serialize_flags[c]) { pStr += 3, pDst += 3; break; } pDst[3] = c; c = pStr[4]; pStr += 4, pDst += 4; } while (c) { if ((pEnd - pDst) < 7) { m_pDst = pEnd; return; } if (!globals::s_str_serialize_flags[c]) *pDst++ = c; else { pDst[0] = '\\'; switch (c) { case '\b': pDst[1] = 'b'; break; case '\r': pDst[1] = 'r'; break; case '\t': pDst[1] = 't'; break; case '\f': pDst[1] = 'f'; break; case '\n': pDst[1] = 'n'; break; case '\\': pDst[1] = '\\'; break; case '\"': pDst[1] = '\"'; break; default: pDst[1] = 'u', pDst[2] = '0', pDst[3] = '0', pDst[4] = s_to_hex[c >> 4], pDst[5] = s_to_hex[c & 0xF]; pDst += 3; break; } pDst += 2; } c = *pStr++; } *pDst++ = '\"'; PJSON_ASSERT(pDst <= pEnd); m_pDst = pDst; } private: char* m_pDst, *m_pStart, *m_pEnd; }; // ---- class serialize_helper template class serialize_helper : public T { public: typedef T base; template inline serialize_helper(I& init) : T(init) { } template inline serialize_helper(I& init1, J& init2) : T(init1, init2) { } inline void puts(const char* pStr) { T::puts(pStr, strlen(pStr)); } inline void puts(const char* pStr, size_t l) { T::puts(pStr, l); } }; // ---- class value_variant #pragma pack(push, 4) class value_variant : public value_variant_data { friend document; friend key_value_t; value_variant(const value_variant&); value_variant& operator= (const value_variant&); public: inline value_variant() { m_type = cJSONValueTypeNull; m_data.m_nVal = 0; } inline value_variant(bool val) { m_type = cJSONValueTypeBool; m_data.m_nVal = val; } inline value_variant(int32 nVal) { m_type = cJSONValueTypeInt; m_data.m_nVal = nVal; } inline value_variant(uint32 nVal) { m_type = cJSONValueTypeInt; m_data.m_nVal = nVal; } inline value_variant(int64 nVal) { m_type = cJSONValueTypeInt; m_data.m_nVal = nVal; } inline value_variant(double flVal) { m_type = cJSONValueTypeDouble; m_data.m_flVal = flVal; } inline value_variant(const char* pStr, pool_allocator& alloc) { m_type = cJSONValueTypeString; if (!pStr) pStr = ""; get_string().construct(pStr, static_cast(strlen(pStr)) + 1, alloc); } inline value_variant(json_value_type_t type) { construct(type); } inline value_variant(const value_variant& other, pool_allocator& alloc) { construct(other, alloc); } inline value_variant &assign(const value_variant& rhs, pool_allocator& alloc) { if (this == &rhs) return *this; if ((m_type >= cJSONValueTypeString) && (m_type == rhs.m_type)) { if (is_string()) get_string().assign(rhs.get_string(), alloc); else if (is_object()) get_object().assign(rhs.get_object(), alloc); else get_array().assign(rhs.get_array(), alloc); } else { construct(rhs, alloc); } return *this; } inline json_value_type_t get_type() const { return m_type; } inline bool is_null() const { return m_type == cJSONValueTypeNull; } inline bool is_valid() const { return m_type != cJSONValueTypeNull; } inline bool is_bool() const { return m_type == cJSONValueTypeBool; } inline bool is_int() const { return m_type == cJSONValueTypeInt; } inline bool is_double() const { return m_type == cJSONValueTypeDouble; } inline bool is_numeric() const { return (m_type == cJSONValueTypeInt) || (m_type == cJSONValueTypeDouble); } inline bool is_string() const { return m_type == cJSONValueTypeString; } inline bool is_object_or_array() const { return m_type >= cJSONValueTypeArray; } inline bool is_object() const { return m_type == cJSONValueTypeObject; } inline bool is_array() const { return m_type == cJSONValueTypeArray; } inline void clear() { set_to_null(); } inline void assume_ownership(value_variant& src_val) { set_to_null(); swap(src_val); } inline void release_ownership(value_variant& dst_value) { dst_value.set_to_null(); dst_value.swap(*this); } inline value_variant& set_to_object() { construct(cJSONValueTypeObject); return *this; } inline value_variant& set_to_array() { construct(cJSONValueTypeArray); return *this; } inline value_variant& set_to_node(bool is_object) { construct(is_object ? cJSONValueTypeObject : cJSONValueTypeArray); return *this; } inline value_variant& set_to_null() { m_data.m_nVal = 0; m_type = cJSONValueTypeNull; return *this; } inline value_variant& set(bool val) { m_data.m_nVal = val; m_type = cJSONValueTypeBool; return *this; } inline value_variant& set(int32 nVal) { m_data.m_nVal = nVal; m_type = cJSONValueTypeInt; return *this; } inline value_variant& set(int64 nVal) { m_data.m_nVal = nVal; m_type = cJSONValueTypeInt; return *this; } inline value_variant& set(uint32 nVal) { set(static_cast(nVal)); return *this; } inline value_variant& set(double flVal) { m_data.m_flVal = flVal; m_type = cJSONValueTypeDouble; return *this; } inline value_variant& set(const char* pStr, pool_allocator& alloc) { if (!pStr) pStr = ""; uint l = static_cast(strlen(pStr)) + 1; if (!is_string()) { m_type = cJSONValueTypeString; get_string().construct(pStr, l, alloc); } else get_string().assign(pStr, l, alloc); return *this; } inline value_variant& set_assume_ownership(char* pStr, uint len) { m_type = cJSONValueTypeString; string_vec_t& str = get_string(); str.m_p = pStr; str.m_size = len; return *this; } inline value_variant& set(const value_variant* pVals, uint n, pool_allocator& alloc) { if (!is_array()) { m_type = cJSONValueTypeArray; get_array().construct(pVals, n, alloc); } else get_array().assign(pVals, n, alloc); return *this; } inline value_variant& set_assume_ownership(value_variant* pVals, uint n) { m_type = cJSONValueTypeArray; value_variant_vec_t& arr = get_array(); arr.m_p = pVals; arr.m_size = n; return *this; } inline value_variant& set(const key_value_t* pKey_values, uint n, pool_allocator& alloc) { if (!is_object()) { m_type = cJSONValueTypeObject; get_object().construct(pKey_values, n, alloc); } else get_object().assign(pKey_values, n, alloc); return *this; } inline value_variant& set_assume_ownership(key_value_t* pKey_values, uint n) { m_type = cJSONValueTypeObject; key_value_vec_t& obj = get_object(); obj.m_p = pKey_values; obj.m_size = n; return *this; } inline value_variant &operator=(bool val) { set(val); return *this; } inline value_variant &operator=(int32 nVal) { set(nVal); return *this; } inline value_variant &operator=(uint32 nVal) { set(nVal); return *this; } inline value_variant &operator=(int64 nVal) { set(nVal); return *this; } inline value_variant &operator=(double flVal) { set(flVal); return *this; } inline bool get_bool_value(bool& val, bool def = false) const { if (is_bool()) { val = (m_data.m_nVal != 0); return true; } else return convert_to_bool(val, def); } inline bool get_numeric_value(int32& val, int32 def = 0) const { if ((is_int()) && (m_data.m_nVal == static_cast(m_data.m_nVal))) { val = static_cast(m_data.m_nVal); return true; } else return convert_to_int32(val, def); } inline bool get_numeric_value(int64& val, int64 def = 0) const { if (is_int()) { val = m_data.m_nVal; return true; } else return convert_to_int64(val, def); } inline bool get_numeric_value(float& val, float def = 0.0f) const { if (is_double()) { val = static_cast(m_data.m_flVal); return true; } else return convert_to_float(val, def); } inline bool get_numeric_value(double& val, double def = 0.0f) const { if (is_double()) { val = m_data.m_flVal; return true; } else return convert_to_double(val, def); } inline bool get_string_value(string_t& val, const char* pDef = "") const { if (is_string()) { val = get_string_ptr(); return true; } else return convert_to_string(val, pDef); } inline bool as_bool(bool def = false) const { bool result; get_bool_value(result, def); return result; } inline int as_int32(int32 def = 0) const { int32 result; get_numeric_value(result, def); return result; } inline int64 as_int64(int64 def = 0) const { int64 result; get_numeric_value(result, def); return result; } inline float as_float(float def = 0.0f) const { float result; get_numeric_value(result, def); return result; } inline double as_double(double def = 0.0f) const { double result; get_numeric_value(result, def); return result; } // Returns value as a string, or the default string if the value cannot be converted. inline string_t as_string(const char* pDef = "") const { string_t result; get_string_value(result, pDef); return result; } // Returns pointer to null terminated string or NULL if the value is not a string. inline const char* as_string_ptr() const { return is_string() ? get_string_ptr() : NULL; } inline void swap(value_variant& other) { pjson::swap(m_type, other.m_type); get_object().swap(other.get_object()); } inline uint size() const { PJSON_ASSERT(is_object_or_array()); return is_object_or_array() ? get_array().size() : 0; } inline const char *get_key_name_at_index(uint index) const { PJSON_ASSERT(is_object()); return get_object()[index].get_key().get_ptr(""); } inline const value_variant *find_child_array(const char *pName) const { int index = find_key(pName); if ((index >= 0) && (get_object()[index].get_value().is_array())) return &get_object()[index].get_value(); return NULL; } inline const value_variant *find_child_object(const char *pName) const { int index = find_key(pName); if ((index >= 0) && (get_object()[index].get_value().is_object())) return &get_object()[index].get_value(); return NULL; } inline const value_variant *find_value_variant(const char *pName) const { int index = find_key(pName); return (index < 0) ? NULL : &get_object()[index].get_value(); } inline int find_key(const char *pName) const { if (!is_object()) { PJSON_ASSERT(0); return -1; } const uint n = get_array().size(); const key_value_vec_t &obj = get_object(); for (uint i = 0; i < n; i++) if (strcmp(pName, obj[i].get_key().get_ptr("")) == 0) return i; return -1; } inline bool has_key(const char *pName) const { return find_key(pName) >= 0; } inline bool as_bool(const char *pName, bool def = false) const { int index = find_key(pName); return (index < 0) ? def : get_object()[index].get_value().as_bool(def); } inline int as_int32(const char *pName, int32 def = 0) const { int index = find_key(pName); return (index < 0) ? def : get_object()[index].get_value().as_int32(def); } inline int64 as_int64(const char *pName, int64 def = 0) const { int index = find_key(pName); return (index < 0) ? def : get_object()[index].get_value().as_int64(def); } inline float as_float(const char *pName, float def = 0.0f) const { int index = find_key(pName); return (index < 0) ? def : get_object()[index].get_value().as_float(def); } inline double as_double(const char *pName, double def = 0.0f) const { int index = find_key(pName); return (index < 0) ? def : get_object()[index].get_value().as_double(def); } inline const char* as_string_ptr(const char *pName, const char *pDef = "") const { int index = find_key(pName); if (index < 0) return pDef; const char *p = get_object()[index].get_value().as_string_ptr(); return p ? p : pDef; } inline value_variant& get_value_at_index(uint index) { PJSON_ASSERT(is_object_or_array()); return is_object() ? get_object()[index].get_value() : get_array()[index]; } inline const value_variant& get_value_at_index(uint index) const { PJSON_ASSERT(is_object_or_array()); return is_object() ? get_object()[index].get_value() : get_array()[index]; } inline value_variant& operator[](uint index) { PJSON_ASSERT(is_object_or_array()); return is_object() ? get_object()[index].get_value() : get_array()[index]; } inline const value_variant& operator[](uint index) const { PJSON_ASSERT(is_object_or_array()); return is_object() ? get_object()[index].get_value() : get_array()[index]; } inline json_value_type_t get_value_type_at_index(uint index) const { return get_value_at_index(index).get_type(); } inline bool is_child_at_index(uint index) const { return get_value_type_at_index(index) >= cJSONValueTypeArray; } inline bool has_children() const { if (is_object()) { const key_value_vec_t& obj = get_object(); const uint s = obj.size(); for (uint i = 0; i < s; ++i) if (obj[i].get_value().is_object_or_array()) return true; } else if (is_array()) { const value_variant_vec_t& arr = get_array(); const uint s = arr.size(); for (uint i = 0; i < s; ++i) if (arr[i].is_object_or_array()) return true; } return false; } inline void clear_object_or_array() { PJSON_ASSERT(is_object_or_array()); if (is_object()) get_object().clear(); else if (is_array()) get_array().clear(); } inline void resize(uint n, pool_allocator& alloc) { PJSON_ASSERT(is_object_or_array()); if (is_object()) get_object().resize(n, alloc); else if (is_array()) get_array().resize(n, alloc); } inline void set_key_name_at_index(uint index, const char *pKey, uint key_len, pool_allocator& alloc) { PJSON_ASSERT(is_object()); string_vec_t& str = get_object()[index].get_key(); str.assign(pKey, key_len + 1, alloc); } inline void set_key_name_at_index(uint index, const char *pKey, pool_allocator& alloc) { set_key_name_at_index(index, pKey, static_cast(strlen(pKey)) + 1, alloc); } inline value_variant& add_key_value(const char* pKey, uint key_len, const value_variant& val, pool_allocator& alloc) { PJSON_ASSERT(is_object()); key_value_vec_t& obj = get_object(); key_value_t* pKey_value = obj.enlarge_no_construct(1, alloc); pKey_value->get_key().construct(pKey, key_len + 1, alloc); pKey_value->get_value().construct(val, alloc); return *this; } inline value_variant& add_key_value(const char* pKey, const value_variant& val, pool_allocator& alloc) { return add_key_value(pKey, static_cast(strlen(pKey)), val, alloc); } inline value_variant& add_value(const value_variant& val, pool_allocator& alloc) { PJSON_ASSERT(is_array()); get_array().enlarge_no_construct(1, alloc)->construct(val, alloc); return *this; } bool serialize(char* pBuf, size_t buf_size, size_t* pSize = NULL, bool formatted = true, bool null_terminate = true) const { serialize_helper helper(pBuf, buf_size); serialize_internal(helper, formatted, null_terminate, 0); if (pSize) *pSize = helper.size(); return (helper.size() < buf_size); } bool serialize(char_vec_t& buf, bool formatted = true, bool null_terminate = true) const { serialize_helper helper(buf); serialize_internal(helper, formatted, null_terminate, 0); return true; } protected: // Manual constructor inline void construct(json_value_type_t type) { m_type = type; memset(&m_data, 0, sizeof(m_data)); } // Assumes variant has NOT been constructed yet. inline void construct(const value_variant& other, pool_allocator& alloc) { m_type = other.m_type; m_data.m_nVal = other.m_data.m_nVal; if (m_type >= cJSONValueTypeString) { if (m_type == cJSONValueTypeObject) get_object().construct(other.get_object(), alloc); else if (m_type == cJSONValueTypeArray) get_array().construct(other.get_array(), alloc); else get_string().construct(other.get_string(), alloc); } } inline bool convert_to_bool(bool& val, bool def) const { switch (m_type) { case cJSONValueTypeBool: case cJSONValueTypeInt: { val = (m_data.m_nVal != 0); return true; } case cJSONValueTypeDouble: { val = (m_data.m_flVal != 0); return true; } case cJSONValueTypeString: { if (!pjson_stricmp(get_string_ptr(), "false")) { val = false; return true; } else if (!pjson_stricmp(get_string_ptr(), "true")) { val = true; return true; } val = (atof(get_string_ptr()) != 0.0f); return true; } } val = def; return false; } inline bool convert_to_int32(int32& val, int32 def) const { val = def; int64 val64; if (!convert_to_int64(val64, def)) return false; if ((val64 < std::numeric_limits::min()) || (val64 > std::numeric_limits::max())) return false; val = static_cast(val64); return true; } inline bool convert_to_int64(int64& val, int64 def) const { switch (m_type) { case cJSONValueTypeBool: case cJSONValueTypeInt: { val = m_data.m_nVal; return true; } case cJSONValueTypeDouble: { val = static_cast(m_data.m_flVal); return true; } case cJSONValueTypeString: { if (!pjson_stricmp(get_string_ptr(), "false")) { val = 0; return true; } else if (!pjson_stricmp(get_string_ptr(), "true")) { val = 1; return true; } double flVal = floor(atof(get_string_ptr())); if ((flVal >= std::numeric_limits::min()) && (flVal <= std::numeric_limits::max())) { val = static_cast(flVal); return true; } break; } } val = def; return false; } inline bool convert_to_float(float& val, float def) const { switch (m_type) { case cJSONValueTypeBool: case cJSONValueTypeInt: { val = static_cast(m_data.m_nVal); return true; } case cJSONValueTypeDouble: { val = static_cast(m_data.m_flVal); return true; } case cJSONValueTypeString: { if (!pjson_stricmp(get_string_ptr(), "false")) { val = 0; return true; } else if (!pjson_stricmp(get_string_ptr(), "true")) { val = 1; return true; } val = static_cast(atof(get_string_ptr())); return true; } } val = def; return false; } inline bool convert_to_double(double& val, double def) const { switch (m_type) { case cJSONValueTypeBool: case cJSONValueTypeInt: { val = static_cast(m_data.m_nVal); return true; } case cJSONValueTypeDouble: { val = m_data.m_flVal; return true; } case cJSONValueTypeString: { if (!pjson_stricmp(get_string_ptr(), "false")) { val = 0; return true; } else if (!pjson_stricmp(get_string_ptr(), "true")) { val = 1; return true; } val = atof(get_string_ptr()); return true; } } val = def; return false; } inline bool convert_to_string(char* pBuf, size_t buf_size) const { switch (m_type) { case cJSONValueTypeNull: { pBuf[0] = 'n', pBuf[1] = 'u', pBuf[2] = 'l', pBuf[3] = 'l', pBuf[4] = '\0'; return true; } case cJSONValueTypeBool: { if (m_data.m_nVal) pBuf[0] = 't', pBuf[1] = 'r', pBuf[2] = 'u', pBuf[3] = 'e', pBuf[4] = '\0'; else pBuf[0] = 'f', pBuf[1] = 'a', pBuf[2] = 'l', pBuf[3] = 's', pBuf[4] = 'e', pBuf[5] = '\0'; return true; } case cJSONValueTypeInt: { char* pDst = pBuf; int64 n = m_data.m_nVal; uint64 s = static_cast(n >> 63); *pDst = '-'; pDst -= s; n = (n ^ s) - s; char* pLeft = pDst; do { *pDst++ = '0' + (n % 10); n /= 10; } while (n); *pDst = '\0'; do { char c = *--pDst; *pDst = *pLeft; *pLeft++ = c; } while (pDst > pLeft); return true; } case cJSONValueTypeDouble: { return 0 == _gcvt_s(pBuf, buf_size, m_data.m_flVal, 15); } } return false; } inline bool convert_to_string(string_t& val, const char* pDef) const { char buf[64]; if (m_type == cJSONValueTypeString) val = get_string_ptr(); else { if (!convert_to_string(buf, sizeof(buf))) { val.assign(pDef); return false; } val.assign(buf); } return true; } inline uint8 get_end_char() const { return (m_type == cJSONValueTypeArray) ? ']' : '}'; } template void serialize_node(serializer& out, bool formatted, uint cur_indent) const { char buf[64]; const uint size = get_array().size(); if (!size) { static const char* g_empty_object_strs[4] = { "[]", "[ ]", "{}", "{ }" }; out.puts(g_empty_object_strs[is_object() * 2 + formatted], 2 + formatted); return; } if (formatted && is_array() && !has_children()) { size_t start_of_line_ofs = out.size(); out.puts("[ ", 2); const uint cMaxLineLen = 100; for (uint i = 0; i < size; i++) { const value_variant& child_val = get_value_at_index(i); if (child_val.is_string()) out.print_escaped(child_val.get_string()); else { child_val.convert_to_string(buf, sizeof(buf)); out.puts(buf); } if (i != size - 1) out.puts(", ", 2); if (((out.size() - start_of_line_ofs) > cMaxLineLen) && (i != size - 1)) { out.print_char('\n'); out.print_tabs(cur_indent + 1); start_of_line_ofs = out.size(); } } out.puts(" ]", 2); return; } out.print_char(is_object() ? '{' : '['); if (formatted) out.print_char('\n'); cur_indent++; for (uint i = 0; i < size; i++) { const value_variant& child_val = get_value_at_index(i); if (formatted) out.print_tabs(cur_indent); if (is_object()) { out.print_escaped(get_object()[i].get_key()); if (formatted) out.puts(" : ", 3); else out.print_char(':'); } json_value_type_t val_type = child_val.get_type(); if (val_type >= cJSONValueTypeArray) child_val.serialize_node(out, formatted, cur_indent); else if (val_type == cJSONValueTypeString) out.print_escaped(child_val.get_string()); else { child_val.convert_to_string(buf, sizeof(buf)); out.puts(buf); } if (i != size - 1) out.print_char(','); if (formatted) out.print_char('\n'); } cur_indent--; if (formatted) out.print_tabs(cur_indent); out.print_char(is_object() ? '}' : ']'); } template void serialize_internal(serializer& out, bool formatted, bool null_terminate, uint cur_indent) const { if (formatted) out.print_tabs(cur_indent); if (is_object_or_array()) { serialize_node(out, formatted, cur_indent); if (formatted) out.print_char('\n'); } else { if (is_string()) out.print_escaped(get_string()); else { string_t str; if (get_string_value(str)) out.puts(str.c_str(), str.length()); } } if (null_terminate) out.print_char('\0'); } template value_variant(T*); template value_variant(const T*); template value_variant& operator= (T*); template value_variant& operator= (const T*); }; #pragma pack(pop) inline key_value_t::key_value_t(const key_value_t& other, pool_allocator& alloc) : m_key(other.get_key(), alloc) { get_value().construct(other.get_value(), alloc); } inline void key_value_t::assign(const key_value_t& src, pool_allocator& alloc) { get_key().assign(src.get_key(), alloc); get_value().assign(src.get_value(), alloc); } // ---- class error_info class error_info { public: inline error_info() : m_ofs(0), m_pError_message(NULL) { } inline void set(size_t ofs, const char* pMsg) { m_ofs = ofs; m_pError_message = pMsg; } size_t m_ofs; const char* m_pError_message; }; // ---- class growable_stack class growable_stack { public: inline growable_stack(uint initial_size) : m_pBuf(NULL), m_size(initial_size), m_ofs(0) { if (initial_size) m_pBuf = static_cast(pjson_malloc(initial_size)); } inline ~growable_stack() { pjson_free(m_pBuf); } inline void clear() { pjson_free(m_pBuf); m_pBuf = NULL; m_size = 0; m_ofs = 0; } inline uint8* get_top_ptr() { return reinterpret_cast(m_pBuf) + m_ofs; } template inline T* get_top_obj() { return reinterpret_cast(m_pBuf + m_ofs - sizeof(T)); } inline void reset() { m_ofs = 0; } inline size_t get_ofs() { return m_ofs; } template PJSON_FORCEINLINE T* push(uint num) { const size_t bytes_needed = sizeof(T) * num; T* pResult = reinterpret_cast(m_pBuf + m_ofs); m_ofs += bytes_needed; if (m_ofs > m_size) { m_ofs -= bytes_needed; m_size = PJSON_MAX(1, m_size * 2); while(m_size <= (m_ofs + bytes_needed)) m_size *= 2; m_pBuf = static_cast(pjson_realloc(m_pBuf, m_size)); pResult = reinterpret_cast(m_pBuf + m_ofs); m_ofs += bytes_needed; } PJSON_ASSERT(m_ofs <= m_size); return pResult; } template inline T* pop(uint num) { size_t bytes_needed = sizeof(T) * num; PJSON_ASSERT(bytes_needed <= m_ofs); m_ofs -= bytes_needed; return reinterpret_cast(m_pBuf + m_ofs); } private: uint8* m_pBuf; size_t m_size; size_t m_ofs; }; // ---- class document class document : public value_variant { document(const document&); document& operator= (const document&); public: inline document(uint initial_pool_size = 0, uint min_pool_chunk_size = PJSON_DEFAULT_MIN_CHUNK_SIZE, uint initial_stack_size = 0) : m_allocator(initial_pool_size, min_pool_chunk_size), m_initial_stack_size(initial_stack_size), m_stack(initial_stack_size) { #if PJSON_PARSE_STATS m_parse_stats.clear(); #endif } inline ~document() { } const pool_allocator& get_allocator() const { return m_allocator; } pool_allocator& get_allocator() { return m_allocator; } void clear() { set_to_null(); m_allocator.clear(); m_stack.clear(); #if PJSON_PARSE_STATS m_parse_stats.clear(); #endif } bool deserialize_in_place(char* pStr) { return deserialize_start((uint8*)pStr); } #if PJSON_PARSE_STATS struct parse_stats_t { size_t m_num_string, m_num_string_chars; size_t m_num_numeric, m_num_numeric_chars; size_t m_num_whitespace_blocks, m_num_whitespace_chars; size_t m_num_control; size_t m_num_comment, m_num_comment_chars; size_t m_num_value_pop, m_value_pop_bytes; size_t m_num_bool_chars; size_t m_num_escape_breaks; size_t m_num_unicode_escapes; void clear() { memset(this, 0, sizeof(*this)); } }; const parse_stats_t& get_parse_stats() const { return m_parse_stats; } parse_stats_t& get_parse_stats() { return m_parse_stats; } #endif const error_info& get_error_info() const { return m_error_info; } private: pool_allocator m_allocator; uint m_initial_stack_size; growable_stack m_stack; error_info m_error_info; const uint8* m_pStart; const uint8* m_pStr; inline bool set_error(const uint8* pStr, const char* pMsg) { m_pStr = pStr; m_error_info.set(m_pStr - m_pStart, pMsg); return false; } #if PJSON_PARSE_STATS parse_stats_t m_parse_stats; #endif #if PJSON_PARSE_STATS #define PJSON_INCREMENT_STAT(x) do { ++m_parse_stats.x; } while(0) #define PJSON_UPDATE_STAT(x, n) do { m_parse_stats.x += n; } while(0) #else #define PJSON_INCREMENT_STAT(x) do { } while(0) #define PJSON_UPDATE_STAT(x, n) do { } while(0) #endif #define PJSON_SKIP_WHITESPACE \ while (globals::s_parse_flags[*pStr] & 4) \ { \ PJSON_INCREMENT_STAT(m_num_whitespace_blocks); \ do { \ if (!(globals::s_parse_flags[pStr[1]] & 4)) { ++pStr; PJSON_INCREMENT_STAT(m_num_whitespace_chars); break; } \ if (!(globals::s_parse_flags[pStr[2]] & 4)) { pStr += 2; PJSON_UPDATE_STAT(m_num_whitespace_chars, 2); break; } \ if (!(globals::s_parse_flags[pStr[3]] & 4)) { pStr += 3; PJSON_UPDATE_STAT(m_num_whitespace_chars, 3); break; } \ pStr += 4; PJSON_UPDATE_STAT(m_num_whitespace_chars, 4); \ } while (globals::s_parse_flags[*pStr] & 4); \ if ((*pStr != '/') || (pStr[1] != '/')) break; \ pStr += 2; PJSON_INCREMENT_STAT(m_num_comment); PJSON_UPDATE_STAT(m_num_comment_chars, 2); \ while ((*pStr) && (*pStr != '\n') && (*pStr != '\r')) { PJSON_INCREMENT_STAT(m_num_comment_chars); ++pStr; } \ } inline const uint8* skip_whitespace(const uint8* p) { uint8 c; while ((c = *p) != '\0') { if ((c == ' ') || (c == '\t')) { do { PJSON_INCREMENT_STAT(m_num_whitespace_chars); } while (*++p == c); continue; } if ((c == '/') && (p[1] == '/')) { p += 2; PJSON_INCREMENT_STAT(m_num_comment); PJSON_UPDATE_STAT(m_num_comment_chars, 2); while ((*p) && (*p != '\n') && (*p != '\r')) { PJSON_UPDATE_STAT(m_num_comment_chars, 2); ++p; } continue; } else if (c > ' ') break; PJSON_INCREMENT_STAT(m_num_whitespace_chars); ++p; } return p; } bool deserialize_internal() { static const uint8 g_utf8_first_byte[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; m_stack.reset(); memcpy(m_stack.push(1), static_cast(this), sizeof(value_variant)); const uint8* pStr = ++m_pStr; PJSON_UPDATE_STAT(m_num_control, 1); bool cur_is_object = is_object(); uint8 cur_end_char = get_end_char(); uint cur_num_elements = 0; for ( ; ; ) { PJSON_SKIP_WHITESPACE; uint8 c = *pStr; if (c == ',') { if (!cur_num_elements) return set_error(pStr, "Unexpected comma"); ++pStr; PJSON_UPDATE_STAT(m_num_control, 1); PJSON_SKIP_WHITESPACE; c = *pStr; } else if ((cur_num_elements) && (c != cur_end_char)) return set_error(pStr, "Expected comma or object/array end character"); while (c == cur_end_char) { PJSON_UPDATE_STAT(m_num_control, 1); ++pStr; for ( ; ; ) { uint n = cur_num_elements, num_bytes = cur_num_elements * (cur_is_object ? sizeof(key_value_t) : sizeof(value_variant)); void* pSrc = m_stack.pop(num_bytes); PJSON_INCREMENT_STAT(m_num_value_pop); PJSON_UPDATE_STAT(m_value_pop_bytes, num_bytes); // The top of the stack (after popping the current array/object) could contain either a value_variant (if cur_is_object is set), // or a key_value_t, which ends in a value_variant. So all we need to do is look at the very end, which always has a value_variant. value_variant* pCur_variant = m_stack.get_top_obj(); value_variant_vec_t& arr = pCur_variant->get_array(); cur_is_object = (arr.m_p != NULL); cur_end_char = cur_is_object ? '}' : ']'; cur_num_elements = arr.m_size; arr.m_size = n; arr.m_p = NULL; if (num_bytes) memcpy(arr.m_p = static_cast(m_allocator.Alloc(num_bytes)), pSrc, num_bytes); if (m_stack.get_ofs() <= sizeof(value_variant)) { PJSON_ASSERT(m_stack.get_ofs() == sizeof(value_variant)); memcpy(static_cast(this), m_stack.pop(1), sizeof(value_variant)); m_pStr = pStr; return true; } PJSON_SKIP_WHITESPACE; if (*pStr == ',') { PJSON_UPDATE_STAT(m_num_control, 1); ++pStr; PJSON_SKIP_WHITESPACE; c = *pStr; break; } if (*pStr++ != cur_end_char) return set_error(pStr, "Unexpected character within object or array"); PJSON_UPDATE_STAT(m_num_control, 1); } } ++cur_num_elements; value_variant* pChild_variant; if (!cur_is_object) pChild_variant = m_stack.push(1); else { if (c != '\"') return set_error(pStr, "Expected quoted key string"); ++pStr; PJSON_INCREMENT_STAT(m_num_string); PJSON_INCREMENT_STAT(m_num_string_chars); uint8* pBuf = (uint8*)pStr; c = *pStr++; PJSON_INCREMENT_STAT(m_num_string_chars); if (!(globals::s_parse_flags[c] & 1)) { do { c = pStr[0]; if (globals::s_parse_flags[c] & 1) { ++pStr; PJSON_UPDATE_STAT(m_num_string_chars, 1); break; } c = pStr[1]; if (globals::s_parse_flags[c] & 1) { pStr += 2; PJSON_UPDATE_STAT(m_num_string_chars, 2); break; } c = pStr[2]; if (globals::s_parse_flags[c] & 1) { pStr += 3; PJSON_UPDATE_STAT(m_num_string_chars, 3); break; } c = pStr[3]; pStr += 4; PJSON_UPDATE_STAT(m_num_string_chars, 4); } while (!(globals::s_parse_flags[c] & 1)); } uint8* pDst = (uint8*)pStr - 1; if (c != '\"') PJSON_INCREMENT_STAT(m_num_escape_breaks); while (c != '\"') { if (globals::s_parse_flags[c] & 2) return set_error(pStr, "Missing end quote"); c = *pStr++; PJSON_INCREMENT_STAT(m_num_string_chars); if (c == 'u') { PJSON_INCREMENT_STAT(m_num_unicode_escapes); uint u = 0; for (uint i = 0; i < 4; i++) { u <<= 4; int cc = *pStr++; PJSON_INCREMENT_STAT(m_num_string_chars); if ((cc >= 'A') && (cc <= 'F')) u += cc - 'A' + 10; else if ((cc >= 'a') && (cc <= 'f')) u += cc - 'a' + 10; else if ((cc >= '0') && (cc <= '9')) u += cc - '0'; else return set_error(pStr, "Invalid Unicode escape"); } uint len = 3; if ((u) && (u < 0x80)) len = 1; else if (u < 0x800) len = 2; pDst += len; uint8* q = pDst; switch (len) { case 3: *--q = static_cast((u | 0x80) & 0xBF); u >>= 6; // falls through case 2: *--q = static_cast((u | 0x80) & 0xBF); u >>= 6; // falls through case 1: *--q = static_cast(u | g_utf8_first_byte[len]); } } else { switch (c) { case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case '\\': case '\"': case '/': break; case '\0': return set_error(pStr, "Incomplete string escape"); default: { *pDst++ = '\\'; break; } // unrecognized escape, so forcefully escape the backslash (not standard) } *pDst++ = c; } c = *pStr++; PJSON_INCREMENT_STAT(m_num_string_chars); while (!(globals::s_parse_flags[c] & 1)) { pDst[0] = c; c = pStr[0]; if (globals::s_parse_flags[c] & 1) { ++pDst; ++pStr; PJSON_INCREMENT_STAT(m_num_string_chars); break; } pDst[1] = c; c = pStr[1]; if (globals::s_parse_flags[c] & 1) { pDst += 2; pStr += 2; PJSON_UPDATE_STAT(m_num_string_chars, 2); break; } pDst[2] = c; c = pStr[2]; if (globals::s_parse_flags[c] & 1) { pDst += 3; pStr += 3; PJSON_UPDATE_STAT(m_num_string_chars, 3); break; } pDst[3] = c; pDst += 4; c = pStr[3]; pStr += 4; PJSON_UPDATE_STAT(m_num_string_chars, 4); } } *pDst++ = '\0'; key_value_t* pKey_value = m_stack.push(1); pChild_variant = &pKey_value->get_value(); pKey_value->get_key().m_p = (char*)pBuf; pKey_value->get_key().m_size = static_cast(pDst - pBuf); PJSON_SKIP_WHITESPACE; if (*pStr != ':') return set_error(pStr, "Missing colon after key"); ++pStr; PJSON_INCREMENT_STAT(m_num_control); PJSON_SKIP_WHITESPACE; c = *pStr; } switch (c) { case '{': case '[': { ++pStr; PJSON_INCREMENT_STAT(m_num_control); pChild_variant->m_type = (c == '{') ? cJSONValueTypeObject : cJSONValueTypeArray; pChild_variant->m_data.m_object.m_size = cur_num_elements; pChild_variant->m_data.m_object.m_p = (key_value_t*)cur_is_object; cur_is_object = (c == '{'); cur_num_elements = 0; cur_end_char = c + 2; break; } case '\"': { ++pStr; PJSON_INCREMENT_STAT(m_num_string); PJSON_INCREMENT_STAT(m_num_string_chars); uint8* pBuf = (uint8*)pStr; c = *pStr++; PJSON_INCREMENT_STAT(m_num_string_chars); if (!(globals::s_parse_flags[c] & 1)) { do { c = pStr[0]; if (globals::s_parse_flags[c] & 1) { ++pStr; PJSON_INCREMENT_STAT(m_num_string_chars); break; } c = pStr[1]; if (globals::s_parse_flags[c] & 1) { pStr += 2; PJSON_UPDATE_STAT(m_num_string_chars, 2); break; } c = pStr[2]; if (globals::s_parse_flags[c] & 1) { pStr += 3; PJSON_UPDATE_STAT(m_num_string_chars, 3); break; } c = pStr[3]; pStr += 4; PJSON_UPDATE_STAT(m_num_string_chars, 4); } while (!(globals::s_parse_flags[c] & 1)); } uint8* pDst = (uint8*)pStr - 1; if (c != '\"') PJSON_INCREMENT_STAT(m_num_escape_breaks); while (c != '\"') { if (globals::s_parse_flags[c] & 2) return set_error(pStr, "Missing end quote"); c = *pStr++; PJSON_INCREMENT_STAT(m_num_string_chars); if (c == 'u') { PJSON_INCREMENT_STAT(m_num_unicode_escapes); uint u = 0; for (uint i = 0; i < 4; i++) { u <<= 4; int cc = *pStr++; PJSON_INCREMENT_STAT(m_num_string_chars); if ((cc >= 'A') && (cc <= 'F')) u += cc - 'A' + 10; else if ((cc >= 'a') && (cc <= 'f')) u += cc - 'a' + 10; else if ((cc >= '0') && (cc <= '9')) u += cc - '0'; else return set_error(pStr, "Invalid Unicode escape"); } uint len = 3; if ((u) && (u < 0x80)) len = 1; else if (u < 0x800) len = 2; pDst += len; uint8* q = pDst; switch (len) { case 3: *--q = static_cast((u | 0x80) & 0xBF); u >>= 6; // falls through case 2: *--q = static_cast((u | 0x80) & 0xBF); u >>= 6; // falls through case 1: *--q = static_cast(u | g_utf8_first_byte[len]); } } else { switch (c) { case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case '\\': case '\"': case '/': break; case '\0': return set_error(pStr, "Incomplete string escape"); default: { *pDst++ = '\\'; break; } // unrecognized escape, so forcefully escape the backslash (not standard) } *pDst++ = c; } c = *pStr++; PJSON_INCREMENT_STAT(m_num_string_chars); while (!(globals::s_parse_flags[c] & 1)) { pDst[0] = c; c = pStr[0]; if (globals::s_parse_flags[c] & 1) { ++pDst; ++pStr; PJSON_INCREMENT_STAT(m_num_string_chars); break; } pDst[1] = c; c = pStr[1]; if (globals::s_parse_flags[c] & 1) { pDst += 2; pStr += 2; PJSON_UPDATE_STAT(m_num_string_chars, 2); break; } pDst[2] = c; c = pStr[2]; if (globals::s_parse_flags[c] & 1) { pDst += 3; pStr += 3; PJSON_UPDATE_STAT(m_num_string_chars, 3); break; } pDst[3] = c; pDst += 4; c = pStr[3]; pStr += 4; PJSON_UPDATE_STAT(m_num_string_chars, 4); } } *pDst++ = '\0'; pChild_variant->m_type = cJSONValueTypeString; string_vec_t& str = pChild_variant->get_string(); str.m_p = (char*)pBuf; str.m_size = static_cast(pDst - pBuf); break; } case 'n': { if ((pStr[1] == 'u') && (pStr[2] == 'l') && (pStr[3] == 'l')) { pStr += 4; PJSON_UPDATE_STAT(m_num_bool_chars, 4); pChild_variant->construct(cJSONValueTypeNull); } else return set_error(pStr, "Unrecognized character"); break; } case 't': { if ((pStr[1] == 'r') && (pStr[2] == 'u') && (pStr[3] == 'e')) { pStr += 4; PJSON_UPDATE_STAT(m_num_bool_chars, 4); pChild_variant->construct(cJSONValueTypeBool); pChild_variant->m_data.m_nVal = 1; } else return set_error(pStr, "Unrecognized character"); break; } case 'f': { if ((pStr[1] == 'a') && (pStr[2] == 'l') && (pStr[3] == 's') && (pStr[4] == 'e')) { pStr += 5; PJSON_UPDATE_STAT(m_num_bool_chars, 5); pChild_variant->construct(cJSONValueTypeBool); } else return set_error(pStr, "Unrecognized character"); break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': case '.': { PJSON_INCREMENT_STAT(m_num_numeric); if (c == '-') PJSON_INCREMENT_STAT(m_num_numeric_chars); uint32 n32 = 0; int is_neg = (c == '-'); c = *(pStr += is_neg); if (globals::s_parse_flags[c] & 8) { n32 = c - '0'; c = *++pStr; PJSON_UPDATE_STAT(m_num_numeric_chars, 1); if (globals::s_parse_flags[c] & 8) { n32 = (n32 * 10U) + (c - '0'); c = *++pStr; PJSON_UPDATE_STAT(m_num_numeric_chars, 1); if (globals::s_parse_flags[c] & 8) { n32 = (n32 * 10U) + (c - '0'); c = *++pStr; PJSON_UPDATE_STAT(m_num_numeric_chars, 1); if (globals::s_parse_flags[c] & 8) { n32 = (n32 * 10U) + (c - '0'); c = *++pStr; PJSON_UPDATE_STAT(m_num_numeric_chars, 1); if (globals::s_parse_flags[c] & 8) { n32 = (n32 * 10U) + (c - '0'); c = *++pStr; PJSON_UPDATE_STAT(m_num_numeric_chars, 1); if (globals::s_parse_flags[c] & 8) { n32 = (n32 * 10U) + (c - '0'); c = *++pStr; PJSON_UPDATE_STAT(m_num_numeric_chars, 1); if (globals::s_parse_flags[c] & 8) { n32 = (n32 * 10U) + (c - '0'); c = *++pStr; PJSON_UPDATE_STAT(m_num_numeric_chars, 1); if (globals::s_parse_flags[c] & 8) { n32 = (n32 * 10U) + (c - '0'); c = *++pStr; PJSON_UPDATE_STAT(m_num_numeric_chars, 1); } } } } } } } } if (!(globals::s_parse_flags[c] & 0x10)) { pChild_variant->m_type = cJSONValueTypeInt; pChild_variant->m_data.m_nVal = is_neg + (static_cast(n32) ^ (-is_neg)); } else { uint64 n64 = n32; while (globals::s_parse_flags[c] & 8) { n64 = n64 * 10U + (c - '0'); PJSON_INCREMENT_STAT(m_num_numeric_chars); c = *++pStr; if ((!(globals::s_parse_flags[c] & 8)) || (n64 > 0xCCCCCCCCCCCCCCBULL)) break; n64 = n64 * 10U + (c - '0'); PJSON_INCREMENT_STAT(m_num_numeric_chars); c = *++pStr; if (n64 > 0xCCCCCCCCCCCCCCBULL) break; } if (!(globals::s_parse_flags[c] & 0x10)) { pChild_variant->m_type = cJSONValueTypeInt; pChild_variant->m_data.m_nVal = is_neg + (static_cast(n64) ^ (-is_neg)); } else { double f = static_cast(n64); int scale = 0, escalesign = 1, escale = 0; while (globals::s_parse_flags[c] & 8) { f = f * 10.0f + (c - '0'); PJSON_INCREMENT_STAT(m_num_numeric_chars); c = *++pStr; if (!(globals::s_parse_flags[c] & 8)) break; f = f * 10.0f + (c - '0'); PJSON_INCREMENT_STAT(m_num_numeric_chars); c = *++pStr; } if (c == '.') { PJSON_INCREMENT_STAT(m_num_numeric_chars); c = *++pStr; while (globals::s_parse_flags[c] & 8) { scale--; f = f * 10.0f + (c - '0'); PJSON_INCREMENT_STAT(m_num_numeric_chars); c = *++pStr; if (!(globals::s_parse_flags[c] & 8)) break; scale--; f = f * 10.0f + (c - '0'); PJSON_INCREMENT_STAT(m_num_numeric_chars); c = *++pStr; } } if ((c == 'e') || (c == 'E')) { PJSON_INCREMENT_STAT(m_num_numeric_chars); c = *++pStr; if (c == '-') { escalesign = -1; PJSON_INCREMENT_STAT(m_num_numeric_chars); c = *++pStr; } else if (c == '+') { PJSON_INCREMENT_STAT(m_num_numeric_chars); c = *++pStr; } while (globals::s_parse_flags[c] & 8) { if (escale > 0xCCCCCCB) return set_error(pStr, "Failed parsing numeric value"); escale = escale * 10 + (c - '0'); PJSON_INCREMENT_STAT(m_num_numeric_chars); c = *++pStr; } } static const float s_neg[2] = { 1.0f, -1.0f }; double v = f * s_neg[is_neg]; int64 final_scale = scale + escale * escalesign; if (static_cast(final_scale + 31) <= 62) v *= globals::s_pow10_table[static_cast(final_scale) + 31]; else { if ((final_scale < INT32_MIN) || (final_scale > INT32_MAX)) return set_error(pStr, "Failed parsing numeric value"); v *= pow(10.0, static_cast(final_scale)); } pChild_variant->m_type = cJSONValueTypeDouble; pChild_variant->m_data.m_flVal = v; } } break; } case '\0': return set_error(pStr, "Premature end of string (expected name or value)"); default: return set_error(pStr, "Unrecognized character"); } } } bool deserialize_start(uint8* pStr) { set_to_null(); #if PJSON_PARSE_STATS m_parse_stats.clear(); #endif m_allocator.reset(); m_pStart = pStr; m_pStr = skip_whitespace(pStr); if (!*m_pStr) return set_error(m_pStr, "Nothing to deserialize"); bool success = false; uint8 c = *m_pStr; if ((c == '{') || (c == '[')) { set_to_node(c == '{'); PJSON_INCREMENT_STAT(m_num_control); success = deserialize_internal(); } else return set_error(m_pStr, "Root value must be an object or array"); if (success) { m_pStr = skip_whitespace(m_pStr); success = !*m_pStr; if (!success) set_error(m_pStr, "Unknown data at end of document"); } return success; } }; } // namespace pjson #endif // PJSON_H