summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Buland <mbuland@penny-arcade.com>2017-06-06 22:45:21 -0700
committerMike Buland <mbuland@penny-arcade.com>2017-06-06 22:45:21 -0700
commitdc759b40b9a18b20454b68244dd063ab56155c3e (patch)
treee16c5b024f44c30b9aeef8fc3bb85087039cf1f0
parentb7751f0136502592e9b1897b51859b1133339a93 (diff)
downloadlibbu++-dc759b40b9a18b20454b68244dd063ab56155c3e.tar.gz
libbu++-dc759b40b9a18b20454b68244dd063ab56155c3e.tar.bz2
libbu++-dc759b40b9a18b20454b68244dd063ab56155c3e.tar.xz
libbu++-dc759b40b9a18b20454b68244dd063ab56155c3e.zip
Reading, writing, and accessing loaded data all work.
-rw-r--r--src/unstable/json.cpp364
-rw-r--r--src/unstable/json.h30
2 files changed, 337 insertions, 57 deletions
diff --git a/src/unstable/json.cpp b/src/unstable/json.cpp
index c31c7f9..0b47da4 100644
--- a/src/unstable/json.cpp
+++ b/src/unstable/json.cpp
@@ -1,5 +1,8 @@
1#include "bu/json.h" 1#include "bu/json.h"
2#include "bu/staticmembuf.h" 2#include "bu/staticmembuf.h"
3#include "bu/membuf.h"
4
5#include "bu/sio.h"
3 6
4#include <stdlib.h> 7#include <stdlib.h>
5 8
@@ -23,46 +26,146 @@ Bu::Json::Json( Bu::Stream &sInput ) :
23 parse( sInput ); 26 parse( sInput );
24} 27}
25 28
29Bu::Json::Json( char &c, Bu::Stream &sInput ) :
30 eType( Invalid )
31{
32 parse( c, sInput );
33}
34
26Bu::Json::~Json() 35Bu::Json::~Json()
27{ 36{
28 reset(); 37 reset();
29} 38}
30 39
40Bu::Json::Type Bu::Json::getType() const
41{
42 return eType;
43}
44
45Bu::String Bu::Json::getString() const
46{
47 if( eType != String )
48 throw Bu::ExceptionBase(
49 "String requested from non-string json object."
50 );
51
52 return *uDat.pString;
53}
54
55double Bu::Json::getNumber() const
56{
57 if( eType != Number )
58 throw Bu::ExceptionBase(
59 "Number requested from non-number json object."
60 );
61
62 return uDat.dNumber;
63}
64
65bool Bu::Json::getBoolean() const
66{
67 if( eType != Boolean )
68 throw Bu::ExceptionBase(
69 "Boolean requested from non-boolean json object."
70 );
71
72 return uDat.bBoolean;
73}
74
75bool Bu::Json::isNull() const
76{
77 return eType == Null;
78}
79
80Bu::Json *Bu::Json::operator[]( const Bu::String &sKey ) const
81{
82 if( eType != Boolean )
83 throw Bu::ExceptionBase(
84 "Object entry requested from non-object json object."
85 );
86
87 return uDat.pObject->get( sKey );
88
89}
90
91int Bu::Json::getSize() const
92{
93 if( eType == Object )
94 return uDat.pObject->getSize();
95 else if( eType == Array )
96 return uDat.pArray->getSize();
97 else if( eType == String )
98 return uDat.pString->getSize();
99 else
100 throw Bu::ExceptionBase(
101 "Size requseted from json type that doesn't support it."
102 );
103}
104
105Bu::Json::iterator Bu::Json::begin()
106{
107 return uDat.pArray->begin();
108}
109
110Bu::Json::const_iterator Bu::Json::begin() const
111{
112 return uDat.pArray->begin();
113}
114
115Bu::Json::iterator Bu::Json::end()
116{
117 return uDat.pArray->end();
118}
119
120Bu::Json::const_iterator Bu::Json::end() const
121{
122 return uDat.pArray->end();
123}
124
31void Bu::Json::parse( Bu::Stream &sInput ) 125void Bu::Json::parse( Bu::Stream &sInput )
32{ 126{
33 reset(); 127 reset();
34 128
35 char c; 129 char c;
36 for(;;) 130 next("json");
131
132 parse( c, sInput );
133}
134
135void Bu::Json::parse( char &c, Bu::Stream &sInput )
136{
137 while( c == ' ' || c == '\t' || c == '\r' || c == '\n' )
37 { 138 {
38 next( "json" ); 139 next( "json" );
39 if( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) 140 }
40 continue; 141 if( c == '"' )
41 if( c == '"' ) 142 {
42 { 143 // String
43 // String 144 parseString( c, sInput );
44 parseString( c, sInput ); 145 }
45 } 146 else if( c == '{' )
46 else if( c == '{' ) 147 {
47 { 148 // Object
48 // Object 149 parseObject( c, sInput );
49 parseObject( c, sInput ); 150 }
50 } 151 else if( c == '[' )
51 else if( c == '[' ) 152 {
52 { 153 // Array
53 // Array 154 parseArray( c, sInput );
54 parseArray( c, sInput ); 155 }
55 } 156 else if( c == '-' || (c >= '0' && c <= '9') )
56 else if( c == '-' || (c >= '0' && c <= '9') ) 157 {
57 { 158 // Number -- apparently they can't start with a period
58 // Number -- apparently they can't start with a period 159 parseNumber( c, sInput );
59 parseNumber( c, sInput ); 160 }
60 } 161 else if( c == 't' || c == 'f' || c == 'n' )
61 else if( c == 't' || c == 'f' || c == 'n' ) 162 {
62 { 163 // True / false / null
63 // True / false / null 164 parseLiteral( c, sInput );
64 parseLiteral( c, sInput ); 165 }
65 } 166 else
167 {
168 throw Bu::ExceptionBase("Invalid characters in json stream.");
66 } 169 }
67} 170}
68 171
@@ -99,12 +202,112 @@ void Bu::Json::reset()
99 uDat.pObject = NULL; 202 uDat.pObject = NULL;
100} 203}
101 204
102void Bu::Json::parseString( char &c, Bu::Stream &sInput, Bu::String &sOut ) 205void Bu::Json::write( Bu::Stream &sOutput ) const
103{ 206{
104 while( c != '"' ) 207 switch( eType )
105 { 208 {
106 next( "string" ); 209 case Invalid:
210 throw Bu::ExceptionBase("Invalid type in json");
211 break;
212
213 case Object:
214 {
215 sOutput.write("{", 1 );
216 bool bFirst = true;
217 for( JsonHash::iterator i = uDat.pObject->begin(); i; i++ )
218 {
219 if( bFirst == true )
220 bFirst = false;
221 else
222 sOutput.write(",");
223 writeStr( i.getKey(), sOutput );
224 sOutput.write(":", 1 );
225 (*i)->write( sOutput );
226 }
227 sOutput.write("}", 1 );
228 }
229 break;
230
231 case Array:
232 {
233 sOutput.write("[", 1);
234 bool bFirst = true;
235 for( JsonList::iterator i = uDat.pArray->begin(); i; i++ )
236 {
237 if( bFirst == true )
238 bFirst = false;
239 else
240 sOutput.write(",", 1 );
241 (*i)->write( sOutput );
242 }
243 sOutput.write("]", 1);
244 }
245 break;
246
247 case String:
248 writeStr( *uDat.pString, sOutput );
249 break;
250
251 case Number:
252 sOutput.write(Bu::String("%1").arg( uDat.dNumber ));
253 break;
254
255 case Boolean:
256 if( uDat.bBoolean )
257 sOutput.write("true", 4 );
258 else
259 sOutput.write("false", 5 );
260 break;
261
262 case Null:
263 sOutput.write("null", 4 );
264 break;
107 } 265 }
266}
267
268void Bu::Json::writeStable( Bu::Stream &sOutput ) const
269{
270 if( eType == Object )
271 {
272 sOutput.write("{", 1 );
273 bool bFirst = true;
274 Bu::List<Bu::String> lKey = uDat.pObject->getKeys();
275 lKey.sort();
276 for( Bu::List<Bu::String>::iterator i = lKey.begin(); i; i++ )
277 {
278 if( bFirst == true )
279 bFirst = false;
280 else
281 sOutput.write(",");
282 writeStr( *i, sOutput );
283 sOutput.write(":", 1 );
284 uDat.pObject->get( *i )->write( sOutput );
285 }
286 sOutput.write("}", 1 );
287 }
288 else
289 {
290 write( sOutput );
291 }
292}
293
294Bu::String Bu::Json::toString() const
295{
296 Bu::MemBuf mb;
297 write( mb );
298 return mb.getString();
299}
300
301Bu::String Bu::Json::toStringStable() const
302{
303 Bu::MemBuf mb;
304 writeStable( mb );
305 return mb.getString();
306}
307
308void Bu::Json::parseString( char &c, Bu::Stream &sInput, Bu::String &sOut )
309{
310 skipWs( c, sInput );
108 bool bEscape = false; 311 bool bEscape = false;
109 for(;;) 312 for(;;)
110 { 313 {
@@ -157,6 +360,7 @@ void Bu::Json::parseString( char &c, Bu::Stream &sInput, Bu::String &sOut )
157 bEscape = true; 360 bEscape = true;
158 if( c == '"' ) 361 if( c == '"' )
159 { 362 {
363 readChar( c, sInput );
160 break; 364 break;
161 } 365 }
162 sOut += c; 366 sOut += c;
@@ -173,11 +377,7 @@ void Bu::Json::parseString( char &c, Bu::Stream &sInput )
173 377
174void Bu::Json::parseObject( char &c, Bu::Stream &sInput ) 378void Bu::Json::parseObject( char &c, Bu::Stream &sInput )
175{ 379{
176 while( c != '{' ) 380 skipWs( c, sInput );
177 {
178 next( "object" );
179 }
180
181 eType = Object; 381 eType = Object;
182 uDat.pObject = new JsonHash(); 382 uDat.pObject = new JsonHash();
183 383
@@ -186,17 +386,21 @@ void Bu::Json::parseObject( char &c, Bu::Stream &sInput )
186 { 386 {
187 Bu::String sKey; 387 Bu::String sKey;
188 parseString( c, sInput, sKey ); 388 parseString( c, sInput, sKey );
189 next( "object" ); 389 skipWs( c, sInput );
190 if( c != ':' ) 390 if( c != ':' )
191 { 391 {
192 throw Bu::ExceptionBase( 392 throw Bu::ExceptionBase(
193 "Invalid json, expected colon after key in object." 393 "Invalid json, expected colon after key in object."
194 ); 394 );
195 } 395 }
196 uDat.pObject->insert( sKey, new Json( sInput ) ); 396 next("object");
197 next( "object" ); 397 uDat.pObject->insert( sKey, new Json( c, sInput ) );
398 skipWs( c, sInput );
198 if( c == '}' ) 399 if( c == '}' )
400 {
401 readChar( c, sInput );
199 break; 402 break;
403 }
200 else if( c == ',' ) 404 else if( c == ',' )
201 next( "object" ); 405 next( "object" );
202 else 406 else
@@ -208,23 +412,27 @@ void Bu::Json::parseObject( char &c, Bu::Stream &sInput )
208 412
209void Bu::Json::parseArray( char &c, Bu::Stream &sInput ) 413void Bu::Json::parseArray( char &c, Bu::Stream &sInput )
210{ 414{
211 while( c != '[' ) 415 skipWs( c, sInput );
212 {
213 next( "array" );
214 }
215 416
216 eType = Array; 417 eType = Array;
217 uDat.pArray = new JsonList(); 418 uDat.pArray = new JsonList();
218 419
219 next( "array" ); 420 next("array");
421
220 for(;;) 422 for(;;)
221 { 423 {
222 uDat.pArray->append( new Json( sInput ) ); 424 uDat.pArray->append( new Json( c, sInput ) );
223 next( "array" ); 425 skipWs( c, sInput );
224 if( c == ']' ) 426 if( c == ']' )
427 {
428 readChar( c, sInput );
225 break; 429 break;
430 }
226 else if( c == ',' ) 431 else if( c == ',' )
432 {
433 next("array");
227 continue; 434 continue;
435 }
228 else 436 else
229 { 437 {
230 throw Bu::ExceptionBase( 438 throw Bu::ExceptionBase(
@@ -236,10 +444,7 @@ void Bu::Json::parseArray( char &c, Bu::Stream &sInput )
236 444
237void Bu::Json::parseNumber( char &c, Bu::Stream &sInput ) 445void Bu::Json::parseNumber( char &c, Bu::Stream &sInput )
238{ 446{
239 while( c != '-' && c < '0' && c > '9' ) 447 skipWs( c, sInput );
240 {
241 next( "number" );
242 }
243 448
244 Bu::String sBuf; 449 Bu::String sBuf;
245 if( c == '-' ) 450 if( c == '-' )
@@ -274,10 +479,7 @@ void Bu::Json::parseNumber( char &c, Bu::Stream &sInput )
274 479
275void Bu::Json::parseLiteral( char &c, Bu::Stream &sInput ) 480void Bu::Json::parseLiteral( char &c, Bu::Stream &sInput )
276{ 481{
277 while( c != 'f' && c != 't' && c != 'n' ) 482 skipWs( c, sInput );
278 {
279 next( "literal" );
280 }
281 483
282 Bu::String s; 484 Bu::String s;
283 do 485 do
@@ -329,3 +531,57 @@ bool Bu::Json::isWs( char c )
329 return c == ' ' || c == '\t' || c == '\r' || c == '\n'; 531 return c == ' ' || c == '\t' || c == '\r' || c == '\n';
330} 532}
331 533
534void Bu::Json::skipWs( char &c, Bu::Stream &sInput )
535{
536 while( isWs( c ) )
537 {
538 next("whitespace");
539 }
540}
541
542void Bu::Json::writeStr( const Bu::String &sStr, Bu::Stream &sOutput ) const
543{
544 sOutput.write("\"", 1 );
545 for( Bu::String::const_iterator i = sStr.begin(); i; i++ )
546 {
547 switch( *i )
548 {
549 case '"':
550 sOutput.write("\\\"", 2 );
551 break;
552
553 case '\\':
554 sOutput.write("\\\\", 2 );
555 break;
556
557 case '/':
558 sOutput.write("\\/", 2 );
559 break;
560
561 case '\b':
562 sOutput.write("\\b", 2 );
563 break;
564
565 case '\f':
566 sOutput.write("\\f", 2 );
567 break;
568
569 case '\n':
570 sOutput.write("\\n", 2 );
571 break;
572
573 case '\r':
574 sOutput.write("\\r", 2 );
575 break;
576
577 case '\t':
578 sOutput.write("\\t", 2 );
579 break;
580
581 default:
582 sOutput.write( &(*i), 1 );
583 }
584 }
585 sOutput.write("\"", 1 );
586}
587
diff --git a/src/unstable/json.h b/src/unstable/json.h
index 47009cb..2d1770d 100644
--- a/src/unstable/json.h
+++ b/src/unstable/json.h
@@ -11,7 +11,14 @@ namespace Bu
11 11
12 class Json 12 class Json
13 { 13 {
14 private:
15 Json( char &c, Bu::Stream &sInput );
16 typedef Bu::Hash<Bu::String, Json *> JsonHash;
17 typedef Bu::List<Json *> JsonList;
18
14 public: 19 public:
20 typedef JsonList::iterator iterator;
21 typedef JsonList::const_iterator const_iterator;
15 enum Type 22 enum Type
16 { 23 {
17 Invalid, 24 Invalid,
@@ -29,10 +36,28 @@ namespace Bu
29 Json( Bu::Stream &sInput ); 36 Json( Bu::Stream &sInput );
30 virtual ~Json(); 37 virtual ~Json();
31 38
39 Type getType() const;
40 Bu::String getString() const;
41 double getNumber() const;
42 bool getBoolean() const;
43 bool isNull() const;
44 Json *operator[]( const Bu::String &sKey ) const;
45 int getSize() const;
46 iterator begin();
47 const_iterator begin() const;
48 iterator end();
49 const_iterator end() const;
50
32 void parse( Bu::Stream &sInput ); 51 void parse( Bu::Stream &sInput );
33 void reset(); 52 void reset();
34 53
54 void write( Bu::Stream &sOutput ) const;
55 void writeStable( Bu::Stream &sOutput ) const;
56 Bu::String toString() const;
57 Bu::String toStringStable() const;
58
35 private: 59 private:
60 void parse( char &c, Bu::Stream &sInput );
36 void parseString( char &c, Bu::Stream &sInput, Bu::String &sOut ); 61 void parseString( char &c, Bu::Stream &sInput, Bu::String &sOut );
37 void parseString( char &c, Bu::Stream &sInput ); 62 void parseString( char &c, Bu::Stream &sInput );
38 void parseObject( char &c, Bu::Stream &sInput ); 63 void parseObject( char &c, Bu::Stream &sInput );
@@ -42,11 +67,10 @@ namespace Bu
42 bool readChar( char &c, Bu::Stream &sInput ); 67 bool readChar( char &c, Bu::Stream &sInput );
43 void readChar( char &c, Bu::Stream &sInput, const char *sSection ); 68 void readChar( char &c, Bu::Stream &sInput, const char *sSection );
44 bool isWs( char c ); 69 bool isWs( char c );
70 void skipWs( char &c, Bu::Stream &sInput );
71 void writeStr( const Bu::String &sStr, Bu::Stream &sOutput ) const;
45 72
46 private: 73 private:
47 typedef Bu::Hash<Bu::String, Json *> JsonHash;
48 typedef Bu::List<Json *> JsonList;
49
50 Type eType; 74 Type eType;
51 union DatUnion 75 union DatUnion
52 { 76 {