@@ -169,197 +169,117 @@ void fossil_jellyfish_dump(const fossil_jellyfish_chain *chain) {
169169 }
170170}
171171
172- static void skip_whitespace (const char * * ptr ) {
173- while (isspace (* * ptr )) (* ptr )++ ;
174- }
175-
176- static bool match_key (const char * * ptr , const char * key ) {
177- skip_whitespace (ptr );
178- size_t len = strlen (key );
179- if (strncmp (* ptr , key , len ) == 0 ) {
180- * ptr += len ;
181- return true;
182- }
183- return false;
184- }
185-
186- static bool parse_string (const char * * ptr , char * out , size_t maxlen ) {
187- skip_whitespace (ptr );
188- if (* * ptr != '"' ) return false;
189- (* ptr )++ ;
190- size_t i = 0 ;
191- while (* * ptr && * * ptr != '"' && i < maxlen - 1 ) {
192- if (* * ptr == '\\' ) (* ptr )++ ; // Skip escape char
193- out [i ++ ] = * (* ptr )++ ;
194- }
195- if (* * ptr != '"' ) return false;
196- (* ptr )++ ;
197- out [i ] = '\0' ;
198- return true;
199- }
200-
201- static bool parse_hash (const char * * ptr , uint8_t * hash_out ) {
202- char hexstr [FOSSIL_JELLYFISH_HASH_SIZE * 2 + 1 ] = {0 };
203- if (!parse_string (ptr , hexstr , sizeof (hexstr ))) return false;
204-
205- if (strlen (hexstr ) != FOSSIL_JELLYFISH_HASH_SIZE * 2 ) return false;
206-
207- for (size_t i = 0 ; i < FOSSIL_JELLYFISH_HASH_SIZE ; ++ i ) {
208- char byte [3 ] = { hexstr [i * 2 ], hexstr [i * 2 + 1 ], 0 };
209- hash_out [i ] = (uint8_t )strtoul (byte , NULL , 16 );
210- }
211- return true;
212- }
213-
214- static bool parse_number (const char * * ptr , double * out_d , uint64_t * out_u64 , int * out_i , uint32_t * out_u32 ) {
215- skip_whitespace (ptr );
216- char buffer [64 ];
217- size_t i = 0 ;
218- while ((isdigit (* * ptr ) || * * ptr == '.' || * * ptr == '-' ) && i < sizeof (buffer ) - 1 )
219- buffer [i ++ ] = * (* ptr )++ ;
220- buffer [i ] = '\0' ;
221-
222- if (out_d ) * out_d = atof (buffer );
223- if (out_u64 ) * out_u64 = strtoull (buffer , NULL , 10 );
224- if (out_i ) * out_i = atoi (buffer );
225- if (out_u32 ) * out_u32 = (uint32_t )strtoul (buffer , NULL , 10 );
226-
227- return i > 0 ;
228- }
229-
230172int fossil_jellyfish_load (fossil_jellyfish_chain * chain , const char * filepath ) {
231173 FILE * fp = fopen (filepath , "rb" );
232174 if (!fp ) return 0 ;
233175
234176 fseek (fp , 0 , SEEK_END );
235- long fsize = ftell (fp );
177+ long length = ftell (fp );
236178 fseek (fp , 0 , SEEK_SET );
237179
238- char * data = malloc (fsize + 1 );
239- if (!data ) {
180+ if (length <= 0 || (size_t )length > 1024 * 1024 ) {
240181 fclose (fp );
241182 return 0 ;
242183 }
243184
244- fread (data , 1 , fsize , fp );
245- data [fsize ] = '\0' ;
246- fclose (fp );
247-
248- const char * ptr = data ;
249- skip_whitespace (& ptr );
185+ char * buffer = malloc (length + 1 );
186+ if (!buffer ) {
187+ fclose (fp );
188+ return 0 ;
189+ }
250190
251- if (!match_key (& ptr , "{\"signature\":" )) { free (data ); return 0 ; }
191+ if (fread (buffer , 1 , length , fp ) != (size_t )length ) {
192+ free (buffer );
193+ fclose (fp );
194+ return 0 ;
195+ }
196+ buffer [length ] = '\0' ;
197+ fclose (fp );
252198
253- char sig [ 8 ] ;
254- if (!parse_string (& ptr , sig , sizeof ( sig )) || strcmp ( sig , "JFS1" ) != 0 ) {
255- free (data );
199+ const char * ptr = buffer ;
200+ if (!match_key_value (& ptr , "signature" , "JFS1" )) {
201+ free (buffer );
256202 return 0 ;
257203 }
258204
259- if (!match_key (& ptr , ",\"blocks\":[" )) { free (data ); return 0 ; }
205+ if (!skip_key (& ptr , "blocks" ) || !skip_symbol (& ptr , '[' )) {
206+ free (buffer );
207+ return 0 ;
208+ }
260209
261210 size_t count = 0 ;
262- bool ok = true;
263-
264- while (* ptr && * ptr != ']' ) {
265- if (count >= FOSSIL_JELLYFISH_MAX_MEM ) break ;
266-
211+ while (* ptr && * ptr != ']' && count < FOSSIL_JELLYFISH_MAX_MEM ) {
267212 fossil_jellyfish_block * b = & chain -> memory [count ];
268213 memset (b , 0 , sizeof (* b ));
269214
270- if (!match_key (& ptr , "{" )) { ok = false; break ; }
271-
272- if (!match_key (& ptr , "\"input\":" )) { ok = false; break ; }
273- if (!parse_string (& ptr , b -> input , sizeof (b -> input ))) { ok = false; break ; }
274-
275- if (!match_key (& ptr , ",\"output\":" )) { ok = false; break ; }
276- if (!parse_string (& ptr , b -> output , sizeof (b -> output ))) { ok = false; break ; }
277-
278- if (!match_key (& ptr , ",\"hash\":" )) { ok = false; break ; }
279- if (!parse_hash (& ptr , b -> hash )) { ok = false; break ; }
215+ if (!skip_symbol (& ptr , '{' )) break ;
216+ if (!parse_string_field (& ptr , "input" , b -> input , sizeof (b -> input ))) break ;
217+ if (!parse_string_field (& ptr , "output" , b -> output , sizeof (b -> output ))) break ;
218+ if (!parse_number_field (& ptr , "timestamp" , NULL , & b -> timestamp , NULL , NULL )) break ;
280219
281- if (!match_key (& ptr , ",\"timestamp\":" )) { ok = false; break ; }
282- if (!parse_number (& ptr , NULL , & b -> timestamp , NULL , NULL )) { ok = false; break ; }
220+ double temp_conf = 0 ;
221+ if (!parse_number_field (& ptr , "confidence" , & temp_conf , NULL , NULL , NULL )) break ;
222+ b -> confidence = (float )temp_conf ;
283223
284- if (!match_key (& ptr , ",\"valid\":" )) { ok = false; break ; }
285- if (! parse_number ( & ptr , NULL , NULL , & b -> valid , NULL )) { ok = false; break ; }
224+ if (!parse_number_field (& ptr , "usage_count" , NULL , NULL , NULL , & b -> usage_count )) break ;
225+ b -> valid = 1 ;
286226
287- if (!match_key (& ptr , ",\"confidence\":" )) { ok = false; break ; }
288- if (!parse_number (& ptr , & b -> confidence , NULL , NULL , NULL )) { ok = false; break ; }
227+ if (!skip_symbol (& ptr , '}' )) break ;
289228
290- if (!match_key (& ptr , ",\"usage_count\":" )) { ok = false; break ; }
291- if (!parse_number (& ptr , NULL , NULL , NULL , & b -> usage_count )) { ok = false; break ; }
292-
293- if (!match_key (& ptr , "}" )) { ok = false; break ; }
294-
295- count ++ ;
296- skip_whitespace (& ptr );
297- if (* ptr == ',' ) ptr ++ ;
229+ skip_comma (& ptr );
230+ ++ count ;
298231 }
299232
300- free (data );
301- chain -> count = ok ? count : 0 ;
302- return ok ? 1 : 0 ;
303- }
304-
305- static void hex_encode (const uint8_t * hash , size_t len , char * out , size_t out_size ) {
306- const char * hex = "0123456789abcdef" ;
307- if (out_size < len * 2 + 1 ) return ;
308- for (size_t i = 0 ; i < len ; ++ i ) {
309- out [i * 2 ] = hex [(hash [i ] >> 4 ) & 0xF ];
310- out [i * 2 + 1 ] = hex [hash [i ] & 0xF ];
233+ if (!skip_symbol (& ptr , ']' ) || !skip_symbol (& ptr , '}' )) {
234+ free (buffer );
235+ return 0 ;
311236 }
312- out [len * 2 ] = '\0' ;
237+
238+ chain -> count = count ;
239+ free (buffer );
240+ return 1 ;
313241}
314242
315243int fossil_jellyfish_save (const fossil_jellyfish_chain * chain , const char * filepath ) {
316244 FILE * fp = fopen (filepath , "wb" );
317245 if (!fp ) return 0 ;
318246
319- fprintf (fp , "{\n \"signature\": \"JFS1\",\n \"blocks\": [\n" );
247+ fprintf (fp , "{\n" );
248+ fprintf (fp , " \"signature\": \"JFS1\",\n" );
249+ fprintf (fp , " \"blocks\": [\n" );
320250
321251 for (size_t i = 0 ; i < chain -> count ; ++ i ) {
322252 const fossil_jellyfish_block * b = & chain -> memory [i ];
323253
324- // Escape input/output strings (basic)
325- char input_escaped [FOSSIL_JELLYFISH_INPUT_SIZE * 2 ] = {0 };
326- char output_escaped [FOSSIL_JELLYFISH_OUTPUT_SIZE * 2 ] = {0 };
254+ char input_escaped [2 * FOSSIL_JELLYFISH_INPUT_SIZE ] = {0 };
255+ char output_escaped [2 * FOSSIL_JELLYFISH_OUTPUT_SIZE ] = {0 };
327256
328- char * dst = input_escaped ;
329- // Escape input
330257 char * dst = input_escaped ;
331258 for (const char * src = b -> input ; * src && (size_t )(dst - input_escaped ) < sizeof (input_escaped ) - 2 ; ++ src ) {
332259 if (* src == '"' || * src == '\\' ) * dst ++ = '\\' ;
333260 * dst ++ = * src ;
334261 }
335262 * dst = '\0' ;
336-
337- // Escape output
263+
338264 dst = output_escaped ;
339265 for (const char * src = b -> output ; * src && (size_t )(dst - output_escaped ) < sizeof (output_escaped ) - 2 ; ++ src ) {
340266 if (* src == '"' || * src == '\\' ) * dst ++ = '\\' ;
341267 * dst ++ = * src ;
342268 }
343269 * dst = '\0' ;
344270
345- char hash_hex [FOSSIL_JELLYFISH_HASH_SIZE * 2 + 1 ];
346- hex_encode (b -> hash , FOSSIL_JELLYFISH_HASH_SIZE , hash_hex , sizeof (hash_hex ));
347-
348- fprintf (fp ,
349- " {\n"
350- " \"input\": \"%s\",\n"
351- " \"output\": \"%s\",\n"
352- " \"hash\": \"%s\",\n"
353- " \"timestamp\": %" PRIu64 ",\n"
354- " \"valid\": %d,\n"
355- " \"confidence\": %.6f,\n"
356- " \"usage_count\": %u\n"
357- " }%s\n" ,
358- input_escaped , output_escaped , hash_hex , b -> timestamp , b -> valid , b -> confidence , b -> usage_count ,
359- (i < chain -> count - 1 ) ? "," : "" );
271+ fprintf (fp , " {\n" );
272+ fprintf (fp , " \"input\": \"%s\",\n" , input_escaped );
273+ fprintf (fp , " \"output\": \"%s\",\n" , output_escaped );
274+ fprintf (fp , " \"timestamp\": %" PRIu64 ",\n" , b -> timestamp );
275+ fprintf (fp , " \"confidence\": %.6f,\n" , b -> confidence );
276+ fprintf (fp , " \"usage_count\": %" PRIu32 "\n" , b -> usage_count );
277+ fprintf (fp , " }%s\n" , (i < chain -> count - 1 ) ? "," : "" );
360278 }
361279
362- fprintf (fp , " ]\n}\n" );
280+ fprintf (fp , " ]\n" );
281+ fprintf (fp , "}\n" );
282+
363283 fclose (fp );
364284 return 1 ;
365285}
0 commit comments