@@ -168,64 +168,195 @@ void fossil_jellyfish_dump(const fossil_jellyfish_chain *chain) {
168168 }
169169}
170170
171+ static bool match_key (const char * * ptr , const char * key ) {
172+ while (* * ptr && (* * ptr == ' ' || * * ptr == '\n' || * * ptr == '\r' || * * ptr == '\t' )) (* ptr )++ ;
173+ size_t len = strlen (key );
174+ if (strncmp (* ptr , key , len ) == 0 ) {
175+ * ptr += len ;
176+ return true;
177+ }
178+ return false;
179+ }
180+
181+ static void skip_whitespace (const char * * ptr ) {
182+ while (* * ptr && (* * ptr == ' ' || * * ptr == '\n' || * * ptr == '\r' || * * ptr == '\t' )) (* ptr )++ ;
183+ }
184+
185+ static bool parse_string (const char * * ptr , char * out , size_t maxlen ) {
186+ skip_whitespace (ptr );
187+ if (* * ptr != '\"' ) return false;
188+ (* ptr )++ ;
189+ size_t i = 0 ;
190+ while (* * ptr && * * ptr != '\"' && i < maxlen - 1 ) {
191+ out [i ++ ] = * (* ptr )++ ;
192+ }
193+ if (* * ptr != '\"' ) return false;
194+ (* ptr )++ ;
195+ out [i ] = '\0' ;
196+ return true;
197+ }
198+
199+ static bool parse_number (const char * * ptr , double * out_double , long * out_long ) {
200+ skip_whitespace (ptr );
201+ char buffer [64 ];
202+ size_t i = 0 ;
203+ while ((* * ptr >= '0' && * * ptr <= '9' ) || * * ptr == '.' || * * ptr == '-' ) {
204+ if (i < sizeof (buffer ) - 1 )
205+ buffer [i ++ ] = * (* ptr )++ ;
206+ else
207+ return false;
208+ }
209+ buffer [i ] = '\0' ;
210+
211+ if (strchr (buffer , '.' )) {
212+ if (out_double ) * out_double = atof (buffer );
213+ } else {
214+ if (out_long ) * out_long = atol (buffer );
215+ }
216+
217+ return true;
218+ }
219+
171220int fossil_jellyfish_load (fossil_jellyfish_chain * chain , const char * filepath ) {
172221 FILE * fp = fopen (filepath , "rb" );
173222 if (!fp ) return 0 ;
174223
175- // Optional: check file signature
176- char sig [4 ];
177- if (fread (sig , sizeof (char ), 4 , fp ) != 4 ) {
224+ fseek (fp , 0 , SEEK_END );
225+ long fsize = ftell (fp );
226+ fseek (fp , 0 , SEEK_SET );
227+
228+ char * data = malloc (fsize + 1 );
229+ if (!data ) {
178230 fclose (fp );
179231 return 0 ;
180232 }
181- if (sig [0 ] != 'J' || sig [1 ] != 'F' || sig [2 ] != 'S' || sig [3 ] != '1' ) {
182- fclose (fp );
233+
234+ fread (data , 1 , fsize , fp );
235+ fclose (fp );
236+ data [fsize ] = '\0' ;
237+
238+ const char * ptr = data ;
239+ skip_whitespace (& ptr );
240+
241+ // Look for signature
242+ if (!match_key (& ptr , "{\"signature\":" )) {
243+ free (data );
183244 return 0 ;
184245 }
185246
186- size_t count = 0 ;
187- if (fread ( & count , sizeof (size_t ), 1 , fp ) != 1 ) {
188- fclose ( fp );
247+ char sig [ 8 ] ;
248+ if (! parse_string ( & ptr , sig , sizeof (sig )) || strcmp ( sig , "JFS1" ) != 0 ) {
249+ free ( data );
189250 return 0 ;
190251 }
191252
192- if (count > FOSSIL_JELLYFISH_MAX_MEM ) {
193- fclose ( fp );
253+ if (! match_key ( & ptr , ",\"blocks\":[" ) ) {
254+ free ( data );
194255 return 0 ;
195256 }
196257
197- if (fread (chain -> memory , sizeof (fossil_jellyfish_block ), count , fp ) != count ) {
198- fclose (fp );
199- return 0 ;
258+ size_t count = 0 ;
259+ while (* ptr && * ptr != ']' ) {
260+ if (count >= FOSSIL_JELLYFISH_MAX_MEM ) break ;
261+
262+ if (!match_key (& ptr , "{" )) {
263+ free (data );
264+ return 0 ;
265+ }
266+
267+ fossil_jellyfish_block * block = & chain -> memory [count ];
268+ memset (block , 0 , sizeof (* block ));
269+
270+ if (!match_key (& ptr , "\"input\":" )) {
271+ free (data );
272+ return 0 ;
273+ }
274+ if (!parse_string (& ptr , block -> input , sizeof (block -> input ))) {
275+ free (data );
276+ return 0 ;
277+ }
278+
279+ if (!match_key (& ptr , ",\"output\":" )) {
280+ free (data );
281+ return 0 ;
282+ }
283+ if (!parse_string (& ptr , block -> output , sizeof (block -> output ))) {
284+ free (data );
285+ return 0 ;
286+ }
287+
288+ if (!match_key (& ptr , ",\"timestamp\":" )) {
289+ free (data );
290+ return 0 ;
291+ }
292+ if (!parse_number (& ptr , NULL , & block -> timestamp )) {
293+ free (data );
294+ return 0 ;
295+ }
296+
297+ if (!match_key (& ptr , ",\"confidence\":" )) {
298+ free (data );
299+ return 0 ;
300+ }
301+ if (!parse_number (& ptr , & block -> confidence , NULL )) {
302+ free (data );
303+ return 0 ;
304+ }
305+
306+ if (!match_key (& ptr , "}" )) {
307+ free (data );
308+ return 0 ;
309+ }
310+
311+ count ++ ;
312+
313+ skip_whitespace (& ptr );
314+ if (* ptr == ',' ) ptr ++ ;
200315 }
201316
202317 chain -> count = count ;
203- fclose ( fp );
318+ free ( data );
204319 return 1 ;
205320}
206321
207322int fossil_jellyfish_save (const fossil_jellyfish_chain * chain , const char * filepath ) {
208323 FILE * fp = fopen (filepath , "wb" );
209324 if (!fp ) return 0 ;
210325
211- // Optional: write file signature
212- const char sig [4 ] = { 'J' , 'F' , 'S' , '1' };
213- if (fwrite (sig , sizeof (char ), 4 , fp ) != 4 ) {
214- fclose (fp );
215- return 0 ;
216- }
326+ fprintf (fp , "{\n" );
327+ fprintf (fp , " \"signature\": \"JFS1\",\n" );
328+ fprintf (fp , " \"blocks\": [\n" );
217329
218- if (fwrite (& chain -> count , sizeof (size_t ), 1 , fp ) != 1 ) {
219- fclose (fp );
220- return 0 ;
221- }
330+ for (size_t i = 0 ; i < chain -> count ; ++ i ) {
331+ const fossil_jellyfish_block * block = & chain -> memory [i ];
332+
333+ // Escape JSON strings (only for quote and backslash for simplicity)
334+ char input_escaped [512 ] = {0 }, output_escaped [512 ] = {0 };
335+ char * dst = input_escaped ;
336+ for (const char * src = block -> input ; * src && (dst - input_escaped ) < sizeof (input_escaped ) - 2 ; ++ src ) {
337+ if (* src == '"' || * src == '\\' ) * dst ++ = '\\' ;
338+ * dst ++ = * src ;
339+ }
340+ * dst = '\0' ;
222341
223- if (fwrite (chain -> memory , sizeof (fossil_jellyfish_block ), chain -> count , fp ) != chain -> count ) {
224- fclose (fp );
225- return 0 ;
342+ dst = output_escaped ;
343+ for (const char * src = block -> output ; * src && (dst - output_escaped ) < sizeof (output_escaped ) - 2 ; ++ src ) {
344+ if (* src == '"' || * src == '\\' ) * dst ++ = '\\' ;
345+ * dst ++ = * src ;
346+ }
347+ * dst = '\0' ;
348+
349+ fprintf (fp , " {\n" );
350+ fprintf (fp , " \"input\": \"%s\",\n" , input_escaped );
351+ fprintf (fp , " \"output\": \"%s\",\n" , output_escaped );
352+ fprintf (fp , " \"timestamp\": %ld,\n" , block -> timestamp );
353+ fprintf (fp , " \"confidence\": %.6f\n" , block -> confidence );
354+ fprintf (fp , " }%s\n" , (i < chain -> count - 1 ) ? "," : "" );
226355 }
227356
228- fflush (fp );
357+ fprintf (fp , " ]\n" );
358+ fprintf (fp , "}\n" );
359+
229360 fclose (fp );
230361 return 1 ;
231362}
0 commit comments