Skip to content

Instantly share code, notes, and snippets.

@NocturnDragon
Last active October 25, 2017 23:50
Show Gist options
  • Save NocturnDragon/8d0799d13b51e308ec916817d2702df5 to your computer and use it in GitHub Desktop.
Save NocturnDragon/8d0799d13b51e308ec916817d2702df5 to your computer and use it in GitHub Desktop.
// 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 <string>
#include <vector>
#include <limits>
#include <assert.h>
// ---- 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> 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<typename T> 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<typename unused=void>
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<typename unused>
const uint8 globals_struct<unused>::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<typename unused>
const double globals_struct<unused>::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<typename unused>
const uint8 globals_struct<unused>::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<chunk*>(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<chunk*>(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<uint8*>(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<typename T>
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<T*>(pDst) = src; }
};
template<typename T>
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<T*>(pDst)->assign(src, alloc); }
};
template <typename T> inline T* construct(T* p) { return new (static_cast<void*>(p)) T; }
template <typename T> inline void construct_array(T* p, uint n) { T* q = p + n; for ( ; p != q; ++p) new (static_cast<void*>(p)) T; }
template<typename T>
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<typename T, bool UseConstructor, typename ConstructionPolicy = simple_vector_default_copy_construction_policy<T> >
struct simple_vector : elemental_vector<T>
{
inline simple_vector() { construct(); }
inline simple_vector(const simple_vector& other, pool_allocator& alloc) { construct(other, alloc); }
// Manual constructor methods
inline void construct() { m_p = NULL; 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)
{
m_size = size;
m_p = NULL;
if (size)
{
uint num_bytes = sizeof(T) * size;
m_p = static_cast<T*>(alloc.Alloc(num_bytes));
if (UseConstructor)
{
T* pDst = m_p;
T* pDst_end = pDst + size;
const T* pSrc = p;
while (pDst != pDst_end)
ConstructionPolicy::copy_construct(pDst++, *pSrc++, alloc);
}
else
memcpy(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 m_size; }
inline uint size_in_bytes() const { return m_size * sizeof(T); }
inline const T& operator[] (uint i) const { PJSON_ASSERT(i < m_size); return m_p[i]; }
inline T& operator[] (uint i) { PJSON_ASSERT(i < m_size); return m_p[i]; }
inline const T* get_ptr() const { return m_p; }
inline T* get_ptr() { return m_p; }
inline const T* get_ptr(const T* pDef) const { return m_p ? m_p : pDef; }
inline T* get_ptr(T* pDef) { return m_p ? m_p : pDef; }
inline void clear() { m_p = NULL; m_size = 0; }
inline void resize(uint new_size, pool_allocator& alloc)
{
if (new_size > m_size)
{
grow(new_size, alloc);
if (UseConstructor)
construct_array(m_p + m_size, new_size - m_size);
}
m_size = new_size;
}
inline void shrink(uint new_size)
{
m_size = new_size;
}
inline T* enlarge_no_construct(uint n, pool_allocator& alloc)
{
PJSON_ASSERT(n);
uint cur_size = m_size, new_size = m_size + n;
grow(new_size, alloc);
m_size = new_size;
return 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(!m_p || (&obj < m_p) || (&obj >= (m_p + m_size)));
grow(m_size + 1, alloc);
if (UseConstructor)
ConstructionPolicy::copy_construct(m_p + m_size, obj, alloc);
else
memcpy(m_p + m_size, &obj, sizeof(T));
m_size++;
}
inline void push_back(const T* p, uint n, pool_allocator& alloc)
{
PJSON_ASSERT(!m_p || ((p + n) <= m_p) || (p >= (m_p + 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(!m_p || ((p + n) <= m_p) || (p >= (m_p + m_size)));
const uint num_to_assign = PJSON_MIN(m_size, n);
if (num_to_assign)
{
if (UseConstructor)
{
for (uint i = 0; i < num_to_assign; ++i)
ConstructionPolicy::assign(&m_p[i], p[i], alloc);
}
else
memcpy(m_p, p, sizeof(T) * num_to_assign);
}
if (n > 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) <= m_size);
if ((!n) || ((start + n) > m_size))
return;
const uint num_to_move = m_size - (start + n);
T* pDst = m_p + start;
memmove(pDst, m_p + start + n, num_to_move * sizeof(T));
m_size -= n;
}
inline void swap(simple_vector& other)
{
pjson::swap(m_p, other.m_p);
pjson::swap(m_size, other.m_size);
}
inline void grow(uint new_size, pool_allocator& alloc)
{
if (new_size > m_size)
m_p = static_cast<T*>(alloc.Realloc(m_p, sizeof(T) * new_size, 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<char, false> string_vec_t;
typedef simple_vector<key_value_t, true, simple_vector_allocator_copy_construction_policy<key_value_t> > key_value_vec_t;
typedef simple_vector<value_variant, true, simple_vector_allocator_copy_construction_policy<value_variant> > value_variant_vec_t;
#pragma pack(push, 4)
struct value_variant_data
{
union json_value_data_t
{
elemental_vector<key_value_vec_t::value_type> m_object;
elemental_vector<value_variant_vec_t::value_type> m_array;
elemental_vector<string_vec_t::value_type> 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<size_t>(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<typename T>
class serialize_helper : public T
{
public:
typedef T base;
template<typename I> inline serialize_helper(I& init) : T(init) { }
template<typename I, typename J> 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<uint>(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<int64>(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<uint>(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<int32>(m_data.m_nVal))) { val = static_cast<int32>(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<float>(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<uint>(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<uint>(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<char_buf_print_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<char_vector_print_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<int32>::min()) || (val64 > std::numeric_limits<int32>::max()))
return false;
val = static_cast<int32>(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<int64>(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<int64>::min()) && (flVal <= std::numeric_limits<int64>::max()))
{
val = static_cast<int64>(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<float>(m_data.m_nVal);
return true;
}
case cJSONValueTypeDouble:
{
val = static_cast<float>(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<float>(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<double>(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<uint64>(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<typename serializer>
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<typename serializer>
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<typename T> value_variant(T*);
template<typename T> value_variant(const T*);
template<typename T> value_variant& operator= (T*);
template<typename T> 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<uint8*>(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<uint8*>(m_pBuf) + m_ofs; }
template<typename T> inline T* get_top_obj() { return reinterpret_cast<T*>(m_pBuf + m_ofs - sizeof(T)); }
inline void reset() { m_ofs = 0; }
inline size_t get_ofs() { return m_ofs; }
template<typename T>
PJSON_FORCEINLINE T* push(uint num)
{
const size_t bytes_needed = sizeof(T) * num;
T* pResult = reinterpret_cast<T*>(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<uint8*>(pjson_realloc(m_pBuf, m_size));
pResult = reinterpret_cast<T*>(m_pBuf + m_ofs);
m_ofs += bytes_needed;
}
PJSON_ASSERT(m_ofs <= m_size);
return pResult;
}
template<typename T>
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<T*>(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
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<value_variant>(1), static_cast<value_variant*>(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<uint8>(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>();
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<value_variant*>(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<value_variant*>(this), m_stack.pop<value_variant>(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<value_variant>(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<uint8>((u | 0x80) & 0xBF); u >>= 6; // falls through
case 2: *--q = static_cast<uint8>((u | 0x80) & 0xBF); u >>= 6; // falls through
case 1: *--q = static_cast<uint8>(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<key_value_t>(1);
pChild_variant = &pKey_value->get_value();
pKey_value->get_key().m_p = (char*)pBuf;
pKey_value->get_key().m_size = static_cast<uint>(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<uint8>((u | 0x80) & 0xBF); u >>= 6; // falls through
case 2: *--q = static_cast<uint8>((u | 0x80) & 0xBF); u >>= 6; // falls through
case 1: *--q = static_cast<uint8>(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<uint>(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<int32>(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<int64>(n64) ^ (-is_neg));
}
else
{
double f = static_cast<double>(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<uint64>(final_scale + 31) <= 62)
v *= globals::s_pow10_table[static_cast<int>(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<int>(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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment