1
1
s-lang/modules/json-module.c

1142 строки
28 KiB
C

/* -*- mode: C; mode: fold -*- */
/*
Copyright (C) 2013-2017,2018 John E. Davis, Manfred Hanke
This file is part of the S-Lang Library.
The S-Lang Library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The S-Lang Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#define _BSD_SOURCE 1 /* to get strtoll */
#define _DEFAULT_SOURCE 1
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <slang.h>
SLANG_MODULE(json);
#define JSON_MODULE_VERSION_NUMBER 300
static char* json_module_version_string = "pre-0.3.0";
/*{{{ JSON grammar based upon json.org & ietf.org/rfc/rfc4627.txt
*
* object:
* { }
* { members }
* members:
* pair
* pair , members
* pair:
* string : value
*
* array:
* [ ]
* [ elements ]
* elements:
* value
* value , elements
*
* value:
* string
* number
* object
* array
* true
* false
* null
*
* Since a pair consists of a (arbitrary string) keyword and a value,
* a JSON object maps onto a structure (Struct_Type) in S-Lang.
*
* Since a JSON array is a heterogenous collection of elements,
* these map onto a list (List_Type) in S-Lang.
*
* Since S-Lang has no separate boolean type,
* true|false are represented as 1|0 of UChar_Type.
*/
#define BEGIN_ARRAY '['
#define BEGIN_OBJECT '{'
#define END_ARRAY ']'
#define END_OBJECT '}'
#define VALUE_SEPARATOR ','
#define NAME_SEPARATOR ':'
#define STRING_DELIMITER '"'
#define ESCAPE_CHARACTER '\\'
/*}}}*/
static int Json_Parse_Error = -1;
static int Json_Invalid_Json_Error = -1;
#define DESCRIBE_CHAR_FMT "'%c' = 0x%02X"
#define DESCRIBE_CHAR(ch) ch, (unsigned int)(unsigned char)ch
static int Max_Recursion_Depth = 100;
typedef struct
{
char *ptr; /* points into input string */
int depth;
}
Parse_Type;
static void skip_white (Parse_Type *p) /*{{{*/
{
unsigned char *s = (unsigned char *)p->ptr;
while ((*s == ' ') || (*s == '\t') || (*s == '\n') || (*s == '\r'))
s++;
p->ptr = (char *)s;
}
/*}}}*/
static int looking_at (Parse_Type *p, char ch) /*{{{*/
{
return *p->ptr == ch;
}
/*}}}*/
static int skip_char (Parse_Type *p, char ch) /*{{{*/
{
if (! looking_at (p, ch))
return 0;
p->ptr++;
return 1;
}
/*}}}*/
static int parse_hex_digit (char ch) /*{{{*/
{
if ('0' <= ch && ch <= '9') return ch - '0';
if ('A' <= ch && ch <= 'F') return 10 + ch - 'A';
if ('a' <= ch && ch <= 'f') return 10 + ch - 'a';
else return -1;
}
/*}}}*/
static char *parse_4_hex_digits (char *s, unsigned int *new_string_len, char *new_string, int *is_binary_stringp) /*{{{*/
{
int d1, d2, d3, d4;
SLwchar_Type wchar;
#define BUFLEN 6
SLuchar_Type buf[BUFLEN], *u;
if ( (-1 == (d1 = parse_hex_digit (s[0])))
|| (-1 == (d2 = parse_hex_digit (s[1])))
|| (-1 == (d3 = parse_hex_digit (s[2])))
|| (-1 == (d4 = parse_hex_digit (s[3]))))
{
SLang_verror (Json_Parse_Error, "Illegal Unicode escape sequence in JSON string: \\u%c%c%c%c", s[0], s[1], s[2], s[3]); /* may contain '\000' */
return NULL;
}
wchar = (d1 << 12) + (d2 << 8) + (d3 << 4) + d4;
if (is_binary_stringp != NULL)
*is_binary_stringp = (wchar == 0);
u = new_string ? (SLuchar_Type*)new_string : buf;
*new_string_len += SLutf8_encode (wchar, u, BUFLEN) - u;
#undef BUFLEN
return s+4;
}
/*}}}*/
static int parse_string_length_and_move_ptr (Parse_Type *p, unsigned int *lenp, int *is_binary_stringp) /*{{{*/
{
unsigned int new_string_len = 0;
char *s = p->ptr;
*lenp = 0; *is_binary_stringp = 0;
while (1)
{
char ch = *s++;
/* STRING_DELIMITER = 34, SPACE = 32 */
if ((unsigned char)ch <= STRING_DELIMITER)
{
if (ch == STRING_DELIMITER)
break;
if (ch == 0)
{
SLang_verror (Json_Parse_Error, "Unexpected end of input seen while parsing a JSON string");
return -1;
}
if (ch < 32)
{
SLang_verror (Json_Parse_Error, "Control character 0x%02X in JSON string must be escaped", (unsigned int)ch);
return -1;
}
/* drop */
}
if (ch == ESCAPE_CHARACTER)
{
ch = *s++;
switch (ch)
{
case STRING_DELIMITER:
case ESCAPE_CHARACTER:
case '/':
case 'b': case 'f': case 'n': case 'r': case 't':
new_string_len++;
break;
case 'u':
{
int isbin;
if (NULL == (s = parse_4_hex_digits (s, &new_string_len, NULL, &isbin)))
return -1;
*is_binary_stringp |= isbin;
break;
}
default:
SLang_verror (Json_Parse_Error, "Illegal escaped character " DESCRIBE_CHAR_FMT " in JSON string", DESCRIBE_CHAR(ch));
return -1;
}
}
else
new_string_len++;
}
p->ptr = s;
*lenp = new_string_len;
return 0;
}
/*}}}*/
/* try to use string_buffer
* if there is enough space and the string is not a binary string.
*/
static char *parse_string (Parse_Type *p,
char *string_buffer, unsigned int buflen,
unsigned int *bstring_lenp) /*{{{*/
{
char *s, *new_string;
unsigned int new_string_pos, new_string_len;
int is_binary_string = 0;
s = p->ptr;
if (-1 == parse_string_length_and_move_ptr (p, &new_string_len, &is_binary_string))
return NULL;
new_string = string_buffer;
if (((new_string_len >= buflen) || is_binary_string)
&& (NULL == (new_string = SLmalloc (new_string_len + 1))))
return NULL;
new_string_pos = 0;
while (new_string_pos < new_string_len)
{
char ch = *s++;
if ((ch == STRING_DELIMITER) || ((unsigned char)ch < 32))
goto return_application_error;
if (ch != ESCAPE_CHARACTER)
{
new_string[new_string_pos++] = ch;
continue;
}
ch = *s++;
switch (ch)
{
case STRING_DELIMITER:
case ESCAPE_CHARACTER:
case '/':
new_string[new_string_pos++] = ch; break;
case 'b':
new_string[new_string_pos++] = '\b'; break;
case 'f':
new_string[new_string_pos++] = '\f'; break;
case 'n':
new_string[new_string_pos++] = '\n'; break;
case 'r':
new_string[new_string_pos++] = '\r'; break;
case 't':
new_string[new_string_pos++] = '\t'; break;
case 'u':
if (NULL != (s = parse_4_hex_digits (s, &new_string_pos, new_string + new_string_pos, NULL)))
break; /* else drop */
default:
goto return_application_error;
}
}
if (bstring_lenp != NULL)
*bstring_lenp = (is_binary_string ? new_string_len : 0);
new_string[new_string_pos] = 0;
return new_string;
return_application_error:
/* Since any JSon_Parse_Error should already have been recognized
* (by parse_string_length_and_move_ptr), something must be wrong here.
*/
SLang_verror (SL_Application_Error, "JSON string being parsed appears to be changing");
if (new_string != string_buffer) SLfree (new_string);
return NULL;
}
/*}}}*/
static int parse_and_push_string (Parse_Type *p) /*{{{*/
{
unsigned int bstring_len;
char *s;
char buf[512];
if (NULL == (s = parse_string (p, buf, sizeof (buf), &bstring_len)))
return -1;
if (bstring_len)
{
SLang_BString_Type *bstr;
int status;
/* NOTE: parse_string will not use buf for a binary string */
if (NULL == (bstr = SLbstring_create_malloced ((unsigned char *)s, bstring_len, 1)))
return -1;
/* s now belongs to bstr */
status = SLang_push_bstring (bstr);
SLbstring_free (bstr);
return status;
}
if (s != buf)
return SLang_push_malloced_string (s); /* frees s upon return */
return SLang_push_string (buf);
}
/*}}}*/
static int parse_and_push_number (Parse_Type *p) /*{{{*/
{
char *s = p->ptr, ch;
int is_int = 1, result;
if (*s == '-')
s++;
while ('0' <= *s && *s <= '9')
s++;
if (*s == '.')
{
is_int = 0;
s++;
while ('0' <= *s && *s <= '9')
s++;
}
if (*s == 'e' || *s == 'E')
{
is_int = 0;
s++;
if (*s == '+' || *s == '-')
s++;
while ('0' <= *s && *s <= '9')
s++;
}
ch = *s;
*s = 0;
errno = 0;
result = is_int ?
#ifdef HAVE_LONG_LONG
SLang_push_long_long (strtoll (p->ptr, NULL, 10))
#else
SLang_push_long (strtol (p->ptr, NULL, 10))
#endif
: SLang_push_double (strtod (p->ptr, NULL));
if (errno == ERANGE)
{
SLang_verror (Json_Parse_Error,
is_int
? "Integer value is too large (%s)"
: "Numeric value is too large (%s)",
p->ptr);
}
*s = ch;
p->ptr = s;
return result;
}
/*}}}*/
static int parse_and_push_literal (Parse_Type *p) /*{{{*/
{
char *s = p->ptr;
if (0 == strncmp (s, "true", 4))
{
p->ptr += 4;
return SLang_push_uchar (1);
}
if (0 == strncmp (s, "false", 5))
{
p->ptr += 5;
return SLang_push_uchar (0);
}
if (0 == strncmp (s, "null", 4))
{
p->ptr += 4;
return SLang_push_null ();
}
SLang_verror (Json_Parse_Error, "Unexpected character " DESCRIBE_CHAR_FMT " seen while parsing a JSON value", DESCRIBE_CHAR(*s));
return -1;
}
/*}}}*/
static int parse_and_push_object_as_struct (Parse_Type *, int);
#if 0
static int parse_and_push_object_as_assoc (Parse_Type *, int);
#endif
static int parse_and_push_array (Parse_Type *, int);
static int parse_and_push_value (Parse_Type *p, int only_toplevel_values) /*{{{*/
{
int ret;
skip_white (p);
if (! only_toplevel_values)
{
if (skip_char (p, STRING_DELIMITER))
return parse_and_push_string (p);
switch (*p->ptr)
{
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return parse_and_push_number (p);
case 'f':
case 't':
case 'n':
return parse_and_push_literal (p);
}
}
if (p->depth > Max_Recursion_Depth)
{
SLang_verror (Json_Parse_Error, "json text exceeds maximum nesting level of %d", Max_Recursion_Depth);
return -1;
}
if (skip_char (p, BEGIN_OBJECT))
{
p->depth++;
ret = parse_and_push_object_as_struct (p, only_toplevel_values);
p->depth--;
return ret;
}
if (skip_char (p, BEGIN_ARRAY))
{
p->depth++;
ret = parse_and_push_array (p, only_toplevel_values);
p->depth--;
return ret;
}
SLang_verror (Json_Parse_Error, (only_toplevel_values
? "Unexpected character " DESCRIBE_CHAR_FMT " seen while parsing JSON data (must be an object or an array)"
: "Unexpected character " DESCRIBE_CHAR_FMT " seen while parsing a JSON value"
), DESCRIBE_CHAR(*p->ptr));
return -1;
}
/*}}}*/
#if 0
static int parse_and_push_object_as_assoc (Parse_Type *p, int toplevel) /*{{{*/
{
SLang_Assoc_Array_Type *assoc;
char buf[512];
if (NULL == (assoc = SLang_create_assoc (SLANG_ANY_TYPE, 0)))
return -1;
skip_white (p);
if (! looking_at (p, END_OBJECT)) do
{
char *keyword;
char *str;
skip_white (p);
if (! skip_char (p, STRING_DELIMITER))
{
SLang_verror (Json_Parse_Error, "Expected a string while parsing a JSON object, found " DESCRIBE_CHAR_FMT, DESCRIBE_CHAR(*p->ptr));
goto return_error;
}
str = parse_string (p, buf, sizeof (buf), NULL); /* ignoring binary strings */
if (str == NULL)
goto return_error;
keyword = SLang_create_slstring (str);
if (str != buf)
SLfree (str);
if (keyword == NULL)
goto return_error;
skip_white (p);
if (! skip_char (p, NAME_SEPARATOR))
{
SLang_verror (Json_Parse_Error, "Expected a '%c' while parsing a JSON object, found " DESCRIBE_CHAR_FMT,
NAME_SEPARATOR, DESCRIBE_CHAR(*p->ptr));
SLang_free_slstring (keyword);
goto return_error;
}
if ((-1 == parse_and_push_value (p, 0))
|| (-1 == SLang_assoc_put (assoc, keyword)))
{
SLang_free_slstring (keyword);
goto return_error;
}
SLang_free_slstring (keyword);
skip_white (p);
}
while (skip_char (p, VALUE_SEPARATOR));
if (skip_char (p, END_OBJECT))
{
skip_white (p);
if (! toplevel || looking_at (p, 0))
return SLang_push_assoc (assoc, 1);
SLang_verror (Json_Parse_Error, "Expected end of input after parsing JSON object, found " DESCRIBE_CHAR_FMT, DESCRIBE_CHAR(*p->ptr));
}
else
{
if (looking_at (p, 0))
SLang_verror (Json_Parse_Error, "Unexpected end of input seen while parsing a JSON object");
else
SLang_verror (Json_Parse_Error, "Expected '%c' or '%c' while parsing a JSON object, found " DESCRIBE_CHAR_FMT,
VALUE_SEPARATOR, END_OBJECT, DESCRIBE_CHAR(*p->ptr));
}
return_error:
SLang_free_assoc (assoc);
return -1;
}
/*}}}*/
#endif
static void free_string_array (char **sp, unsigned int n)
{
if (sp == NULL)
return;
while (n > 0)
{
n--;
SLang_free_slstring (sp[n]);
}
SLfree ((char *)sp);
}
/* This has table implementation does not copy the strings */
#define HASH_TABLE_SIZE 601
typedef struct String_Hash_Elem_Type
{
SLstr_Type *string; /* not copied! */
unsigned int val;
struct String_Hash_Elem_Type *next;
}
String_Hash_Elem_Type;
typedef struct
{
String_Hash_Elem_Type hash_table[HASH_TABLE_SIZE];
unsigned int num_strings;
unsigned int num_collisions;
}
String_Hash_Type;
static String_Hash_Type *create_string_hash (void)
{
String_Hash_Type *h;
if (NULL == (h = (String_Hash_Type *)SLmalloc(sizeof(String_Hash_Type))))
return NULL;
memset ((char *)h, 0, sizeof(String_Hash_Type));
return h;
}
/* returns -1 upon failure, 0 if string added, 1 if already there */
static int add_string_to_hash (String_Hash_Type *h, char *s, unsigned int val, unsigned int *valp)
{
SLstr_Hash_Type hash;
String_Hash_Elem_Type *e, *e1;
hash = SLcompute_string_hash (s);
e = &h->hash_table[hash % HASH_TABLE_SIZE];
if (e->string == NULL)
{
e->string = s;
*valp = e->val = val;
h->num_strings++;
return 0;
}
while (1)
{
if (e->string == s)
{
*valp = e->val;
return 1;
}
if (e->next == NULL)
break;
e = e->next;
}
e1 = (String_Hash_Elem_Type *)SLmalloc (sizeof (String_Hash_Elem_Type));
if (e1 == NULL)
return -1;
e1->string = s;
*valp = e1->val = val;
e1->next = NULL;
e->next = e1;
h->num_strings++;
h->num_collisions++;
return 0;
}
static void free_string_hash (String_Hash_Type *h)
{
String_Hash_Elem_Type *e, *emax;
unsigned int num_collisions;
if (h == NULL)
return;
e = h->hash_table;
emax = e + HASH_TABLE_SIZE;
num_collisions = h->num_collisions;
while (num_collisions && (e < emax))
{
String_Hash_Elem_Type *e1;
if (e->next == NULL)
{
e++;
continue;
}
e1 = e->next;
while (e1 != NULL)
{
String_Hash_Elem_Type *e2 = e1->next;
SLfree ((char *)e1);
num_collisions--;
e1 = e2;
}
e++;
}
SLfree ((char *)h);
}
static int parse_and_push_object_as_struct (Parse_Type *p, int toplevel) /*{{{*/
{
char buf[512];
unsigned int num_fields, max_fields;
char **fields;
String_Hash_Type *h = NULL;
max_fields = 16;
num_fields = 0;
if ((NULL == (fields = (char **)SLmalloc (max_fields * sizeof (char *))))
|| (NULL == (h = create_string_hash ())))
goto return_error;
skip_white (p);
if (! looking_at (p, END_OBJECT)) do
{
char *keyword;
char *str;
int status;
unsigned int idx;
skip_white (p);
if (! skip_char (p, STRING_DELIMITER))
{
SLang_verror (Json_Parse_Error, "Expected a string while parsing a JSON object, found " DESCRIBE_CHAR_FMT, DESCRIBE_CHAR(*p->ptr));
goto return_error;
}
str = parse_string (p, buf, sizeof (buf), NULL); /* ignoring binary strings */
if (str == NULL)
goto return_error;
keyword = SLang_create_slstring (str);
if (str != buf)
SLfree (str);
if (keyword == NULL)
goto return_error;
if (-1 == (status = add_string_to_hash (h, keyword, num_fields, &idx)))
goto return_error;
if (status == 0)
{
if (num_fields == max_fields)
{
char **new_fields;
unsigned int new_max_fields = max_fields + 32;
if (NULL == (new_fields = (char **) SLrealloc ((char *)fields, new_max_fields*sizeof(char *))))
{
SLang_free_slstring (keyword);
goto return_error;
}
fields = new_fields;
max_fields = new_max_fields;
}
fields[num_fields++] = keyword;
}
skip_white (p);
if (! skip_char (p, NAME_SEPARATOR))
{
SLang_verror (Json_Parse_Error, "Expected a '%c' while parsing a JSON object, found " DESCRIBE_CHAR_FMT,
NAME_SEPARATOR, DESCRIBE_CHAR(*p->ptr));
goto return_error;
}
if (-1 == parse_and_push_value (p, 0))
goto return_error;
if (status == 1)
{
/* keyword already exists -- update value */
if ((-1 == SLstack_exch (0, num_fields - idx))
|| (-1 == SLdo_pop ()))
goto return_error;
}
skip_white (p);
}
while (skip_char (p, VALUE_SEPARATOR));
if (skip_char (p, END_OBJECT))
{
skip_white (p);
if (! toplevel || looking_at (p, 0))
{
SLang_Struct_Type *s;
if (NULL == (s = SLang_create_struct (fields, num_fields)))
goto return_error;
if ((-1 == SLang_pop_struct_fields (s, num_fields))
|| (-1 == SLang_push_struct (s)))
{
SLang_free_struct (s);
goto return_error;
}
SLang_free_struct (s);
free_string_hash (h);
free_string_array (fields, num_fields);
return 0;
}
SLang_verror (Json_Parse_Error, "Expected end of input after parsing JSON object, found " DESCRIBE_CHAR_FMT, DESCRIBE_CHAR(*p->ptr));
}
else
{
if (looking_at (p, 0))
SLang_verror (Json_Parse_Error, "Unexpected end of input seen while parsing a JSON object");
else
SLang_verror (Json_Parse_Error, "Expected '%c' or '%c' while parsing a JSON object, found " DESCRIBE_CHAR_FMT,
VALUE_SEPARATOR, END_OBJECT, DESCRIBE_CHAR(*p->ptr));
}
return_error:
free_string_array (fields, num_fields);
free_string_hash (h);
return -1;
}
/*}}}*/
static int parse_and_push_array (Parse_Type *p, int toplevel) /*{{{*/
{
SLang_List_Type *list = SLang_create_list (8); /* let's start with 8 elements */
if (list == NULL)
return -1;
skip_white (p);
if (! looking_at (p, END_ARRAY)) do
{
if ((-1 == parse_and_push_value (p, 0))
|| (-1 == SLang_list_append (list, -1)))
goto return_error;
skip_white (p);
}
while (skip_char (p, VALUE_SEPARATOR));
if (skip_char (p, END_ARRAY))
{
skip_white (p);
if (! toplevel || looking_at (p, 0))
return SLang_push_list (list, 1);
SLang_verror (Json_Parse_Error, "Expected end of input after parsing JSON array, found " DESCRIBE_CHAR_FMT, DESCRIBE_CHAR(*p->ptr));
}
else
{
if (looking_at (p, 0))
SLang_verror (Json_Parse_Error, "Unexpected end of input seen while parsing a JSON array");
else
SLang_verror (Json_Parse_Error, "Expected '%c' or '%c' while parsing a JSON array, found " DESCRIBE_CHAR_FMT,
VALUE_SEPARATOR, END_ARRAY, DESCRIBE_CHAR(*p->ptr));
}
return_error:
SLang_free_list (list);
return -1;
}
/*}}}*/
static void parse_start (char *input_string) /*{{{*/
{
Parse_Type pbuf, *p = &pbuf;
memset ((char *)p, 0, sizeof (Parse_Type));
p->ptr = input_string;
if ((NULL == input_string)
|| (0 == *input_string))
SLang_verror (Json_Parse_Error, "Unexpected empty input string");
else
parse_and_push_value (p, 1);
}
/*}}}*/
static void json_decode (void) /*{{{*/
{
char *buffer;
if ((SLang_Num_Function_Args != 1)
|| (-1 == SLpop_string (&buffer)))
{
SLang_verror (SL_Usage_Error, "Usage: json_decode (String_Type json_text)");
return;
}
parse_start (buffer);
SLfree (buffer);
}
/*}}}*/
/*{{{ json_generate_string implementation and support functions */
static unsigned int Len_Map[128] = /*{{{*/
{
6,6,6,6,6,6,6,6,
2,2,2,6,2,2,6,6,
6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,
1,1,2,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,2,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,6
};
/*}}}*/
static char *String_Map[128] = /*{{{*/
{
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007",
"\\b", "\\t", "\\n", "\\u000B", "\\f", "\\r", "\\u000E", "\\u000F",
"\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017",
"\\u0018", "\\u0019", "\\u001A", "\\u001B", "\\u001C", "\\u001D", "\\u001E", "\\u001F",
" ", "!", "\\\"", "#", "$", "%", "&", "'",
"(", ")", "*", "+", ",", "-", ".", "/",
"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", ":", ";", "<", "=", ">", "?",
"@", "A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "[", "\\\\", "]", "^", "_",
"`", "a", "b", "c", "d", "e", "f", "g",
"h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w",
"x", "y", "z", "{", "|", "}", "~", "\\u007F"
};
/*}}}*/
/* Adapted from SLutf8.c */
static int is_invalid_or_overlong_utf8 (SLuchar_Type *u, unsigned int len)
{
unsigned int i;
unsigned char ch, ch1;
/* Check for invalid sequences */
for (i = 1; i < len; i++)
{
if ((u[i] & 0xC0) != 0x80)
return 1;
}
/* Illegal (overlong) sequences */
/* 1100000x (10xxxxxx) */
/* 11100000 100xxxxx (10xxxxxx) */
/* 11110000 1000xxxx (10xxxxxx 10xxxxxx) */
/* 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx) */
/* 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx) */
ch = *u;
if ((ch == 0xC0) || (ch == 0xC1))
return 1;
ch1 = u[1];
if (((ch1 & ch) == 0x80)
&& ((ch == 0xE0)
|| (ch == 0xF0)
|| (ch == 0xF8)
|| (ch == 0xFC)))
return 1;
return 0;
}
static SLstrlen_Type compute_multibyte_char_len (char *p, char *pmax) /*{{{*/
{
SLstrlen_Type len;
unsigned char ch;
ch = (unsigned char)*p;
len = ((ch & 0xE0) == 0xC0) ? 2 /* (ch & 0b11100000) == 0b11000000 */
: ((ch & 0xF0) == 0xE0) ? 3 /* (ch & 0b11110000) == 0b11100000 */
: ((ch & 0xF8) == 0xF0) ? 4 /* (ch & 0b11111000) == 0b11110000 */
: ((ch & 0xFC) == 0xF8) ? 5 /* (ch & 0b11111100) == 0b11111000 */
: 6;
if (p + len > pmax)
return 1;
if (is_invalid_or_overlong_utf8 ((SLuchar_Type *)p, len))
return 1;
return len;
}
/*}}}*/
static char *alloc_encoded_json_string (char *ptr, char *end_of_input_string, SLstrlen_Type *lenp) /*{{{*/
{
SLstrlen_Type len = 2; /* first '"' and last '"' */
while (ptr < end_of_input_string)
{
unsigned char ch = (unsigned char) *ptr;
if (ch < 0x80)
{
len += Len_Map[ch];
ptr++;
continue;
}
len += 6; /* FIXME: Does not handle 0x1UUUU */
ptr += compute_multibyte_char_len (ptr, end_of_input_string);
if (ptr > end_of_input_string)
{
SLang_verror (Json_Invalid_Json_Error, "Invalid UTF-8 at end of string");
return NULL;
}
}
*lenp = len;
return SLmalloc (len + 1);
}
/*}}}*/
static char *fill_encoded_json_string (char *ptr, char *end_of_input_string,
char *dest_ptr) /*{{{*/
{
*dest_ptr++ = STRING_DELIMITER;
while (ptr < end_of_input_string)
{
unsigned char ch = *ptr;
unsigned int len;
if (ch < 0x80)
{
if (1 == (len = Len_Map[ch]))
*dest_ptr++ = ch;
else
{
char *str = String_Map[ch];
while (len--)
*dest_ptr++ = *str++;
}
ptr++;
continue;
}
/* We cannot use SLutf8_decode, since we need to handle invalid_or_overlong_utf8 or ILLEGAL_UNICODE as well. */
len = compute_multibyte_char_len (ptr, end_of_input_string);
if (len == 1)
{
/* Malformed or overlong */
sprintf (dest_ptr, "<%02X>", (unsigned char)*ptr);
dest_ptr += 4;
}
else
{ /* stolen from slutf8.c : fast_utf8_decode */
static unsigned char masks[7] = { 0, 0, 0x1F, 0xF, 0x7, 0x3, 0x1 };
SLwchar_Type w = (ch & masks[len]);
SLstrlen_Type i;
for (i = 1; i < len; i++)
w = (w << 6) | (ptr[i] & 0x3F);
if (w > 0xFFFF)
{
/* FIXME: Must be encoded as a pair of UTF-16 surrogates */
memcpy (dest_ptr, ptr, len);
dest_ptr += len;
}
else
{
sprintf (dest_ptr, "\\u%04X", w);
dest_ptr += 6;
}
}
ptr += len;
}
*dest_ptr++ = STRING_DELIMITER;
*dest_ptr = 0;
return dest_ptr;
}
/*}}}*/
static void json_encode_string (void) /*{{{*/
{
SLang_BString_Type *bstring = NULL;
char *string, *encoded_json_string;
SLstrlen_Type len, new_len;
if (SLang_peek_at_stack () == SLANG_BSTRING_TYPE)
{
if (-1 == SLang_pop_bstring (&bstring))
return;
string = (char *)SLbstring_get_pointer (bstring, &len);
}
else
{
if (-1 == SLang_pop_slstring (&string))
{
SLang_verror (SL_Usage_Error, "usage: _json_generate_string (String_Type json_string)");
return;
}
len = strlen (string);
}
if ((encoded_json_string = alloc_encoded_json_string (string, string + len, &new_len)) != NULL)
{
SLang_BString_Type *b;
char *enc_end;
enc_end = fill_encoded_json_string (string, string + len, encoded_json_string);
new_len = enc_end - encoded_json_string;
b = SLbstring_create_malloced ((unsigned char *)encoded_json_string, new_len, 1);
if (b != NULL)
{
(void) SLang_push_bstring (b);
SLbstring_free (b);
}
}
if (bstring != NULL)
SLbstring_free (bstring);
else
SLang_free_slstring (string);
}
/*}}}*/
/*}}}*/
static SLang_Intrin_Fun_Type Module_Intrinsics [] = /*{{{*/
{
MAKE_INTRINSIC_0("json_decode", json_decode, SLANG_VOID_TYPE),
MAKE_INTRINSIC_0("_json_encode_string", json_encode_string, SLANG_VOID_TYPE),
SLANG_END_INTRIN_FUN_TABLE
};
/*}}}*/
static SLang_Intrin_Var_Type Module_Variables [] = /*{{{*/
{
MAKE_VARIABLE("_json_module_version_string", &json_module_version_string, SLANG_STRING_TYPE, 1),
SLANG_END_INTRIN_VAR_TABLE
};
/*}}}*/
static SLang_IConstant_Type Module_Constants [] = /*{{{*/
{
MAKE_ICONSTANT("_json_module_version", JSON_MODULE_VERSION_NUMBER),
SLANG_END_ICONST_TABLE
};
/*}}}*/
int init_json_module_ns (char *ns_name) /*{{{*/
{
SLang_NameSpace_Type *ns = SLns_create_namespace (ns_name);
if (ns == NULL)
return -1;
if ((Json_Parse_Error == -1)
&& (-1 == (Json_Parse_Error = SLerr_new_exception (SL_RunTime_Error, "Json_Parse_Error", "JSON Parse Error"))))
return -1;
if ((Json_Invalid_Json_Error == -1)
&& (-1 == (Json_Invalid_Json_Error = SLerr_new_exception (SL_RunTime_Error, "Json_Invalid_Json_Error", "Invalid JSON Error"))))
return -1;
if ((-1 == SLns_add_intrin_fun_table (ns, Module_Intrinsics, NULL))
|| (-1 == SLns_add_intrin_var_table (ns, Module_Variables, NULL))
|| (-1 == SLns_add_iconstant_table (ns, Module_Constants, NULL)))
return -1;
return 0;
}
/*}}}*/
void deinit_json_module (void) /*{{{*/
{
/* This function is optional */
}
/*}}}*/