11#if PC_ENABLE_EDGE_CACHE
17#define PC_EDGE_SD_VERSION 1
19void put_u16(uint8_t *p, uint16_t v)
21 p[0] = (uint8_t)(v & 0xFF);
22 p[1] = (uint8_t)(v >> 8);
24uint16_t get_u16(
const uint8_t *p)
26 return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8));
30bool put_str(uint8_t *out,
size_t cap,
size_t *pos,
const char *s)
32 size_t sl = strnlen(s, cap);
41 if (*pos + 2 + sl > cap)
45 put_u16(out + *pos, (uint16_t)sl);
47 memcpy(out + *pos, s, sl);
53bool get_str(
const uint8_t *buf,
size_t len,
size_t *pos,
char *out,
size_t out_cap)
59 uint16_t sl = get_u16(buf + *pos);
61 if (*pos + sl > len || sl >= out_cap)
65 memcpy(out, buf + *pos, sl);
72size_t edge_sd_serialize(
const EdgeEntry *e, uint8_t *out,
size_t cap)
74 if (!e || !out || cap < 3)
79 out[pos++] = PC_EDGE_SD_VERSION;
80 put_u16(out + pos, (uint16_t)e->status);
82 if (!put_str(out, cap, &pos, e->key) || !put_str(out, cap, &pos, e->content_type) ||
83 !put_str(out, cap, &pos, e->etag) || !put_str(out, cap, &pos, e->last_modified) ||
84 !put_str(out, cap, &pos, e->content_encoding) || !put_str(out, cap, &pos, e->vary_names) ||
85 !put_str(out, cap, &pos, e->vary_vals))
89 if (pos + 2 + e->body_len > cap)
93 put_u16(out + pos, e->body_len);
95 memcpy(out + pos, e->body, e->body_len);
100bool edge_sd_deserialize(
const uint8_t *buf,
size_t len, EdgeEntry *e)
102 if (!buf || !e || len < 3 || buf[0] != PC_EDGE_SD_VERSION)
107 e->status = get_u16(buf + pos);
109 if (!get_str(buf, len, &pos, e->key,
sizeof(e->key)) ||
110 !get_str(buf, len, &pos, e->content_type,
sizeof(e->content_type)) ||
111 !get_str(buf, len, &pos, e->etag,
sizeof(e->etag)) ||
112 !get_str(buf, len, &pos, e->last_modified,
sizeof(e->last_modified)) ||
113 !get_str(buf, len, &pos, e->content_encoding,
sizeof(e->content_encoding)) ||
114 !get_str(buf, len, &pos, e->vary_names,
sizeof(e->vary_names)) ||
115 !get_str(buf, len, &pos, e->vary_vals,
sizeof(e->vary_vals)))
123 uint16_t bl = get_u16(buf + pos);
129 memcpy(e->body, buf + pos, bl);
131 edge_key_digest(e->key, strnlen(e->key,
sizeof(e->key)), e->digest);
138static_assert(
PC_DBM_KEY_MAX >= 32,
"edge cache L2 uses a 32-byte SHA-256 digest as the dbm key");
143const char *canon_path(
const char *canon)
146 for (
const char *p = canon; *p; p++)
161bool peek_canon(
const uint8_t *buf,
size_t len,
char *canon_out,
size_t cap)
163 if (len < 3 || buf[0] != PC_EDGE_SD_VERSION)
168 return get_str(buf, len, &pos, canon_out, cap);
172#define PC_EDGE_SD_PURGE_BATCH 8
180 uint8_t batch[PC_EDGE_SD_PURGE_BATCH][32];
185bool collect_cb(
const char *key, uint16_t key_len,
void *vctx)
187 CollectCtx *c = (CollectCtx *)vctx;
192 long n = pc_dbm_get(c->db, key, key_len, c->scratch, c->scratch_cap);
198 if (!peek_canon(c->scratch, (
size_t)n, canon,
sizeof(canon)))
204 const char *path = canon_path(canon);
205 if (!path || strncmp(path, c->prefix, c->plen) != 0)
210 if (c->count >= PC_EDGE_SD_PURGE_BATCH)
215 memcpy(c->batch[c->count++], key, 32);
219uint32_t purge_matching(pc_dbm *db,
const char *prefix, uint8_t *scratch,
size_t scratch_cap)
229 c.scratch_cap = scratch_cap;
232 pc_dbm_iterate(db, collect_cb, &c);
233 for (
int i = 0; i < c.count; i++)
235 if (pc_dbm_del(db, (
const char *)c.batch[i], 32))
249bool edge_sd_put(pc_dbm *db,
const EdgeEntry *e, uint8_t *scratch,
size_t scratch_cap)
251 if (!db || !e || !scratch)
255 if (!edge_entry_has_validator(e))
259 size_t n = edge_sd_serialize(e, scratch, scratch_cap);
264 return pc_dbm_put(db, (
const char *)e->digest, 32, scratch, (uint32_t)n);
267bool edge_sd_get(pc_dbm *db,
const uint8_t digest[32], EdgeEntry *e, uint8_t *scratch,
size_t scratch_cap)
269 if (!db || !digest || !e || !scratch)
273 long n = pc_dbm_get(db, (
const char *)digest, 32, scratch, scratch_cap);
278 return edge_sd_deserialize(scratch, (
size_t)n, e);
281bool edge_sd_del(pc_dbm *db,
const uint8_t digest[32])
283 return db && digest && pc_dbm_del(db, (
const char *)digest, 32);
286uint32_t edge_sd_purge_prefix(pc_dbm *db,
const char *path_prefix, uint8_t *scratch,
size_t scratch_cap)
288 if (!db || !path_prefix || !scratch)
292 return purge_matching(db, path_prefix, scratch, scratch_cap);
295uint32_t edge_sd_purge_all(pc_dbm *db)
304 return purge_matching(db,
nullptr, scratch,
sizeof(scratch));
CDN edge-cache tier - L2 SD persistence (PC_ENABLE_EDGE_CACHE && PC_ENABLE_DBM).
#define PC_EDGE_SD_VALUE_MAX
Worst-case serialized L2 entry (edge_sd_serialize).