sic v2 client WP1: v2Frame — one v2 wire frame (leaf function)

First function-level package of Piece 2. v2Frame(command, payload) builds one v2 frame:
0x00 + be32(len(netstring)) + netstring(command 0x00 payload). Byte-exact tests (cat/hi, empty
payload nil==[]byte{}, binary payload riding untouched). @testdesigner proved satisfiability in
scratch + caught a be32 arithmetic error in the spec; @godev (deepseek default) implemented it —
the small-package decomposition carrying on the room grinder where the monolithic parser could not.
This commit is contained in:
Claude (noether)
2026-07-23 11:07:34 +02:00
parent 189bb31737
commit d2b9eb4870
2 changed files with 84 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
package main
import (
"encoding/binary"
"strconv"
)
// v2Frame returns one v2 wire frame:
//
// 0x00 ++ big-endian uint32 length of the netstring ++ netstring
//
// where netstring = "<len>:<content>," and content = command ++ 0x00 ++ payload.
func v2Frame(command []byte, payload []byte) []byte {
// Build content: command + 0x00 + payload
content := make([]byte, len(command)+1+len(payload))
copy(content, command)
content[len(command)] = 0x00
copy(content[len(command)+1:], payload)
// Build netstring: "<len>:<content>,"
netstr := strconv.Itoa(len(content)) + ":" + string(content) + ","
// Build frame: 0x00 ++ be32(len(netstr)) ++ netstr
frame := make([]byte, 1+4+len(netstr))
frame[0] = 0x00
binary.BigEndian.PutUint32(frame[1:5], uint32(len(netstr)))
copy(frame[5:], netstr)
return frame
}
+54
View File
@@ -0,0 +1,54 @@
package main
import (
"bytes"
"testing"
)
// WP1 spec for v2Frame: one v2 wire frame, byte-exact to what cmd/sicd parses:
// 0x00 + big-endian uint32 length of the netstring + netstring(command 0x00 payload)
// Hand-written byte literals (no helper arithmetic that could cancel a framer bug out of both
// sides). Byte counts verified against cmd/sicd/main.go's readNetstring, which rejects an
// over- or under-counted length, so these lengths must be exact.
func TestV2FrameCatHi(t *testing.T) {
// content = "cat\x00hi" (6 bytes); netstring "6:cat\x00hi," (9 bytes); frame 14 bytes.
want := []byte{
0x00, // magic
0x00, 0x00, 0x00, 0x09, // be32(9) = length of the netstring
'6', ':', 'c', 'a', 't', 0x00, 'h', 'i', ',',
}
if got := v2Frame([]byte("cat"), []byte("hi")); !bytes.Equal(got, want) {
t.Fatalf("v2Frame(cat,hi) =\n % x\nwant\n % x", got, want)
}
}
func TestV2FrameEmptyPayload(t *testing.T) {
// content = "x\x00" (2 bytes); netstring "2:x\x00," (5 bytes); frame 10 bytes.
want := []byte{
0x00,
0x00, 0x00, 0x00, 0x05, // be32(5)
'2', ':', 'x', 0x00, ',',
}
if got := v2Frame([]byte("x"), nil); !bytes.Equal(got, want) {
t.Fatalf("v2Frame(x,nil) =\n % x\nwant\n % x", got, want)
}
if got := v2Frame([]byte("x"), []byte{}); !bytes.Equal(got, want) {
t.Fatalf("nil and []byte{} payload must frame identically; got\n % x\nwant\n % x", got, want)
}
}
func TestV2FrameBinaryPayloadRidesUntouched(t *testing.T) {
// Payload bytes that look like framing (NUL, 0xFF, comma, colon) must ride INSIDE the
// netstring untouched — it is length-delimited, never re-parsed by delimiter.
payload := []byte{0x00, 0xFF, ',', ':'}
// content = "cat" + 0x00 + payload = 8 bytes; netstring "8:...," = 11 bytes; frame 16 bytes.
want := []byte{
0x00,
0x00, 0x00, 0x00, 0x0B, // be32(11)
'8', ':', 'c', 'a', 't', 0x00, 0x00, 0xFF, ',', ':', ',',
}
if got := v2Frame([]byte("cat"), payload); !bytes.Equal(got, want) {
t.Fatalf("v2Frame binary payload =\n % x\nwant\n % x", got, want)
}
}