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 顺序回归通过。
160 lines
6.3 KiB
Plaintext
160 lines
6.3 KiB
Plaintext
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
|
||
|