Files
Viper_VRTE/json/__writer.vp
T
TermiNexus 0055ab7ea7 refactor: rawptr[T] -> T* 与 t.CConst 前缀迁移(R14/R16)
R14:rawptr[X] -> X*(41 文件 / 2037 行改动),X 含模块限定名与嵌套下标(rawptr[GSList[NameEntry]] -> GSList[NameEntry]*)。保留 6 处 rawptr[t.CPtr](规范例外:存纯裸指针);# 注释与字符串里的 rawptr[...] 属元语法描述,未动。 R16(保守子集):t.CConst | str/bytes -> const str/bytes,t.CConst | char | t.CPtr -> const str;typedef 右值位置豁免(const str 在该处无法注册,与 R1 同源)。 验证:TPC 自举三重重编 SUCCESS;TransPyV.exe 全量 77/77 顺序回归通过。
2026-10-05 17:38:21 +08:00

160 lines
6.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import memhub
import string
from stdio import snprintf
from . import (
JsonValue, JSON_NULL, JSON_BOOL, JSON_INT, JSON_FLOAT,
JSON_STRING, JSON_ARRAY, JSON_OBJECT,
)
# ============================================================
# 容量约定(cap 只由调用方显式传入,签名里没有默认值)
# write —— 三参入口,固定用 WRITE_CAP_DEFAULT(4096)作容量。
# write_cap —— 四参入口,容量由调用方显式传入。
# cap = 输出缓冲总字节数(含结尾 NUL)。所有写入先判容量:
# 写 n 字节要求 pos + n <= cap - 1(末位留给 write 收尾的 '\0')。
# 放不下时不写,并把 pos 置为"溢出标志" cap——此后任何写入都以
# `pos >= cap` 短路返回,最终 write 判 `end_pos >= cap` 返回 None。
# 即:内容超出容量时既不写穿缓冲,也不再继续写(绝不越界)。
# cap 的合法区间是 [WRITE_CAP_MIN, WRITE_CAP_MAX]:越界一律返回 None,
# 非法容量不进入 pool.alloc(不会按可疑值去申请内存)。
# ============================================================
define WRITE_CAP_MIN = 64# lower bound: shortest JSON doc (e.g. {"id":""}) incl NUL fits in 64 bytes
define WRITE_CAP_MAX = 67108864# upper bound: 64 MiB, not less than the largest output buffer a caller may request
define WRITE_CAP_DEFAULT = 4096# fixed capacity for the 3-arg write entry
def _write_char(buf: str, pos: sizet, cap: sizet, ch: char) -> sizet:
if pos + 1 >= cap:
if pos >= cap:
return pos # overflow flag already set: return as-is, do not write
return cap # cannot reserve trailing NUL slot => set overflow flag
buf[pos] = ch
return pos + 1
def _write_str(buf: str, pos: sizet, cap: sizet, s: str) -> sizet:
if s == None: return pos
if pos >= cap:
return pos # overflow flag already set: stop writing
slen: sizet = string.strlen(s)
if pos + slen >= cap:
return cap # cannot reserve trailing NUL slot => set overflow flag
string.memcpy(char(uint64(buf) + pos, t.CPtr), s, slen)
return pos + slen
def _write_string_escaped(buf: str, pos: sizet, cap: sizet, s: str) -> sizet:
if s == None:
pos = _write_str(buf, pos, cap, "null")
return pos
pos = _write_char(buf, pos, cap, '"')
if pos >= cap:
return pos # already overflowed: stop writing
slen: sizet = string.strlen(s)
for i in range(slen):
if pos >= cap:
return pos # already overflowed: converge early, skip pointless per-char looping
ch: char = s[i]
if ch == '"':
pos = _write_str(buf, pos, cap, "\\\"")
elif ch == '\\':
pos = _write_str(buf, pos, cap, "\\\\")
elif ch == '\n':
pos = _write_str(buf, pos, cap, "\\n")
elif ch == '\r':
pos = _write_str(buf, pos, cap, "\\r")
elif ch == '\t':
pos = _write_str(buf, pos, cap, "\\t")
else:
pos = _write_char(buf, pos, cap, ch)
pos = _write_char(buf, pos, cap, '"')
return pos
def _write_value(buf: str, pos: sizet, cap: sizet, val: JsonValue*,
pool: memhub.MemManager*) -> sizet:
if pos >= cap:
return pos # already overflowed: stop writing (recursion converges here)
if val == None:
pos = _write_str(buf, pos, cap, "null")
return pos
if val.vtype == JSON_NULL:
pos = _write_str(buf, pos, cap, "null")
elif val.vtype == JSON_BOOL:
if val.bool_val != 0:
pos = _write_str(buf, pos, cap, "true")
else:
pos = _write_str(buf, pos, cap, "false")
elif val.vtype == JSON_INT:
tmp: str = pool.alloc(32)
if tmp != None:
snprintf(tmp, 32, "%lld", val.int_val)
pos = _write_str(buf, pos, cap, tmp)
elif val.vtype == JSON_FLOAT:
tmp2: str = pool.alloc(32)
if tmp2 != None:
snprintf(tmp2, 32, "%.6g", val.float_val)
pos = _write_str(buf, pos, cap, tmp2)
elif val.vtype == JSON_STRING:
pos = _write_string_escaped(buf, pos, cap, val.str_val)
elif val.vtype == JSON_ARRAY:
if val.child == None:
pos = _write_str(buf, pos, cap, "[]")
return pos
pos = _write_char(buf, pos, cap, '[')
cur: JsonValue* = val.child
first: bool = True
while cur != None:
if pos >= cap:
return pos # already overflowed: stop iterating, do not write
if not first:
pos = _write_char(buf, pos, cap, ',')
pos = _write_value(buf, pos, cap, cur, pool)
first = False
cur = cur.next
pos = _write_char(buf, pos, cap, ']')
elif val.vtype == JSON_OBJECT:
if val.child == None:
pos = _write_str(buf, pos, cap, "{}")
return pos
pos = _write_char(buf, pos, cap, '{')
cur2: JsonValue* = val.child
first2: bool = True
while cur2 != None:
if pos >= cap:
return pos # already overflowed: stop iterating, do not write
if not first2:
pos = _write_char(buf, pos, cap, ',')
pos = _write_string_escaped(buf, pos, cap, cur2.key)
pos = _write_char(buf, pos, cap, ':')
pos = _write_value(buf, pos, cap, cur2, pool)
first2 = False
cur2 = cur2.next
pos = _write_char(buf, pos, cap, '}')
return pos
# Serialize to a JSON string. cap = output buffer size (bytes, incl trailing NUL), default 4096.
# Returns None when content does not fit (never writes past the buffer).
def write(pool: memhub.MemManager*, val: JsonValue*, pretty: bool,
cap: sizet = 4096) -> str:
"""Serialize a JsonValue into a JSON string."""
if cap < 2:
return None # not enough room even for the trailing NUL
buf: str = pool.alloc(cap)
if buf == None:
return None
end_pos: sizet = _write_value(buf, 0, cap, val, pool)
if end_pos >= cap:
return None # overflow flag: content exceeds capacity
buf[end_pos] = '\0'
return buf