#include "store/fsst.h"
#include <rayforce.h>
#include <stdlib.h>
#include <string.h>
void ray_fsst_index_build(const ray_fsst_table_t* t, ray_fsst_index_t* ix) {
memset(ix, 0, sizeof *ix);
for (int c = 0; c < t->n; c++) ix->cnt[t->sym[c][0]]++;
uint8_t pos[256]; int acc = 0;
for (int f = 0; f < 256; f++) { ix->start[f] = (uint8_t)acc; pos[f] = (uint8_t)acc; acc += ix->cnt[f]; }
for (int len = 8; len >= 1; len--)
for (int c = 0; c < t->n; c++)
if (t->len[c] == len) ix->ids[pos[t->sym[c][0]]++] = (uint8_t)c;
}
static int fsst_match(const ray_fsst_table_t* t, const ray_fsst_index_t* ix,
const uint8_t* s, uint32_t rem) {
uint8_t f = s[0];
const uint8_t* ids = ix->ids + ix->start[f];
for (int j = 0; j < ix->cnt[f]; j++) {
int c = ids[j];
if (t->len[c] <= rem && memcmp(t->sym[c], s, t->len[c]) == 0) return c;
}
return -1;
}
int64_t ray_fsst_encode_ix(const ray_fsst_table_t* t, const ray_fsst_index_t* ix,
const uint8_t* s, uint32_t len, uint8_t* out) {
int64_t o = 0;
for (uint32_t i = 0; i < len;) {
int c = fsst_match(t, ix, s + i, len - i);
if (c >= 0) { out[o++] = (uint8_t)c; i += t->len[c]; }
else { out[o++] = RAY_FSST_ESC; out[o++] = s[i++]; }
}
return o;
}
int64_t ray_fsst_encode(const ray_fsst_table_t* t, const uint8_t* s, uint32_t len, uint8_t* out) {
ray_fsst_index_t ix; ray_fsst_index_build(t, &ix);
return ray_fsst_encode_ix(t, &ix, s, len, out);
}
int64_t ray_fsst_expanded_len(const ray_fsst_table_t* t, const uint8_t* in, uint32_t len) {
int64_t x = 0;
for (uint32_t i = 0; i < len; i++) {
uint8_t c = in[i];
if (c == RAY_FSST_ESC) { i++; x += 1; }
else x += t->len[c];
}
return x;
}
int64_t ray_fsst_decode(const ray_fsst_table_t* t, const uint8_t* in, uint32_t len, uint8_t* out) {
size_t o = 0;
for (uint32_t i = 0; i < len; i++) {
uint8_t c = in[i];
if (c == RAY_FSST_ESC) out[o++] = in[++i];
else { memcpy(out + o, t->sym[c], t->len[c]); o += t->len[c]; }
}
return (int64_t)o;
}
int64_t ray_fsst_decode_fast(const ray_fsst_table_t* t, const uint8_t* in, uint32_t len, uint8_t* out) {
size_t o = 0;
for (uint32_t i = 0; i < len;) {
uint8_t c = in[i++];
if (c != RAY_FSST_ESC) { memcpy(out + o, t->sym[c], 8); o += t->len[c]; }
else out[o++] = in[i++];
}
return (int64_t)o;
}
int64_t ray_fsst_table_bytes(const ray_fsst_table_t* t) {
return 8 + (((int64_t)t->n + 7) & ~(int64_t)7) + 8 * (int64_t)t->n;
}
typedef struct { uint8_t len; uint8_t b[8]; uint64_t gain; } fsst_cand_t;
static int cand_cmp(const fsst_cand_t* a, const fsst_cand_t* b) {
if (a->gain != b->gain) return a->gain > b->gain ? -1 : 1;
if (a->len != b->len) return a->len > b->len ? -1 : 1;
return memcmp(a->b, b->b, 8);
}
#define FSST_SAMPLE_BYTES (1u << 18)
static int cand_qcmp(const void* a, const void* b) {
return cand_cmp((const fsst_cand_t*)a, (const fsst_cand_t*)b);
}
#define FSST_MAP_BITS 17
typedef struct { fsst_cand_t* e; uint32_t n, cap; } fsst_map_t;
static uint64_t cand_key_hash(uint8_t len, const uint8_t* b) {
uint64_t h = 0x9E3779B97F4A7C15ull ^ len;
for (int i = 0; i < len; i++) h = (h ^ b[i]) * 0x100000001B3ull;
return h;
}
static void map_add(fsst_map_t* m, uint8_t len, const uint8_t* b, uint64_t g) {
uint32_t mask = m->cap - 1;
for (uint32_t i = (uint32_t)cand_key_hash(len, b) & mask;; i = (i + 1) & mask) {
fsst_cand_t* e = &m->e[i];
if (e->len == 0) { if (m->n >= m->cap / 2) return; e->len = len; memset(e->b, 0, 8); memcpy(e->b, b, len); e->gain = g; m->n++; return; }
if (e->len == len && memcmp(e->b, b, len) == 0) { e->gain += g; return; }
}
}
int ray_fsst_train(const uint8_t* const* strs, const uint32_t* lens, int64_t n,
ray_fsst_table_t* t) {
memset(t, 0, sizeof *t);
uint64_t total = 0;
for (int64_t i = 0; i < n; i++) total += lens[i];
if (total == 0) return 0;
int64_t stride = total > FSST_SAMPLE_BYTES ? (int64_t)((total + FSST_SAMPLE_BYTES - 1) / FSST_SAMPLE_BYTES) : 1;
fsst_map_t m = { NULL, 0, 1u << FSST_MAP_BITS };
m.e = ray_calloc_raw(sizeof(fsst_cand_t) * m.cap);
fsst_cand_t* all = ray_calloc_raw(sizeof(fsst_cand_t) * (m.cap / 2 + 1));
if (!m.e || !all) { ray_free_raw(m.e); ray_free_raw(all); return -1; }
for (int round = 0; round < 5; round++) {
memset(m.e, 0, sizeof(fsst_cand_t) * m.cap); m.n = 0;
ray_fsst_index_t ix; ray_fsst_index_build(t, &ix);
for (int64_t si = 0; si < n; si += stride) {
const uint8_t* s = strs[si]; uint32_t len = lens[si];
int prev_len = 0; uint8_t prev[8];
for (uint32_t i = 0; i < len;) {
int c = t->n ? fsst_match(t, &ix, s + i, len - i) : -1;
uint8_t cl = c >= 0 ? t->len[c] : 1;
const uint8_t* cb = c >= 0 ? t->sym[c] : s + i;
map_add(&m, cl, cb, cl);
if (prev_len && prev_len + cl <= 8) {
uint8_t cat[8]; memcpy(cat, prev, (size_t)prev_len); memcpy(cat + prev_len, cb, cl);
map_add(&m, (uint8_t)(prev_len + cl), cat, (uint64_t)(prev_len + cl));
}
memcpy(prev, cb, cl); prev_len = cl;
i += cl;
}
}
size_t k = 0;
for (uint32_t i = 0; i < m.cap; i++) if (m.e[i].len) all[k++] = m.e[i];
qsort(all, k, sizeof *all, cand_qcmp);
if (k > 255) k = 255;
ray_fsst_table_t nt; memset(&nt, 0, sizeof nt);
nt.n = (uint8_t)k;
for (size_t c = 0; c < k; c++) { nt.len[c] = all[c].len; memcpy(nt.sym[c], all[c].b, 8); }
*t = nt;
}
ray_free_raw(all);
ray_free_raw(m.e);
return t->n > 0 ? 1 : 0;
}