feat(ludic.telemetry): objects and lists inside an event's properties, an item made of numbers, and a signature of what was written - all in the kept buffer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 15:17:20 +03:00
parent d1bea7f10e
commit 78a70765af
5 changed files with 83 additions and 3 deletions

View file

@ -7,6 +7,7 @@ import "state.ludic"
import "facts.ludic"
import "ports.ludic"
import "props.ludic"
import "props_nest.ludic"
import "stamp.ludic"
import "ring.ludic"
import "queue.ludic"

View file

@ -4,7 +4,9 @@ export const TELEMETRY_LINE: int = 2048 # the longest line an event may be; a
export property TelemetryProps {
sb: StrBuf = null
n: int = 0 # properties written
n: int = 0 # properties written at the top
lv: []int = null # inside an object or a list: how many written at each level
d: int = 0 # how deep
}
# the writer, emptied, for the next event's properties
@ -12,6 +14,7 @@ export function telemetry_props(telemetry_st: mut TelemetryState) -> TelemetryPr
let p = telemetry_st.telemetry__props
sb_clear(p.sb)
p.n = 0
p.d = 0
return p
}
export function telemetry_str(p: TelemetryProps, k: string, s: string) -> void {
@ -27,10 +30,19 @@ export function telemetry_bool(p: TelemetryProps, k: string, b: bool) -> void {
if b { sb_add(p.sb, "true") } else { sb_add(p.sb, "false") }
}
function tp_key(p: TelemetryProps, k: string) -> void {
if p.n > 0 { sb_byte(p.sb, 44) }
tp_next(p)
tj_str(p.sb, k)
sb_byte(p.sb, 58)
p.n += 1
}
# a comma before all but the first at this level, and the count kept
function tp_next(p: TelemetryProps) -> void {
if p.d == 0 {
if p.n > 0 { sb_byte(p.sb, 44) }
p.n += 1
return
}
if p.lv[p.d] > 0 { sb_byte(p.sb, 44) }
p.lv[p.d] += 1
}
# a JSON string: quoted, a quote or a backslash escaped, a control byte as \u00XX

View file

@ -0,0 +1,37 @@
# props_nest.ludic - an object or a list inside an event's properties ($set, a photograph's subjects, the
# party's names), written into the same kept buffer; and a signature of what was written, to tell a
# change without keeping a copy
export function telemetry_obj(p: TelemetryProps, k: string) -> void { tn_open(p, k, 123) }
export function telemetry_list(p: TelemetryProps, k: string) -> void { tn_open(p, k, 91) }
export function telemetry_obj_end(p: TelemetryProps) -> void { tn_close(p, 125) }
export function telemetry_list_end(p: TelemetryProps) -> void { tn_close(p, 93) }
function tn_open(p: TelemetryProps, k: string, c: int) -> void {
tp_key(p, k)
sb_byte(p.sb, c)
if p.lv == null { p.lv = words(8) }
if p.d < len(p.lv) - 1 { p.d += 1 }
p.lv[p.d] = 0
}
function tn_close(p: TelemetryProps, c: int) -> void {
sb_byte(p.sb, c)
if p.d > 0 { p.d -= 1 }
}
# a string in the list being written
export function telemetry_item_str(p: TelemetryProps, s: string) -> void {
tp_next(p)
tj_str(p.sb, s)
}
# a string item made of numbers the caller writes itself (sb_int, sb_byte - no quote or backslash)
export function telemetry_item_open(p: TelemetryProps) -> StrBuf {
tp_next(p)
sb_byte(p.sb, 34)
return p.sb
}
export function telemetry_item_close(p: TelemetryProps) -> void { sb_byte(p.sb, 34) }
# a number standing for what has been written: equal when the bytes are (FNV-1a, 31 bits)
export function telemetry_props_sig(p: TelemetryProps) -> int {
var h = 2166136261 & 2147483647
for i in 0 .. sb_len(p.sb) { h = ((h ^ p.sb.b[i]) * 16777619) & 2147483647 }
return h
}

View file

@ -97,5 +97,6 @@ function telemetry__facts__new() -> Queue<TelemetryFact> { return queue_new("tel
function telemetry__props_new() -> TelemetryProps {
let p = new TelemetryProps
p.sb = sb_new(TELEMETRY_LINE)
p.lv = words(8)
return p
}

View file

@ -90,4 +90,33 @@ program RingTest {
expect(Text.contains(telemetry_batch_body(telemetry_st, 4), "\"n\":3,"))
expect(not Text.contains(telemetry_batch_body(telemetry_st, 4), "TELEMETRY_PLAYER"))
}
test "an object and a list inside the properties are JSON, and the signature tells a change" (telemetry_st: mut TelemetryState) {
start(telemetry_st, 50, 65536)
let p = telemetry_props(telemetry_st)
telemetry_int(p, "grade", 3)
telemetry_list(p, "subjects")
telemetry_item_str(p, "elk")
telemetry_item_str(p, "deer")
telemetry_list_end(p)
telemetry_obj(p, "$set")
telemetry_str(p, "arch", "arm64")
telemetry_list(p, "looks")
let sb = telemetry_item_open(p)
sb_int(sb, 4)
sb_byte(sb, 44)
sb_int(sb, 2)
telemetry_item_close(p)
telemetry_list_end(p)
telemetry_obj_end(p)
telemetry_bool(p, "last", false)
let sig = telemetry_props_sig(p)
telemetry_event(telemetry_st, "shot", p)
let a = telemetry_line(telemetry_st, 0)
expect(Text.contains(a, "{\"grade\":3,\"subjects\":[\"elk\",\"deer\"],\"$set\":{\"arch\":\"arm64\",\"looks\":[\"4,2\"]},\"last\":false,"))
expect(Json.parse(a) != null)
let q = telemetry_props(telemetry_st)
telemetry_int(q, "grade", 3)
expect(telemetry_props_sig(q) != sig)
}
}