11#if DETWS_ENABLE_EDGE_CACHE && DETWS_ENABLE_DBM
16static_assert(
DETWS_DBM_KEY_MAX >= 32,
"edge cache L2 uses a 32-byte SHA-256 digest as the dbm key");
20constexpr uint8_t EDGE_SD_VERSION = 1;
22void put_u16(uint8_t *p, uint16_t v)
24 p[0] = (uint8_t)(v & 0xFF);
25 p[1] = (uint8_t)(v >> 8);
27uint16_t get_u16(
const uint8_t *p)
29 return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8));
33bool put_str(uint8_t *out,
size_t cap,
size_t *pos,
const char *s)
35 size_t sl = strlen(s);
36 if (sl > 0xFFFFu || *pos + 2 + sl > cap)
38 put_u16(out + *pos, (uint16_t)sl);
40 memcpy(out + *pos, s, sl);
46bool get_str(
const uint8_t *buf,
size_t len,
size_t *pos,
char *out,
size_t out_cap)
50 uint16_t sl = get_u16(buf + *pos);
52 if (*pos + sl > len || sl >= out_cap)
54 memcpy(out, buf + *pos, sl);
61const char *canon_path(
const char *canon)
64 for (
const char *p = canon; *p; p++)
65 if (*p ==
'\n' && ++nl == 2)
71bool peek_canon(
const uint8_t *buf,
size_t len,
char *canon_out,
size_t cap)
73 if (len < 3 || buf[0] != EDGE_SD_VERSION)
76 return get_str(buf, len, &pos, canon_out, cap);
80constexpr int EDGE_SD_PURGE_BATCH = 8;
88 uint8_t batch[EDGE_SD_PURGE_BATCH][32];
93bool collect_cb(
const char *key, uint16_t key_len,
void *vctx)
95 CollectCtx *c = (CollectCtx *)vctx;
98 long n = detws_dbm_get(c->db, key, key_len, c->scratch, c->scratch_cap);
102 if (!peek_canon(c->scratch, (
size_t)n, canon,
sizeof(canon)))
106 const char *path = canon_path(canon);
107 if (!path || strncmp(path, c->prefix, c->plen) != 0)
110 if (c->count >= EDGE_SD_PURGE_BATCH)
115 memcpy(c->batch[c->count++], key, 32);
119uint32_t purge_matching(DetwsDbm *db,
const char *prefix, uint8_t *scratch,
size_t scratch_cap)
127 c.plen = prefix ? strlen(prefix) : 0;
129 c.scratch_cap = scratch_cap;
132 detws_dbm_iterate(db, collect_cb, &c);
133 for (
int i = 0; i < c.count; i++)
134 if (detws_dbm_del(db, (
const char *)c.batch[i], 32))
143size_t edge_sd_serialize(
const EdgeEntry *e, uint8_t *out,
size_t cap)
145 if (!e || !out || cap < 3)
148 out[pos++] = EDGE_SD_VERSION;
149 put_u16(out + pos, (uint16_t)e->status);
151 if (!put_str(out, cap, &pos, e->key) || !put_str(out, cap, &pos, e->content_type) ||
152 !put_str(out, cap, &pos, e->etag) || !put_str(out, cap, &pos, e->last_modified) ||
153 !put_str(out, cap, &pos, e->content_encoding) || !put_str(out, cap, &pos, e->vary_names) ||
154 !put_str(out, cap, &pos, e->vary_vals))
156 if (pos + 2 + e->body_len > cap)
158 put_u16(out + pos, e->body_len);
160 memcpy(out + pos, e->body, e->body_len);
165bool edge_sd_deserialize(
const uint8_t *buf,
size_t len, EdgeEntry *e)
167 if (!buf || !e || len < 3 || buf[0] != EDGE_SD_VERSION)
170 e->status = get_u16(buf + pos);
172 if (!get_str(buf, len, &pos, e->key,
sizeof(e->key)) ||
173 !get_str(buf, len, &pos, e->content_type,
sizeof(e->content_type)) ||
174 !get_str(buf, len, &pos, e->etag,
sizeof(e->etag)) ||
175 !get_str(buf, len, &pos, e->last_modified,
sizeof(e->last_modified)) ||
176 !get_str(buf, len, &pos, e->content_encoding,
sizeof(e->content_encoding)) ||
177 !get_str(buf, len, &pos, e->vary_names,
sizeof(e->vary_names)) ||
178 !get_str(buf, len, &pos, e->vary_vals,
sizeof(e->vary_vals)))
182 uint16_t bl = get_u16(buf + pos);
186 memcpy(e->body, buf + pos, bl);
188 edge_key_digest(e->key, strlen(e->key), e->digest);
192bool edge_sd_put(DetwsDbm *db,
const EdgeEntry *e, uint8_t *scratch,
size_t scratch_cap)
194 if (!db || !e || !scratch)
196 if (!edge_entry_has_validator(e))
198 size_t n = edge_sd_serialize(e, scratch, scratch_cap);
201 return detws_dbm_put(db, (
const char *)e->digest, 32, scratch, (uint32_t)n);
204bool edge_sd_get(DetwsDbm *db,
const uint8_t digest[32], EdgeEntry *e, uint8_t *scratch,
size_t scratch_cap)
206 if (!db || !digest || !e || !scratch)
208 long n = detws_dbm_get(db, (
const char *)digest, 32, scratch, scratch_cap);
211 return edge_sd_deserialize(scratch, (
size_t)n, e);
214bool edge_sd_del(DetwsDbm *db,
const uint8_t digest[32])
216 return db && digest && detws_dbm_del(db, (
const char *)digest, 32);
219uint32_t edge_sd_purge_prefix(DetwsDbm *db,
const char *path_prefix, uint8_t *scratch,
size_t scratch_cap)
221 if (!db || !path_prefix || !scratch)
223 return purge_matching(db, path_prefix, scratch, scratch_cap);
226uint32_t edge_sd_purge_all(DetwsDbm *db)
232 uint8_t scratch[EDGE_SD_VALUE_MAX];
233 return purge_matching(db,
nullptr, scratch,
sizeof(scratch));
#define DETWS_EDGE_BODY_MAX
#define DETWS_DBM_VAL_MAX
#define DETWS_DBM_KEY_MAX
#define DETWS_EDGE_KEY_MAX
CDN edge-cache tier - L2 SD persistence (DETWS_ENABLE_EDGE_CACHE && DETWS_ENABLE_DBM).