/*
* json: Reading and Writing JSON
*/
#include "gap_all.h" // GAP headers
#include "gap-functions.h"
#include "picojson/picojson.h"
#include "picojson/gap-traits.h"
static Obj _GapToJsonStreamInternal;
typedef picojson::value_t<gap_type_traits> gmp_value;
Obj JsonToGap(const gmp_value& v)
{
if (v.is<picojson::null>()) {
return Fail;
} else if (v.is<bool>()) {
if(v.get<bool>())
return True;
else
return False;
} else if (v.is<gap_val>()) {
return v.get<gap_val>().obj;
} else if (v.is<std::string>()) {
Obj str;
const char* c_str = v.get<std::string>().c_str();
Int len = v.get<std::string>().size();
str = NEW_STRING(len);
memcpy(CHARS_STRING(str), c_str, len);
return str;
} else if (v.is<gmp_value::array>()) {
const gmp_value::array& a = v.get<gmp_value::array>();
Obj list = NEW_PLIST(T_PLIST_DENSE, a.size());
SET_LEN_PLIST(list, a.size());
for(int i = 1; i <= a.size(); ++i)
{
Obj val = JsonToGap(a[i-1]);
SET_ELM_PLIST(list, i, val);
CHANGED_BAG(list);
}
return list;
} else if (v.is<gmp_value::object>()) {
const gmp_value::object& o = v.get<gmp_value::object>();
Obj rec = NEW_PREC(0);
for (gmp_value::object::const_iterator i = o.begin(); i != o.end(); ++i) {
Obj res = JsonToGap(i->second);
AssPRec(rec, RNamName(i->first.c_str()), res);
CHANGED_BAG(rec);
}
return rec;
}
return Fail;
}
// Add function prototype missing from string.h
extern Obj CopyToStringRep(Obj string);
static Int numberOfBytes(UInt c)
{
if (c < 128)
return 1;
if (c < 224)
return 2;
if (c < 240)
return 3;
return 4;
}
static Int getChar(Obj list, Int pos)
{
Obj c = ELM_LIST(list, pos);
if (c == NULL)
return 0;
else
return *((UChar *)ADDR_OBJ(c));
}
static Int getUTF8Char(Obj list, Int * basepos)
{
Int pos = *basepos;
UInt val = getChar(list, pos);
UInt singlebyte_val = val;
Int len = numberOfBytes(val);
pos++;
if (len == 1) {
*basepos = pos;
return val;
}
switch (len) {
case 2:
val = val & 0x3F;
if (val & 0x20)
goto invalid;
break;
case 3:
val = val & 0x1F;
if (val & 0x10)
goto invalid;
break;
case 4:
val = val & 0x0F;
if (val & 0x08)
goto invalid;
break;
default:
abort();
}
val = val & 0x3F;
for (Int i = 1; i < len; ++i) {
UInt c = getChar(list, pos);
if ((c & 0xC0) != 0x80)
goto invalid;
val = (val << 6) | (c & 0x3F);
pos++;
}
// Too high
if (val > 0x10ffff)
goto invalid;
// UTF-16 Surrogate pair
if (val >= 0xd800 && val <= 0xdfff)
goto invalid;
*basepos = pos;
return val;
// Hope this is Latin-1
invalid:
*basepos = *basepos + 1;
return singlebyte_val;
}
static UChar * outputUnicodeChar(UChar * s, UInt val)
{
if (val <= 0x7f)
*(s++) = val;
else if (val <= 0x7ff) {
*(s++) = (0xc0 | ((val >> 6) & 0x1f));
*(s++) = (0x80 | (val & 0x3f));
}
else if (val <= 0xffff) {
*(s++) = (0xe0 | ((val >> 12) & 0x0f));
*(s++) = (0x80 | ((val >> 6) & 0x3f));
*(s++) = (0x80 | (val & 0x3f));
}
else {
*(s++) = (0xf0 | ((val >> 18) & 0x07));
*(s++) = (0x80 | ((val >> 12) & 0x3f));
*(s++) = (0x80 | ((val >> 6) & 0x3f));
*(s++) = (0x80 | (val & 0x3f));
}
return s;
}
static Obj FuncJSON_ESCAPE_STRING(Obj self, Obj param)
{
if(!IS_STRING(param))
{
ErrorQuit("Input to JsonEscapeString must be a string", 0, 0);
}
Int needEscaping = 0;
Int lenString = LEN_LIST(param);
for (Int i = 1; i <= lenString && needEscaping == 0; ++i) {
Obj gapchar = ELMW_LIST(param, i);
UChar u = *((UChar*)ADDR_OBJ(gapchar));
switch(u)
{
case '\\': case '"': case '/': case '\b':
case '\t': case '\n': case '\f': case '\r':
needEscaping = 1;
break;
default:
if (u < ' ' || u >= 128)
needEscaping = 1;
}
}
if (needEscaping == 0)
return param;
// Massively over-long string
Obj copy = NEW_STRING(lenString * 6 + 7);
UChar * base = CHARS_STRING(copy);
UChar * out = base;
Int i = 1;
while (i <= lenString) {
Int u = getUTF8Char(param, &i);
switch(u)
{
case '\\': case '"': case '/':
out[0] = '\\';
out[1] = u;
out += 2;
break;
#define ESCAPE_CASE(x,y) case x: out[0] = '\\'; out[1] = y; out += 2; break;
ESCAPE_CASE('\b', 'b');
ESCAPE_CASE('\t', 't');
ESCAPE_CASE('\n', 'n');
ESCAPE_CASE('\f', 'f');
ESCAPE_CASE('\r', 'r');
#undef ESCAPE_CASE
default:
if(u < ' ')
{
snprintf((char*)out,7, "\\u%04X",(unsigned)u);
out += 6;
}
else
{
out = outputUnicodeChar(out, u);
}
}
}
SET_LEN_STRING(copy, out - base);
ResizeBag(copy, SIZEBAG_STRINGLEN(out - base));
return copy;
}
static Obj FuncGAP_LIST_TO_JSON_STRING(Obj self, Obj string, Obj stream, Obj list) {
RequireDenseList("list", list);
Int len = LEN_LIST(list);
char buf[50] = {};
// Call this at the start
ConvString(string);
AppendCStr(string, "[", 1);
for(int i = 1; i <= len; ++i) {
if(i != 1) {
AppendCStr(string, ",", 1);
}
Obj val = ELM_LIST(list, i);
if(IS_INTOBJ(val)) {
snprintf(buf, sizeof(buf), "%ld", INT_INTOBJ(val));
AppendCStr(string, buf, strlen(buf));
} else if(IS_LIST(val) && !(IS_STRING(val))) {
FuncGAP_LIST_TO_JSON_STRING(self, string, stream, val);
} else {
CALL_2ARGS(_GapToJsonStreamInternal, stream, val);
// Convert back in case this call modified the string
ConvString(string);
}
}
AppendCStr(string, "]", 1);
return 0;
}
// WARNING: This class is only complete enough to work with
// picojson's iterator support.
struct GapStreamToInputIterator
{
Obj stream;
enum State {
notread, failed, cached
};
State state;
char store;
GapStreamToInputIterator()
: stream(0), state(notread), store(0)
{ }
GapStreamToInputIterator(Obj s)
: stream(s), state(notread), store(0)
{ }
GapStreamToInputIterator(const GapStreamToInputIterator& gstii)
: stream(gstii.stream), state(gstii.state), store(gstii.store)
{ }
char operator*()
{
if(state == cached)
return store;
if(state == failed)
return 0;
Obj val = callGAPFunction(ReadByteFunction, stream);
if(val == Fail)
{
state = failed;
return 0;
}
else
{
state = cached;
store = INT_INTOBJ(val);
return store;
}
}
void operator++()
{
if(state == failed)
return;
// skip character
if(state == notread)
{
*(*this);
}
state = notread;
}
friend bool operator==(GapStreamToInputIterator& lhs, GapStreamToInputIterator& rhs)
{ return (lhs.state == failed) == (rhs.state == failed); }
};
static GapStreamToInputIterator endGapStreamIterator()
{
GapStreamToInputIterator g;
g.state = GapStreamToInputIterator::failed;
return g;
}
// making an object of this type will ensure when the scope is exited
// we clean up any GAP objects we cached
struct CleanupCacheGuard
{
~CleanupCacheGuard()
{ callGAPFunction(ClearGAPCacheFunction); }
};
static Obj FuncJSON_STREAM_TO_GAP(Obj self, Obj stream)
{
JSON_setupGAPFunctions();
CleanupCacheGuard ccg;
typedef picojson::value_t<gap_type_traits> gmp_value;
gmp_value v;
std::string err;
bool ungotc_check = false;
picojson::parse(v, GapStreamToInputIterator(stream), endGapStreamIterator(), &err, &ungotc_check);
if (! err.empty()) {
ErrorQuit(err.c_str(), 0, 0);
return Fail;
}
return JsonToGap(v);
}
// WARNING: This class is only complete enough to work with
// picojson's iterator support.
struct GapStringToInputIterator {
Obj obj;
size_t pos;
GapStringToInputIterator() : obj(0), pos(0)
{
}
GapStringToInputIterator(Obj s, size_t startpos = 0)
: obj(s), pos(startpos)
{
}
GapStringToInputIterator(const GapStringToInputIterator & gstii)
: obj(gstii.obj), pos(gstii.pos)
{
}
char operator*()
{
return CSTR_STRING(obj)[pos];
}
void operator++()
{
pos++;
}
friend bool operator==(GapStringToInputIterator & lhs,
GapStringToInputIterator & rhs)
{
return (lhs.pos == rhs.pos);
}
};
static GapStringToInputIterator endGapStringIterator(Obj obj)
{
GapStringToInputIterator g(obj, GET_LEN_STRING(obj));
return g;
}
static Obj FuncJSON_STRING_TO_GAP(Obj self, Obj param)
{
JSON_setupGAPFunctions();
CleanupCacheGuard ccg;
if(!IS_STRING(param))
{
ErrorQuit("Input to JsonToGap must be a string", 0, 0);
}
Obj real_string = param;
if(!IS_STRING_REP(param))
{
real_string = CopyToStringRep(param);
}
typedef picojson::value_t<gap_type_traits> gmp_value;
gmp_value v;
std::string err;
bool ungotc_check = false;
GapStringToInputIterator endparse = picojson::parse(
v, GapStringToInputIterator(real_string),
endGapStringIterator(real_string), &err, &ungotc_check);
// char* res = picojson::parse(v, ptr, ptrend, &err, &ungotc_check);
if (! err.empty()) {
ErrorQuit(err.c_str(), 0, 0);
return Fail;
}
// Check end of string
const char * ptr = CSTR_STRING(real_string);
const char * ptrend = ptr + strlen(ptr);
// Extra position in the string
const char * res = ptr + endparse.pos;
// Woo, this is horrible. The parser steps one character too far
// if the only thing parsed is a number. So step back.
if(ungotc_check) {
res--;
}
for(; res != ptrend; res++)
{
if(!(isspace(*res)) && *res)
{
ErrorMayQuit("Failed to parse end of string: '%s'",(Int)res,0);
return Fail;
}
}
return JsonToGap(v);
}
// Table of functions to export
static StructGVarFunc GVarFuncs [] = {
GVAR_FUNC_1ARGS(JSON_STRING_TO_GAP, string),
GVAR_FUNC_1ARGS(JSON_ESCAPE_STRING, string),
GVAR_FUNC_1ARGS(JSON_STREAM_TO_GAP, string),
GVAR_FUNC_3ARGS(GAP_LIST_TO_JSON_STRING, string, stream, list),
{ 0 } /* Finish with an empty entry */
};
/******************************************************************************
*F InitKernel( <module> ) . . . . . . . . initialise kernel data structures
*/
static Int InitKernel( StructInitInfo *module )
{
/* init filters and functions */
InitHdlrFuncsFromTable( GVarFuncs );
ImportGVarFromLibrary("_GapToJsonStreamInternal", &_GapToJsonStreamInternal);
/* return success */
return 0;
}
/******************************************************************************
*F InitLibrary( <module> ) . . . . . . . initialise library data structures
*/
static Int InitLibrary( StructInitInfo *module )
{
/* init filters and functions */
InitGVarFuncsFromTable( GVarFuncs );
/* return success */
return 0;
}
/******************************************************************************
*F InitInfopl() . . . . . . . . . . . . . . . . . table of init functions
*/
static StructInitInfo module = {
/* type = */ MODULE_DYNAMIC,
/* name = */ "json",
/* revision_c = */ 0,
/* revision_h = */ 0,
/* version = */ 0,
/* crc = */ 0,
/* initKernel = */ InitKernel,
/* initLibrary = */ InitLibrary,
/* checkInit = */ 0,
/* preSave = */ 0,
/* postSave = */ 0,
/* postRestore = */ 0
};
extern "C"
StructInitInfo * Init__Dynamic ( void )
{
return &module;
}
¤ Dauer der Verarbeitung: 0.5 Sekunden
¤
*© Formatika GbR, Deutschland
|
|