DLIST binary format (guest reader spec)
Status: v1, written with stream 2 (M2 host half). The reference encoder is
browser/host/dlist.py; the reference painter is browser/host/dlist_paint_ref.py;
tests/browser/dlist_test.py decodes the hex example below. Decisions where the spec
(docs/browser-architecture.md c) was ambiguous are listed at the top of dlist.py (D1 to D19).
Conventions#
- All integers little endian. Coordinates are i32 in 1/16 CSS px, page origin (scroll 0).
Band
icovers 4096 units (256 CSS px):[i*4096, (i+1)*4096). - Colour refs are u8:
0xFFnone,0..254index into the band colour table. - String refs are u16:
0xFFFFnone. Strings are UTF-8, ASCII in v1. - Scale: device px = units * scale / 16 (scale 1 or 2).
Band payload (one DLIST message, type 0x06)#
| Offset | Size | Field |
|---|---|---|
| 0 | 2 | band index (u16) |
| 2 | 2 | flags (u16): bit0 last band, bit1 band holds FIXED ops |
| 4 | 4 | band version (u32) |
| 8 | 8 | band hash (u64), FNV-1a 64 over bytes 16 to end |
| 16 | 4 | band top y (i32, units) |
| 20 | 4 | band bottom y (i32, units, exclusive) |
| 24 | 2 | n_colours (u16, 0..255) |
| 26 | 2 | n_strings (u16) |
| 28 | 4 | n_ops (u32) |
| 32 | 4 x n_colours | colour table: R, G, B, A bytes |
| then | per string: 2 + len | u16 length, UTF-8 bytes |
| then | per op | opcode (u8), fields in the order below |
The guest reads header, colours and strings, then ops until n_ops are consumed. Any other byte count after the last op is an error. The hash lets the guest skip a band it already holds (BANDREQ/HAVE in b5).
Ops#
Field codes: i i32, I u32, Q u64, H u16, B u8, h i16, b i8. Sizes include the opcode.
| Op | Code | Fields | Size |
|---|---|---|---|
| BOX | 01 | x y w h i, fill B, border B, bw B, rad B, flags B | 22 |
| TEXT | 02 | x y i, w H (px), str H, face B, col B, deco B, flags B | 17 |
| IMAGE | 03 | x y w h i, id Q, fit B (0 fill, 1 contain, 2 cover, 3 none), flags B | 27 |
| CLIP | 04 | x y w h i | 17 |
| CLIP_POP | 05 | none | 1 |
| LINK | 06 | x y w h i, str H | 19 |
| CONTROL | 07 | kind B, id H, x y w h i, val H, label H, state B | 25 |
| SCROLL_BEGIN | 08 | sid H, x y w h i, content_h i, offset i, overflow B | 28 |
| SCROLL_END | 09 | none | 1 |
| RUNIMG | 0A | hash Q, x y w h i | 25 |
| TILE | 0B | id I, x y w h i, kind B (low nibble kind, high nibble alpha 0..15) | 22 |
| FIXED | 0C | prefix for the next op (viewport coordinates) | 1 |
| STICKY | 0D | min i, max i, top i; prefix for the next op | 13 |
| (XFORM) | 0E | reserved, never emitted | |
| DECOR | 0F | x y w h i, kind B, col B | 19 |
| (STR) | 10 | reserved | |
| GRAD | 11 | x y w h i, a B, b B, angle B, rad B | 21 |
| SHADOW | 12 | x y w h i, dx h, dy h, blur B, spread b, col B, rad B | 25 |
BOX: fill and border are colour refs; border is drawn bw px wide inside the box
rectangle. flags bits are reserved (0). TEXT: face bits are bold (bit0),
size class (bits 1-2: 0 <= 12 px, 1 <= 16, 2 <= 24, 3 larger), mono (bit3), italic (bit4),
serif (bit5). deco bits: underline 1, line-through 2, overline 4. IMAGE: flags bits
0-3 alpha 0..15 (15 opaque), bit4 placeholder (no content yet), bit5 CSS background. TILE kind:
0 svg, 1 canvas or webgl, 2 video, 4 embed. CONTROL kind: 0 text, 1 password (val is
0xFFFF, never sent), 2 checkbox, 3 radio, 4 button, 5 select, 6 textarea, 7 file, 8 range,
9 submit, 10 reset, 11 other. CONTROL state bits: 0 disabled, 1 checked, 2 readonly,
3 required. GRAD: linear two-stop from a to b; angle 0 = to top, 64 = to right,
128 = to bottom, 192 = to left. SHADOW: offsets in units, blur and spread in px.
Prefix ops: FIXED and STICKY apply to the op that follows them, and nothing else. A
STICKY op paints at y + shift, where shift = clamp(scroll_y + top, min, max) - min.
Paint an op only in the band that holds its shifted top. FIXED ops are in band 0 and use
viewport coordinates.
Clip groups: CLIP (or SCROLL_BEGIN) opens a group, CLIP_POP (or SCROLL_END) closes it. Groups do not nest; the encoder never opens one inside another.
Worked example (hex)#
A 200 x 200 px page: white background, "Hello" in black, a link "Example" underlined in blue,
and a button with the value "Go". Source: dlist_test.py decodes this block and compares it
with the listing below. Each line is hex bytes, then ; and the annotation.
00 00 ; band index u16 = 0 01 00 ; flags u16 = 1 (bit0 last band) 01 00 00 00 ; band version u32 = 1 c1 45 ce 40 34 8c 86 33 ; band hash u64 (FNV-1a 64 over bytes 16..end) 00 00 00 00 ; band top y i32 = 0 (1/16 px units) 00 10 00 00 ; band bottom y i32 = 4096 04 00 ; n_colours u16 = 4 04 00 ; n_strings u16 = 4 06 00 00 00 ; n_ops u32 = 6 ff ff ff ff ; colour C0 = R G B A 00 00 00 ff ; colour C1 = R G B A 00 00 ee ff ; colour C2 = R G B A e0 e0 e0 ff ; colour C3 = R G B A 07 00 45 78 61 6d 70 6c 65 ; string S0: u16 length 7 + UTF-8 "Example" 05 00 48 65 6c 6c 6f ; string S1: u16 length 5 + UTF-8 "Hello" 14 00 68 74 74 70 73 3a 2f 2f 65 78 61 6d 70 6c 65 2e 63 6f 6d 2f ; string S2: u16 length 20 + UTF-8 "https://example.com/" 02 00 47 6f ; string S3: u16 length 2 + UTF-8 "Go" 01 00 00 00 00 00 00 00 00 80 0c 00 00 80 07 00 00 00 ff 00 00 00 ; op BOX (0x01) 0 0 3200 1920 0 255 0 0 0 02 80 00 00 00 80 00 00 00 38 00 01 00 02 01 00 00 ; op TEXT (0x02) 128 128 56 1 2 1 0 0 06 80 00 00 00 c0 00 00 00 e8 03 00 00 00 01 00 00 02 00 ; op LINK (0x06) 128 192 1000 256 2 02 80 00 00 00 c0 00 00 00 38 00 00 00 02 02 01 00 ; op TEXT (0x02) 128 192 56 0 2 2 1 0 01 40 06 00 00 60 09 00 00 00 0a 00 00 80 02 00 00 03 ff 00 00 00 ; op BOX (0x01) 1600 2400 2560 640 3 255 0 0 0 07 04 01 00 40 06 00 00 60 09 00 00 00 0a 00 00 80 02 00 00 03 00 ff ff 00 ; op CONTROL (0x07) 4 1 1600 2400 2560 640 3 65535 0
Decoded listing (the output of dlist.dump_band):
BAND 0 flags=1 version=1 y=0..4096 ops=6 colours=4 strings=4
C0 #ffffff/255
C1 #000000/255
C2 #0000ee/255
C3 #e0e0e0/255
S0 Example
S1 Hello
S2 https://example.com/
S3 Go
BOX 0 0 3200 1920 0 255 0 0 0
TEXT 128 128 56 1 2 1 0 0
LINK 128 192 1000 256 2
TEXT 128 192 56 0 2 2 1 0
BOX 1600 2400 2560 640 3 255 0 0 0
CONTROL 4 1 1600 2400 2560 640 3 65535 0Reading order for the guest#
- Read the 32-byte header. Check the hash if the band is new (guest FNV-1a 64 over the bytes from offset 16 to the end).
- Read the colour table and the string table into arrays.
- Read n_ops ops. Keep a clip state (none, CLIP rect, or SCROLL rect) and the pending FIXED or STICKY prefix.
- Paint ops in order. Ops of bands are painted in band index order; band 0 FIXED ops last.