1818#include <stdlib.h>
1919#include <ctype.h>
2020#include <time.h>
21+ #include <math.h>
2122
2223
2324// HASH Algorithm magic
@@ -168,6 +169,17 @@ static bool skip_comma(const char **ptr) {
168169 return false;
169170}
170171
172+ int parse_hex_field (const char * * ptr , const char * key , uint8_t * out , size_t len ) {
173+ if (!skip_key (ptr , key ) || !skip_symbol (ptr , '"' )) return 0 ;
174+ for (size_t i = 0 ; i < len ; ++ i ) {
175+ unsigned int val = 0 ;
176+ if (sscanf (* ptr , "%2x" , & val ) != 1 ) return 0 ;
177+ if (out ) out [i ] = (uint8_t )val ;
178+ * ptr += 2 ;
179+ }
180+ return skip_symbol (ptr , '"' );
181+ }
182+
171183static bool parse_string_field (const char * * ptr , const char * key , char * out , size_t max ) {
172184 if (!skip_key (ptr , key )) return false;
173185 if (!skip_symbol (ptr , '"' )) return false;
@@ -392,80 +404,68 @@ int fossil_jellyfish_load(fossil_jellyfish_chain *chain, const char *filepath) {
392404 fclose (fp );
393405
394406 const char * ptr = buffer ;
395- if (!match_key_value (& ptr , "signature" , "JFS1" )) {
396- free (buffer );
397- return 0 ;
398- }
407+ int ok = 1 ; // unified success flag
399408
400- if (!skip_key (& ptr , "blocks" ) || !skip_symbol (& ptr , '[' )) {
401- free (buffer );
402- return 0 ;
409+ if (!match_key_value (& ptr , "signature" , "JFS1" )) ok = 0 ;
410+ if (ok ) skip_key_value (& ptr , "version" );
411+
412+ if (ok && (!skip_key (& ptr , "origin_device_id" ) || !skip_symbol (& ptr , '"' ))) ok = 0 ;
413+ if (ok ) {
414+ for (size_t i = 0 ; i < FOSSIL_DEVICE_ID_SIZE && ok ; ++ i ) {
415+ unsigned int val = 0 ;
416+ if (sscanf (ptr , "%2x" , & val ) != 1 ) ok = 0 ;
417+ chain -> device_id [i ] = (uint8_t )val ;
418+ ptr += 2 ;
419+ }
420+ if (!skip_symbol (& ptr , '"' )) ok = 0 ;
403421 }
404422
423+ if (ok ) ok = parse_number_field (& ptr , "created_at" , NULL , & chain -> created_at , NULL , NULL );
424+ if (ok ) ok = parse_number_field (& ptr , "updated_at" , NULL , & chain -> updated_at , NULL , NULL );
425+
426+ if (ok && (!skip_key (& ptr , "blocks" ) || !skip_symbol (& ptr , '[' ))) ok = 0 ;
427+
405428 size_t count = 0 ;
406- while (* ptr && * ptr != ']' && count < FOSSIL_JELLYFISH_MAX_MEM ) {
429+ while (ok && * ptr && * ptr != ']' && count < FOSSIL_JELLYFISH_MAX_MEM ) {
407430 fossil_jellyfish_block * b = & chain -> memory [count ];
408431 memset (b , 0 , sizeof (* b ));
409432
410- if (!skip_symbol (& ptr , '{' )) break ;
411- if (!parse_string_field (& ptr , "input" , b -> input , sizeof (b -> input ))) break ;
412- if (!parse_string_field (& ptr , "output" , b -> output , sizeof (b -> output ))) break ;
433+ if (!skip_symbol (& ptr , '{' )) { ok = 0 ; break ; }
434+ if (!parse_number_field (& ptr , "block_index" , NULL , NULL , NULL , NULL )) { ok = 0 ; break ; }
435+ if (!parse_string_field (& ptr , "input" , b -> input , sizeof (b -> input ))) { ok = 0 ; break ; }
436+ if (!parse_string_field (& ptr , "output" , b -> output , sizeof (b -> output ))) { ok = 0 ; break ; }
413437
414- // Parse hash as hex string
415- if (!skip_key (& ptr , "hash" ) || !skip_symbol (& ptr , '"' )) break ;
416- for (size_t i = 0 ; i < FOSSIL_JELLYFISH_HASH_SIZE ; ++ i ) {
417- unsigned int val = 0 ;
418- if (sscanf (ptr , "%2x" , & val ) != 1 ) break ;
419- b -> hash [i ] = (uint8_t )val ;
420- ptr += 2 ;
421- }
422- if (!skip_symbol (& ptr , '"' )) break ;
438+ if (!parse_hex_field (& ptr , "hash" , b -> hash , FOSSIL_JELLYFISH_HASH_SIZE )) { ok = 0 ; break ; }
439+ if (!parse_hex_field (& ptr , "previous_hash" , NULL , FOSSIL_JELLYFISH_HASH_SIZE )) { ok = 0 ; break ; }
423440
424- if (!parse_number_field (& ptr , "timestamp" , NULL , & b -> timestamp , NULL , NULL )) break ;
425- if (!parse_number_field (& ptr , "delta_ms" , NULL , NULL , NULL , & b -> delta_ms )) break ;
426- if (!parse_number_field (& ptr , "duration_ms" , NULL , NULL , NULL , & b -> duration_ms )) break ;
427- if (!parse_number_field (& ptr , "valid" , NULL , NULL , & b -> valid , NULL )) break ;
441+ if (!parse_number_field (& ptr , "timestamp" , NULL , & b -> timestamp , NULL , NULL )) { ok = 0 ; break ; }
442+ if (!parse_number_field (& ptr , "delta_ms" , NULL , NULL , NULL , & b -> delta_ms )) { ok = 0 ; break ; }
443+ if (!parse_number_field (& ptr , "duration_ms" , NULL , NULL , NULL , & b -> duration_ms )) { ok = 0 ; break ; }
444+ if (!parse_number_field (& ptr , "valid" , NULL , NULL , & b -> valid , NULL )) { ok = 0 ; break ; }
428445
429- double temp_conf = 0 ;
430- if (!parse_number_field (& ptr , "confidence" , & temp_conf , NULL , NULL , NULL )) break ;
446+ double temp_conf = 0.0 ;
447+ if (!parse_number_field (& ptr , "confidence" , & temp_conf , NULL , NULL , NULL )) { ok = 0 ; break ; }
431448 b -> confidence = (float )temp_conf ;
432449
433- if (!parse_number_field (& ptr , "usage_count" , NULL , NULL , NULL , & b -> usage_count )) break ;
434-
435- // Parse device_id as hex string
436- if (!skip_key (& ptr , "device_id" ) || !skip_symbol (& ptr , '"' )) break ;
437- for (size_t i = 0 ; i < FOSSIL_DEVICE_ID_SIZE ; ++ i ) {
438- unsigned int val = 0 ;
439- if (sscanf (ptr , "%2x" , & val ) != 1 ) break ;
440- b -> device_id [i ] = (uint8_t )val ;
441- ptr += 2 ;
442- }
443- if (!skip_symbol (& ptr , '"' )) break ;
450+ if (!parse_number_field (& ptr , "usage_count" , NULL , NULL , NULL , & b -> usage_count )) { ok = 0 ; break ; }
444451
445- // Parse signature as hex string
446- if (!skip_key (& ptr , "signature" ) || !skip_symbol (& ptr , '"' )) break ;
447- for (size_t i = 0 ; i < FOSSIL_SIGNATURE_SIZE ; ++ i ) {
448- unsigned int val = 0 ;
449- if (sscanf (ptr , "%2x" , & val ) != 1 ) break ;
450- b -> signature [i ] = (uint8_t )val ;
451- ptr += 2 ;
452- }
453- if (!skip_symbol (& ptr , '"' )) break ;
452+ if (!parse_hex_field (& ptr , "device_id" , b -> device_id , FOSSIL_DEVICE_ID_SIZE )) { ok = 0 ; break ; }
453+ if (!parse_hex_field (& ptr , "signature" , b -> signature , FOSSIL_SIGNATURE_SIZE )) { ok = 0 ; break ; }
454454
455- if (!skip_symbol (& ptr , '}' )) break ;
455+ if (!skip_symbol (& ptr , '}' )) { ok = 0 ; break ; }
456456
457457 skip_comma (& ptr );
458458 ++ count ;
459459 }
460460
461- if (!skip_symbol (& ptr , ']' ) || !skip_symbol (& ptr , '}' )) {
462- free (buffer );
463- return 0 ;
461+ if (ok && (!skip_symbol (& ptr , ']' ) || !skip_symbol (& ptr , '}' ))) ok = 0 ;
462+
463+ if (ok ) {
464+ chain -> count = count ;
464465 }
465466
466- chain -> count = count ;
467467 free (buffer );
468- return 1 ;
468+ return ok ;
469469}
470470
471471int fossil_jellyfish_save (const fossil_jellyfish_chain * chain , const char * filepath ) {
@@ -474,11 +474,22 @@ int fossil_jellyfish_save(const fossil_jellyfish_chain *chain, const char *filep
474474
475475 fprintf (fp , "{\n" );
476476 fprintf (fp , " \"signature\": \"JFS1\",\n" );
477+ fprintf (fp , " \"version\": \"1.0.0\",\n" );
478+
479+ // Write origin_device_id as hex
480+ fprintf (fp , " \"origin_device_id\": \"" );
481+ for (size_t i = 0 ; i < FOSSIL_DEVICE_ID_SIZE ; ++ i )
482+ fprintf (fp , "%02x" , chain -> device_id [i ]);
483+ fprintf (fp , "\",\n" );
484+
485+ fprintf (fp , " \"created_at\": %" PRIu64 ",\n" , chain -> created_at );
486+ fprintf (fp , " \"updated_at\": %" PRIu64 ",\n" , chain -> updated_at );
477487 fprintf (fp , " \"blocks\": [\n" );
478488
479489 for (size_t i = 0 ; i < chain -> count ; ++ i ) {
480490 const fossil_jellyfish_block * b = & chain -> memory [i ];
481491
492+ // Escape input/output strings
482493 char input_escaped [2 * FOSSIL_JELLYFISH_INPUT_SIZE ] = {0 };
483494 char output_escaped [2 * FOSSIL_JELLYFISH_OUTPUT_SIZE ] = {0 };
484495
@@ -497,66 +508,75 @@ int fossil_jellyfish_save(const fossil_jellyfish_chain *chain, const char *filep
497508 * dst = '\0' ;
498509
499510 fprintf (fp , " {\n" );
511+ fprintf (fp , " \"block_index\": %zu,\n" , i );
500512 fprintf (fp , " \"input\": \"%s\",\n" , input_escaped );
501513 fprintf (fp , " \"output\": \"%s\",\n" , output_escaped );
514+
515+ // hash
502516 fprintf (fp , " \"hash\": \"" );
503- for (size_t j = 0 ; j < FOSSIL_JELLYFISH_HASH_SIZE ; ++ j ) {
517+ for (size_t j = 0 ; j < FOSSIL_JELLYFISH_HASH_SIZE ; ++ j )
504518 fprintf (fp , "%02x" , b -> hash [j ]);
519+ fprintf (fp , "\",\n" );
520+
521+ // previous_hash
522+ fprintf (fp , " \"previous_hash\": \"" );
523+ if (i > 0 ) {
524+ for (size_t j = 0 ; j < FOSSIL_JELLYFISH_HASH_SIZE ; ++ j )
525+ fprintf (fp , "%02x" , chain -> memory [i - 1 ].hash [j ]);
526+ } else {
527+ fprintf (fp , "00000000000000000000000000000000" );
505528 }
506529 fprintf (fp , "\",\n" );
530+
531+ // timestamps and timing
507532 fprintf (fp , " \"timestamp\": %" PRIu64 ",\n" , b -> timestamp );
508533 fprintf (fp , " \"delta_ms\": %" PRIu32 ",\n" , b -> delta_ms );
509534 fprintf (fp , " \"duration_ms\": %" PRIu32 ",\n" , b -> duration_ms );
535+
536+ // metrics
510537 fprintf (fp , " \"valid\": %d,\n" , b -> valid );
511538 fprintf (fp , " \"confidence\": %.6f,\n" , b -> confidence );
512539 fprintf (fp , " \"usage_count\": %" PRIu32 ",\n" , b -> usage_count );
513540
514- // Write device_id as hex string
541+ // device_id
515542 fprintf (fp , " \"device_id\": \"" );
516- for (size_t j = 0 ; j < FOSSIL_DEVICE_ID_SIZE ; ++ j ) {
543+ for (size_t j = 0 ; j < FOSSIL_DEVICE_ID_SIZE ; ++ j )
517544 fprintf (fp , "%02x" , b -> device_id [j ]);
518- }
519545 fprintf (fp , "\",\n" );
520546
521- // Write signature as hex string
547+ // signature
522548 fprintf (fp , " \"signature\": \"" );
523- for (size_t j = 0 ; j < FOSSIL_SIGNATURE_SIZE ; ++ j ) {
549+ for (size_t j = 0 ; j < FOSSIL_SIGNATURE_SIZE ; ++ j )
524550 fprintf (fp , "%02x" , b -> signature [j ]);
525- }
526551 fprintf (fp , "\"\n" );
527552
528553 fprintf (fp , " }%s\n" , (i < chain -> count - 1 ) ? "," : "" );
529554 }
530555
531556 fprintf (fp , " ]\n" );
532557 fprintf (fp , "}\n" );
533-
534558 fclose (fp );
535559 return 1 ;
536560}
537561
538562static int fossil_jellyfish_similarity (const char * a , const char * b ) {
539- int cost = 0 ;
563+ if (!a || !b ) return -1 ; // invalid input
564+
540565 size_t i = 0 , j = 0 ;
566+ int cost = 0 ;
541567
542568 while (a [i ] && b [j ]) {
543- char ac = a [i ];
544- char bc = b [j ];
545-
546- // case-insensitive match
547- if (ac >= 'A' && ac <= 'Z' ) ac += 32 ;
548- if (bc >= 'A' && bc <= 'Z' ) bc += 32 ;
569+ char ac = tolower ((unsigned char )a [i ]);
570+ char bc = tolower ((unsigned char )b [j ]);
549571
550- if (ac != bc ) {
551- cost ++ ;
552- }
572+ if (ac != bc ) cost ++ ;
553573 i ++ ;
554574 j ++ ;
555575 }
556576
557- // Penalty for remaining characters
558- while ( a [ i ++ ]) cost ++ ;
559- while ( b [ j ++ ]) cost ++ ;
577+ // Add cost for leftover characters
578+ cost += ( int ) strlen ( a + i ) ;
579+ cost += ( int ) strlen ( b + j ) ;
560580
561581 return cost ;
562582}
@@ -592,21 +612,32 @@ const char* fossil_jellyfish_reason(fossil_jellyfish_chain *chain, const char *i
592612}
593613
594614void fossil_jellyfish_decay_confidence (fossil_jellyfish_chain * chain , float decay_rate ) {
615+ if (!chain || chain -> count == 0 ) return ;
616+
595617 const float MIN_CONFIDENCE = 0.05f ;
618+ const float MAX_CONFIDENCE = 1.0f ;
619+
620+ // Half-life of confidence in seconds (e.g. 24 hours = 86400s)
621+ const double HALF_LIFE_SECONDS = 86400.0 ;
622+
623+ // Current time in seconds
624+ time_t now = time (NULL );
596625
597626 for (size_t i = 0 ; i < chain -> count ; ++ i ) {
598627 fossil_jellyfish_block * block = & chain -> memory [i ];
599628 if (!block -> valid ) continue ;
600629
601- // Apply exponential decay
602- block -> confidence *= (1.0f - decay_rate );
630+ // Convert timestamps to seconds
631+ time_t block_time = (time_t )(block -> timestamp / 1000 ); // assuming ms
632+ time_t age_seconds = now - block_time ;
633+ if (age_seconds <= 0 ) continue ; // future or zero-age blocks aren't decayed
603634
604- // Clamp to zero
605- if (block -> confidence < 0.0f ) {
606- block -> confidence = 0.0f ;
607- }
635+ // Compute decay factor: confidence *= 0.5^(age / half-life)
636+ double decay_factor = pow (0.5 , (double )age_seconds / HALF_LIFE_SECONDS );
637+ block -> confidence *= (float )decay_factor ;
608638
609- // Invalidate if confidence too low
639+ // Clamp and check
640+ block -> confidence = fmaxf (0.0f , fminf (block -> confidence , MAX_CONFIDENCE ));
610641 if (block -> confidence < MIN_CONFIDENCE ) {
611642 block -> valid = 0 ;
612643 }
0 commit comments