001 #define J_STR 1
002 #define J_NUM 2
003 #define J_ARRAY 3
004 #define J_NODE 4
005 class CJson {
006   I64 type;
007   union {
008     U8 *str;
009     F64 num;
010     CHashTable *hash_table;
011   }
012   I64 cnt;
013   Bool used;
014 };
015 
016 U8 *sqstring(U8 *ptr,U8 **en) {
017   U8 *buf=MAlloc(1024);
018   I64 ch=0,C;
019   if(en) *en=ptr;
020   if(*ptr!='\'') return NULL;
021   ptr++;
022   while(*ptr&&*ptr!='\'') {
023     if(*ptr=='\\') {
024       ptr++;
025 read_one:
026       if(ch<1023)
027         buf[ch++]=*(ptr++);
028       else
029         ptr++;
030     } else
031         goto read_one;
032   }
033   buf[ch]=0;
034   if(en) *en=ptr+1;
035   return buf;
036 }
037 
038 U8 *dqstring(U8 *ptr,U8 **en) {
039   U8 *buf=MAlloc(1024);
040   I64 ch=0,C;
041   if(en) *en=ptr;
042   if(*ptr!='"') return NULL;
043   ptr++;
044   while(*ptr&&*ptr!='"') {
045     if(*ptr=='\\') {
046       ptr++;
047 read_one:
048       if(ch<1023)
049         buf[ch++]=*(ptr++);
050       else
051         ptr++;
052     } else
053         goto read_one;
054   }
055   buf[ch]=0;
056   if(en) *en=ptr+1;
057   return buf;
058 }
059 
060 
061 U8 *SkipWhitespace(U8 *s) {
062   while(*s&&Bt(char_bmp_white_space,*s))
063     s++;
064   return s;
065 }
066 U8 *word(U8 *ptr,U8 **en) {
067   U8 *buf=MAlloc(256);
068   I64 ch=0;
069   if(en) *en=ptr;
070   if(!Bt(char_bmp_alpha_numeric,*ptr)) return NULL;
071   while(Bt(char_bmp_alpha_numeric,*ptr))
072     buf[ch++]=*ptr++;
073   buf[ch]=0;
074   if(en) *en=ptr;
075   return buf;
076 }
077 class CIndexBlk {
078     I32 inst_cnt;
079     I64 inst_offsets[9];
080     I64 inst_flags[9];
081     U32 translation_idx;
082     U32 body['z'-'a'+1];
083 };
084 CJson *ParseJson(U8 *st,U8 **en=NULL) {
085   CJson *ret=NULL;
086   U8 *name;
087   CHashGeneric *g;
088   st=SkipWhitespace(st);
089   if(*st=='{') {
090     ret=CAlloc(sizeof CJson);
091     ret->type=J_NODE;
092     ret->hash_table=HashTableNew(0x8);
093     st=SkipWhitespace(st+1);
094     while(*st!='}') {
095       if(!*st) throw('JSON');
096       switch(*st) {
097         case '\'':
098         name=sqstring(st,&st);
099         break;
100         case '"':
101         name=dqstring(st,&st);
102         break;
103         default:
104         name=word(st,&st);
105         break;
106       }
107       if(!name) throw('JSON');
108       st=StrFirstOcc(st,":");
109       if(!st) throw('JSON');
110       st++;
111       g=CAlloc(sizeof CHashGeneric);
112       g->str=name;
113       g->type=HTT_FRAME_PTR;
114       g->user_data0=ParseJson(st,&st);
115       HashAdd(g,ret->hash_table);
116       st=StrFirstOcc(st,",}");
117       if(!st) throw('JSON');
118       if(*st==',') st++;
119       st=SkipWhitespace(st);
120     }
121     st++;
122   } else if(*st=='\'') {
123     ret=CAlloc(sizeof CJson);
124     ret->type=J_STR;
125     ret->str=sqstring(st,&st);
126   } else if(*st=='\"') {
127     ret=CAlloc(sizeof CJson);
128     ret->type=J_STR;
129     ret->str=dqstring(st,&st);
130   } else if(*st=='[') {
131     st=SkipWhitespace(st+1);
132     ret=CAlloc(sizeof CJson);
133     ret->type=J_ARRAY;
134     ret->hash_table=HashTableNew(0x10);
135     while(*st!=']') {
136       g=CAlloc(sizeof CHashGeneric);
137       g->str=MStrPrint("%d",ret->cnt++);
138       g->type=HTT_FRAME_PTR;
139       g->user_data0=ParseJson(st,&st);
140       HashAdd(g,ret->hash_table);
141       st=StrFirstOcc(st,",]");
142       if(!st) throw('JSON');
143       if(*st==',') st++;
144       st=SkipWhitespace(st);
145     }
146     st++;
147   } else {
148     name=st;
149     ret=CAlloc(sizeof CJson);
150     ret->type=J_NUM;
151     ret->num=Str2F64(st,&st);
152     if(name==st)
153       throw('JSON');
154   }
155   if(en) *en=st;
156   if(!ret) throw('JSON');
157   return ret;
158 }
159 U0 JsonDel(CJson *j) {
160   I64 bucket;
161   CHashGeneric *g;
162   switch(j->type) {
163     case J_STR:
164       Free(j->str);
165       break;
166     case J_NUM:
167       break;
168     case J_NODE:
169     case J_ARRAY:
170       for(bucket=0;bucket<=j->hash_table->mask;bucket++)
171         for(g=j->hash_table->body[bucket];g;g=g->next) {
172           JsonDel(g->user_data0);
173         }
174       HashTableDel(j->hash_table);
175   }
176   Free(j);
177 }
178 U0 DumpJson(U8 *d=NULL,CJson *j) {
179   I64 bucket;
180   Bool first=TRUE;
181   U8 num[STR_LEN];
182   CHashGeneric *g;
183   switch(j->type) {
184     case J_STR:
185       CatPrint(d,"\"%Q\"",j->str);
186       break;
187     case J_NUM:
188       CatPrint(d,"%n",j->num);
189       break;
190     case J_NODE:
191       CatPrint(d,"{");
192       for(bucket=0;bucket<=j->hash_table->mask;bucket++)
193         for(g=j->hash_table->body[bucket];g;g=g->next) {
194           if(!first) CatPrint(d,",");
195           CatPrint(d,"%s:",g->str);
196           DumpJson(d,g->user_data0);
197           first=FALSE;
198         }
199       CatPrint(d,"}");
200       break;
201     case J_ARRAY:
202       CatPrint(d,"[");
203       for(bucket=0;bucket<=j->hash_table->mask;bucket++)
204         for(g=j->hash_table->body[bucket];g;g=g->next) {
205           if(!first) CatPrint(d,",");
206           DumpJson(d,g->user_data0);
207           first=FALSE;
208         }
209       CatPrint(d,"]");
210   }
211 }
212 Bool TrimJson0(CJson *j,I64 argc,U8 **argv) {
213   I64 i;
214   I64 bucket;
215   CHashGeneric *g;
216   Bool ret=FALSE;
217   if(j->type!=J_NODE&&j->type!=J_ARRAY) return FALSE;
218   CJson *sub;
219   for(bucket=0;bucket<=j->hash_table->mask;bucket++) {
220 again:;
221     for(g=j->hash_table->body[bucket];g;g=g->next) {
222       for(i=0;i!=argc;i++) {
223         if(!StrCmp(g->str,argv[i])) {
224           ret=TRUE;
225           goto skip;
226         }
227       }
228       sub=g->user_data0;
229       if(sub->used) {
230         goto skip;
231       }
232       if(!TrimJson0(sub,argc,argv)) {
233         HashRemDel(g,j->hash_table);
234         JsonDel(sub);
235       } else {
236         ret=TRUE;
237         sub->used=TRUE;
238       }
239       goto again;
240 skip:;
241     }
242   }
243   return ret;
244 } 
245 Bool TrimJson(CJson *j,...) {
246   return TrimJson0(j,argc,argv);
247 }
248 
249 U0 SubFBlkRead(CFile *file,CIndexBlk *out,I64 ptr)  {
250   U8 dummies[BLK_SIZE];
251   FBlkRead(file,&dummies,ptr>>1,1);
252   if(ptr&1)
253      MemCpy(out,&dummies[sizeof(CIndexBlk)],sizeof(CIndexBlk));
254   else
255      MemCpy(out,&dummies[0],sizeof(CIndexBlk));
256 }
257 U0 SubFBlkWrite(CFile *file,CIndexBlk *in,I64 ptr)  {
258   U8 dummies[BLK_SIZE];
259   MemSet(&dummies,0,BLK_SIZE);
260   FBlkRead(file,&dummies,ptr>>1,1);
261   if(ptr&1)
262      MemCpy(&dummies[sizeof(CIndexBlk)],in,sizeof(CIndexBlk));
263   else
264      MemCpy(&dummies[0],in,sizeof(CIndexBlk));
265   FBlkWrite(file,dummies,ptr>>1);
266 }
267 
268 I64 GetWordPtr(I64 *max,CFile *file,U8 *str,I64 ptr=0,Bool *new=NULL) {
269   if(new) *new=FALSE;
270   if(!*str) return ptr;
271   I64 idx=ToUpper(*str)-'A';
272   CIndexBlk dummy;
273   SubFBlkRead(file,&dummy,ptr);
274   if(!dummy.body[idx]) {
275     dummy.body[idx]=*max;
276     SubFBlkWrite(file,&dummy,ptr);
277     MemSet(&dummy,0,sizeof CIndexBlk);
278     SubFBlkWrite(file,&dummy,ptr=(*max)++);
279     if(new) *new=TRUE;
280     return GetWordPtr(max,file,str+1,ptr);
281   }
282   if(ptr>*max) throw('trie');
283   return GetWordPtr(max,file,str+1,dummy.body[idx],new);
284 }
285 CJson *GetJsonMember(CJson *j,U8 *member,I64 type=-1) {
286   CHashGeneric *g;
287   if(j->type==J_ARRAY||j->type==J_NODE) {
288     g=HashFind(member,j->hash_table,-1);
289     if(g) {
290       j=g->user_data0;
291       if(type==-1) {
292         return j;
293       }
294       else if(type==j->type) {
295         return j;
296       }
297     }
298   }
299   return NULL;
300 }