diff --git a/src/durable_abi.c b/src/durable_abi.c index ea8c293..f9f95a7 100644 --- a/src/durable_abi.c +++ b/src/durable_abi.c @@ -34,7 +34,7 @@ typedef struct { uint64_t body_cap; } kvspaceHead_t; -/* 固定 8 字节 head → 元数据(langtype 不在 head,见 kvspaceGetHead attr key)。 */ +/* 固定 8 字节 head → 元数据(langtype 在 body 尾 @8+len,见 kvspaceGetHead)。 */ static int from_wire_head(const uint8_t *d, uint32_t total, kvspaceHead_t *out) { if (!out || !d || total < KVSPACE_XVALUE_HEAD_LEN) return 1; @@ -204,32 +204,20 @@ int kvspaceGetHead(void *h, const char *key, kvspaceHead_t *out) { return 1; if (from_wire_head(d, (uint32_t)total, out) != 0) return 1; - /* langtype 在 x‥langtype attr key:值是字符串 XValue(8 字节 head + body=明文 - 类型串),从 body 取 out->langtype。attr 缺失/非 wire 串/空 body = - None/无类型(普通值也走 attr,由上层创建时写)。 */ - char ltkey[1028]; - int ltn = snprintf(ltkey, sizeof ltkey, "%s\xE2\x80\xA5" "langtype", key); - if (ltn < 0 || ltn >= (int)sizeof ltkey) - return 1; - int32_t ltl = 0; - uint8_t *ltd = kvspaceShmGet((kvspace_t *)h, ltkey, 0, <l); - if (ltd && ltl >= (int32_t)KVSPACE_XVALUE_HEAD_LEN) { - kvspaceXvalueHead lth; - memcpy(<h, ltd, sizeof lth); - uint64_t body_len = kvspaceXvalueHeadLen(<h); - if (lth.storetype <= KVSPACE_XVALUE_ST_EXT && - lth.ptr <= KVSPACE_XVALUE_PTR_EXT && - body_len <= (uint64_t)ltl - KVSPACE_XVALUE_HEAD_LEN) { - size_t take = body_len < (uint64_t)sizeof out->langtype - 1 - ? (size_t)body_len - : (size_t)sizeof out->langtype - 1; - memcpy(out->langtype, ltd + KVSPACE_XVALUE_HEAD_LEN, take); - out->langtype[take] = 0; - out->langtype_len = (int32_t)take; - return 0; - } + /* langtype 在 attr 区 tag=1 记录(@8+len 自描述);无记录 = 无类型。 */ + uint64_t blen = (uint64_t)out->body_len; + const uint8_t *lt = NULL; + uint16_t ltl = kvspaceXvalueHeadLangtype(d, blen, <); + if (lt && ltl > 0) { + size_t take = (size_t)ltl < (uint64_t)sizeof out->langtype - 1 + ? (size_t)ltl + : (size_t)sizeof out->langtype - 1; + memcpy(out->langtype, lt, take); + out->langtype[take] = 0; + out->langtype_len = (int32_t)take; + } else { + out->langtype_len = 0; } - out->langtype_len = 0; return 0; } @@ -246,14 +234,15 @@ int kvspaceWriteInPlace(void *h, const char *key, int resolve, return 0; } -/* 新位置写:按 (ref, storetype, body_len, body_cap) 分配 box、写固定头, - * 返回 body 偏移指针。kvspace 层不写 attr key——`x‥langtype` 归属上层: - * 调用方需要类型时自写 attr key(attr 先落、x 后落 = 提交点),kvspace 只做裸写。 */ +/* 新位置写:按 (ref, storetype, body_len, body_cap, langtype) 分配 box、写固定头 + + * langtype 尾部(整体单写),返回 body 偏移指针。 */ int kvspaceWriteNewPlace(void *h, const char *key, uint8_t ref, uint8_t storetype, uint32_t body_len, uint64_t body_cap, + const char *langtype, uint8_t **body, char *err, uint32_t err_cap) { if (kvspaceShmWriteNewPlace((kvspace_t *)h, key, ref, storetype, - (int32_t)body_len, body_cap, body) != 0) { + (int32_t)body_len, body_cap, langtype, + body) != 0) { if (err && err_cap) snprintf(err, err_cap, "kvspace: write-new-place failed at %s", key); return 1; @@ -371,42 +360,50 @@ int kvspaceNewPtr(const char *target_kindexpr, const char *target, uint8_t **out, uint32_t *out_len) { if (!target_kindexpr || !target || !out || !out_len) return 1; + /* `any` 不是类型名(空 kindexpr = 不约束)。 */ + if (strcmp(target_kindexpr, "any") == 0) + return 1; uint8_t *value = NULL; uint64_t total = 0; if (kvspaceXvalueHeadNewPtr((const uint8_t *)target, - (uint32_t)strlen(target), &value, &total) != 0) + (uint32_t)strlen(target), target_kindexpr, + &value, &total) != 0) return 1; return finish(value, total, out, out_len); } -int kvspaceNewChar(const uint8_t *bytes, uint32_t len, uint8_t **out, uint32_t *out_len) { +int kvspaceNewChar(const uint8_t *bytes, uint32_t len, const char *langtype, + uint8_t **out, uint32_t *out_len) { uint8_t *value = NULL; uint64_t total = 0; - int rc = kvspaceXvalueHeadNewSlack(bytes, len, len, &value, &total); + int rc = kvspaceXvalueHeadNewSlack(bytes, len, len, langtype, &value, &total); return rc == 0 ? finish(value, total, out, out_len) : 1; } -int kvspaceNewBool(uint8_t v, uint8_t **out, uint32_t *out_len) { +int kvspaceNewBool(uint8_t v, const char *langtype, + uint8_t **out, uint32_t *out_len) { uint8_t b = v ? 1 : 0; uint8_t *value = NULL; uint64_t total = 0; - int rc = kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, &b, 1, + int rc = kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, &b, 1, langtype, &value, &total); return rc == 0 ? finish(value, total, out, out_len) : 1; } -int kvspaceNewInt64(int64_t v, uint8_t **out, uint32_t *out_len) { +int kvspaceNewInt64(int64_t v, const char *langtype, + uint8_t **out, uint32_t *out_len) { uint8_t raw[8]; for (int i = 0; i < 8; i++) raw[i] = (uint8_t)((uint64_t)v >> (8 * i)); uint8_t *value = NULL; uint64_t total = 0; - int rc = kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, + int rc = kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, langtype, &value, &total); return rc == 0 ? finish(value, total, out, out_len) : 1; } -int kvspaceNewFloat64(double v, uint8_t **out, uint32_t *out_len) { +int kvspaceNewFloat64(double v, const char *langtype, + uint8_t **out, uint32_t *out_len) { uint64_t bits; memcpy(&bits, &v, sizeof bits); uint8_t raw[8]; @@ -414,7 +411,7 @@ int kvspaceNewFloat64(double v, uint8_t **out, uint32_t *out_len) { raw[i] = (uint8_t)(bits >> (8 * i)); uint8_t *value = NULL; uint64_t total = 0; - int rc = kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, + int rc = kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, langtype, &value, &total); return rc == 0 ? finish(value, total, out, out_len) : 1; } diff --git a/src/kvspace.c b/src/kvspace.c index ced92dc..24dabf9 100644 --- a/src/kvspace.c +++ b/src/kvspace.c @@ -2537,8 +2537,13 @@ static int read_value(kvspace_t *kv, uint64_t off, uint8_t **out, int32_t *ol) { if (l > INT32_MAX) return -1; *out = s; - /* None = 空 body(len 0);ptr/ext 由 ptr 字段判定(值恒为叶子)。 */ - *ol = (int32_t)(KVSPACE_XVALUE_HEAD_LEN + l); + /* 完整值长 = head + body + attr(attr_len:u16 @8+len 自描述;空 attr=2)。 */ + if ((uint64_t)sz < KVSPACE_XVALUE_HEAD_LEN + l + 2) + return -1; + uint64_t alen = kvspaceXvalueHeadAttrLen(s, l); + if (alen < 2 || alen > (uint64_t)sz - KVSPACE_XVALUE_HEAD_LEN - l) + return -1; + *ol = (int32_t)(KVSPACE_XVALUE_HEAD_LEN + l + alen); return 0; } return -1; @@ -2600,8 +2605,14 @@ static art_hdr_t *resolve_fetch(kvspace_t *kv, const char *path, char *out, int if (head_of(s, (int32_t)sz, &wire) == 0) { uint64_t l3 = kvspaceXvalueHeadLen(&wire); if (wire.ptr != KVSPACE_XVALUE_PTR_PTR || l3 >= (uint64_t)osz) { + /* 完整值长 = head + body + attr(attr_len @8+len 自描述)。 */ + if ((uint64_t)sz < KVSPACE_XVALUE_HEAD_LEN + l3 + 2) + return NULL; + uint64_t alen = kvspaceXvalueHeadAttrLen(s, l3); + if (alen < 2 || alen > (uint64_t)sz - KVSPACE_XVALUE_HEAD_LEN - l3) + return NULL; *raw = s; - *rl = (int32_t)(KVSPACE_XVALUE_HEAD_LEN + l3); + *rl = (int32_t)(KVSPACE_XVALUE_HEAD_LEN + l3 + alen); *fetched = 1; return h; } @@ -3173,7 +3184,7 @@ int kvspaceShmWriteInPlace(kvspace_t *kv, const char *key, int resolve, int kvspaceShmWriteNewPlace(kvspace_t *kv, const char *key, uint8_t ref, uint8_t storetype, int32_t body_len, - uint64_t body_cap, + uint64_t body_cap, const char *langtype, uint8_t **body) { if (!kv || !key || !body || body_len < 0) return -1; @@ -3197,13 +3208,13 @@ int kvspaceShmWriteNewPlace(kvspace_t *kv, const char *key, uint8_t ref, uint8_t *value = NULL; uint64_t total = 0; if (kvspaceXvalueHeadReserve(storetype, (uint64_t)body_len, - body_cap, &value, &total) != 0) + body_cap, langtype, &value, &total) != 0) return -1; if (total > INT32_MAX) { free(value); return -1; } - /* 固定头 8 字节:body 偏移恒 = 8。langtype 由调用方写 x‥langtype attr key。 */ + /* 固定头 8 字节:body 偏移恒 = 8;langtype 尾部 @8+body_len 整体单写。 */ uint32_t headlen = KVSPACE_XVALUE_HEAD_LEN; (void)ptr; int rc = shm_set_raw(kv, key, value, (int32_t)total); @@ -3630,11 +3641,28 @@ int kvspaceShmExtindex(kvspace_t *kv, const char *p, const char *ep, snprintf(err, err_cap, "ExtIndex target missing: %s", ep); return -1; } - /* source 类型在 ep‥langtype attr key(明文事实源);ext 句柄同样侧写。 */ + /* source 类型在 ep 值 attr 区(明文事实源);ext 句柄同样携带。 */ + char lt[256]; + const uint8_t *ltd = NULL; + uint16_t ltl = 0; + if (source_len >= (int32_t)KVSPACE_XVALUE_HEAD_LEN) { + kvspaceXvalueHead sh; + memcpy(&sh, source, sizeof sh); + if (sh.storetype <= KVSPACE_XVALUE_ST_EXT && + sh.ptr <= KVSPACE_XVALUE_PTR_EXT) { + uint64_t sbody = kvspaceXvalueHeadLen(&sh); + if (sbody <= (uint64_t)source_len - KVSPACE_XVALUE_HEAD_LEN) + ltl = kvspaceXvalueHeadLangtype(source, sbody, <d); + } + } + if (ltl > (uint16_t)(sizeof lt - 1)) + ltl = (uint16_t)(sizeof lt - 1); + memcpy(lt, ltd, ltl); + lt[ltl] = 0; uint8_t *v = NULL; uint64_t vl = 0; int r = kvspaceXvalueHeadNewExt((const uint8_t *)ep, (uint32_t)strlen(ep), - &v, &vl); + lt, &v, &vl); if (r == 0) r = kvspaceShmSetValue(kv, p, v, (int32_t)vl); free(v); diff --git a/src/kvspace_shm.h b/src/kvspace_shm.h index a4492b8..7a9be9d 100644 --- a/src/kvspace_shm.h +++ b/src/kvspace_shm.h @@ -77,11 +77,11 @@ int kvspaceShmSetValueByRef(kvspace_t *kv, kvspaceRef_t *ref, // 覆写);否则返回非 0。 int kvspaceShmWriteInPlace(kvspace_t *kv, const char *key, int resolve, int32_t body_len, uint8_t **body); -// WriteNewPlace: 按 (ref, storetype, body_len) 分配新 box、写固定头、 -// 维护父索引,返回 body 指针。langtype 由调用方顺写 x‥langtype attr key。 +// WriteNewPlace: 按 (ref, storetype, body_len, langtype) 分配新 box、写固定头 + +// langtype 尾部(整体单写)、维护父索引,返回 body 指针。 int kvspaceShmWriteNewPlace(kvspace_t *kv, const char *key, uint8_t ref, uint8_t storetype, int32_t body_len, - uint64_t body_cap, + uint64_t body_cap, const char *langtype, uint8_t **body); // ListLen: 只返回前缀下子项计数,无缓冲。 int kvspaceShmListLen(kvspace_t *kv, const char *prefix, bool expand_ext, diff --git a/src/xvalue_head.c b/src/xvalue_head.c index 0627623..24ed5f3 100644 --- a/src/xvalue_head.c +++ b/src/xvalue_head.c @@ -3,14 +3,15 @@ #include #include -/* V2 最终态(#566):head 固定 8 字节 [len:u48][storetype:u8][ptr:u8], - body 偏移恒 = 8。类型/形状(含 dims)不在 head——明文 langtype 串存 x‥langtype - attr key。固定布局:直接 memcpy struct + body。 */ +/* V2 最终态(#571):head 固定 8 字节 [len:u48][storetype:u8][ptr:u8], + body 偏移恒 = 8;attr 区 @8+len(长度自描述,含 langtype 记录)。整体单 key + 一次写,天然原子——无 attr key。langtype 空 = 无记录(空 attr)。 */ #define UINT48_MAX UINT64_C(0xFFFFFFFFFFFF) static int build(uint8_t storetype, uint8_t ptr, const uint8_t *body, - uint64_t len, uint8_t **out, uint64_t *out_len) { + uint64_t len, const char *langtype, uint8_t **out, + uint64_t *out_len) { if (!out || !out_len || (len && !body)) return -1; *out = NULL; @@ -18,7 +19,11 @@ static int build(uint8_t storetype, uint8_t ptr, const uint8_t *body, return -1; if (len > UINT48_MAX) return -1; - uint64_t n = (uint64_t)KVSPACE_XVALUE_HEAD_LEN + len; + uint64_t ltl = langtype ? strlen(langtype) : 0; + uint64_t alen = 2 + (ltl ? 3 + ltl : 0); /* attr 总长(含自身 2 字节) */ + if (alen > UINT16_MAX) + return -1; + uint64_t n = (uint64_t)KVSPACE_XVALUE_HEAD_LEN + len + alen; if (n > SIZE_MAX) return -1; uint8_t *p = (uint8_t *)calloc(1, (size_t)n); @@ -29,6 +34,15 @@ static int build(uint8_t storetype, uint8_t ptr, const uint8_t *body, memcpy(p, &h, sizeof h); if (len) memcpy(p + KVSPACE_XVALUE_HEAD_LEN, body, len); + uint8_t *a = p + KVSPACE_XVALUE_HEAD_LEN + len; + a[0] = (uint8_t)alen; + a[1] = (uint8_t)(alen >> 8); + if (ltl) { + a[2] = KVSPACE_XVALUE_ATTR_TAG_LANGTYPE; + a[3] = (uint8_t)ltl; + a[4] = (uint8_t)(ltl >> 8); + memcpy(a + 5, langtype, ltl); + } *out = p; *out_len = n; return 0; @@ -36,72 +50,85 @@ static int build(uint8_t storetype, uint8_t ptr, const uint8_t *body, int kvspaceXvalueHeadNewNone(uint8_t **out, uint64_t *out_len) { return build(KVSPACE_XVALUE_ST_SHORT, KVSPACE_XVALUE_PTR_INLINE, NULL, 0, - out, out_len); + NULL, out, out_len); } int kvspaceXvalueHeadNewScalar(uint8_t storetype, const uint8_t *raw, - uint32_t raw_len, uint8_t **out, uint64_t *out_len) { + uint32_t raw_len, const char *langtype, + uint8_t **out, uint64_t *out_len) { if (raw_len && !raw) return -1; - return build(storetype, KVSPACE_XVALUE_PTR_INLINE, raw, raw_len, out, out_len); + return build(storetype, KVSPACE_XVALUE_PTR_INLINE, raw, raw_len, langtype, + out, out_len); } int kvspaceXvalueHeadNewSlack(const uint8_t *data, uint64_t data_len, - uint64_t cap, uint8_t **out, uint64_t *out_len) { + uint64_t cap, const char *langtype, + uint8_t **out, uint64_t *out_len) { if (cap < data_len || data_len > UINT48_MAX || (data_len && !data)) return -1; (void)cap; return build(KVSPACE_XVALUE_ST_SLACK, KVSPACE_XVALUE_PTR_INLINE, data, - data_len, out, out_len); + data_len, langtype, out, out_len); } int kvspaceXvalueHeadNewTensor(const uint64_t *dims, uint32_t ndim, const uint8_t *raw, uint64_t raw_len, + const char *langtype, uint8_t **out, uint64_t *out_len) { if (!dims || ndim == 0 || ndim > 64 || raw_len > UINT48_MAX || (raw_len && !raw)) return -1; (void)dims; (void)ndim; - /* 形状在 attr key langtype;body 字节数 = raw_len(明文层校验 numel×width) */ + /* 形状在 langtype 尾部;body 字节数 = raw_len(明文层校验 numel×width) */ return build(KVSPACE_XVALUE_ST_LARGE, KVSPACE_XVALUE_PTR_INLINE, raw, - raw_len, out, out_len); + raw_len, langtype, out, out_len); } int kvspaceXvalueHeadNewPtr(const uint8_t *path, uint32_t path_len, + const char *langtype, uint8_t **out, uint64_t *out_len) { if (!path_len || !path) return -1; return build(KVSPACE_XVALUE_ST_SLACK, KVSPACE_XVALUE_PTR_PTR, path, path_len, - out, out_len); + langtype, out, out_len); } int kvspaceXvalueHeadNewExt(const uint8_t *locator, uint32_t locator_len, + const char *langtype, uint8_t **out, uint64_t *out_len) { if (!locator_len || !locator) return -1; return build(KVSPACE_XVALUE_ST_EXT, KVSPACE_XVALUE_PTR_EXT, locator, - locator_len, out, out_len); + locator_len, langtype, out, out_len); } int kvspaceXvalueHeadNewCode(const uint8_t *body, uint64_t body_len, - uint64_t cap, uint8_t **out, uint64_t *out_len) { + uint64_t cap, const char *langtype, + uint8_t **out, uint64_t *out_len) { if (body_len > cap || body_len > UINT48_MAX || (body_len && !body)) return -1; (void)cap; return build(KVSPACE_XVALUE_ST_SLACK, KVSPACE_XVALUE_PTR_INLINE, body, - body_len, out, out_len); + body_len, langtype, out, out_len); } int kvspaceXvalueHeadReserve(uint8_t storetype, uint64_t body_len, - uint64_t cap, uint8_t **out, uint64_t *out_len) { + uint64_t cap, const char *langtype, + uint8_t **out, uint64_t *out_len) { if (body_len > UINT48_MAX || cap < body_len) return -1; - /* 固定头无容量字段:预留 = 产 body_len 字节零 body 的占位(后由调用方填充)。 */ + /* 固定头无容量字段:预留 = 产 body_len 字节零 body 的占位(后由调用方填充), + attr 区(含 langtype 记录)一并写入。 */ (void)cap; - if (body_len > SIZE_MAX - KVSPACE_XVALUE_HEAD_LEN) + uint64_t ltl = langtype ? strlen(langtype) : 0; + uint64_t alen = 2 + (ltl ? 3 + ltl : 0); + if (alen > UINT16_MAX) + return -1; + if (body_len > SIZE_MAX - KVSPACE_XVALUE_HEAD_LEN - alen) return -1; - uint64_t n = (uint64_t)KVSPACE_XVALUE_HEAD_LEN + body_len; + uint64_t n = (uint64_t)KVSPACE_XVALUE_HEAD_LEN + body_len + alen; if (n > SIZE_MAX) return -1; uint8_t *p = (uint8_t *)calloc(1, (size_t)n); @@ -111,6 +138,15 @@ int kvspaceXvalueHeadReserve(uint8_t storetype, uint64_t body_len, .ptr = KVSPACE_XVALUE_PTR_INLINE}; kvspaceXvalueHeadSetLen(&h, body_len); memcpy(p, &h, sizeof h); + uint8_t *a = p + KVSPACE_XVALUE_HEAD_LEN + body_len; + a[0] = (uint8_t)alen; + a[1] = (uint8_t)(alen >> 8); + if (ltl) { + a[2] = KVSPACE_XVALUE_ATTR_TAG_LANGTYPE; + a[3] = (uint8_t)ltl; + a[4] = (uint8_t)(ltl >> 8); + memcpy(a + 5, langtype, ltl); + } *out = p; *out_len = n; return 0; diff --git a/src/xvalue_head.h b/src/xvalue_head.h index 11a56c1..4c56f92 100644 --- a/src/xvalue_head.h +++ b/src/xvalue_head.h @@ -1,9 +1,12 @@ -/* Wire: [len:u48][storetype:u8][ptr:u8][body]. Head is fixed 8 bytes, - body offset is always 8. Type/shape (including dims) is NOT in the head — it - lives in the x‥langtype attr key key as a plaintext langtype string. */ +/* Wire: [len:u48][storetype:u8][ptr:u8][body][attr]. Head is fixed 8 bytes, + body offset is always 8; attr region starts at 8+len and is length-prefixed + (total = 8 + len + attr_len). Type rides in the same single-key value — no + attr key. attr = [attr_len:u16][records]; record = [tag:u8][len:u16][data], + tag 1 = langtype, tags 2+ reserved (ro/vid...). */ #ifndef XVALUE_HEAD_H #define XVALUE_HEAD_H +#include #include #ifdef __cplusplus @@ -24,7 +27,7 @@ extern "C" { #define KVSPACE_XVALUE_PTR_EXT 2u /* 固定 8 字节布局(直接 memcpy struct + body)。 - wire:len:u48@0-5(小端)、storetype@6、ptr@7、body@8。 */ + wire:len:u48@0-5(小端)、storetype@6、ptr@7、body@8、langtype@8+len。 */ typedef struct kvspaceXvalueHead { uint8_t len[6]; /* u48 小端(offset 0-5) */ uint8_t storetype; @@ -45,24 +48,65 @@ static inline void kvspaceXvalueHeadSetLen(kvspaceXvalueHead *h, uint64_t v) { } } -/* 构造(固定头 + body,不含类型;langtype 由调用方写 x‥langtype attr key)。 - 0 on success. -1 and *out cleared on failure. */ +/* attr 区常量:record tag 1 = langtype;2+ 预留扩展(ro/vid 等,未实现)。 */ +#define KVSPACE_XVALUE_ATTR_TAG_LANGTYPE 1u + +/* 读 attr 总长(含自身 2 字节;空 attr = 2)。 */ +static inline uint16_t kvspaceXvalueHeadAttrLen(const uint8_t *wire, + uint64_t body_len) { + const uint8_t *p = wire + KVSPACE_XVALUE_HEAD_LEN + body_len; + return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8)); +} + +/* 扫 attr records 取 tag=1(langtype)记录 data;未找到/坏 attr → 返回 0。 */ +static inline uint16_t kvspaceXvalueHeadLangtype(const uint8_t *wire, + uint64_t body_len, + const uint8_t **out) { + *out = NULL; + uint16_t alen = kvspaceXvalueHeadAttrLen(wire, body_len); + if (alen < 2) + return 0; + const uint8_t *p = wire + KVSPACE_XVALUE_HEAD_LEN + body_len + 2; + const uint8_t *end = wire + KVSPACE_XVALUE_HEAD_LEN + body_len + alen; + while (p + 3 <= end) { + uint8_t tag = p[0]; + uint16_t rlen = (uint16_t)((uint16_t)p[1] | ((uint16_t)p[2] << 8)); + if (p + 3 + rlen > end) + return 0; + if (tag == KVSPACE_XVALUE_ATTR_TAG_LANGTYPE) { + *out = p + 3; + return rlen; + } + p += 3 + rlen; + } + return 0; +} + +/* 构造(固定头 + body + attr 区,单 key 整体)。langtype 空/NULL = 无 langtype + 记录(空 attr = [attr_len:2])。0 on success. -1 and *out cleared on failure. */ int kvspaceXvalueHeadNewNone(uint8_t **out, uint64_t *out_len); int kvspaceXvalueHeadNewScalar(uint8_t storetype, const uint8_t *raw, - uint32_t raw_len, uint8_t **out, uint64_t *out_len); + uint32_t raw_len, const char *langtype, + uint8_t **out, uint64_t *out_len); int kvspaceXvalueHeadNewSlack(const uint8_t *data, uint64_t data_len, - uint64_t cap, uint8_t **out, uint64_t *out_len); + uint64_t cap, const char *langtype, + uint8_t **out, uint64_t *out_len); int kvspaceXvalueHeadNewTensor(const uint64_t *dims, uint32_t ndim, const uint8_t *raw, uint64_t raw_len, + const char *langtype, uint8_t **out, uint64_t *out_len); int kvspaceXvalueHeadNewPtr(const uint8_t *path, uint32_t path_len, + const char *langtype, uint8_t **out, uint64_t *out_len); int kvspaceXvalueHeadNewExt(const uint8_t *locator, uint32_t locator_len, + const char *langtype, uint8_t **out, uint64_t *out_len); int kvspaceXvalueHeadNewCode(const uint8_t *body, uint64_t body_len, - uint64_t cap, uint8_t **out, uint64_t *out_len); + uint64_t cap, const char *langtype, + uint8_t **out, uint64_t *out_len); int kvspaceXvalueHeadReserve(uint8_t storetype, uint64_t body_len, - uint64_t cap, uint8_t **out, uint64_t *out_len); + uint64_t cap, const char *langtype, + uint8_t **out, uint64_t *out_len); #ifdef __cplusplus } diff --git a/tests/test_art_alignment.c b/tests/test_art_alignment.c index b7060ac..502f0ee 100644 --- a/tests/test_art_alignment.c +++ b/tests/test_art_alignment.c @@ -26,7 +26,7 @@ static uint8_t *slack(const char *s, uint64_t *n) { uint8_t *v = NULL; if (kvspaceXvalueHeadNewSlack( (const uint8_t *)s, strlen(s), - strlen(s), &v, n) != 0) + strlen(s), "", &v, n) != 0) return NULL; return v; } diff --git a/tests/test_art_scan.c b/tests/test_art_scan.c index 6f30214..6173f9f 100644 --- a/tests/test_art_scan.c +++ b/tests/test_art_scan.c @@ -40,7 +40,7 @@ static int set_int32(kvspace_t *kv, const char *key, int32_t v) { (uint8_t)(v >> 24)}; uint8_t *xv; uint64_t n = 0; - if (kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 4, &xv, &n) != 0) + if (kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 4, "", &xv, &n) != 0) return -1; int rc = kvspaceShmSet(kv, key, xv, (int32_t)n); free(xv); @@ -99,7 +99,7 @@ static void t_correct(const char *db) { uint8_t raw[4] = {7}; uint8_t *permitted = NULL; uint64_t permitted_len = 0; - CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 4, &permitted, &permitted_len) == 0 && + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 4, "", &permitted, &permitted_len) == 0 && kvspaceShmSetValue(kv, "/private", permitted, (int32_t)permitted_len) == 0, "set permitted value"); @@ -110,7 +110,7 @@ static void t_correct(const char *db) { uint8_t *ptr = NULL; uint64_t ptr_len = 0; - CHECK(kvspaceXvalueHeadNewPtr((const uint8_t *)"/private", 8, + CHECK(kvspaceXvalueHeadNewPtr((const uint8_t *)"/private", 8, "", &ptr, &ptr_len) == 0 && kvspaceShmSetValue(kv, "/link", ptr, (int32_t)ptr_len) == 0, "set pointer"); @@ -167,7 +167,7 @@ static void t_correct(const char *db) { CHECK(set_int32(kv, "/private", 8) == 0, "overwrite permitted value"); uint8_t *body = NULL; CHECK(kvspaceShmWriteNewPlace(kv, "/private", 0, KVSPACE_XVALUE_ST_SHORT, - 4, 4, &body) == 0, + 4, 4, "", &body) == 0, "write new permitted place"); if (body) memcpy(body, raw, 4); @@ -175,7 +175,7 @@ static void t_correct(const char *db) { permitted = NULL; permitted_len = 0; - CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 4, + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 4, "", &permitted, &permitted_len) == 0 && kvspaceShmSetValue(kv, "/tree/a", permitted, (int32_t)permitted_len) == 0, diff --git a/tests/test_codec_abi.c b/tests/test_codec_abi.c index ce34815..e17f63d 100644 --- a/tests/test_codec_abi.c +++ b/tests/test_codec_abi.c @@ -9,11 +9,10 @@ #define CHECK(x) do { if (!(x)) { fprintf(stderr, "failed: %s\n", #x); return 1; } } while (0) -/* #566:head 固定 8 字节 [len:u48][storetype][ptr],body 偏移恒 8。直接 memcpy - kvspaceXvalueHead + 字段访问。langtype 不在 head(x‥langtype attr key)。 - 测试保留的 ABI New* 构造器产物 + 固定头字段。 */ +/* #571:head 固定 8 字节 [len:u48][storetype][ptr],body 偏移恒 8, + attr 区 @8+len(attr_len:u16 自描述,含 langtype 记录)。整体单 key——无 attr key。 */ -/* 读固定头:返回 (storetype, ptr, body_len),并校验总长。 */ +/* 读固定头:返回 (storetype, ptr, body_len),并校验总长 ≥ head+body。 */ static int peek(const uint8_t *v, uint32_t total, uint8_t *st, uint8_t *ptr, uint64_t *blen) { if (total < KVSPACE_XVALUE_HEAD_LEN) @@ -23,7 +22,7 @@ static int peek(const uint8_t *v, uint32_t total, uint8_t *st, uint8_t *ptr, if (h.storetype > KVSPACE_XVALUE_ST_EXT || h.ptr > KVSPACE_XVALUE_PTR_EXT) return 1; uint64_t l = kvspaceXvalueHeadLen(&h); - if ((uint64_t)KVSPACE_XVALUE_HEAD_LEN + l != total) + if ((uint64_t)KVSPACE_XVALUE_HEAD_LEN + l > total) return 1; *st = h.storetype; *ptr = h.ptr; @@ -31,133 +30,155 @@ static int peek(const uint8_t *v, uint32_t total, uint8_t *st, uint8_t *ptr, return 0; } +/* 校验 attr 区:total = 8+body_len+attr_len;langtype 记录存在且与 lt 一致。 */ +static int attr_ok(const uint8_t *v, uint32_t total, uint64_t body_len, + const char *lt) { + size_t l = strlen(lt); + const uint8_t *a = v + KVSPACE_XVALUE_HEAD_LEN + body_len; + uint64_t alen = (uint64_t)a[0] | ((uint64_t)a[1] << 8); + if ((uint64_t)total != KVSPACE_XVALUE_HEAD_LEN + body_len + alen) + return 0; + if (l == 0) + return alen == 2; + if (alen != 2 + 3 + l) + return 0; + return a[2] == KVSPACE_XVALUE_ATTR_TAG_LANGTYPE && a[3] == l && a[4] == 0 && + memcmp(a + 5, lt, l) == 0; +} + static int test_head_constructors(void) { - /* None:8 字节空体。 */ + /* None:8 字节空体 + 空 attr。 */ uint8_t *v = NULL; uint64_t n = 0; - CHECK(kvspaceXvalueHeadNewNone(&v, &n) == 0 && n == 8); + CHECK(kvspaceXvalueHeadNewNone(&v, &n) == 0 && n == 10); kvspaceXvalueHead h; memcpy(&h, v, sizeof h); CHECK(h.storetype == KVSPACE_XVALUE_ST_SHORT && h.ptr == KVSPACE_XVALUE_PTR_INLINE && kvspaceXvalueHeadLen(&h) == 0); + CHECK(attr_ok(v, (uint32_t)n, 0, "")); free(v); - /* 标量:int64 8 字节小端 body。 */ + /* 标量:int64 8 字节小端 body + attr。 */ uint8_t raw[8] = {42, 0, 0, 0, 0, 0, 0, 0}; - CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, &v, &n) == 0); - CHECK(n == 16); + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, "int64", + &v, &n) == 0); + CHECK(n == 26); memcpy(&h, v, sizeof h); CHECK(kvspaceXvalueHeadLen(&h) == 8 && h.storetype == KVSPACE_XVALUE_ST_SHORT); CHECK(memcmp(v + KVSPACE_XVALUE_HEAD_LEN, raw, 8) == 0); + CHECK(attr_ok(v, (uint32_t)n, 8, "int64")); + free(v); + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, NULL, 8, "", + &v, &n) != 0); + /* 空 langtype:空 attr [attr_len:2]。 */ + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, "", + &v, &n) == 0); + CHECK(n == 18); + CHECK(attr_ok(v, (uint32_t)n, 8, "")); free(v); - CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, NULL, 8, &v, &n) != 0); - /* slack:变长 body。 */ + /* slack:变长 body + 类型记录。 */ const uint8_t utf8[] = {0xe4, 0xbd, 0xa0, 0xf0, 0x9f, 0x98, 0x80}; - CHECK(kvspaceXvalueHeadNewSlack(utf8, sizeof utf8, sizeof utf8, &v, &n) == 0); - CHECK(n == 8 + sizeof utf8); + CHECK(kvspaceXvalueHeadNewSlack(utf8, sizeof utf8, sizeof utf8, "char/utf8", + &v, &n) == 0); + CHECK(n == 8 + sizeof utf8 + 14); memcpy(&h, v, sizeof h); CHECK(kvspaceXvalueHeadLen(&h) == sizeof utf8 && h.storetype == KVSPACE_XVALUE_ST_SLACK); CHECK(memcmp(v + KVSPACE_XVALUE_HEAD_LEN, utf8, sizeof utf8) == 0); + CHECK(attr_ok(v, (uint32_t)n, sizeof utf8, "char/utf8")); free(v); - /* tensor:LARGE storetype,body=元素连续。 */ + /* tensor:LARGE storetype,body=元素连续,形状在 langtype。 */ uint64_t dims[2] = {2, 3}; uint8_t tbody[24]; memset(tbody, 0, sizeof tbody); - CHECK(kvspaceXvalueHeadNewTensor(dims, 2, tbody, sizeof tbody, &v, &n) == 0); + CHECK(kvspaceXvalueHeadNewTensor(dims, 2, tbody, sizeof tbody, "[2,3]int64", + &v, &n) == 0); memcpy(&h, v, sizeof h); CHECK(h.storetype == KVSPACE_XVALUE_ST_LARGE && kvspaceXvalueHeadLen(&h) == 24); + CHECK(attr_ok(v, (uint32_t)n, 24, "[2,3]int64")); free(v); /* ptr / ext。 */ - CHECK(kvspaceXvalueHeadNewPtr((const uint8_t *)"/x", 2, &v, &n) == 0); + CHECK(kvspaceXvalueHeadNewPtr((const uint8_t *)"/x", 2, "int64", + &v, &n) == 0); memcpy(&h, v, sizeof h); CHECK(h.storetype == KVSPACE_XVALUE_ST_SLACK && h.ptr == KVSPACE_XVALUE_PTR_PTR && kvspaceXvalueHeadLen(&h) == 2); + CHECK(attr_ok(v, (uint32_t)n, 2, "int64")); free(v); - CHECK(kvspaceXvalueHeadNewExt((const uint8_t *)"s3://", 5, &v, &n) == 0); + CHECK(kvspaceXvalueHeadNewExt((const uint8_t *)"s3://", 5, "", + &v, &n) == 0); memcpy(&h, v, sizeof h); CHECK(h.storetype == KVSPACE_XVALUE_ST_EXT && h.ptr == KVSPACE_XVALUE_PTR_EXT); + CHECK(attr_ok(v, (uint32_t)n, 5, "")); free(v); return 0; } -/* 保留的 ABI New* 构造器 → 固定头 + body 字段断言。 */ +/* 保留的 ABI New* 构造器 → 固定头 + body + attr 断言。 */ static int test_new_abi(void) { uint8_t *v = NULL; uint32_t len = 0; uint8_t st, ptr; uint64_t blen; - CHECK(kvspaceNewInt64(42, &v, &len) == 0); - CHECK(len == 16 && peek(v, len, &st, &ptr, &blen) == 0); + CHECK(kvspaceNewInt64(42, "int64", &v, &len) == 0); + CHECK(len == 26 && peek(v, len, &st, &ptr, &blen) == 0); CHECK(st == KVSPACE_XVALUE_ST_SHORT && ptr == KVSPACE_XVALUE_PTR_INLINE && blen == 8); CHECK(v[8] == 42 && v[9] == 0); /* 小端 */ + CHECK(attr_ok(v, len, 8, "int64")); free(v); - CHECK(kvspaceNewFloat64(1.5, &v, &len) == 0); - CHECK(len == 16 && peek(v, len, &st, &ptr, &blen) == 0); + CHECK(kvspaceNewFloat64(1.5, "float64", &v, &len) == 0); + CHECK(len == 28 && peek(v, len, &st, &ptr, &blen) == 0); CHECK(st == KVSPACE_XVALUE_ST_SHORT && blen == 8); + CHECK(attr_ok(v, len, 8, "float64")); free(v); - CHECK(kvspaceNewBool(1, &v, &len) == 0); - CHECK(len == 9 && peek(v, len, &st, &ptr, &blen) == 0); + CHECK(kvspaceNewBool(1, "bool", &v, &len) == 0); + CHECK(len == 18 && peek(v, len, &st, &ptr, &blen) == 0); CHECK(st == KVSPACE_XVALUE_ST_SHORT && blen == 1 && v[8] == 1); + CHECK(attr_ok(v, len, 1, "bool")); free(v); const uint8_t utf8[] = {0xe4, 0xbd, 0xa0, 0xf0, 0x9f, 0x98, 0x80}; - CHECK(kvspaceNewChar(utf8, sizeof utf8, &v, &len) == 0); - CHECK(len == 8 + sizeof utf8 && peek(v, len, &st, &ptr, &blen) == 0); + CHECK(kvspaceNewChar(utf8, sizeof utf8, "char/utf8", &v, &len) == 0); + CHECK(len == 8 + sizeof utf8 + 14 && peek(v, len, &st, &ptr, &blen) == 0); CHECK(st == KVSPACE_XVALUE_ST_SLACK && blen == sizeof utf8); CHECK(memcmp(v + KVSPACE_XVALUE_HEAD_LEN, utf8, sizeof utf8) == 0); + CHECK(attr_ok(v, len, sizeof utf8, "char/utf8")); free(v); CHECK(kvspaceNewPtr("int64", "/x", &v, &len) == 0); - CHECK(len == 10 && peek(v, len, &st, &ptr, &blen) == 0); + CHECK(len == 20 && peek(v, len, &st, &ptr, &blen) == 0); CHECK(ptr == KVSPACE_XVALUE_PTR_PTR && st == KVSPACE_XVALUE_ST_SLACK && blen == 2); CHECK(memcmp(v + KVSPACE_XVALUE_HEAD_LEN, "/x", 2) == 0); + CHECK(attr_ok(v, len, 2, "int64")); free(v); return 0; } -/* #566:x‥langtype attr key 值为字符串 XValue(8 字节 head + body=明文类型串)。 - kvspace 层不写 attr key——由上层(测试在此模拟)先写 attr、后写主值; - kvspaceGetHead 解 attr body 得明文。 */ -static int test_gethead_attr(void) { +/* #571:langtype 在 attr 区——WriteNewPlace 带类型整体单写,GetHead 读 attr 记录; + 无类型值 langtype 空。无 attr key。 */ +static int test_gethead_tail(void) { char dir[] = "/tmp/kvspace-gethead-XXXXXX"; CHECK(mkdtemp(dir) != NULL); char dsn[256]; snprintf(dsn, sizeof dsn, "shm://%s/db", dir); void *h = kvspaceConnect(dsn); CHECK(h != NULL); - /* 上层写 attr(attr 先落、x 后落):attr 值是字符串 XValue。 */ - uint8_t *lt = NULL; - uint64_t ltn = 0; - CHECK(kvspaceXvalueHeadNewSlack((const uint8_t *)"int64", 5, 5, <, <n) == 0); - CHECK(kvspaceSetValue(h, "/x\xE2\x80\xA5langtype", lt, (uint32_t)ltn, NULL, 0) == 0); - free(lt); uint8_t *body = NULL; - CHECK(kvspaceWriteNewPlace(h, "/x", 0, KVSPACE_XVALUE_ST_SHORT, 8, 8, + CHECK(kvspaceWriteNewPlace(h, "/x", 0, KVSPACE_XVALUE_ST_SHORT, 8, 8, "int64", &body, NULL, 0) == 0); - /* attr 值是字符串 XValue:wire = 8 字节 head + body=明文串。 */ - lt = NULL; - uint32_t ltlen = 0; - CHECK(kvspaceGet(h, "/x\xE2\x80\xA5langtype", 0, <, <len) == 0); - CHECK(lt && ltlen == KVSPACE_XVALUE_HEAD_LEN + 5); - kvspaceXvalueHead lth; - memcpy(<h, lt, sizeof lth); - CHECK(lth.storetype == KVSPACE_XVALUE_ST_SLACK && - kvspaceXvalueHeadLen(<h) == 5); - CHECK(memcmp(lt + KVSPACE_XVALUE_HEAD_LEN, "int64", 5) == 0); - /* GetHead 解 body 得明文类型串。 */ + memset(body, 42, 8); kvspaceHead_t head; CHECK(kvspaceGetHead(h, "/x", &head) == 0); CHECK(head.langtype_len == 5 && strcmp(head.langtype, "int64") == 0); - /* 无 attr 的普通值:GetHead 仍成功、langtype 为空。 */ + /* 无类型值:GetHead 成功、langtype 为空。 */ uint8_t *v = NULL; uint32_t vlen = 0; - CHECK(kvspaceNewInt64(42, &v, &vlen) == 0); + CHECK(kvspaceNewInt64(42, "", &v, &vlen) == 0); CHECK(kvspaceSetValue(h, "/y", v, vlen, NULL, 0) == 0); free(v); CHECK(kvspaceGetHead(h, "/y", &head) == 0); @@ -171,7 +192,7 @@ static int test_gethead_attr(void) { int main(void) { if (test_head_constructors() != 0 || test_new_abi() != 0 || - test_gethead_attr() != 0) + test_gethead_tail() != 0) return 1; puts("ok"); return 0; diff --git a/tests/test_handle_cache.c b/tests/test_handle_cache.c index eceb456..136cf44 100644 --- a/tests/test_handle_cache.c +++ b/tests/test_handle_cache.c @@ -21,7 +21,7 @@ static int failures; static int set_i64(kvspace_t *kv, const char *key, int64_t v) { uint8_t *xv; uint64_t n = 0; - if (kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, (const uint8_t *)&v, 8, &xv, &n) != 0) + if (kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, (const uint8_t *)&v, 8, "", &xv, &n) != 0) return -1; int rc = kvspaceShmSet(kv, key, xv, (int32_t)n); free(xv); diff --git a/tests/test_new_wire_shm.c b/tests/test_new_wire_shm.c index 904ca1c..993ab24 100644 --- a/tests/test_new_wire_shm.c +++ b/tests/test_new_wire_shm.c @@ -15,7 +15,8 @@ static int wire_len_of(const uint8_t *stored) { kvspaceXvalueHead h; memcpy(&h, stored, sizeof h); - return (int)(KVSPACE_XVALUE_HEAD_LEN + kvspaceXvalueHeadLen(&h)); + uint64_t l = kvspaceXvalueHeadLen(&h); + return (int)(KVSPACE_XVALUE_HEAD_LEN + l + kvspaceXvalueHeadAttrLen(stored, l)); } int main(void) { @@ -28,25 +29,25 @@ int main(void) { uint8_t *value = NULL; uint64_t n = 0; uint8_t raw[8] = {42}; - CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, "", &value, &n) == 0); - CHECK(n == 16); + CHECK(n == 18); CHECK(kvspaceShmSet(kv, "/x", value, (int32_t)n) == 0); free(value); int32_t got = 0; uint8_t *stored = kvspaceShmGet(kv, "/x", 0, &got); - CHECK(stored && got == 16 && stored[8] == 42); + CHECK(stored && got == 18 && stored[8] == 42); - CHECK(kvspaceXvalueHeadNewPtr((const uint8_t *)"/x", 2, &value, &n) == 0); + CHECK(kvspaceXvalueHeadNewPtr((const uint8_t *)"/x", 2, "", &value, &n) == 0); CHECK(kvspaceShmSet(kv, "/p", value, (int32_t)n) == 0); free(value); stored = kvspaceShmGet(kv, "/p", 1, &got); - CHECK(stored && got == 16 && stored[8] == 42); + CHECK(stored && got == 18 && stored[8] == 42); CHECK(kvspaceXvalueHeadNewNone(&value, &n) == 0); CHECK(kvspaceShmSet(kv, "/m", value, (int32_t)n) == 0); free(value); - CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, "", &value, &n) == 0); CHECK(kvspaceShmSet(kv, "/m·[0]", value, (int32_t)n) == 0); free(value); @@ -79,13 +80,13 @@ int main(void) { CHECK(kvspaceShmMkindex(kv, "/lib/f/", 0) == 0); const uint8_t code[] = {0, 0, 0, 0, 0, 'x'}; - CHECK(kvspaceXvalueHeadNewCode(code, sizeof code, sizeof code, + CHECK(kvspaceXvalueHeadNewCode(code, sizeof code, sizeof code, "", &value, &n) == 0); CHECK(kvspaceShmSet(kv, "/lib/f/[1,-1]", value, (int32_t)n) == 0); CHECK(kvspaceShmSet(kv, "/lib/f/[1,0]", value, (int32_t)n) == 0); free(value); CHECK(kvspaceShmExtindex(kv, "/vthread/1/[1]/", "/lib/f/", NULL, 0) == 0); - CHECK(kvspaceXvalueHeadNewPtr((const uint8_t *)"/x", 2, &value, &n) == 0); + CHECK(kvspaceXvalueHeadNewPtr((const uint8_t *)"/x", 2, "", &value, &n) == 0); CHECK(kvspaceShmSet(kv, "/vthread/1/[1]/[1,0]", value, (int32_t)n) != 0); CHECK(kvspaceShmSetValue(kv, "/vthread/1/[1]/[1,-1]", value, (int32_t)n) == 0); @@ -97,7 +98,7 @@ int main(void) { CHECK(wh.storetype == KVSPACE_XVALUE_ST_SLACK && wh.ptr == KVSPACE_XVALUE_PTR_PTR); - CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, "", &value, &n) == 0); CHECK(kvspaceShmSetValue(kv, "/vthread/1/[1]/[1,-1]", value, (int32_t)n) == 0); @@ -115,7 +116,7 @@ int main(void) { CHECK(stored && stored[8] == 43); CHECK(kvspaceShmWriteNewPlace(kv, "/fresh", 0, KVSPACE_XVALUE_ST_SHORT, - 8, 8, &body) == 0); + 8, 8, "", &body) == 0); memcpy(body, raw, 8); stored = kvspaceShmGet(kv, "/fresh", 0, &got); CHECK(stored && stored[8] == 42); @@ -124,19 +125,19 @@ int main(void) { static const uint8_t utf8[] = {0xc3, 0xa9, 'a'}; CHECK(kvspaceShmWriteNewPlace(kv, "/text", 0, KVSPACE_XVALUE_ST_SLACK, - 3, 3, &body) == 0); + 3, 3, "", &body) == 0); memcpy(body, utf8, sizeof utf8); stored = kvspaceShmGet(kv, "/text", 0, &got); - CHECK(stored && wire_len_of(stored) == 3 + KVSPACE_XVALUE_HEAD_LEN && + CHECK(stored && wire_len_of(stored) == 3 + KVSPACE_XVALUE_HEAD_LEN + 2 && memcmp(stored + KVSPACE_XVALUE_HEAD_LEN, utf8, sizeof utf8) == 0); body[0] = 0xff; - /* 固定头存储层不校验 UTF-8(码点校验移明文层按 attr key 类型做)——可读回。 */ + /* 固定头存储层不校验 UTF-8(码点校验移明文层按 attr 类型做)——可读回。 */ CHECK(kvspaceShmGet(kv, "/text", 0, &got) != NULL); body[0] = utf8[0]; CHECK(kvspaceShmGet(kv, "/text", 1, &got) != NULL); uint8_t boolean = 1; - CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, &boolean, 1, + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, &boolean, 1, "", &value, &n) == 0); CHECK(kvspaceShmSet(kv, "/flag", value, (int32_t)n) == 0); free(value); diff --git a/tests/test_set_part_fastpath.c b/tests/test_set_part_fastpath.c index 9fabfcf..0099d77 100644 --- a/tests/test_set_part_fastpath.c +++ b/tests/test_set_part_fastpath.c @@ -40,7 +40,7 @@ static void mkpc(char *out, size_t len) { static int put(kvspace_t *kv, const char *key, const char *body, uint64_t n) { uint8_t *v = NULL; uint64_t vl = 0; - if (kvspaceXvalueHeadNewSlack( (const uint8_t *)body, n, CAP, &v, &vl) != 0) + if (kvspaceXvalueHeadNewSlack( (const uint8_t *)body, n, CAP, "", &v, &vl) != 0) return -1; int rc = kvspaceShmSet(kv, key, v, (int32_t)vl); free(v); diff --git a/tests/test_shm_resize.c b/tests/test_shm_resize.c index 3ba734b..471bc68 100644 --- a/tests/test_shm_resize.c +++ b/tests/test_shm_resize.c @@ -35,7 +35,7 @@ static int set_bytes(kvspace_t *kv, const char *key, int32_t n, int seed) { raw[j] = (uint8_t)(seed + j); uint8_t *xv; uint64_t dims[1] = {(uint64_t)n}, len = 0; - if (kvspaceXvalueHeadNewTensor(dims, 1, raw, (uint64_t)n, &xv, &len) != 0) { + if (kvspaceXvalueHeadNewTensor(dims, 1, raw, (uint64_t)n, "", &xv, &len) != 0) { free(raw); return -1; } diff --git a/tests/test_shm_split.c b/tests/test_shm_split.c index fb41d04..e71c4c7 100644 --- a/tests/test_shm_split.c +++ b/tests/test_shm_split.c @@ -33,7 +33,7 @@ static int set_int32(kvspace_t *kv, const char *key, int32_t v) { (uint8_t)(v >> 24)}; uint8_t *xv; uint64_t n = 0; - if (kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 4, &xv, &n) != 0) + if (kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 4, "", &xv, &n) != 0) return -1; int rc = kvspaceShmSet(kv, key, xv, (int32_t)n); free(xv); diff --git a/tests/test_slot_view.c b/tests/test_slot_view.c index 024e771..fc455d2 100644 --- a/tests/test_slot_view.c +++ b/tests/test_slot_view.c @@ -22,7 +22,7 @@ static uint8_t *slack(const char *s, uint64_t *n) { uint8_t *v = NULL; if (kvspaceXvalueHeadNewSlack( (const uint8_t *)s, strlen(s), - strlen(s), &v, n) != 0) + strlen(s), "", &v, n) != 0) return NULL; return v; } @@ -88,14 +88,14 @@ int main(void) { CHECK(kvspaceShmSet(kv, sk, v, (int32_t)n) != 0); free(v); uint8_t raw[8] = {1}; - CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, &v, &n) == 0); + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, "", &v, &n) == 0); CHECK(kvspaceShmSet(kv, sk, v, (int32_t)n) != 0); free(v); /* status 槽(固定头 SLACK):创建即 view 成功(类型在 attr key,不在 head)。 */ const char *lk = "/vthread/8/\xE2\x80\xA5status"; uint8_t *lbody = NULL; - CHECK(kvspaceShmWriteNewPlace(kv, lk, 0, KVSPACE_XVALUE_ST_SLACK, 0, 0, &lbody) == 0); + CHECK(kvspaceShmWriteNewPlace(kv, lk, 0, KVSPACE_XVALUE_ST_SLACK, 0, 0, "", &lbody) == 0); kvspaceRef_t lr; memset(&lr, 0, sizeof lr); CHECK(kvspaceShmResolveRef(kv, lk, &lr) == 0); @@ -109,7 +109,7 @@ int main(void) { /* 非 SLACK 形态(SHORT)作 status:view -1、写入硬错。 */ const char *wk = "/vthread/9/\xE2\x80\xA5status"; uint8_t *wbody = NULL; - CHECK(kvspaceShmWriteNewPlace(kv, wk, 0, KVSPACE_XVALUE_ST_SHORT, 0, 0, &wbody) == 0); + CHECK(kvspaceShmWriteNewPlace(kv, wk, 0, KVSPACE_XVALUE_ST_SHORT, 0, 0, "", &wbody) == 0); kvspaceRef_t wr; memset(&wr, 0, sizeof wr); CHECK(kvspaceShmResolveRef(kv, wk, &wr) == 0); diff --git a/tests/test_xvalue_head.c b/tests/test_xvalue_head.c index 437a5f9..cd3c279 100644 --- a/tests/test_xvalue_head.c +++ b/tests/test_xvalue_head.c @@ -4,8 +4,8 @@ #include #include -/* 固定 head 8 字节 [len:u48][storetype][ptr],body 偏移恒 8。 - 类型不在 head(x‥langtype attr key)——存储层只验固定头/body。 */ +/* 固定 head 8 字节 [len:u48 LE@0-5][storetype@6][ptr@7],body 偏移恒 8, + attr 区 @8+len(attr_len:u16 自描述,含 langtype 记录)。单 key 整体——无 attr key。 */ static int failures = 0; #define CHECK(cond, ...) \ @@ -18,15 +18,36 @@ static int failures = 0; } \ } while (0) +/* 校验 attr 区:total = 8+body_len+attr_len;langtype 记录存在且与 lt 一致。 */ +static int attr_ok(const uint8_t *v, uint64_t total, uint64_t body_len, + const char *lt) { + size_t l = strlen(lt); + const uint8_t *a = v + KVSPACE_XVALUE_HEAD_LEN + body_len; + uint64_t alen = (uint64_t)a[0] | ((uint64_t)a[1] << 8); + if (total != KVSPACE_XVALUE_HEAD_LEN + body_len + alen) + return 0; + if (l == 0) + return alen == 2; + if (alen != 2 + 3 + l) + return 0; + return a[2] == KVSPACE_XVALUE_ATTR_TAG_LANGTYPE && a[3] == l && a[4] == 0 && + memcmp(a + 5, lt, l) == 0; +} + static void test_none(void) { uint8_t *buf = NULL; uint64_t n = 0; CHECK(kvspaceXvalueHeadNewNone(&buf, &n) == 0, "none new"); - CHECK(n == 8, "none len %llu", (unsigned long long)n); + CHECK(n == 10, "none len %llu", (unsigned long long)n); kvspaceXvalueHead h; memcpy(&h, buf, sizeof h); CHECK(h.storetype == KVSPACE_XVALUE_ST_SHORT && h.ptr == KVSPACE_XVALUE_PTR_INLINE && kvspaceXvalueHeadLen(&h) == 0, "none head"); + /* wire 布局:len:u48@0-5、storetype@6、ptr@7、attr_len@8 */ + CHECK(buf[0] == 0 && buf[5] == 0 && + buf[6] == KVSPACE_XVALUE_ST_SHORT && buf[7] == KVSPACE_XVALUE_PTR_INLINE, + "none wire layout"); + CHECK(attr_ok(buf, n, 0, ""), "none empty attr"); free(buf); } @@ -34,13 +55,20 @@ static void test_scalar(void) { uint8_t raw[8] = {0x0a, 0, 0, 0, 0, 0, 0, 0}; uint8_t *buf = NULL; uint64_t n = 0; - CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, "int64", &buf, &n) == 0, "i64 new"); - CHECK(n == 16, "i64 len %llu", (unsigned long long)n); + CHECK(n == 26, "i64 len %llu", (unsigned long long)n); kvspaceXvalueHead h; memcpy(&h, buf, sizeof h); CHECK(kvspaceXvalueHeadLen(&h) == 8 && h.storetype == KVSPACE_XVALUE_ST_SHORT, "i64 head"); CHECK(memcmp(buf + KVSPACE_XVALUE_HEAD_LEN, raw, 8) == 0, "i64 body@8"); + CHECK(attr_ok(buf, n, 8, "int64"), "i64 langtype attr"); + free(buf); + /* 无类型(空 langtype):空 attr [attr_len:2]。 */ + CHECK(kvspaceXvalueHeadNewScalar(KVSPACE_XVALUE_ST_SHORT, raw, 8, "", + &buf, &n) == 0, "scalar no-langtype"); + CHECK(n == 18, "no-langtype len %llu", (unsigned long long)n); + CHECK(attr_ok(buf, n, 8, ""), "no-langtype empty attr"); free(buf); } @@ -48,14 +76,20 @@ static void test_slack(void) { const uint8_t hi[] = {'h', 'i'}; uint8_t *buf = NULL; uint64_t n = 0; - CHECK(kvspaceXvalueHeadNewSlack(hi, 2, 2, &buf, &n) == 0, "slack new"); - CHECK(n == 10, "slack len %llu", (unsigned long long)n); + CHECK(kvspaceXvalueHeadNewSlack(hi, 2, 2, "[]char/utf8", &buf, &n) == 0, "slack new"); + CHECK(n == 26, "slack len %llu", (unsigned long long)n); kvspaceXvalueHead h; memcpy(&h, buf, sizeof h); - CHECK(h.storetype == KVSPACE_XVALUE_ST_SLACK && kvspaceXvalueHeadLen(&h) == 2, "slack head"); + CHECK(h.storetype == KVSPACE_XVALUE_ST_SLACK && + kvspaceXvalueHeadLen(&h) == 2, "slack head"); + /* wire:len u48 LE@0-5=2,storetype@6,ptr@7 */ + CHECK(buf[0] == 2 && buf[1] == 0 && buf[5] == 0 && + buf[6] == KVSPACE_XVALUE_ST_SLACK && buf[7] == KVSPACE_XVALUE_PTR_INLINE, + "slack wire layout"); CHECK(memcmp(buf + KVSPACE_XVALUE_HEAD_LEN, hi, 2) == 0, "slack body"); + CHECK(attr_ok(buf, n, 2, "[]char/utf8"), "slack langtype attr"); free(buf); - CHECK(kvspaceXvalueHeadNewSlack(hi, 2, 1, &buf, &n) != 0, "slack capcap"); const uint8_t call_body[] = {0, 0, 0, 0, 0, 'f'}; CHECK(kvspaceXvalueHeadNewCode(call_body, sizeof call_body, sizeof call_body, - &value, &len) == 0, "code new"); + "rwir", &value, &len) == 0, "code new"); memcpy(&h, value, sizeof h); - CHECK(h.storetype == KVSPACE_XVALUE_ST_SLACK && kvspaceXvalueHeadLen(&h) == sizeof call_body, + CHECK(h.storetype == KVSPACE_XVALUE_ST_SLACK && + kvspaceXvalueHeadLen(&h) == sizeof call_body, "code head"); CHECK(memcmp(value + KVSPACE_XVALUE_HEAD_LEN, call_body, sizeof call_body) == 0, "code body"); + CHECK(attr_ok(value, len, sizeof call_body, "rwir"), "code langtype attr"); free(value); - CHECK(kvspaceXvalueHeadNewCode(call_body, 5, 3, &value, &len) != 0, + CHECK(kvspaceXvalueHeadNewCode(call_body, 5, 3, "", &value, &len) != 0, "code cap