Commit 1b2758c3 authored by zhaoyunfei's avatar zhaoyunfei

juefx ajson

parents
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
ajson
=====
0.2.0版本要求C++11的编译器
a utility for serialize C++ and json.
动机:
=====
为什么要设计ajson?
在工作中经常有这样的需求,获得一段json编码的文本,将它解析为dom对象(比如rapidjson)。
然后读取各节点的值,为了方便,经常将其保存到某个数据结构。
然后这个dom就不需要了,这里dom的解析和构建就感觉到了浪费。
也有反过来的情况,将一个C++结构体,序列化到dom,然后将dom再次序列化到文本。
再次,感觉到了dom的浪费,能跳过dom这层吗?
这时,我希望能有一个像javascript那样方便的将json文本到数据结构直接方便的转换。
于是ajson就诞生了。
简介
=====
```cpp
------------------------------------------------------------------
struct Person
{
std::string Name;
int Age;
};
AJSON(Person , Name , Age)
Person obj;
char * json= "{ \"Name\" : \"Boo\", \"Age\" : 28}";
ajson::load_from_buff(obj,json);
```
以上代码解释了什么是AJSON,AJSON还支持stl的顺序容器。
再来个复杂点的例子
```cpp
enum PhoneType
{
MOBILE = 0,
HOME = 1,
WORK = 2
};
struct PhoneNumber
{
std::string number;
PhoneType type = MOBILE;
PhoneNumber(std::string const& number_ = "", PhoneType type_ = MOBILE)
:number(number_),type(type_)
{}
};
struct Person
{
std::string name;
int32_t id;
std::string mail;
std::vector<PhoneNumber> phones;
Person(std::string name_ = "" , int32_t id_ = 0, std::string mail_ = "")
:name(name_),id(id_),mail(mail_)
{}
};
struct AddressBook
{
std::vector<Person> peoples;
};
AJSON(PhoneNumber, number, type)
AJSON(Person, name, id, mail, phones)
AJSON(AddressBook, peoples)
```
结构体支持嵌套,stl容器也支持嵌套。
--------------------------------------------------------
特点
======
ajson反序列化扫描的时候,直接将字面量类型同时解析,并将结果直接存入对应的数据结构字段。
如此一来就不需要临时的DOM,减少了中间处理的工作以及内存的申请释放,大大提升了性能。
使用方式简单,只要定义相应的宏AJSON,便可方便的序列化/反序列化操作。
依赖小,完全不依赖第三方库,只有一个头文件,省去了编译的麻烦。
要点
======
如果json内部附带了转义符,ajson会改动原始文本来避免新申请内存。
如果不希望原始文本被修改,请创建一份新的拷贝。
由于ajson全是内存操作,文件读取操作时,将会全部读入内存,所以不适合大文件操作。
其他
======
为什么要选择json而不是xml?
因为json的数据描述更符合C++的数据描述,
举个例子,xml有Node和Attribute两种方式,
给出一个C++的Struct的Member field,
应该对应那种类型呢,如果是写入,
那么写成Node呢还是Attribute呢,
这里面对一个二义性问题。
---------------------------------------------------------
AJSON完全免费,没有任何限制,使用boost的授权方式。
0.2.2版本
=====
修改原先的内存修改策略,改为不修改源数据内存,
老的方式重新声明文件为ajson_msd.hpp,
习惯老方式的用户可以沿用,
新的方式会有轻微的性能降低,但是使用方式的限制取消了
0.2.1版本
=====
去掉了宏定义中的v.语法,感谢[奏之章]( https://github.com/mm304321141 )的建议
注意:由于该改动,ajson不能使用在final修饰的class上
0.2.0版本
=====
相对于0.1.x版本,ajson没有功能上的增加,主要的改动是去掉了对rapidjson和boost的依赖,完全独立了。
\ No newline at end of file
#pragma once
/*
__ _____ _____ _____
__| | __| | | | JSON for ORM C++ struct
| | |__ | | | | | | version 0.2.2
|_____|_____|_____|_|___| https://github.com/lordoffox/ajson
Licensed under the boost 1.0 License
Copyright (c) 2013-2018 Evan
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <array>
#include <cstdint>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <deque>
#include <list>
#include <map>
#include <memory>
#include <queue>
#include <string>
#include <type_traits>
#include <vector>
#include <unordered_map>
#include <ios>
#define STRINGFY_LIST(...) #__VA_ARGS__
namespace ajson
{
namespace detail
{
struct string_ref
{
char const * str = nullptr;
size_t len = 0;
bool operator == (string_ref const& rhs) const
{
if (len == rhs.len)
{
return std::memcmp(str, rhs.str, len) == 0;
}
return false;
}
};
typedef ::std::vector<string_ref> field_list;
inline void add_field(char const * pre, char const * cur, field_list& fields)
{
size_t len = cur - pre;
if (len > 0)
{
fields.emplace_back();
auto& f = fields.back();
f.str = pre;
f.len = len;
}
}
inline char const * char_table()
{
static char table[] = {
16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 16, 17, 17, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 16, 16, 16, 16,
16, 16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 10, 11, 12, 13, 14, 15 };
return table;
}
inline bool is_ws(char c)
{
if (c > ' ' || c <= 0)
return false;
return char_table()[c] == 17;
}
inline char const * skip_ws(char const * str)
{
while (is_ws(*str))++str;
return str;
}
//input "v.abc,v.def,v.xyz"
//output vector<string_ref> = ["abc","def","xyz"];
inline field_list split_fields(char const * info)
{
field_list fields;
char const * pre = info = skip_ws(info);
while (*info != 0)
{
++info;
if (is_ws(*info))
{
add_field(pre, info, fields);
info = skip_ws(info);
++info;
pre = skip_ws(info);
}
else if (*info == ',')
{
add_field(pre, info, fields);
++info;
pre = skip_ws(info);
}
}
add_field(pre, info, fields);
return fields;
}
template< class T >
struct is_signed_intergral_like : std::integral_constant < bool,
(std::is_integral<T>::value) &&
std::is_signed<T>::value
> {};
template< class T >
struct is_unsigned_intergral_like : std::integral_constant < bool,
(std::is_integral<T>::value) &&
std::is_unsigned<T>::value
> {};
template < template <typename...> class U, typename T >
struct is_template_instant_of : std::false_type {};
template < template <typename...> class U, typename... args >
struct is_template_instant_of< U, U<args...> > : std::true_type{};
template<typename T>
struct is_stdstring : is_template_instant_of < std::basic_string, T >
{};
template< class T >
struct is_sequence_container : std::integral_constant < bool,
is_template_instant_of<std::deque, T>::value ||
is_template_instant_of<std::list, T>::value ||
is_template_instant_of<std::vector, T>::value ||
is_template_instant_of<std::queue, T>::value
> {};
template< class T >
struct is_associat_container : std::integral_constant < bool,
is_template_instant_of<std::map, T>::value ||
is_template_instant_of<std::unordered_map, T>::value
> {};
template< class T >
struct is_emplace_back_able : std::integral_constant < bool,
is_template_instant_of<std::deque, T>::value ||
is_template_instant_of<std::list, T>::value ||
is_template_instant_of<std::vector, T>::value
> {};
}
struct token
{
detail::string_ref str;
enum
{
t_string,
t_int,
t_uint,
t_number,
t_ctrl,
t_end,
} type;
union
{
int64_t i64;
uint64_t u64;
double d64;
} value;
bool neg = false;
};
#ifdef _MSC_VER
#define NOEXCEPT
#else
#define NOEXCEPT noexcept
#endif
class exception : public std::exception
{
std::string msg_;
public:
exception(std::string const& msg)
:msg_(msg)
{}
char const * what() const NOEXCEPT{ return msg_.c_str(); }
};
class reader
{
token cur_tok_;
size_t cur_col_ = 0;
size_t cur_line_ = 0;
size_t len_ = 0;
size_t cur_offset_ = 0;
bool end_mark_ = false;
const char * ptr_ = nullptr;
double decimal = 0.1;
int exp = 0;
inline void decimal_reset(){ decimal = 0.1; }
inline char read() const
{
if (end_mark_)
return 0;
return ptr_[cur_offset_];
}
inline void take()
{
if (end_mark_ == false)
{
++cur_offset_;
char v = ptr_[cur_offset_];
if (v != '\r')
++cur_col_;
if (len_ > 0 && cur_offset_ >= len_)
{
end_mark_ = true;
}
else if (v == 0)
{
end_mark_ = true;
}
if (v == '\n')
{
cur_col_ = 0;
++cur_line_;
}
}
}
char skip()
{
auto c = read();
while (c == ' ' || c == '\t' || c == '\r' || c == '\n')
{
take();
c = read();
}
return c;
}
void parser_quote_string()
{
take();
cur_tok_.str.str = ptr_ + cur_offset_;
auto c = read();
do
{
switch (c)
{
case 0:
case '\n':
{
error("not a valid quote string!");
break;
}
case '\\':
{
take();
c = read();
break;
}
case '"':
{
cur_tok_.str.len = ptr_ + cur_offset_ - cur_tok_.str.str;
take();
return;
}
}
take();
c = read();
} while (true);
}
void parser_string()
{
cur_tok_.str.str = ptr_ + cur_offset_;
take();
auto c = read();
do
{
switch (c)
{
case 0:
{
error("not a valid string!");
break;
}
case ' ':
case '\t':
case '\r':
case '\n':
case ',':
case '[':
case ']':
case ':':
case '{':
case '}':
{
cur_tok_.str.len = ptr_ + cur_offset_ - cur_tok_.str.str;
return;
}
}
take();
c = read();
} while (true);
}
void parser_exp_pos()
{
take();
auto c = read();
do
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
exp *= 10;
exp += (c - '0');
break;
}
default:
{
for (int i = 0; i < exp; ++i)
{
cur_tok_.value.d64 *= 10.0;
}
exp = 0;
cur_tok_.str.len = ptr_ + cur_offset_ - cur_tok_.str.str;
return;
}
}
take();
c = read();
} while (1);
}
void parser_exp_neg()
{
take();
auto c = read();
do
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
exp *= 10;
exp += (c - '0');
break;
}
default:
{
for (int i = 0; i < exp; ++i)
{
cur_tok_.value.d64 *= 0.1;
}
exp = 0;
cur_tok_.str.len = ptr_ + cur_offset_ - cur_tok_.str.str;
return;
}
}
take();
c = read();
} while (1);
}
void parser_exp()
{
take();
auto c = read();
do
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
exp *= 10;
exp += (c - '0');
parser_exp_pos();
return;
}
case '-':
{
parser_exp_neg();
return;
}
default:
{
exp = 0;
cur_tok_.str.len = ptr_ + cur_offset_ - cur_tok_.str.str;
return;
}
}
} while (1);
}
void parser_number()
{
cur_tok_.str.str = ptr_ + cur_offset_;
take();
auto c = read();
do
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
if (cur_tok_.type == token::t_int)
{
cur_tok_.value.i64 *= 10;
cur_tok_.value.i64 += c - '0';
}
else if (cur_tok_.type == token::t_uint)
{
cur_tok_.value.u64 *= 10;
cur_tok_.value.u64 += c - '0';
}
else if (cur_tok_.type == token::t_number)
{
cur_tok_.value.d64 += decimal * (c - '0');
decimal *= 0.1;
}
break;
}
case '.':
{
if (cur_tok_.type == token::t_int)
{
cur_tok_.type = token::t_number;
cur_tok_.value.d64 = (double)cur_tok_.value.i64;
decimal_reset();
}
else if (cur_tok_.type == token::t_uint)
{
cur_tok_.type = token::t_number;
cur_tok_.value.d64 = (double)cur_tok_.value.u64;
decimal_reset();
}
else if (cur_tok_.type == token::t_number)
{
error("not a valid number!");
}
break;
}
case 'e':
case 'E':
{
if (cur_tok_.type == token::t_int)
{
cur_tok_.type = token::t_number;
cur_tok_.value.d64 = (double)cur_tok_.value.i64;
}
else if (cur_tok_.type == token::t_uint)
{
cur_tok_.type = token::t_number;
cur_tok_.value.d64 = (double)cur_tok_.value.u64;
}
parser_exp();
}
default:
{
cur_tok_.str.len = ptr_ + cur_offset_ - cur_tok_.str.str;
return;
}
}
take();
c = read();
} while (1);
}
public:
reader(const char * ptr = nullptr, size_t len = -1)
:ptr_(ptr), len_(len)
{
if (ptr == nullptr)
{
end_mark_ = true;
}
else if (len == 0)
{
end_mark_ = true;
}
else if (ptr[0] == 0)
{
end_mark_ = true;
}
next();
}
static inline char* itoa_native(size_t val , char * buffer , size_t len)
{
buffer[len] = 0;
size_t pos = len - 1;
if (val == 0)
{
buffer[pos--] = '0';
}
while (val)
{
buffer[pos--] = (char)((val % 10) + '0');
val = val / 10;
}
++pos;
return &buffer[0] + pos;
}
inline void error(const char * message) const
{
char buffer[20];
std::string msg = "error at line :";
msg += itoa_native(cur_line_,buffer,19);
msg += " col :";
msg += itoa_native(cur_col_, buffer, 19);
msg += " msg:";
msg += message;
throw exception(msg);
}
inline token const& peek() const
{
return cur_tok_;
}
void next()
{
auto c = skip();
bool do_next = false;
cur_tok_.neg = false;
switch (c)
{
case 0:
cur_tok_.type = token::t_end;
cur_tok_.str.str = ptr_ + cur_offset_;
cur_tok_.str.len = 1;
break;
case '{':
case '}':
case '[':
case ']':
case ':':
case ',':
{
cur_tok_.type = token::t_ctrl;
cur_tok_.str.str = ptr_ + cur_offset_;
cur_tok_.str.len = 1;
take();
break;
}
case '/':
{
take();
c = read();
if (c == '/')
{
take();
c = read();
while (c != '\n' && c != 0)
{
take();
c = read();
}
do_next = true;
break;
}
else if (c == '*')
{
take();
c = read();
do
{
while (c != '*')
{
if (c == 0)
{
return;
}
take();
c = read();
}
take();
c = read();
if (c == '/')
{
take();
do_next = true;
break;
}
} while (true);
}
//error parser comment
error("not a comment!");
}
case '"':
{
cur_tok_.type = token::t_string;
parser_quote_string();
break;
}
default:
{
if (c >= '0' && c <= '9')
{
cur_tok_.type = token::t_uint;
cur_tok_.value.u64 = c - '0';
parser_number();
}
else if (c == '-')
{
cur_tok_.type = token::t_int;
cur_tok_.value.i64 = 0;
cur_tok_.neg = true;
parser_number();
}
else
{
cur_tok_.type = token::t_string;
parser_string();
}
}
}
if (do_next == false)
return;
next();
}
inline bool expect(char c)
{
return cur_tok_.str.str[0] == c;
}
};
template <typename alloc_ty>
struct ajson_string_stream
{
private:
alloc_ty alloc;
public:
enum { good, read_overflow };
char * m_header_ptr;
char * m_read_ptr;
char * m_write_ptr;
char * m_tail_ptr;
int m_status;
std::size_t m_length;
enum{ INIT_BUFF_SIZE = 1024 };
ajson_string_stream() :m_length(INIT_BUFF_SIZE), m_status(good)
{
this->m_header_ptr = this->alloc.allocate(INIT_BUFF_SIZE);
this->m_read_ptr = this->m_header_ptr;
this->m_write_ptr = this->m_header_ptr;
this->m_tail_ptr = this->m_header_ptr + m_length;
}
~ajson_string_stream()
{
this->alloc.deallocate(m_header_ptr, this->m_length);
}
inline std::size_t read(char * buffer, std::size_t len)
{
if (this->m_read_ptr + len > this->m_tail_ptr)
{
m_status = read_overflow;
return 0;
}
std::memcpy(buffer, this->m_read_ptr, len);
this->m_read_ptr += len;
return len;
}
inline std::size_t growpup(std::size_t want_size)
{
std::size_t new_size = ((want_size + INIT_BUFF_SIZE - 1) / INIT_BUFF_SIZE)*INIT_BUFF_SIZE;
std::size_t write_pos = this->m_write_ptr - this->m_header_ptr;
std::size_t read_pos = this->m_read_ptr - this->m_header_ptr;
char * temp = this->m_header_ptr;
this->m_header_ptr = this->alloc.allocate(new_size);
std::memcpy(this->m_header_ptr, temp, this->m_length);
this->alloc.deallocate(temp, this->m_length);
this->m_length = new_size;
this->m_write_ptr = this->m_header_ptr + write_pos;
this->m_read_ptr = this->m_header_ptr + read_pos;
this->m_tail_ptr = this->m_header_ptr + m_length;
return new_size;
}
inline std::size_t write(const char * buffer, std::size_t len)
{
std::size_t writed_len = this->m_write_ptr + len - this->m_header_ptr;
if (writed_len > this->m_length)
{
this->growpup(writed_len);
}
std::memcpy((void*)this->m_write_ptr, buffer, len);
this->m_write_ptr += len;
return len;
}
inline void put(char c)
{
std::size_t writed_len = this->m_write_ptr + 1 - this->m_header_ptr;
if (writed_len > this->m_length)
{
this->growpup(writed_len);
}
*this->m_write_ptr = c;
++this->m_write_ptr;
}
inline bool bad()const{ return m_status != good; }
inline ajson_string_stream& seekp(int offset, int seek_dir)
{
switch (seek_dir)
{
case std::ios::beg:
{
if (offset < 0)
{
offset = 0;
}
this->m_write_ptr = this->m_header_ptr + offset;
break;
}
case std::ios::cur:
{
if (offset < 0)
{
offset = offset + int(this->m_write_ptr - this->m_header_ptr);
if (offset < 0)
{
offset = 0;
}
this->m_write_ptr = this->m_header_ptr + offset;
}
else
{
if (this->m_write_ptr + offset > this->m_tail_ptr)
{
this->m_write_ptr = this->m_tail_ptr;
}
}
break;
}
case std::ios::end:
{
if (offset < 0)
{
offset = offset + int(this->m_write_ptr - this->m_header_ptr);
if (offset < 0)
{
offset = 0;
}
this->m_write_ptr = this->m_header_ptr + offset;
}
break;
}
}
return *this;
}
inline void clear()
{
this->m_read_ptr = this->m_header_ptr;
this->m_write_ptr = this->m_header_ptr;
}
inline const char * data() const
{
return this->m_header_ptr;
}
std::basic_string<char, std::char_traits<char>, alloc_ty> str()
{
std::basic_string<char, std::char_traits<char>, alloc_ty> s(this->m_header_ptr, this->write_length());
return s;
}
inline ::std::size_t read_length() const
{
return this->m_read_ptr - this->m_header_ptr;
}
inline ::std::size_t write_length() const
{
return this->m_write_ptr - this->m_header_ptr;
}
};
typedef ajson_string_stream<std::allocator<char> > string_stream;
struct ajson_file_stream
{
public:
enum { good, read_overflow, file_error };
FILE * m_f;
int m_status;
enum{ INIT_AMSG_BUFF_SIZE = 1024 };
ajson_file_stream(const char * filename) :m_f(NULL), m_status(good)
{
this->m_f = std::fopen(filename, "w");
if (NULL == this->m_f)
{
this->m_status = file_error;
}
}
~ajson_file_stream()
{
if (this->m_f)
{
fclose(m_f);
}
}
inline std::size_t read(char * buffer, std::size_t len)
{
std::size_t rlen = std::fread(buffer, len, 1, this->m_f);
return rlen;
}
inline std::size_t write(const char * buffer, std::size_t len)
{
std::size_t wlen = std::fwrite(buffer, len, 1, this->m_f);
return wlen;
}
inline void put(char c)
{
std::fputc(c, this->m_f);
}
inline bool bad(){ return m_status != good; }
inline int seekp(size_t offset, int seek_dir)
{
return std::fseek(this->m_f, (int)offset, seek_dir);
}
inline void clear()
{
std::fseek(this->m_f, 0, SEEK_SET);
}
};
template<typename stream_ty>
struct lite_write
{
stream_ty& s_;
lite_write(stream_ty& s) :s_(s){}
inline void write_bool(bool v)
{
if (v)
s_.put('1');
else
s_.put('0');
}
inline void write_liter(char const * str, size_t len)
{
s_.write(str, len);
}
inline uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte)
{
static const std::array<uint8_t, 400> utf8d =
{
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
}
};
const uint8_t type = utf8d[byte];
codep = (state != 0)
? (byte & 0x3fu) | (codep << 6)
: static_cast<uint32_t>(0xff >> type) & (byte);
state = utf8d[256u + state * 16u + type];
return state;
}
inline void write_str(char const * str, size_t len)
{
static char const * hex_table = "0123456789ABCDEF";
put('"');
char const * ptr = str;
char const * end = ptr + len;
uint32_t codepoint;
uint8_t state = 0;
while (ptr < end)
{
char c = *ptr;
if (c == 0)
break;
++ptr;
decode(state, codepoint, (uint8_t)c);
if (state== 0)
{
switch (codepoint)
{
case 0x08: // backspace
{
put('\\');
put('b');
break;
}
case 0x09: // horizontal tab
{
put('\\');
put('t');
break;
}
case 0x0A: // newline
{
put('\\');
put('n');
break;
}
case 0x0C: // formfeed
{
put('\\');
put('f');
break;
}
case 0x0D: // carriage return
{
put('\\');
put('r');
break;
}
case 0x22: // quotation mark
{
put('\\');
put('\"');
break;
}
case 0x5C: // reverse solidus
{
put('\\');
put('\\');
break;
}
default:
{
// escape control characters (0x00..0x1F) or, if
// ensure_ascii parameter is used, non-ASCII characters
if ((codepoint <= 0x1F) || (codepoint >= 0x7F))
{
if (codepoint <= 0xFFFF)
{
unsigned char c1 = (uint8_t)(codepoint >> 8);
unsigned char c2 = (uint8_t)codepoint;
put('\\');
put('u');
put(hex_table[(c1) >> 4]);
put(hex_table[(c1) & 0xF]);
put(hex_table[(c2) >> 4]);
put(hex_table[(c2) & 0xF]);
}
else
{
unsigned char c1 = (uint8_t)(codepoint >> 24);
unsigned char c2 = (uint8_t)(codepoint >> 16);
unsigned char c3 = (uint8_t)(codepoint >> 8);
unsigned char c4 = (uint8_t)codepoint;
put('\\');
put('u');
put(hex_table[(c1) >> 4]);
put(hex_table[(c1)& 0xF]);
put(hex_table[(c2) >> 4]);
put(hex_table[(c2)& 0xF]);
put('\\');
put('u');
put(hex_table[(c3) >> 4]);
put(hex_table[(c3)& 0xF]);
put(hex_table[(c4) >> 4]);
put(hex_table[(c4)& 0xF]);
}
}
else
{
put(c);
}
break;
}
}
}
}
put('"');
}
inline void put(char c)
{
s_.put(c);
}
};
template<typename ty, class enable = void>
struct json_impl;
inline bool is_true(token const& tok)
{
char const * ptr = tok.str.str;
if (tok.str.len == 4)
{
return (ptr[0] == 't' || ptr[0] == 'T') &&
(ptr[1] == 'r' || ptr[1] == 'R') &&
(ptr[2] == 'u' || ptr[2] == 'U') &&
(ptr[3] == 'e' || ptr[3] == 'E');
}
return false;
}
template<>
struct json_impl < bool, void >
{
static inline void read(reader& rd, bool& val)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
{
val = is_true(tok);
break;
}
case token::t_int:
{
val = (tok.value.i64 != 0);
break;
}
case token::t_uint:
{
val = (tok.value.u64 != 0);
break;
}
case token::t_number:
{
val = (tok.value.d64 != 0.0);
break;
}
default:
{
rd.error("not a valid bool.");
}
}
rd.next();
}
template<typename write_ty>
static inline void write(write_ty& wt, bool const& val)
{
wt.write_bool(val);
}
template<typename write_ty>
static inline void write_key(write_ty& wt, bool const& val)
{
wt.put('"');
wt.write_bool(val);
wt.put('"');
}
};
template<>
struct json_impl < __uint128_t, void >
{
static inline void read(reader& rd, __uint128_t& val)
{
auto& tok = rd.peek();
{
__uint128_t temp = 0;
// save the begin of str to ptr
const char *ptr = tok.str.str;
// if "-", move to the next
if (*ptr == '-' || *ptr == '+')
{
ptr++;
}
while(*ptr != 0)
{
// if not number, exit
if ((*ptr < '0') || (*ptr > '9'))
{
break;
}
temp = temp * 10 + (*ptr - '0');
ptr++;
}
// if "-", reverse
if (*ptr == '-')
{
temp = -temp;
}
val = temp;
}
rd.next();
}
template<typename write_ty>
static inline void write(write_ty& wt, __uint128_t const& val)
{
__uint128_t temp = (__uint128_t)val;
char buffer[128];
buffer[127] = 0;
size_t len = 128;
size_t pos = 126;
if (temp == 0)
{
buffer[pos--] = '0';
}
while (temp)
{
buffer[pos--] = (char)((temp % 10) + '0');
temp = temp / 10;
}
++pos;
wt.write_liter(buffer + pos, len - pos - 1);
}
template<typename write_ty>
static inline void write_key(write_ty& wt, __uint128_t const& val)
{
wt.put('"');
write<write_ty>(wt, val);
wt.put('"');
}
};
template<typename ty>
struct json_impl < ty,
typename std::enable_if <detail::is_signed_intergral_like<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
{
if (is_true(tok))
{
val = 1;
}
else
{
int64_t temp = std::strtoll(tok.str.str, nullptr, 10);
val = static_cast<ty>(temp);
}
break;
}
case token::t_int:
{
val = static_cast<ty>(tok.value.i64);
if (tok.neg)
val = -val;
break;
}
case token::t_uint:
{
val = static_cast<ty>(tok.value.u64);
break;
}
case token::t_number:
{
val = static_cast<ty>(tok.value.d64);
if (tok.neg)
val = -val;
break;
}
default:
{
rd.error("not a valid signed integral like number.");
}
}
rd.next();
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
int64_t temp = (int64_t)val;
char buffer[64];
buffer[63] = 0;
size_t len = 64;
size_t pos = 62;
bool Sig = false;
if (temp < 0)
{
Sig = true;
temp *= -1;
}
if (temp == 0)
{
buffer[pos--] = '0';
}
while (temp)
{
buffer[pos--] = (char)((temp % 10) + '0');
temp = temp / 10;
}
if (Sig)
{
buffer[pos--] = '-';
}
++pos;
wt.write_liter(buffer + pos, len - pos - 1);
}
template<typename write_ty>
static inline void write_key(write_ty& wt, ty const& val)
{
wt.put('"');
write<write_ty>(wt, val);
wt.put('"');
}
};
template<typename ty>
struct json_impl < ty,
typename std::enable_if <detail::is_unsigned_intergral_like<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
{
if (is_true(tok))
{
val = 1;
}
else
{
uint64_t temp = std::strtoull(tok.str.str, nullptr, 10);
val = static_cast<ty>(temp);
}
break;
}
case token::t_int:
{
if (tok.neg)
{
rd.error("assign a negative signed integral to unsigned integral number.");
}
val = static_cast<ty>(tok.value.i64);
break;
}
case token::t_uint:
{
val = static_cast<ty>(tok.value.u64);
break;
}
case token::t_number:
{
if (tok.neg)
{
rd.error("assign a negative float point to unsigned integral number.");
}
val = static_cast<ty>(tok.value.d64);
break;
}
default:
{
rd.error("not a valid unsigned integral like number.");
}
}
rd.next();
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
uint64_t temp = (uint64_t)val;
char buffer[64];
buffer[63] = 0;
size_t len = 64;
size_t pos = 62;
if (temp == 0)
{
buffer[pos--] = '0';
}
while (temp)
{
buffer[pos--] = (char)((temp % 10) + '0');
temp = temp / 10;
}
++pos;
wt.write_liter(buffer + pos, len - pos - 1);
}
template<typename write_ty>
static inline void write_key(write_ty& wt, ty const& val)
{
wt.put('"');
write<write_ty>(wt, val);
wt.put('"');
}
};
template<typename ty>
struct json_impl < ty,
typename std::enable_if <std::is_enum<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
typedef typename std::underlying_type<ty>::type raw_type;
json_impl<raw_type>::read(rd, (raw_type&)val);
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
typedef typename std::underlying_type<ty>::type raw_type;
json_impl<raw_type>::write(wt, val);
}
template<typename write_ty>
static inline void write_key(write_ty& wt, ty const& val)
{
typedef typename std::underlying_type<ty>::type raw_type;
json_impl<raw_type>::write_key(wt, val);
}
};
template<typename ty>
struct json_impl < ty,
typename std::enable_if <std::is_floating_point<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
{
double temp = std::strtold(tok.str.str, nullptr);
val = static_cast<ty>(temp);
break;
}
case token::t_int:
{
val = static_cast<ty>(tok.value.i64);
if (tok.neg)
val = -val;
break;
}
case token::t_uint:
{
val = static_cast<ty>(tok.value.u64);
break;
}
case token::t_number:
{
val = static_cast<ty>(tok.value.d64);
if (tok.neg)
val = -val;
break;
}
default:
{
rd.error("not a valid float point number.");
}
}
rd.next();
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
char buffer[64] = { 0 };
#ifdef _MSC_VER
_gcvt_s(buffer, 63 , val, 8);
#else
gcvt(val, 62, buffer);
#endif // MSVC
size_t len = std::strlen(buffer);
if (buffer[len - 1] == '.')
{
buffer[len - 1] = '\0';
-- len;
}
wt.write_liter(buffer, len);
}
template<typename write_ty>
static inline void write_key(write_ty& wt, ty const& val)
{
wt.put('"');
write<write_ty>(wt, val);
wt.put('"');
}
};
inline char char_to_hex(char v)
{
if (v>=0 && v <= 'f')
{
v = detail::char_table()[v];
}
else
{
v = 16;
}
return v;
}
inline uint64_t read_utf(const char * data, size_t)
{
char v = char_to_hex(*data++);
if (v >= 16)
return 0;
uint64_t utf = v;
utf <<= 4;
v = char_to_hex(*data++);
if (v >= 16)
return 0;
utf += v;
utf <<= 4;
v = char_to_hex(*data++);
if (v >= 16)
return 0;
utf += v;
utf <<= 4;
v = char_to_hex(*data++);
if (v >= 16)
return 0;
utf += v;
return utf;
}
template<typename string_ty>
inline bool esacpe_utf8(string_ty& str , uint64_t utf1)
{
if (utf1 < 0x80)
{
str.append(1, (char)utf1);
}
else if (utf1 < 0x800)
{
str.append(1, (char)(0xC0 | ((utf1 >> 6) & 0xFF)));
str.append(1, (char)(0x80 | ((utf1 & 0x3F))));
}
else if (utf1 < 0x80000)
{
str.append(1, (char)(0xE0 | ((utf1 >> 12) & 0xFF)));
str.append(1, (char)(0x80 | ((utf1 >> 6) & 0x3F)));
str.append(1, (char)(0x80 | ((utf1 & 0x3F))));
}
else
{
if (utf1 < 0x110000)
{
return false;
}
str.append(1, (char)(0xF0 | ((utf1 >> 18) & 0xFF)));
str.append(1, (char)(0x80 | ((utf1 >> 12) & 0x3F)));
str.append(1, (char)(0x80 | ((utf1 >> 6) & 0x3F)));
str.append(1, (char)(0x80 | ((utf1 & 0x3F))));
}
return true;
}
template<typename string_ty>
bool escape_string(string_ty& str , const char * data , size_t len)
{
str.clear();
str.reserve(len);
if (len == 0)
return true;
do
{
auto c = *data++;
--len;
switch (c)
{
case '\\':
{
c = *data++;
--len;
switch (c)
{
case '\\':
{
c = '\\';
break;
}
case '/':
{
c = '/';
break;
}
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 '"':
{
break;
}
case 'u':
{
if (len < 4)
return false;
uint64_t uft1 = read_utf(data, len);
data += 4;
len -= 4;
if (uft1 == 0)
return false;
if (!esacpe_utf8(str, uft1))
return false;
continue;
}
default:
{
return false;
}
}
break;
}
}
str.append(1, c);
} while (len > 0);
return true;
}
template<typename ty>
struct json_impl < ty,
typename std::enable_if <detail::is_stdstring<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
auto& tok = rd.peek();
if (tok.type == token::t_string)
{
if (!escape_string(val, tok.str.str, (int)tok.str.len))
{
rd.error("not a valid string.");
}
}
else
{
rd.error("not a valid string.");
}
rd.next();
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
wt.write_str(val.data(), val.length());
}
template<typename write_ty>
static inline void write_key(write_ty& wt, ty const& val)
{
write<write_ty>(wt, val);
}
};
inline void char_array_read(reader& rd, char * val , size_t N)
{
auto& tok = rd.peek();
if (tok.type == token::t_string)
{
std::string str;
if (!escape_string(str, tok.str.str, tok.str.len))
{
rd.error("not a valid string.");
}
size_t len = str.length();
if (len > N)
len = N;
std::memcpy(val, str.data(), len);
if (len < N)
val[len] = 0;
}
else
{
rd.error("not a valid string.");
}
rd.next();
}
template<typename write_ty>
inline void char_array_write(write_ty& wt, const char * val, size_t N)
{
wt.write_str(val, N);
}
template<size_t N>
struct json_impl <char[N]>
{
static inline void read(reader& rd, char * val)
{
char_array_read(rd, val, N);
}
template<typename write_ty>
static inline void write(write_ty& wt, const char * val)
{
char_array_write(wt, val, N);
}
};
template<size_t N>
struct json_impl <const char[N] >
{
static inline void read(reader& rd, char * val)
{
char_array_read(rd, val, N);
}
template<typename write_ty>
static inline void write(write_ty& wt, const char * val)
{
char_array_write(wt, val, N);
}
};
template<typename T>
inline void array_read(reader& rd, T * val, int N)
{
if (rd.expect('[') == false)
{
rd.error("array must start with [.");
}
rd.next();
auto tok = &rd.peek();
size_t count = 0;
while (tok->str.str[0] != ']')
{
if (count < N)
{
json_impl<T>::read(rd, val[count]);
}
else
{
rd.error("array overflow.");
}
++count;
tok = &rd.peek();
if (tok->str.str[0] == ',')
{
rd.next();
tok = &rd.peek();
continue;
}
else if (tok->str.str[0] == ']')
{
break;
}
else
{
rd.error("no valid array!");
}
}
rd.next();
return;
}
template<typename T, typename write_ty>
inline void array_write(write_ty& wt, const T * val, int N)
{
wt.put('[');
int last = N - 1;
for (int i = 0; i < N; ++i)
{
json_impl<T>::write(wt, val[i]);
if (i < last)
wt.put(',');
}
wt.put(']');
}
template<typename T, size_t N>
struct json_impl <T[N]>
{
static inline void read(reader& rd, T * val)
{
array_read(rd, val, N);
}
template<typename write_ty>
static inline void write(write_ty& wt, const T * val)
{
array_write(wt, val, N);
}
};
template<typename T, size_t N>
struct json_impl < const T[N]>
{
static inline void read(reader& rd, T * val)
{
array_read(rd, val, N);
}
template<typename write_ty>
static inline void write(write_ty& wt, const T * val)
{
array_write(wt, val, N);
}
};
template<typename ty>
inline typename std::enable_if <detail::is_emplace_back_able<ty>::value, void>::type
emplace_back(ty& val)
{
val.emplace_back();
}
template<typename ty>
inline typename std::enable_if <detail::is_template_instant_of<std::queue, ty>::value, void>::type
emplace_back(ty& val)
{
val.emplace();
}
template<typename ty>
struct json_impl < ty,
typename std::enable_if <detail::is_sequence_container<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
if (rd.expect('[') == false)
{
rd.error("array must start with [.");
}
rd.next();
auto tok = &rd.peek();
while (tok->str.str[0] != ']')
{
::ajson::emplace_back(val);
json_impl<typename ty::value_type>::read(rd, val.back());
tok = &rd.peek();
if (tok->str.str[0] == ',')
{
rd.next();
tok = &rd.peek();
continue;
}
else if (tok->str.str[0] == ']')
{
break;
}
else
{
rd.error("no valid array!");
}
}
rd.next();
return;
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
wt.put('[');
auto sz = val.size();
for (auto& i : val)
{
json_impl<typename ty::value_type>::write(wt, i);
if (sz-- > 1)
wt.put(',');
}
wt.put(']');
}
};
template<typename ty>
struct json_impl < ty,
typename std::enable_if <detail::is_associat_container<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
if (rd.expect('{') == false)
{
rd.error("object must start with {!");
}
rd.next();
auto tok = &rd.peek();
while (tok->str.str[0] != '}')
{
typename ty::key_type key;
typename ty::mapped_type value;
json_impl<typename ty::key_type>::read(rd, key);
if (rd.expect(':') == false)
{
rd.error("invalid object!");
}
rd.next();
json_impl<typename ty::mapped_type>::read(rd, value);
val[key] = value;
tok = &rd.peek();
if (tok->str.str[0] == ',')
{
rd.next();
tok = &rd.peek();
continue;
}
else if (tok->str.str[0] == '}')
{
break;
}
else
{
rd.error("no valid object!");
}
}
rd.next();
return;
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
wt.put('{');
auto sz = val.size();
for (auto& i : val)
{
json_impl<typename ty::key_type>::write_key(wt, i.first);
wt.put(':');
json_impl<typename ty::mapped_type>::write(wt, i.second);
if (sz-- > 1)
wt.put(',');
}
wt.put('}');
}
};
inline void skip_array(reader& rd);
inline void skip_object(reader& rd);
inline void skip(reader& rd)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
case token::t_int:
case token::t_uint:
case token::t_number:
{
rd.next();
return;
}
case token::t_ctrl:
{
if (tok.str.str[0] == '[')
{
rd.next();
skip_array(rd);
return;
}
else if (tok.str.str[0] == '{')
{
rd.next();
skip_object(rd);
return;
}
}
case token::t_end:
{
return;
}
}
rd.error("invalid json document!");
}
inline void skip_array(reader& rd)
{
auto tok = &rd.peek();
while (tok->str.str[0] != ']')
{
skip(rd);
tok = &rd.peek();
if (tok->str.str[0] == ',')
{
rd.next();
tok = &rd.peek();
continue;
}
}
rd.next();
}
inline void skip_key(reader& rd)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
case token::t_int:
case token::t_uint:
case token::t_number:
{
rd.next();
return;
}
default:
break;
}
rd.error("invalid json document!");
}
inline void skip_object(reader& rd)
{
auto tok = &rd.peek();
while (tok->str.str[0] != '}')
{
skip_key(rd);
tok = &rd.peek();
if (tok->str.str[0] == ':')
{
rd.next();
skip(rd);
//rd.next();
tok = &rd.peek();
}
else
{
rd.error("invalid json document!");
}
if (tok->str.str[0] == ',')
{
rd.next();
tok = &rd.peek();
continue;
}
}
rd.next();
}
template<typename head, typename... args>
inline int read_members(reader& rd, detail::string_ref const * member_ptr,
detail::string_ref const& member, size_t pos, head& h, args& ... args_);
inline int read_members(reader&, detail::string_ref const*,
detail::string_ref const&, size_t);
template<typename head, typename... args>
struct read_members_impl
{
static inline int read(reader& rd, detail::string_ref const * member_ptr,
detail::string_ref const& member, size_t pos, head& val, args& ... args_)
{
if (member_ptr[pos] == member)
{
json_impl<head>::read(rd, val);
return 1;
}
if (sizeof...(args))
return read_members(rd, member_ptr, member, pos + 1, args_...);
return 0;
}
};
template<typename head, typename... args>
inline int read_members(reader& rd, detail::string_ref const * member_ptr,
detail::string_ref const& member, size_t pos, head& h, args& ... args_)
{
return read_members_impl<head, args...>::read(rd, member_ptr,
member, pos, h, args_...);
}
inline int read_members(reader&, detail::string_ref const*,
detail::string_ref const&, size_t)
{
return 0;
}
template<typename ty>
inline void load_from_buff(ty& val, const char * buff, size_t len = -1)
{
typedef typename std::remove_cv<ty>::type rty;
reader rd(buff, len);
json_impl<rty>::read(rd, val);
}
template<typename ty>
inline void load_from_file(ty& val, const char * filename)
{
struct file_guard
{
std::FILE * f_;
file_guard(std::FILE * f):f_(f){}
~file_guard()
{
if (f_)
std::fclose(f_);
}
};
std::FILE * f = std::fopen(filename, "rb");
if (nullptr == f)
{
std::string errmsg = "can't open file:";
throw std::logic_error( errmsg + filename);
}
file_guard fg(f);
std::fseek(f, 0, SEEK_END);
size_t sz = std::ftell(f);
std::fseek(f, 0, SEEK_SET);
struct buffer_guard
{
char * buff_ = nullptr;
buffer_guard(char * buff) :buff_(buff){}
~buffer_guard()
{
if (buff_)
delete [] buff_;
}
};
char * buffer = new char[sz+1];
buffer_guard bg(buffer);
sz = std::fread(buffer, 1, sz, f);
buffer[sz] = 0;
if (sz >= 3)
{
if (buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF)
{
buffer += 3;
sz -= 3;
}
}
reader rd(buffer, sz);
typedef typename std::remove_cv<ty>::type rty;
json_impl<rty>::read(rd, val);
}
template<typename write_ty, typename head, typename... args>
inline void write_members(write_ty& wt, detail::string_ref const * member_ptr,
size_t pos, head& h, args& ... args_);
template<typename write_ty>
inline void write_members(write_ty&, detail::string_ref *, size_t);
template<typename write_ty, typename head, typename... args>
struct write_members_impl
{
static inline void write(write_ty& wt, detail::string_ref const * member_ptr,
size_t pos, head const& val, args const& ... args_)
{
wt.write_str(member_ptr[pos].str, member_ptr[pos].len);
wt.put(':');
json_impl<head>::write(wt, val);
if (sizeof...(args))
{
wt.put(',');
write_members(wt, member_ptr, pos + 1, args_...);
}
}
};
template<typename write_ty, typename head, typename... args>
inline void write_members(write_ty& wt, detail::string_ref const* member_ptr,
size_t pos, head const& h, args& ... args_)
{
return write_members_impl<write_ty, head, args...>::write(wt, member_ptr,
pos, h, args_...);
}
template<typename write_ty>
inline void write_members(write_ty&, detail::string_ref const*, size_t)
{
return;
}
template<typename ty, typename stream_ty, class write_tp = lite_write<stream_ty> >
inline void save_to(stream_ty& ss, ty& val)
{
typedef typename std::remove_cv<ty>::type rty;
write_tp wt(ss);
json_impl<rty>::write(wt, val);
}
template<typename ty, typename stream_ty = ajson_file_stream, class write_tp = lite_write<stream_ty> >
inline void save_to_file(ty& val, char const * filename)
{
stream_ty fs(filename);
save_to < ty, stream_ty, write_tp>(fs, val);
}
}
#define AJSON(TYPE,...) \
namespace ajson\
{\
template<>\
struct json_impl < TYPE , void >\
{\
struct json_helper : public TYPE\
{\
inline void read_(reader& rd)\
{\
auto& fields = this_field_list();\
if (rd.expect('{') == false){ rd.error("read object must start with {!"); }\
rd.next();\
if (rd.expect('}'))\
return;\
auto mber = rd.peek();\
do\
{\
if (mber.type != token::t_string){ rd.error("object key must be string"); }\
rd.next();\
if (rd.expect(':') == false){ rd.error("invalid json document!"); }\
rd.next();\
if (read_members(rd, &fields[0], mber.str, 0,__VA_ARGS__) == 0)\
{\
skip(rd);\
}\
if (rd.expect('}'))\
{\
rd.next();\
return;\
}\
else if (rd.expect(','))\
{\
rd.next();\
mber = rd.peek();\
continue;\
}\
rd.error("invalid json document!");\
} while (true);\
}\
template<typename write_ty>\
inline void write_(write_ty& wt) const\
{\
auto& fields = this_field_list();\
wt.put('{');\
::ajson::write_members(wt, &fields[0], 0,__VA_ARGS__);\
wt.put('}');\
}\
};\
static inline detail::field_list& this_field_list()\
{\
static auto fields = detail::split_fields(STRINGFY_LIST(__VA_ARGS__));\
return fields;\
}\
static inline void read(reader& rd, TYPE& v)\
{\
reinterpret_cast<json_helper &>(v).read_(rd);\
}\
template<typename write_ty>\
static inline void write(write_ty& wt, TYPE const& v)\
{\
reinterpret_cast<json_helper const &>(v).write_(wt);\
}\
};\
}
#pragma once
/*
__ _____ _____ _____
__| | __| | | | JSON for ORM C++ struct
| | |__ | | | | | | version 0.2.2
|_____|_____|_____|_|___| https://github.com/lordoffox/ajson
Licensed under the boost 1.0 License
Copyright (c) 2013-2018 Evan
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <array>
#include <cstdint>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <deque>
#include <list>
#include <map>
#include <memory>
#include <queue>
#include <string>
#include <type_traits>
#include <vector>
#include <unordered_map>
#include <ios>
#define STRINGFY_LIST(...) #__VA_ARGS__
namespace ajson
{
namespace detail
{
struct string_ref
{
char const * str = nullptr;
size_t len = 0;
bool operator == (string_ref const& rhs) const
{
if (len == rhs.len)
{
return std::memcmp(str, rhs.str, len) == 0;
}
return false;
}
};
typedef ::std::vector<string_ref> field_list;
inline void add_field(char const * pre, char const * cur, field_list& fields)
{
size_t len = cur - pre;
if (len > 0)
{
fields.emplace_back();
auto& f = fields.back();
f.str = pre;
f.len = len;
}
}
inline char const * char_table()
{
static char table[] = {
16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 16, 17, 17, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 16, 16, 16, 16,
16, 16, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 10, 11, 12, 13, 14, 15 };
return table;
}
inline bool is_ws(char c)
{
if (c > ' ')
return false;
return char_table()[int(c)] == 17;
}
inline char const * skip_ws(char const * str)
{
while (is_ws(*str))++str;
return str;
}
//input "v.abc,v.def,v.xyz"
//output vector<string_ref> = ["abc","def","xyz"];
inline field_list split_fields(char const * info)
{
field_list fields;
char const * pre = info = skip_ws(info);
while (*info != 0)
{
++info;
if (is_ws(*info))
{
add_field(pre, info, fields);
info = skip_ws(info);
++info;
pre = skip_ws(info);
}
else if (*info == ',')
{
add_field(pre, info, fields);
++info;
pre = skip_ws(info);
}
}
add_field(pre, info, fields);
return fields;
}
template< class T >
struct is_signed_intergral_like : std::integral_constant < bool,
(std::is_integral<T>::value) &&
std::is_signed<T>::value
> {};
template< class T >
struct is_unsigned_intergral_like : std::integral_constant < bool,
(std::is_integral<T>::value) &&
std::is_unsigned<T>::value
> {};
template < template <typename...> class U, typename T >
struct is_template_instant_of : std::false_type {};
template < template <typename...> class U, typename... args >
struct is_template_instant_of< U, U<args...> > : std::true_type{};
template<typename T>
struct is_stdstring : is_template_instant_of < std::basic_string, T >
{};
template< class T >
struct is_sequence_container : std::integral_constant < bool,
is_template_instant_of<std::deque, T>::value ||
is_template_instant_of<std::list, T>::value ||
is_template_instant_of<std::vector, T>::value ||
is_template_instant_of<std::queue, T>::value
> {};
template< class T >
struct is_associat_container : std::integral_constant < bool,
is_template_instant_of<std::map, T>::value ||
is_template_instant_of<std::unordered_map, T>::value
> {};
template< class T >
struct is_emplace_back_able : std::integral_constant < bool,
is_template_instant_of<std::deque, T>::value ||
is_template_instant_of<std::list, T>::value ||
is_template_instant_of<std::vector, T>::value
> {};
}
struct token
{
detail::string_ref str;
enum
{
t_string,
t_int,
t_uint,
t_number,
t_ctrl,
t_end,
} type;
union
{
int64_t i64;
uint64_t u64;
double d64;
} value;
bool neg = false;
};
#ifdef _MSC_VER
#define NOEXCEPT
#else
#define NOEXCEPT noexcept
#endif
class exception : public std::exception
{
std::string msg_;
public:
exception(std::string const& msg)
:msg_(msg)
{}
char const * what() const NOEXCEPT{ return msg_.c_str(); }
};
class reader
{
token cur_tok_;
size_t cur_col_ = 0;
size_t cur_line_ = 0;
size_t len_ = 0;
size_t cur_offset_ = 0;
bool end_mark_ = false;
char * ptr_ = nullptr;
double decimal = 0.1;
int exp = 0;
inline void decimal_reset(){ decimal = 0.1; }
inline char read() const
{
if (end_mark_)
return 0;
return ptr_[cur_offset_];
}
inline void take()
{
if (end_mark_ == false)
{
++cur_offset_;
char v = ptr_[cur_offset_];
if (v != '\r')
++cur_col_;
if (len_ > 0 && cur_offset_ >= len_)
{
end_mark_ = true;
}
else if (v == 0)
{
end_mark_ = true;
}
if (v == '\n')
{
cur_col_ = 0;
++cur_line_;
}
}
}
char skip()
{
auto c = read();
while (c == ' ' || c == '\t' || c == '\r' || c == '\n')
{
take();
c = read();
}
return c;
}
inline void fill_escape_char(size_t count, char c)
{
if (count == 0)
return;
ptr_[cur_offset_ - count] = c;
}
inline char char_to_hex(char v)
{
if (v <= 'f')
{
v = detail::char_table()[int(v)];
}
else
{
v = 16;
}
if (v > 15)
error("utf8 code error!");
return v;
}
inline uint64_t read_utf()
{
char v = char_to_hex(read());
uint64_t utf = v;
utf <<= 4;
take();
v = char_to_hex(read());
utf += v;
utf <<= 4;
take();
v = char_to_hex(read());
utf += v;
utf <<= 4;
take();
v = char_to_hex(read());
utf += v;
take();
return utf;
}
inline void esacpe_utf8(size_t& esc_count)
{
uint64_t utf1 = read_utf();
esc_count += 6;
if (utf1 < 0x80)
{
fill_escape_char(esc_count, (char)utf1);
--esc_count;
}
else if (utf1 < 0x800)
{
fill_escape_char(esc_count, (char)(0xC0 | ((utf1 >> 6) & 0xFF)));
fill_escape_char(esc_count - 1, (char)(0x80 | ((utf1 & 0x3F))));
esc_count -= 2;
}
else if (utf1 < 0x80000)
{
fill_escape_char(esc_count, (char)(0xE0 | ((utf1 >> 12) & 0xFF)));
fill_escape_char(esc_count - 1, (char)(0x80 | ((utf1 >> 6) & 0x3F)));
fill_escape_char(esc_count - 2, (char)(0x80 | ((utf1 & 0x3F))));
esc_count -= 3;
}
else
{
if (utf1 < 0x110000)
{
error("utf8 code error!");
}
fill_escape_char(esc_count, (char)(0xF0 | ((utf1 >> 18) & 0xFF)));
fill_escape_char(esc_count - 1, (char)(0x80 | ((utf1 >> 12) & 0x3F)));
fill_escape_char(esc_count - 2, (char)(0x80 | ((utf1 >> 6) & 0x3F)));
fill_escape_char(esc_count - 3, (char)(0x80 | ((utf1 & 0x3F))));
esc_count -= 4;
}
}
void parser_quote_string()
{
take();
cur_tok_.str.str = ptr_ + cur_offset_;
auto c = read();
size_t esc_count = 0;
do
{
switch (c)
{
case 0:
case '\n':
{
error("not a valid quote string!");
break;
}
case '\\':
{
take();
c = read();
switch (c)
{
case '\\':
{
c = '\\';
break;
}
case '/':
{
c = '/';
break;
}
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 '"':
{
break;
}
case 'u':
{
take();
esacpe_utf8(esc_count);
c = read();
continue;
}
default:
{
error("unknown escape char!");
}
}
++esc_count;
break;
}
case '"':
{
cur_tok_.str.len = ptr_ + cur_offset_ - esc_count - cur_tok_.str.str;
take();
return;
}
}
fill_escape_char(esc_count, c);
take();
c = read();
} while (true);
}
void parser_string()
{
cur_tok_.str.str = ptr_ + cur_offset_;
take();
auto c = read();
size_t esc_count = 0;
do
{
switch (c)
{
case 0:
{
error("not a valid string!");
break;
}
case '\\':
{
take();
c = read();
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 'u':
{
take();
esacpe_utf8(esc_count);
continue;
}
default:
{
error("unknown escape char!");
}
}
++esc_count;
break;
}
case ' ':
case '\t':
case '\r':
case '\n':
case ',':
case '[':
case ']':
case ':':
case '{':
case '}':
{
cur_tok_.str.len = ptr_ + cur_offset_ - esc_count - cur_tok_.str.str;
return;
}
}
fill_escape_char(esc_count, c);
take();
c = read();
} while (true);
}
void parser_exp_pos()
{
take();
auto c = read();
do
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
exp *= 10;
exp += (c - '0');
break;
}
default:
{
for (int i = 0; i < exp; ++i)
{
cur_tok_.value.d64 *= 10.0;
}
exp = 0;
cur_tok_.str.len = ptr_ + cur_offset_ - cur_tok_.str.str;
return;
}
}
take();
c = read();
} while (1);
}
void parser_exp_neg()
{
take();
auto c = read();
do
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
exp *= 10;
exp += (c - '0');
break;
}
default:
{
for (int i = 0; i < exp; ++i)
{
cur_tok_.value.d64 *= 0.1;
}
exp = 0;
cur_tok_.str.len = ptr_ + cur_offset_ - cur_tok_.str.str;
return;
}
}
take();
c = read();
} while (1);
}
void parser_exp()
{
take();
auto c = read();
do
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
exp *= 10;
exp += (c - '0');
parser_exp_pos();
return;
}
case '-':
{
parser_exp_neg();
return;
}
default:
{
exp = 0;
cur_tok_.str.len = ptr_ + cur_offset_ - cur_tok_.str.str;
return;
}
}
} while (1);
}
void parser_number()
{
cur_tok_.str.str = ptr_ + cur_offset_;
take();
auto c = read();
do
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
if (cur_tok_.type == token::t_int)
{
cur_tok_.value.i64 *= 10;
cur_tok_.value.i64 += c - '0';
}
else if (cur_tok_.type == token::t_uint)
{
cur_tok_.value.u64 *= 10;
cur_tok_.value.u64 += c - '0';
}
else if (cur_tok_.type == token::t_number)
{
cur_tok_.value.d64 += decimal * (c - '0');
decimal *= 0.1;
}
break;
}
case '.':
{
if (cur_tok_.type == token::t_int)
{
cur_tok_.type = token::t_number;
cur_tok_.value.d64 = (double)cur_tok_.value.i64;
decimal_reset();
}
else if (cur_tok_.type == token::t_uint)
{
cur_tok_.type = token::t_number;
cur_tok_.value.d64 = (double)cur_tok_.value.u64;
decimal_reset();
}
else if (cur_tok_.type == token::t_number)
{
error("not a valid number!");
}
break;
}
case 'e':
case 'E':
{
if (cur_tok_.type == token::t_int)
{
cur_tok_.type = token::t_number;
cur_tok_.value.d64 = (double)cur_tok_.value.i64;
}
else if (cur_tok_.type == token::t_uint)
{
cur_tok_.type = token::t_number;
cur_tok_.value.d64 = (double)cur_tok_.value.u64;
}
parser_exp();
}
default:
{
cur_tok_.str.len = ptr_ + cur_offset_ - cur_tok_.str.str;
return;
}
}
take();
c = read();
} while (1);
}
public:
reader(char * ptr = nullptr, size_t len = -1)
:len_(len), ptr_(ptr)
{
if (ptr == nullptr)
{
end_mark_ = true;
}
else if (len == 0)
{
end_mark_ = true;
}
else if (ptr[0] == 0)
{
end_mark_ = true;
}
next();
}
static inline char* itoa_native(size_t val , char * buffer , size_t len)
{
buffer[len] = 0;
size_t pos = len - 1;
if (val == 0)
{
buffer[pos--] = '0';
}
while (val)
{
buffer[pos--] = (char)((val % 10) + '0');
val = val / 10;
}
++pos;
return &buffer[0] + pos;
}
inline void error(const char * message) const
{
char buffer[20];
std::string msg = "error at line :";
msg += itoa_native(cur_line_,buffer,19);
msg += " col :";
msg += itoa_native(cur_col_, buffer, 19);
msg += " msg:";
msg += message;
throw exception(msg);
}
inline token const& peek() const
{
return cur_tok_;
}
void next()
{
auto c = skip();
bool do_next = false;
cur_tok_.neg = false;
switch (c)
{
case 0:
cur_tok_.type = token::t_end;
cur_tok_.str.str = ptr_ + cur_offset_;
cur_tok_.str.len = 1;
break;
case '{':
case '}':
case '[':
case ']':
case ':':
case ',':
{
cur_tok_.type = token::t_ctrl;
cur_tok_.str.str = ptr_ + cur_offset_;
cur_tok_.str.len = 1;
take();
break;
}
case '/':
{
take();
c = read();
if (c == '/')
{
take();
c = read();
while (c != '\n' && c != 0)
{
take();
c = read();
}
do_next = true;
break;
}
else if (c == '*')
{
take();
c = read();
do
{
while (c != '*')
{
if (c == 0)
{
return;
}
take();
c = read();
}
take();
c = read();
if (c == '/')
{
take();
do_next = true;
break;
}
} while (true);
}
//error parser comment
error("not a comment!");
}
case '"':
{
cur_tok_.type = token::t_string;
parser_quote_string();
break;
}
default:
{
if (c >= '0' && c <= '9')
{
cur_tok_.type = token::t_uint;
cur_tok_.value.u64 = c - '0';
parser_number();
}
else if (c == '-')
{
cur_tok_.type = token::t_int;
cur_tok_.value.i64 = 0;
cur_tok_.neg = true;
parser_number();
}
else
{
cur_tok_.type = token::t_string;
parser_string();
}
}
}
if (do_next == false)
return;
next();
}
inline bool expect(char c)
{
return cur_tok_.str.str[0] == c;
}
};
template <typename alloc_ty>
struct ajson_string_stream
{
private:
alloc_ty alloc;
public:
enum { good, read_overflow };
char * m_header_ptr;
char * m_read_ptr;
char * m_write_ptr;
char * m_tail_ptr;
int m_status;
std::size_t m_length;
enum{ INIT_BUFF_SIZE = 1024 };
ajson_string_stream() :m_status(good), m_length(INIT_BUFF_SIZE)
{
this->m_header_ptr = this->alloc.allocate(INIT_BUFF_SIZE);
this->m_read_ptr = this->m_header_ptr;
this->m_write_ptr = this->m_header_ptr;
this->m_tail_ptr = this->m_header_ptr + m_length;
}
~ajson_string_stream()
{
this->alloc.deallocate(m_header_ptr, this->m_length);
}
inline std::size_t read(char * buffer, std::size_t len)
{
if (this->m_read_ptr + len > this->m_tail_ptr)
{
m_status = read_overflow;
return 0;
}
std::memcpy(buffer, this->m_read_ptr, len);
this->m_read_ptr += len;
return len;
}
inline std::size_t growpup(std::size_t want_size)
{
std::size_t new_size = ((want_size + INIT_BUFF_SIZE - 1) / INIT_BUFF_SIZE)*INIT_BUFF_SIZE;
std::size_t write_pos = this->m_write_ptr - this->m_header_ptr;
std::size_t read_pos = this->m_read_ptr - this->m_header_ptr;
char * temp = this->m_header_ptr;
this->m_header_ptr = this->alloc.allocate(new_size);
std::memcpy(this->m_header_ptr, temp, this->m_length);
this->alloc.deallocate(temp, this->m_length);
this->m_length = new_size;
this->m_write_ptr = this->m_header_ptr + write_pos;
this->m_read_ptr = this->m_header_ptr + read_pos;
this->m_tail_ptr = this->m_header_ptr + m_length;
return new_size;
}
inline std::size_t write(const char * buffer, std::size_t len)
{
std::size_t writed_len = this->m_write_ptr + len - this->m_header_ptr;
if (writed_len > this->m_length)
{
this->growpup(writed_len);
}
std::memcpy((void*)this->m_write_ptr, buffer, len);
this->m_write_ptr += len;
return len;
}
inline void putc(char c)
{
std::size_t writed_len = this->m_write_ptr + 1 - this->m_header_ptr;
if (writed_len > this->m_length)
{
this->growpup(writed_len);
}
*this->m_write_ptr = c;
++this->m_write_ptr;
}
inline bool bad()const{ return m_status != good; }
inline ajson_string_stream& seekp(int offset, int seek_dir)
{
switch (seek_dir)
{
case std::ios::beg:
{
if (offset < 0)
{
offset = 0;
}
this->m_write_ptr = this->m_header_ptr + offset;
break;
}
case std::ios::cur:
{
if (offset < 0)
{
offset = offset + int(this->m_write_ptr - this->m_header_ptr);
if (offset < 0)
{
offset = 0;
}
this->m_write_ptr = this->m_header_ptr + offset;
}
else
{
if (this->m_write_ptr + offset > this->m_tail_ptr)
{
this->m_write_ptr = this->m_tail_ptr;
}
}
break;
}
case std::ios::end:
{
if (offset < 0)
{
offset = offset + int(this->m_write_ptr - this->m_header_ptr);
if (offset < 0)
{
offset = 0;
}
this->m_write_ptr = this->m_header_ptr + offset;
}
break;
}
}
return *this;
}
inline void clear()
{
this->m_read_ptr = this->m_header_ptr;
this->m_write_ptr = this->m_header_ptr;
}
inline const char * data() const
{
return this->m_header_ptr;
}
std::basic_string<char, std::char_traits<char>, alloc_ty> str()
{
std::basic_string<char, std::char_traits<char>, alloc_ty> s(this->m_header_ptr, this->write_length());
return s;
}
inline ::std::size_t read_length() const
{
return this->m_read_ptr - this->m_header_ptr;
}
inline ::std::size_t write_length() const
{
return this->m_write_ptr - this->m_header_ptr;
}
};
typedef ajson_string_stream<std::allocator<char> > string_stream;
struct ajson_file_stream
{
public:
enum { good, read_overflow, file_error };
FILE * m_f;
int m_status;
enum{ INIT_AMSG_BUFF_SIZE = 1024 };
ajson_file_stream(const char * filename) :m_f(NULL), m_status(good)
{
this->m_f = std::fopen(filename, "w");
if (NULL == this->m_f)
{
this->m_status = file_error;
}
}
~ajson_file_stream()
{
if (this->m_f)
{
fclose(m_f);
}
}
inline std::size_t read(char * buffer, std::size_t len)
{
std::size_t rlen = std::fread(buffer, len, 1, this->m_f);
return rlen;
}
inline std::size_t write(const char * buffer, std::size_t len)
{
std::size_t wlen = std::fwrite(buffer, len, 1, this->m_f);
return wlen;
}
inline void putc(char c)
{
std::fputc(c, this->m_f);
}
inline bool bad(){ return m_status != good; }
inline int seekp(int offset, int seek_dir)
{
return std::fseek(this->m_f, offset, seek_dir);
}
inline void clear()
{
std::fseek(this->m_f, 0, SEEK_SET);
}
};
template<typename stream_ty>
struct lite_write
{
stream_ty& s_;
lite_write(stream_ty& s) :s_(s){}
inline void write_bool(bool v)
{
if (v)
s_.putc('1');
else
s_.putc('0');
}
inline void write_liter(char const * str, size_t len)
{
s_.write(str, len);
}
inline uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte)
{
static const std::array<uint8_t, 400> utf8d =
{
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
}
};
const uint8_t type = utf8d[byte];
codep = (state != 0)
? (byte & 0x3fu) | (codep << 6)
: static_cast<uint32_t>(0xff >> type) & (byte);
state = utf8d[256u + state * 16u + type];
return state;
}
inline void write_str(char const * str, size_t len)
{
static char const * hex_table = "0123456789ABCDEF";
putc('"');
char const * ptr = str;
char const * end = ptr + len;
uint32_t codepoint;
uint8_t state = 0;
while (ptr < end)
{
char c = *ptr;
if (c == 0)
break;
++ptr;
decode(state, codepoint, (uint8_t)c);
if( state == 0)
{
switch (codepoint)
{
case 0x08: // backspace
{
putc('\\');
putc('b');
break;
}
case 0x09: // horizontal tab
{
putc('\\');
putc('t');
break;
}
case 0x0A: // newline
{
putc('\\');
putc('n');
break;
}
case 0x0C: // formfeed
{
putc('\\');
putc('f');
break;
}
case 0x0D: // carriage return
{
putc('\\');
putc('r');
break;
}
case 0x22: // quotation mark
{
putc('\\');
putc('\"');
break;
}
case 0x5C: // reverse solidus
{
putc('\\');
putc('\\');
break;
}
default:
{
// escape control characters (0x00..0x1F) or, if
// ensure_ascii parameter is used, non-ASCII characters
if ((codepoint <= 0x1F) || (codepoint >= 0x7F))
{
if (codepoint <= 0xFFFF)
{
unsigned char c1 = (uint8_t)(codepoint >> 8);
unsigned char c2 = (uint8_t)codepoint;
putc('\\');
putc('u');
putc(hex_table[(c1) >> 4]);
putc(hex_table[(c1)& 0xF]);
putc(hex_table[(c2) >> 4]);
putc(hex_table[(c2)& 0xF]);
}
else
{
unsigned char c1 = (uint8_t)(codepoint >> 24);
unsigned char c2 = (uint8_t)(codepoint >> 16);
unsigned char c3 = (uint8_t)(codepoint >> 8);
unsigned char c4 = (uint8_t)codepoint;
putc('\\');
putc('u');
putc(hex_table[(c1) >> 4]);
putc(hex_table[(c1)& 0xF]);
putc(hex_table[(c2) >> 4]);
putc(hex_table[(c2)& 0xF]);
putc('\\');
putc('u');
putc(hex_table[(c3) >> 4]);
putc(hex_table[(c3)& 0xF]);
putc(hex_table[(c4) >> 4]);
putc(hex_table[(c4)& 0xF]);
}
}
else
{
putc(c);
}
break;
}
}
}
}
putc('"');
}
inline void putc(char c)
{
s_.putc(c);
}
};
template<typename ty, class enable = void>
struct json_impl;
inline bool is_true(token const& tok)
{
char const * ptr = tok.str.str;
if (tok.str.len == 4)
{
return (ptr[0] == 't' || ptr[0] == 'T') &&
(ptr[1] == 'r' || ptr[1] == 'R') &&
(ptr[2] == 'u' || ptr[2] == 'U') &&
(ptr[3] == 'e' || ptr[3] == 'E');
}
return false;
}
template<>
struct json_impl < bool, void >
{
static inline void read(reader& rd, bool& val)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
{
val = is_true(tok);
break;
}
case token::t_int:
{
val = (tok.value.i64 != 0);
break;
}
case token::t_uint:
{
val = (tok.value.u64 != 0);
break;
}
case token::t_number:
{
val = (tok.value.d64 != 0.0);
break;
}
default:
{
rd.error("not a valid bool.");
}
}
rd.next();
}
template<typename write_ty>
static inline void write(write_ty& wt, bool const& val)
{
wt.write_bool(val);
}
template<typename write_ty>
static inline void write_key(write_ty& wt, bool const& val)
{
wt.putc('"');
wt.write_bool(val);
wt.putc('"');
}
};
template<typename ty>
struct json_impl < ty,
typename std::enable_if <detail::is_signed_intergral_like<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
{
if (is_true(tok))
{
val = 1;
}
else
{
int64_t temp = std::strtoll(tok.str.str, nullptr, 10);
val = static_cast<ty>(temp);
}
break;
}
case token::t_int:
{
val = static_cast<ty>(tok.value.i64);
if (tok.neg)
val = -val;
break;
}
case token::t_uint:
{
val = static_cast<ty>(tok.value.u64);
break;
}
case token::t_number:
{
val = static_cast<ty>(tok.value.d64);
if (tok.neg)
val = -val;
break;
}
default:
{
rd.error("not a valid signed integral like number.");
}
}
rd.next();
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
int64_t temp = (int64_t)val;
char buffer[64];
buffer[63] = 0;
size_t len = 64;
size_t pos = 62;
bool Sig = false;
if (temp < 0)
{
Sig = true;
temp *= -1;
}
if (temp == 0)
{
buffer[pos--] = '0';
}
while (temp)
{
buffer[pos--] = (char)((temp % 10) + '0');
temp = temp / 10;
}
if (Sig)
{
buffer[pos--] = '-';
}
++pos;
wt.write_liter(buffer + pos, len - pos - 1);
}
template<typename write_ty>
static inline void write_key(write_ty& wt, ty const& val)
{
wt.putc('"');
write<write_ty>(wt, val);
wt.putc('"');
}
};
template<typename ty>
struct json_impl < ty,
typename std::enable_if <detail::is_unsigned_intergral_like<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
{
if (is_true(tok))
{
val = 1;
}
else
{
uint64_t temp = std::strtoull(tok.str.str, nullptr, 10);
val = static_cast<ty>(temp);
}
break;
}
case token::t_int:
{
if (tok.neg)
{
rd.error("assign a negative signed integral to unsigned integral number.");
}
val = static_cast<ty>(tok.value.i64);
break;
}
case token::t_uint:
{
val = static_cast<ty>(tok.value.u64);
break;
}
case token::t_number:
{
if (tok.neg)
{
rd.error("assign a negative float point to unsigned integral number.");
}
val = static_cast<ty>(tok.value.d64);
break;
}
default:
{
rd.error("not a valid unsigned integral like number.");
}
}
rd.next();
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
uint64_t temp = (uint64_t)val;
char buffer[64];
buffer[63] = 0;
size_t len = 64;
size_t pos = 62;
if (temp == 0)
{
buffer[pos--] = '0';
}
while (temp)
{
buffer[pos--] = (char)((temp % 10) + '0');
temp = temp / 10;
}
++pos;
wt.write_liter(buffer + pos, len - pos - 1);
}
template<typename write_ty>
static inline void write_key(write_ty& wt, ty const& val)
{
wt.putc('"');
write<write_ty>(wt, val);
wt.putc('"');
}
};
template<typename ty>
struct json_impl < ty,
typename std::enable_if <std::is_enum<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
typedef typename std::underlying_type<ty>::type raw_type;
json_impl<raw_type>::read(rd, (raw_type&)val);
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
typedef typename std::underlying_type<ty>::type raw_type;
json_impl<raw_type>::template write(wt, val);
}
template<typename write_ty>
static inline void write_key(write_ty& wt, ty const& val)
{
typedef typename std::underlying_type<ty>::type raw_type;
json_impl<raw_type>::template write_key(wt, val);
}
};
template<typename ty>
struct json_impl < ty,
typename std::enable_if <std::is_floating_point<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
{
double temp = std::strtold(tok.str.str, nullptr);
val = static_cast<ty>(temp);
break;
}
case token::t_int:
{
val = static_cast<ty>(tok.value.i64);
if (tok.neg)
val = -val;
break;
}
case token::t_uint:
{
val = static_cast<ty>(tok.value.u64);
break;
}
case token::t_number:
{
val = static_cast<ty>(tok.value.d64);
if (tok.neg)
val = -val;
break;
}
default:
{
rd.error("not a valid float point number.");
}
}
rd.next();
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
char buffer[64] = { 0 };
#ifdef _MSC_VER
_gcvt_s(buffer, 63 , val, 8);
#else
gcvt(val, 62, buffer);
#endif // MSVC
size_t len = std::strlen(buffer);
if (buffer[len - 1] == '.')
{
buffer[len - 1] = '\0';
-- len;
}
wt.write_liter(buffer, len);
}
template<typename write_ty>
static inline void write_key(write_ty& wt, ty const& val)
{
wt.putc('"');
write<write_ty>(wt, val);
wt.putc('"');
}
};
template<typename ty>
struct json_impl < ty,
typename std::enable_if <detail::is_stdstring<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
auto& tok = rd.peek();
if (tok.type == token::t_string)
{
val.assign(tok.str.str, tok.str.len);
}
else
{
rd.error("not a valid string.");
}
rd.next();
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
wt.write_str(val.data(), val.length());
}
template<typename write_ty>
static inline void write_key(write_ty& wt, ty const& val)
{
write<write_ty>(wt, val);
}
};
inline void char_array_read(reader& rd, char * val, size_t N)
{
auto& tok = rd.peek();
if (tok.type == token::t_string)
{
size_t len = tok.str.len;
if (len > N)
len = N;
std::memcpy(val, tok.str.str, len);
if (len < N)
val[len] = 0;
}
else
{
rd.error("not a valid string.");
}
rd.next();
}
template<typename write_ty>
inline void char_array_write(write_ty& wt, const char * val, size_t N)
{
wt.write_str(val, N);
}
template<size_t N>
struct json_impl <char[N]>
{
static inline void read(reader& rd, char * val)
{
char_array_read(rd, val, N);
}
template<typename write_ty>
static inline void write(write_ty& wt, const char * val)
{
char_array_write(wt, val, N);
}
};
template<size_t N>
struct json_impl <const char[N] >
{
static inline void read(reader& rd, char * val)
{
char_array_read(rd, val, N);
}
template<typename write_ty>
static inline void write(write_ty& wt, const char * val)
{
char_array_write(wt, val, N);
}
};
template<typename T>
inline void array_read(reader& rd, T * val, size_t N)
{
if (rd.expect('[') == false)
{
rd.error("array must start with [.");
}
rd.next();
auto tok = &rd.peek();
size_t count = 0;
while (tok->str.str[0] != ']')
{
if (count < N)
{
json_impl<T>::read(rd, val[count]);
}
else
{
rd.error("array overflow.");
}
++count;
tok = &rd.peek();
if (tok->str.str[0] == ',')
{
rd.next();
tok = &rd.peek();
continue;
}
else if (tok->str.str[0] == ']')
{
break;
}
else
{
rd.error("no valid array!");
}
}
rd.next();
return;
}
template<typename T, typename write_ty>
inline void array_write(write_ty& wt, const T * val, int N)
{
wt.putc('[');
int last = N - 1;
for (int i = 0; i < N; ++i)
{
json_impl<T>::template write(wt, val[i]);
if (i < last)
wt.putc(',');
}
wt.putc(']');
}
template<typename T, size_t N>
struct json_impl <T[N]>
{
static inline void read(reader& rd, T * val)
{
array_read(rd, val, N);
}
template<typename write_ty>
static inline void write(write_ty& wt, const T * val)
{
array_write(wt, val, N);
}
};
template<typename T, size_t N>
struct json_impl < const T[N]>
{
static inline void read(reader& rd, T * val)
{
array_read(rd, val, N);
}
template<typename write_ty>
static inline void write(write_ty& wt, const T * val)
{
array_write(wt, val, N);
}
};
template<typename ty>
inline typename std::enable_if <detail::is_emplace_back_able<ty>::value, void>::type
emplace_back(ty& val)
{
val.emplace_back();
}
template<typename ty>
inline typename std::enable_if <detail::is_template_instant_of<std::queue, ty>::value, void>::type
emplace_back(ty& val)
{
val.emplace();
}
template<typename ty>
struct json_impl < ty,
typename std::enable_if <detail::is_sequence_container<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
if (rd.expect('[') == false)
{
rd.error("array must start with [.");
}
rd.next();
auto tok = &rd.peek();
while (tok->str.str[0] != ']')
{
::ajson::emplace_back(val);
json_impl<typename ty::value_type>::read(rd, val.back());
tok = &rd.peek();
if (tok->str.str[0] == ',')
{
rd.next();
tok = &rd.peek();
continue;
}
else if (tok->str.str[0] == ']')
{
break;
}
else
{
rd.error("no valid array!");
}
}
rd.next();
return;
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
wt.putc('[');
auto sz = val.size();
for (auto& i : val)
{
json_impl<typename ty::value_type>::template write(wt, i);
if (sz-- > 1)
wt.putc(',');
}
wt.putc(']');
}
};
template<typename ty>
struct json_impl < ty,
typename std::enable_if <detail::is_associat_container<ty>::value>::type >
{
static inline void read(reader& rd, ty& val)
{
if (rd.expect('{') == false)
{
rd.error("object must start with {!");
}
rd.next();
auto tok = &rd.peek();
while (tok->str.str[0] != '}')
{
typename ty::key_type key;
typename ty::mapped_type value;
json_impl<typename ty::key_type>::read(rd, key);
if (rd.expect(':') == false)
{
rd.error("invalid object!");
}
rd.next();
json_impl<typename ty::mapped_type>::read(rd, value);
val[key] = value;
tok = &rd.peek();
if (tok->str.str[0] == ',')
{
rd.next();
tok = &rd.peek();
continue;
}
else if (tok->str.str[0] == '}')
{
break;
}
else
{
rd.error("no valid object!");
}
}
rd.next();
return;
}
template<typename write_ty>
static inline void write(write_ty& wt, ty const& val)
{
wt.putc('{');
auto sz = val.size();
for (auto& i : val)
{
json_impl<typename ty::key_type>::template write_key(wt, i.first);
wt.putc(':');
json_impl<typename ty::mapped_type>::template write(wt, i.second);
if (sz-- > 1)
wt.putc(',');
}
wt.putc('}');
}
};
inline void skip_array(reader& rd);
inline void skip_object(reader& rd);
inline void skip(reader& rd)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
case token::t_int:
case token::t_uint:
case token::t_number:
{
rd.next();
return;
}
case token::t_ctrl:
{
if (tok.str.str[0] == '[')
{
rd.next();
skip_array(rd);
return;
}
else if (tok.str.str[0] == '{')
{
rd.next();
skip_object(rd);
return;
}
}
case token::t_end:
{
return;
}
}
rd.error("invalid json document!");
}
inline void skip_array(reader& rd)
{
auto tok = &rd.peek();
while (tok->str.str[0] != ']')
{
skip(rd);
tok = &rd.peek();
if (tok->str.str[0] == ',')
{
rd.next();
tok = &rd.peek();
continue;
}
}
rd.next();
}
inline void skip_key(reader& rd)
{
auto& tok = rd.peek();
switch (tok.type)
{
case token::t_string:
case token::t_int:
case token::t_uint:
case token::t_number:
{
rd.next();
return;
}
default:
break;
}
rd.error("invalid json document!");
}
inline void skip_object(reader& rd)
{
auto tok = &rd.peek();
while (tok->str.str[0] != '}')
{
skip_key(rd);
tok = &rd.peek();
if (tok->str.str[0] == ':')
{
rd.next();
skip(rd);
//rd.next();
tok = &rd.peek();
}
else
{
rd.error("invalid json document!");
}
if (tok->str.str[0] == ',')
{
rd.next();
tok = &rd.peek();
continue;
}
}
rd.next();
}
template<typename head, typename... args>
inline int read_members(reader& rd, detail::string_ref const * member_ptr,
detail::string_ref const& member, size_t pos, head& h, args& ... args_);
inline int read_members(reader&, detail::string_ref const*,
detail::string_ref const&, size_t);
template<typename head, typename... args>
struct read_members_impl
{
static inline int read(reader& rd, detail::string_ref const * member_ptr,
detail::string_ref const& member, size_t pos, head& val, args& ... args_)
{
if (member_ptr[pos] == member)
{
json_impl<head>::read(rd, val);
return 1;
}
if (sizeof...(args))
return read_members(rd, member_ptr, member, pos + 1, args_...);
return 0;
}
};
template<typename head, typename... args>
inline int read_members(reader& rd, detail::string_ref const * member_ptr,
detail::string_ref const& member, size_t pos, head& h, args& ... args_)
{
return read_members_impl<head, args...>::read(rd, member_ptr,
member, pos, h, args_...);
}
inline int read_members(reader&, detail::string_ref const*,
detail::string_ref const&, size_t)
{
return 0;
}
template<typename ty>
inline void load_from_buff(ty& val, char * buff, size_t len = -1)
{
typedef typename std::remove_cv<ty>::type rty;
reader rd(buff, len);
json_impl<rty>::read(rd, val);
}
template<typename ty>
inline void load_from_file(ty& val, const char * filename)
{
struct file_guard
{
std::FILE * f_;
file_guard(std::FILE * f):f_(f){}
~file_guard()
{
if (f_)
std::fclose(f_);
}
};
std::FILE * f = std::fopen(filename, "rb");
if (nullptr == f)
{
std::string errmsg = "can't open file:";
throw std::logic_error( errmsg + filename);
}
file_guard fg(f);
std::fseek(f, 0, SEEK_END);
size_t sz = std::ftell(f);
std::fseek(f, 0, SEEK_SET);
struct buffer_guard
{
char * buff_ = nullptr;
buffer_guard(char * buff) :buff_(buff){}
~buffer_guard()
{
if (buff_)
delete [] buff_;
}
};
char * buffer = new char[sz+1];
buffer_guard bg(buffer);
sz = std::fread(buffer, 1, sz, f);
buffer[sz] = 0;
if (sz >= 3)
{
auto ubuffer = reinterpret_cast<unsigned char*>(buffer);
if (ubuffer[0] == 0xEFU && ubuffer[1] == 0xBBU && ubuffer[2] == 0xBFU)
{
buffer += 3;
sz -= 3;
}
}
reader rd(buffer, sz);
typedef typename std::remove_cv<ty>::type rty;
json_impl<rty>::read(rd, val);
}
template<typename write_ty, typename head, typename... args>
inline void write_members(write_ty& wt, detail::string_ref const * member_ptr,
size_t pos, head& h, args& ... args_);
template<typename write_ty>
inline void write_members(write_ty&, detail::string_ref *, size_t);
template<typename write_ty, typename head, typename... args>
struct write_members_impl
{
static inline void write(write_ty& wt, detail::string_ref const * member_ptr,
size_t pos, head const& val, args const& ... args_)
{
wt.write_str(member_ptr[pos].str, member_ptr[pos].len);
wt.putc(':');
json_impl<head>::template write(wt, val);
if (sizeof...(args))
{
wt.putc(',');
write_members(wt, member_ptr, pos + 1, args_...);
}
}
};
template<typename write_ty, typename head, typename... args>
inline void write_members(write_ty& wt, detail::string_ref const* member_ptr,
size_t pos, head const& h, args& ... args_)
{
return write_members_impl<write_ty, head, args...>::write(wt, member_ptr,
pos, h, args_...);
}
template<typename write_ty>
inline void write_members(write_ty&, detail::string_ref const*, size_t)
{
return;
}
template<typename ty, typename stream_ty, class write_tp = lite_write<stream_ty> >
inline void save_to(stream_ty& ss, ty& val)
{
typedef typename std::remove_cv<ty>::type rty;
write_tp wt(ss);
json_impl<rty>::template write(wt, val);
}
template<typename ty, typename stream_ty = ajson_file_stream, class write_tp = lite_write<stream_ty> >
inline void save_to_file(ty& val, char const * filename)
{
stream_ty fs(filename);
save_to<ty, stream_ty, write_tp>(fs, val);
}
}
#define AJSON(TYPE,...) \
namespace ajson\
{\
template<>\
struct json_impl < TYPE , void >\
{\
struct json_helper : public TYPE\
{\
inline void read_(reader& rd)\
{\
auto& fields = this_field_list();\
if (rd.expect('{') == false){ rd.error("read object must start with {!"); }\
rd.next();\
if (rd.expect('}'))\
return;\
auto mber = rd.peek();\
do\
{\
if (mber.type != token::t_string){ rd.error("object key must be string"); }\
rd.next();\
if (rd.expect(':') == false){ rd.error("invalid json document!"); }\
rd.next();\
if (read_members(rd, &fields[0], mber.str, 0,__VA_ARGS__) == 0)\
{\
skip(rd);\
}\
if (rd.expect('}'))\
{\
rd.next();\
return;\
}\
else if (rd.expect(','))\
{\
rd.next();\
mber = rd.peek();\
continue;\
}\
rd.error("invalid json document!");\
} while (true);\
}\
template<typename write_ty>\
inline void write_(write_ty& wt) const\
{\
auto& fields = this_field_list();\
wt.putc('{');\
::ajson::write_members(wt, &fields[0], 0,__VA_ARGS__);\
wt.putc('}');\
}\
};\
static inline detail::field_list& this_field_list()\
{\
static auto fields = detail::split_fields(STRINGFY_LIST(__VA_ARGS__));\
return fields;\
}\
static inline void read(reader& rd, TYPE& v)\
{\
reinterpret_cast<json_helper &>(v).read_(rd);\
}\
template<typename write_ty>\
static inline void write(write_ty& wt, TYPE const& v)\
{\
reinterpret_cast<json_helper const &>(v).write_(wt);\
}\
};\
}
#include <iostream>
#include <string>
#include "ajson.hpp"
using namespace std;
using namespace ajson;
struct demo
{
string hello;
string world;
};
AJSON(demo,hello,world)
int main(int argc, char * argv[])
{
char * buff = "{\"hello\" : \"Hello\", \"world\" : \"world.\"}";
demo the_demo;
load_from_buff(the_demo,buff);
cout << the_demo.hello << " " << the_demo.world << std::endl;
cin.get();
return 0;
}
#include <iostream>
#include <string>
#include <vector>
#include "ajson.hpp"
using namespace std;
using namespace ajson;
struct Education
{
string School;
double GPA;
Education():GPA(0.0){}
Education(const string& school , double gpa)
:School(school),GPA(gpa)
{
}
};
struct Person
{
string Name;
int Age;
vector<Education> Educations;
};
AJSON(Education , School , GPA)
AJSON(Person, Name , Age , Educations)
int main(int argc, char * argv[])
{
Person person;
person.Name = "Bob";
person.Age = 28;
person.Educations.push_back(Education("MIT",600));
string_stream ss;
save_to(ss,person);
std::cout << ss.str() << std::endl;
cin.get();
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment