Files
DNF_DEV/source/squirrel/sqdbg/json.h
2025-10-23 15:21:33 +08:00

1352 lines
26 KiB
C++

//-----------------------------------------------------------------------
// github.com/samisalreadytaken/sqdbg
//-----------------------------------------------------------------------
//
#ifndef SQDBG_JSON_H
#define SQDBG_JSON_H
// Most messages are going to require less than 256 bytes,
// only approaching 1024 on large breakpoint requests
#define JSON_SCRATCH_CHUNK_SIZE 1024
typedef enum
{
JSON_NULL = 0x0000,
JSON_BOOL = 0x0001,
JSON_INTEGER = 0x0002,
JSON_FLOAT = 0x0004,
JSON_STRING = 0x0008,
JSON_TABLE = 0x0010,
JSON_ARRAY = 0x0020,
} JSONTYPE;
class json_table_t;
class json_array_t;
struct json_value_t;
struct ostr_t
{
// ushort can handle strings that fit the recv net buf,
// use a larger type for validation of sent messages in the send net buf
#ifdef SQDBG_VALIDATE_SENT_MSG
typedef unsigned int index_t;
#else
typedef unsigned short index_t;
#endif
index_t ofs;
index_t len;
};
struct json_value_t
{
union
{
ostr_t _string;
int _integer;
json_table_t *_table;
json_array_t *_array;
};
int type;
};
struct json_field_t
{
ostr_t key;
json_value_t val;
};
class json_array_t
{
public:
const char *m_pBase;
CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *m_Allocator;
int *m_Elements;
unsigned short m_nElementCount;
unsigned short m_nElementsSize;
void Init( const char *base, CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *allocator )
{
m_pBase = base;
m_Allocator = allocator;
m_nElementCount = 0;
m_nElementsSize = 0;
}
json_value_t *NewElement()
{
if ( m_nElementCount == m_nElementsSize )
{
// doesn't free old ptr, this is an uncommon operation and extra allocation is fine
int oldsize = m_nElementsSize;
int *oldptr = m_Elements;
m_nElementsSize = !m_nElementsSize ? 8 : ( m_nElementsSize << 1 );
m_Elements = (int*)m_Allocator->Alloc( m_nElementsSize * sizeof(int) );
if ( oldsize )
memcpy( m_Elements, oldptr, oldsize * sizeof(int) );
}
int index;
json_value_t *ret = (json_value_t*)m_Allocator->Alloc( sizeof(json_value_t), &index );
m_Elements[ m_nElementCount++ ] = index;
return ret;
}
int Size() const
{
return m_nElementCount;
}
bool GetString( int i, string_t *out )
{
Assert( m_nElementCount );
Assert( i >= 0 && i < m_nElementCount );
json_value_t *val = (json_value_t*)m_Allocator->Get( m_Elements[i] );
if ( val->type & JSON_STRING )
{
out->Assign( m_pBase + val->_string.ofs, val->_string.len );
return true;
}
return false;
}
bool GetTable( int i, json_table_t **out )
{
Assert( m_nElementCount );
Assert( i >= 0 && i < m_nElementCount );
json_value_t *val = (json_value_t*)m_Allocator->Get( m_Elements[i] );
if ( val->type & JSON_TABLE )
{
*out = val->_table;
return true;
}
return false;
}
};
class json_table_t
{
public:
const char *m_pBase;
CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *m_Allocator;
int *m_Elements;
unsigned short m_nElementCount;
unsigned short m_nElementsSize;
void Init( const char *base, CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *allocator )
{
m_pBase = base;
m_Allocator = allocator;
m_nElementCount = 0;
m_nElementsSize = 0;
}
json_value_t *Get( const string_t &key )
{
for ( int i = 0; i < m_nElementCount; i++ )
{
json_field_t *kv = (json_field_t*)m_Allocator->Get( m_Elements[i] );
if ( key.IsEqualTo( m_pBase + kv->key.ofs, kv->key.len ) )
return &kv->val;
}
return NULL;
}
json_value_t *Get( const string_t &key ) const
{
return const_cast< json_table_t * >( this )->Get( key );
}
json_field_t *NewElement()
{
if ( m_nElementCount == m_nElementsSize )
{
int oldsize = m_nElementsSize;
int *oldptr = m_Elements;
m_nElementsSize = !m_nElementsSize ? 8 : ( m_nElementsSize << 1 );
m_Elements = (int*)m_Allocator->Alloc( m_nElementsSize * sizeof(int) );
if ( oldsize )
memcpy( m_Elements, oldptr, oldsize * sizeof(int) );
}
int index;
json_field_t *ret = (json_field_t*)m_Allocator->Alloc( sizeof(json_field_t), &index );
m_Elements[ m_nElementCount++ ] = index;
return ret;
}
bool GetBool( const string_t &key, bool *out ) const
{
json_value_t *kval = Get( key );
if ( kval && ( kval->type & JSON_BOOL ) )
{
*out = kval->_integer;
return true;
}
*out = false;
return false;
}
bool GetInt( const string_t &key, int *out, int defaultVal = 0 ) const
{
json_value_t *kval = Get( key );
if ( kval && ( kval->type & JSON_INTEGER ) )
{
*out = kval->_integer;
return true;
}
*out = defaultVal;
return false;
}
bool GetString( const string_t &key, string_t *out, const char *defaultVal = "" ) const
{
json_value_t *kval = Get( key );
if ( kval && ( kval->type & JSON_STRING ) )
{
out->Assign( m_pBase + kval->_string.ofs, kval->_string.len );
return true;
}
out->Assign( defaultVal, strlen(defaultVal) );
return false;
}
bool GetTable( const string_t &key, json_table_t **out ) const
{
json_value_t *kval = Get( key );
if ( kval && ( kval->type & JSON_TABLE ) )
{
*out = kval->_table;
return true;
}
*out = NULL;
return false;
}
bool GetArray( const string_t &key, json_array_t **out ) const
{
json_value_t *kval = Get( key );
if ( kval && ( kval->type & JSON_ARRAY ) )
{
*out = kval->_array;
return true;
}
*out = NULL;
return false;
}
bool Get( const string_t &key, bool *out ) const { return GetBool( key, out ); }
bool Get( const string_t &key, int *out ) const { return GetInt( key, out ); }
bool Get( const string_t &key, string_t *out ) const { return GetString( key, out ); }
bool Get( const string_t &key, json_table_t **out ) const { return GetTable( key, out ); }
bool Get( const string_t &key, json_array_t **out ) const { return GetArray( key, out ); }
};
static inline void PutStr( CBuffer *buffer, const string_t &str )
{
buffer->base.Ensure( buffer->Size() + str.len );
memcpy( buffer->Base() + buffer->Size(), str.ptr, str.len );
buffer->size += str.len;
#ifdef SQDBG_VALIDATE_SENT_MSG
for ( unsigned int i = 0; i < str.len; i++ )
{
if ( str.ptr[i] == '\\' &&
( str.ptr[i+1] == '\\' ||
str.ptr[i+1] == '\"' ||
str.ptr[i+1] == 'n' ||
str.ptr[i+1] == 'r' ||
str.ptr[i+1] == 't' ) )
{
i++;
continue;
}
AssertMsg( str.ptr[i] != '\\' && IN_RANGE_CHAR( str.ptr[i], 0x20, 0x7E ), "control char in json string" );
}
#endif
}
static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote )
{
const char *c = str.ptr;
unsigned int i = str.len;
unsigned int len = i;
if ( quote )
len += 4;
for ( ; i--; c++ )
{
switch ( *c )
{
case '\\': case '\"':
case '\a': case '\b': case '\f':
case '\n': case '\r': case '\t': case '\v':
len++;
if ( quote )
{
len++;
if ( *c == '\\' || *c == '\"' )
len++;
}
break;
default:
if ( !IN_RANGE_CHAR( *c, 0x20, 0x7E ) )
{
int ret = IsValidUTF8( (unsigned char*)c, i + 1 );
if ( ret != 0 )
{
i -= ret - 1;
c += ret - 1;
}
else
{
if ( !quote )
{
len += sizeof(uint16_t) * 2 + 1;
}
else
{
len += sizeof(SQChar) * 2 + 2;
}
}
}
}
}
buffer->base.Ensure( buffer->Size() + len );
char *mem = buffer->Base();
unsigned int idx = buffer->Size();
c = str.ptr;
i = str.len;
if ( quote )
{
mem[idx++] = '\\';
mem[idx++] = '\"';
}
for ( ; i--; c++ )
{
mem[idx++] = *c;
switch ( *c )
{
case '\\':
case '\"':
mem[idx-1] = '\\';
if ( quote )
{
mem[idx++] = '\\';
mem[idx++] = '\\';
}
mem[idx++] = *c;
break;
case '\a':
mem[idx-1] = '\\';
if ( quote )
mem[idx++] = '\\';
mem[idx++] = 'a';
break;
case '\b':
mem[idx-1] = '\\';
if ( quote )
mem[idx++] = '\\';
mem[idx++] = 'b';
break;
case '\f':
mem[idx-1] = '\\';
if ( quote )
mem[idx++] = '\\';
mem[idx++] = 'f';
break;
case '\n':
mem[idx-1] = '\\';
if ( quote )
mem[idx++] = '\\';
mem[idx++] = 'n';
break;
case '\r':
mem[idx-1] = '\\';
if ( quote )
mem[idx++] = '\\';
mem[idx++] = 'r';
break;
case '\t':
mem[idx-1] = '\\';
if ( quote )
mem[idx++] = '\\';
mem[idx++] = 't';
break;
case '\v':
mem[idx-1] = '\\';
if ( quote )
mem[idx++] = '\\';
mem[idx++] = 'v';
break;
default:
if ( !IN_RANGE_CHAR( *c, 0x20, 0x7E ) )
{
int ret = IsValidUTF8( (unsigned char*)c, i + 1 );
if ( ret != 0 )
{
memcpy( mem + idx, c + 1, ret - 1 );
idx += ret - 1;
i -= ret - 1;
c += ret - 1;
}
else
{
mem[idx-1] = '\\';
if ( !quote )
{
mem[idx++] = 'u';
idx += printhex< true, false >(
mem + idx,
buffer->Capacity() - idx,
(uint16_t)*(unsigned char*)c );
}
else
{
mem[idx++] = '\\';
mem[idx++] = 'x';
idx += printhex< true, false >(
mem + idx,
buffer->Capacity() - idx,
(SQUnsignedChar)*(unsigned char*)c );
}
}
}
}
}
if ( quote )
{
mem[idx++] = '\\';
mem[idx++] = '\"';
}
buffer->size = idx;
}
#ifdef SQUNICODE
static inline void PutStr( CBuffer *buffer, const sqstring_t &str, bool quote )
{
unsigned int len;
if ( !quote )
{
len = UTF8Length< kUTFEscapeJSON >( str.ptr, str.len );
}
else
{
len = UTF8Length< kUTFEscapeQuoted >( str.ptr, str.len );
}
buffer->base.Ensure( buffer->Size() + len );
if ( !quote )
{
len = SQUnicodeToUTF8< kUTFEscapeJSON >(
buffer->Base() + buffer->Size(),
buffer->Capacity() - buffer->Size(),
str.ptr,
str.len );
}
else
{
len = SQUnicodeToUTF8< kUTFEscapeQuoted >(
buffer->Base() + buffer->Size(),
buffer->Capacity() - buffer->Size(),
str.ptr,
str.len );
}
buffer->size += len;
}
#endif
static inline void PutChar( CBuffer *buffer, char c )
{
buffer->base.Ensure( buffer->Size() + 1 );
buffer->Base()[buffer->size++] = c;
}
template < typename I >
static inline void PutInt( CBuffer *buffer, I val )
{
buffer->base.Ensure( buffer->Size() + countdigits( val ) + 1 );
int len = printint( buffer->Base() + buffer->Size(), buffer->Capacity() - buffer->Size(), val );
buffer->size += len;
}
template < bool padding, typename I >
static inline void PutHex( CBuffer *buffer, I val )
{
STATIC_ASSERT( IS_UNSIGNED( I ) );
buffer->base.Ensure( buffer->Size() + countdigits<16>( val ) + 1 );
int len = printhex< padding >( buffer->Base() + buffer->Size(), buffer->Capacity() - buffer->Size(), val );
buffer->size += len;
}
struct jstringbuf_t
{
CBuffer *m_pBuffer;
jstringbuf_t( CBuffer *b ) : m_pBuffer(b)
{
::PutChar( m_pBuffer, '\"' );
}
~jstringbuf_t()
{
::PutChar( m_pBuffer, '\"' );
}
jstringbuf_t( const jstringbuf_t &src );
void Seek( int i )
{
m_pBuffer->size += i;
}
template < int SIZE >
void Puts( const char (&str)[SIZE] )
{
::PutStr( m_pBuffer, str );
}
void Puts( const conststring_t &str )
{
::PutStr( m_pBuffer, str );
}
void Puts( const string_t &str, bool quote = false )
{
::PutStr( m_pBuffer, str, quote );
}
#ifdef SQUNICODE
void Puts( const sqstring_t &str, bool quote = false )
{
::PutStr( m_pBuffer, str, quote );
}
#endif
void Put( char c )
{
::PutChar( m_pBuffer, c );
}
template < typename I >
void PutInt( I val )
{
::PutInt( m_pBuffer, val );
}
template < typename I >
void PutHex( I val, bool padding = true )
{
if ( padding )
{
::PutHex< true >( m_pBuffer, val );
}
else
{
::PutHex< false >( m_pBuffer, val );
}
}
};
class wjson_t
{
public:
CBuffer *m_pBuffer;
int m_nElementCount;
wjson_t( CBuffer *b ) :
m_pBuffer(b),
m_nElementCount(0)
{
}
};
class wjson_table_t : public wjson_t
{
public:
wjson_table_t( CBuffer &b ) : wjson_t(&b)
{
PutChar( m_pBuffer, '{' );
}
~wjson_table_t()
{
PutChar( m_pBuffer, '}' );
}
wjson_table_t( const wjson_t &src ) : wjson_t(src)
{
}
wjson_table_t( const wjson_table_t &src );
void PutKey( const string_t &key )
{
if ( m_nElementCount++ )
PutChar( m_pBuffer, ',' );
PutChar( m_pBuffer, '\"' );
PutStr( m_pBuffer, key );
PutChar( m_pBuffer, '\"' );
PutChar( m_pBuffer, ':' );
}
void SetInt( const string_t &key, int val )
{
PutKey( key );
PutInt( m_pBuffer, val );
}
void SetNull( const string_t &key )
{
PutKey( key );
PutStr( m_pBuffer, "null" );
}
void SetBool( const string_t &key, bool val )
{
PutKey( key );
PutStr( m_pBuffer, val ? string_t("true") : string_t("false") );
}
jstringbuf_t SetStringAsBuf( const string_t &key )
{
PutKey( key );
return { m_pBuffer };
}
template < int SIZE >
void SetString( const string_t &key, const char (&val)[SIZE] )
{
PutKey( key );
PutChar( m_pBuffer, '\"' );
PutStr( m_pBuffer, val );
PutChar( m_pBuffer, '\"' );
}
void SetString( const string_t &key, const conststring_t &val )
{
PutKey( key );
PutChar( m_pBuffer, '\"' );
PutStr( m_pBuffer, val );
PutChar( m_pBuffer, '\"' );
}
void SetString( const string_t &key, const string_t &val, bool quote = false )
{
PutKey( key );
PutChar( m_pBuffer, '\"' );
PutStr( m_pBuffer, val, quote );
PutChar( m_pBuffer, '\"' );
}
#ifdef SQUNICODE
void SetString( const string_t &key, const sqstring_t &val, bool quote = false )
{
PutKey( key );
PutChar( m_pBuffer, '\"' );
PutStr( m_pBuffer, val, quote );
PutChar( m_pBuffer, '\"' );
}
#endif
void SetIntString( const string_t &key, int val )
{
PutKey( key );
PutChar( m_pBuffer, '\"' );
PutInt( m_pBuffer, val );
PutChar( m_pBuffer, '\"' );
}
template < typename I >
void SetIntBrackets( const string_t &key, I val, bool hex = false )
{
PutKey( key );
PutChar( m_pBuffer, '\"' );
PutChar( m_pBuffer, '[' );
if ( !hex )
{
PutInt( m_pBuffer, val );
}
else
{
PutHex< false >( m_pBuffer, cast_unsigned( I, val ) );
}
PutChar( m_pBuffer, ']' );
PutChar( m_pBuffer, '\"' );
}
wjson_t SetArray( const string_t &key )
{
PutKey( key );
PutChar( m_pBuffer, '[' );
return { m_pBuffer };
}
wjson_t SetTable( const string_t &key )
{
PutKey( key );
PutChar( m_pBuffer, '{' );
return { m_pBuffer };
}
void Set( const string_t &key, bool val ) { SetBool( key, val ); }
void Set( const string_t &key, int val ) { SetInt( key, val ); }
void Set( const string_t &key, unsigned int val ) { SetInt( key, val ); }
void Set( const string_t &key, const string_t &val ) { SetString( key, val ); }
};
class wjson_array_t : public wjson_t
{
public:
wjson_array_t( CBuffer &b ) : wjson_t(&b)
{
PutChar( m_pBuffer, '[' );
}
~wjson_array_t()
{
PutChar( m_pBuffer, ']' );
}
wjson_array_t( const wjson_t &src ) : wjson_t(src)
{
}
wjson_array_t( const wjson_array_t &src );
int Size()
{
return m_nElementCount;
}
wjson_t AppendTable()
{
if ( m_nElementCount++ )
PutChar( m_pBuffer, ',' );
PutChar( m_pBuffer, '{' );
return { m_pBuffer };
}
void Append( int val )
{
if ( m_nElementCount++ )
PutChar( m_pBuffer, ',' );
PutInt( m_pBuffer, val );
}
void Append( const string_t &val )
{
if ( m_nElementCount++ )
PutChar( m_pBuffer, ',' );
PutChar( m_pBuffer, '\"' );
PutStr( m_pBuffer, val );
PutChar( m_pBuffer, '\"' );
}
};
class JSONParser
{
private:
char *m_cur;
char *m_end;
char *m_start;
CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *m_Allocator;
char *m_error;
enum
{
Token_Error = 0,
Token_String,
Token_Integer,
Token_Float,
Token_False,
Token_True = Token_False + 1,
Token_Null,
Token_Table = '{',
Token_Array = '[',
};
public:
JSONParser( CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *allocator, char *ptr, int len, json_table_t *pTable ) :
m_cur( ptr ),
m_end( ptr + len + 1 ),
m_start( ptr ),
m_Allocator( allocator ),
m_error( NULL )
{
string_t token;
char type = NextToken( token );
if ( type == '{' )
{
pTable->Init( m_start, m_Allocator );
ParseTable( pTable, token );
}
else
{
SetError( "expected '%c', got %s @ %i", '{', Char(type), Index() );
}
}
const char *GetError() const
{
return m_error;
}
private:
int Index()
{
return m_cur - m_start;
}
char *Char( char token )
{
char *buf;
if ( token == Token_Error )
token = *m_cur;
if ( IN_RANGE_CHAR( token, 0x20, 0x7E ) )
{
buf = m_Allocator->Alloc(4);
buf[0] = '\'';
buf[1] = token;
buf[2] = '\'';
buf[3] = 0;
}
else
{
buf = m_Allocator->Alloc(5);
int i = printhex< true, true, false >( buf, 5, (unsigned char)token );
Assert( i == 4 );
buf[i] = 0;
}
return buf;
}
void SetError( const char *fmt, ... )
{
if ( m_error )
return;
const int size = 48;
m_error = m_Allocator->Alloc( size );
va_list va;
va_start( va, fmt );
int len = vsnprintf( m_error, size, fmt, va );
va_end( va );
if ( len < 0 || len > size-1 )
len = size-1;
m_error[len] = 0;
}
bool IsValue( char token )
{
switch ( token )
{
case Token_String:
case Token_Integer:
case Token_Float:
case Token_False:
case Token_True:
case Token_Null:
case Token_Table:
case Token_Array:
return true;
default:
return false;
}
}
char NextToken( string_t &token )
{
while ( m_cur < m_end )
{
switch ( *m_cur )
{
case 0x20: case 0x0A: case 0x0D: case 0x09:
m_cur++;
break;
case '\"':
return ParseString( token );
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return ParseNumber( token );
case ':': case ',':
case '{': case '}':
case '[': case ']':
return *m_cur++;
case 't':
if ( m_cur + 4 < m_end &&
m_cur[1] == 'r' && m_cur[2] == 'u' && m_cur[3] == 'e' )
{
m_cur += 4;
return Token_True;
}
SetError( "expected %s @ %i", "\"true\"", Index() );
return Token_Error;
case 'f':
if ( m_cur + 5 < m_end &&
m_cur[1] == 'a' && m_cur[2] == 'l' && m_cur[3] == 's' && m_cur[4] == 'e' )
{
m_cur += 5;
return Token_False;
}
SetError( "expected %s @ %i", "\"false\"", Index() );
return Token_Error;
case 'n':
if ( m_cur + 4 < m_end &&
m_cur[1] == 'u' && m_cur[2] == 'l' && m_cur[3] == 'l' )
{
m_cur += 4;
return Token_Null;
}
SetError( "expected %s @ %i", "\"null\"", Index() );
return Token_Error;
default:
return Token_Error;
}
}
return Token_Error;
}
char ParseString( string_t &token )
{
char *pStart = ++m_cur;
bool bEscape = false;
for (;;)
{
if ( m_cur >= m_end )
{
SetError( "unfinished string @ %i", Index() );
return Token_Error;
}
if ( *m_cur == '\"' )
{
token.Assign( pStart, m_cur - pStart );
*m_cur++ = 0;
break;
}
if ( *m_cur != '\\' )
{
m_cur++;
continue;
}
m_cur++;
if ( m_cur >= m_end )
{
SetError( "unfinished string @ %i", Index() );
return Token_Error;
}
bEscape = true;
// Defer unescape until the end of the string is found
switch ( *m_cur )
{
case '\\': case '\"': case '/':
case 'b': case 'f':
case 'n': case 'r': case 't':
m_cur++;
break;
case 'u':
if ( m_cur + 4 >= m_end ||
!_isxdigit( m_cur[1] ) || !_isxdigit( m_cur[2] ) ||
!_isxdigit( m_cur[3] ) || !_isxdigit( m_cur[4] ) )
{
SetError( "invalid hex escape @ %i", Index() );
return Token_Error;
}
m_cur += 4;
break;
default:
SetError( "invalid escape char 0x%02x @ %i", *(unsigned char*)m_cur, Index() );
return Token_Error;
}
}
if ( bEscape )
{
char *cur = pStart;
char *end = pStart + token.len;
do
{
if ( cur[0] != '\\' )
{
cur++;
continue;
}
#define _shift( bytesWritten, bytesRead ) \
Assert( (bytesWritten) < (bytesRead) ); \
memmove( cur + (bytesWritten), cur + (bytesRead), end - ( cur + (bytesRead) ) ); \
cur += (bytesWritten); \
end -= (bytesRead) - (bytesWritten);
switch ( cur[1] )
{
case '\\':
shift_one:
_shift( 1, 2 );
break;
case '\"': cur[0] = '\"'; goto shift_one;
case '/': cur[0] = '/'; goto shift_one;
case 'b': cur[0] = '\b'; goto shift_one;
case 'f': cur[0] = '\f'; goto shift_one;
case 'n': cur[0] = '\n'; goto shift_one;
case 'r': cur[0] = '\r'; goto shift_one;
case 't': cur[0] = '\t'; goto shift_one;
case 'u':
{
unsigned int val;
Verify( atox( { cur + 2, 4 }, &val ) );
if ( val <= 0x7F )
{
cur[0] = (char)val;
_shift( 1, 6 );
break;
}
else if ( val <= 0x7FF )
{
UTF8_2_FROM_UTF32( (unsigned char*)cur, val );
_shift( 2, 6 );
break;
}
else if ( UTF_SURROGATE(val) )
{
if ( UTF_SURROGATE_LEAD(val) )
{
if ( cur + 11 < end &&
cur[6] == '\\' && cur[7] == 'u' &&
_isxdigit( cur[8] ) && _isxdigit( cur[9] ) &&
_isxdigit( cur[10] ) && _isxdigit( cur[11] ) )
{
unsigned int low;
Verify( atox( { cur + 8, 4 }, &low ) );
if ( UTF_SURROGATE_TRAIL( low ) )
{
val = UTF32_FROM_UTF16_SURROGATE( val, low );
UTF8_4_FROM_UTF32( (unsigned char*)cur, val );
_shift( 4, 12 );
break;
}
}
}
}
UTF8_3_FROM_UTF32( (unsigned char*)cur, val );
_shift( 3, 6 );
break;
}
default: UNREACHABLE();
}
#undef _shift
}
while ( cur < end );
token.len = end - pStart;
token.ptr[token.len] = 0;
}
return Token_String;
}
char ParseNumber( string_t &token )
{
const char *pStart = m_cur;
char type;
if ( *m_cur == '-' )
{
m_cur++;
if ( m_cur >= m_end )
goto err_eof;
}
if ( *m_cur == '0' )
{
m_cur++;
if ( m_cur >= m_end )
goto err_eof;
}
else if ( IN_RANGE_CHAR( *m_cur, '1', '9' ) )
{
do
{
m_cur++;
if ( m_cur >= m_end )
goto err_eof;
}
while ( IN_RANGE_CHAR( *m_cur, '0', '9' ) );
}
else
{
SetError( "unexpected char 0x%02x in number @ %i", *(unsigned char*)m_cur, Index() );
return Token_Error;
}
type = Token_Integer;
if ( *m_cur == '.' )
{
type = Token_Float;
m_cur++;
while ( m_cur < m_end && IN_RANGE_CHAR( *m_cur, '0', '9' ) )
m_cur++;
if ( m_cur >= m_end )
goto err_eof;
}
if ( *m_cur == 'e' || *m_cur == 'E' )
{
type = Token_Float;
m_cur++;
if ( m_cur >= m_end )
goto err_eof;
if ( *m_cur == '-' || *m_cur == '+' )
{
m_cur++;
if ( m_cur >= m_end )
goto err_eof;
}
while ( m_cur < m_end && IN_RANGE_CHAR( *m_cur, '0', '9' ) )
m_cur++;
}
token.Assign( pStart, m_cur - pStart );
return type;
err_eof:
SetError( "unexpected eof" );
return Token_Error;
}
char ParseTable( json_table_t *pTable, string_t &token )
{
char type = NextToken( token );
if ( type == '}' )
return Token_Table;
for (;;)
{
if ( type != Token_String )
{
SetError( "expected '%c', got %s @ %i", '\"', Char(type), Index() );
return Token_Error;
}
type = NextToken( token );
if ( type != ':' )
{
SetError( "expected '%c', got %s @ %i", ':', Char(type), Index() );
return Token_Error;
}
json_field_t *kv = pTable->NewElement();
Assert( token.ptr - m_start < (ostr_t::index_t)-1 );
kv->key.ofs = token.ptr - m_start;
kv->key.len = (ostr_t::index_t)token.len;
type = NextToken( token );
type = ParseValue( type, token, &kv->val );
if ( !IsValue( type ) )
{
SetError( "invalid token %s @ %i", Char(type), Index() );
return Token_Error;
}
type = NextToken( token );
if ( type == ',' )
{
type = NextToken( token );
}
else if ( type == '}' )
{
return Token_Table;
}
else
{
SetError( "expected '%c', got %s @ %i", '}', Char(type), Index() );
return Token_Error;
}
}
}
char ParseArray( json_array_t *pArray, string_t &token )
{
char type = NextToken( token );
if ( type == ']' )
return Token_Array;
for (;;)
{
if ( !IsValue( type ) )
{
SetError( "expected '%c', got %s @ %i", ']', Char(type), Index() );
return Token_Error;
}
json_value_t *val = pArray->NewElement();
type = ParseValue( type, token, val );
if ( type == Token_Error )
{
SetError( "invalid token %s @ %i", Char(type), Index() );
return Token_Error;
}
type = NextToken( token );
if ( type == ',' )
{
type = NextToken( token );
}
else if ( type == ']' )
{
return Token_Array;
}
else
{
SetError( "expected '%c', got %s @ %i", ']', Char(type), Index() );
return Token_Error;
}
}
}
char ParseValue( char type, string_t &token, json_value_t *value )
{
switch ( type )
{
case Token_Integer:
if ( token.len > FMT_UINT32_LEN + 1 )
{
SetError( "invalid integer literal @ %i", Index() );
return Token_Error;
}
value->type = JSON_INTEGER;
Verify( atoi( token, &value->_integer ) );
return type;
case Token_Float:
value->type = JSON_FLOAT;
// floats are unused, ignore value
return type;
case Token_String:
value->type = JSON_STRING;
Assert( token.ptr - m_start < (ostr_t::index_t)-1 );
value->_string.ofs = token.ptr - m_start;
value->_string.len = (ostr_t::index_t)token.len;
return type;
case '{':
value->type = JSON_TABLE;
value->_table = (json_table_t*)m_Allocator->Alloc( sizeof(json_table_t) );
value->_table->Init( m_start, m_Allocator );
return ParseTable( value->_table, token );
case '[':
value->type = JSON_ARRAY;
value->_array = (json_array_t*)m_Allocator->Alloc( sizeof(json_array_t) );
value->_array->Init( m_start, m_Allocator );
return ParseArray( value->_array, token );
case Token_False:
case Token_True:
value->type = JSON_BOOL;
value->_integer = type - Token_False;
return type;
case Token_Null:
value->type = JSON_NULL;
return type;
default:
return Token_Error;
}
}
};
#endif // SQDBG_JSON_H