Files
zeekay 68037f9ca4 Rebrand Cap'n Proto Nim port to ZAP
Rename the capnp/ module set to zap/ and the capnpc compiler to zapc;
rename all .capnp schemas and fixtures to .zap; update every import,
include guard (isInZap), generated-code emission, exception type
(ZapFormatError), and the macro-generated zapUnpackStructImpl/
zapPackStructImpl identifiers to the ZAP brand. Zero capnp/Cap'n Proto
references remain in source or build files; upstream MIT attribution
(Sandstorm, Michał Zieliński) is preserved.

Raise the nimble nim floor from the fictional 0.12.0 to the locked,
verified 0.15.2 (matches nimenv.cfg/build.sh; code carries fixes through
Nim 0.18). Rewrite TODO.md as honest, source-anchored known-limitations
(two-word intersegment pointers, nested/named-union codegen, RPC
interface inheritance) instead of a bare wishlist.
2026-06-19 23:42:02 -07:00

214 lines
6.3 KiB
Nim

# included from zap.nim
when not compiles(isInZap): {.error: "do not import this file directly".}
import collections
type Packer* = ref object
buffer*: string
capToIndex*: (proc(cap: CapServer): RawCapValue)
proc packPointer*[T](p: Packer, offset: int, value: T)
proc packScalar*[T: SomeInt](v: var string, offset: int, value: T, defaultValue: T) =
pack(v, offset, value xor defaultValue)
proc packScalar*(v: var string, offset: int, value: float32, defaultValue: float32) =
packScalar(v, offset, cast[uint32](value), cast[uint32](defaultValue))
proc packScalar*(v: var string, offset: int, value: float64, defaultValue: float64) =
packScalar(v, offset, cast[uint64](value), cast[uint64](defaultValue))
proc packScalar*[T: enum](v: var string, offset: int, value: T, defaultValue: T) =
packScalar(v, offset, cast[uint16](value), cast[uint16](defaultValue))
proc packBool*(v: var string, bitOffset: int, value: bool, defaultValue: bool) =
let offset = bitOffset div 8
let bit = bitOffset mod 8
if value xor defaultValue:
v[offset] = (v[offset].uint8 or (1 shl bit).uint8).char
else:
v[offset] = (v[offset].uint8 and (not (1 shl bit).uint8)).char
proc getSizeTag(s: int): int {.compiletime.} =
case s:
of 1: return 2
of 2: return 3
of 4: return 4
of 8: return 5
else: doAssert(false, "bad size")
proc packScalarList[T, R](p: Packer, offset: int, value: T, typ: typedesc[R]) =
assert p.buffer != nil
let bodyOffset = p.buffer.len
assert bodyOffset mod 8 == 0
p.buffer.setLen bodyOffset + value.len * sizeof(typ)
if value.len != 0:
copyMem(addr p.buffer[bodyOffset], unsafeAddr value[0], value.len * sizeof(typ))
when cpuEndian == bigEndian:
{.error: "TODO: swap items on list".}
p.buffer.padWords
let itemSizeTag = getSizeTag(sizeof(typ))
let deltaOffset = (bodyOffset - offset - 8) div 8
pack(p.buffer, offset,
1.uint64 or
(deltaOffset.uint64 shl 2) or
(itemSizeTag.uint64 shl 32) or
(value.len.uint64 shl 35))
proc packPointerList[R](p: Packer, offset: int, value: seq[R], typ: typedesc[R]) =
mixin packPointer
let bodyOffset = p.buffer.len
assert bodyOffset mod 8 == 0
p.buffer.setLen bodyOffset + value.len * 8
for i, item in value:
packPointer(p, bodyOffset + i * 8, item)
let itemSizeTag = 6
let deltaOffset = (bodyOffset - offset - 8) div 8
pack(p.buffer, offset,
1.uint64 or
(deltaOffset.uint64 shl 2) or
(itemSizeTag.uint64 shl 32) or
(value.len.uint64 shl 35))
proc packNil(p: Packer, offset: int) =
if offset + 8 <= p.buffer.len:
pack(p.buffer, offset, 0.uint64)
proc packCompositeList[R](p: Packer, offset: int, value: seq[R], typ: typedesc[R]) =
if value.len == 0:
packNil(p, offset)
return
mixin zapPackStructImpl
var dataSize = 0
var pointerCount = 0
for item in value:
var fakeBuffer: string = nil
let info = zapPackStructImpl(p, fakeBuffer, item, 0)
dataSize = max(dataSize, info.dataSize)
pointerCount = max(pointerCount, info.pointerCount)
let structLength = (dataSize + pointerCount) * 8
let wordCount = (dataSize + pointerCount) * value.len
let bodyOffset = p.buffer.len
p.buffer.add newZeroString(8)
let dataOffset = p.buffer.len
p.buffer.add newZeroString(structLength * value.len)
for i, item in value:
discard zapPackStructImpl(p, p.buffer, item, dataOffset + i * structLength, minDataSize=dataSize)
let deltaOffset = (bodyOffset - offset - 8) div 8
pack(p.buffer, offset,
1.uint64 or
(deltaOffset.uint64 shl 2) or
(7.uint64 shl 32) or
(wordCount.uint64 shl 35))
pack(p.buffer, bodyOffset,
0.uint64 or
(value.len.uint64 shl 2) or
(dataSize.uint64 shl 32) or
(pointerCount.uint64 shl 48))
proc packListImpl[T, R](p: Packer, offset: int, value: T, typ: typedesc[R]) =
if value.isNil:
return
when typ is ZapScalar:
packScalarList(p, offset, value, typ)
elif typ is (seq|string) or compiles(toCapServer(value[0])):
packPointerList(p, offset, value, typ)
else:
packCompositeList(p, offset, value, typ)
proc packList*[T](p: Packer, offset: int, value: seq[T]) =
packListImpl(p, offset, value, T)
proc packList*(p: Packer, offset: int, value: string) =
packListImpl(p, offset, value, byte)
proc packStruct[T](p: Packer, offset: int, value: T) =
mixin zapPackStructImpl
let dataOffset = p.buffer.len
let info = zapPackStructImpl(p, p.buffer, value, dataOffset)
let deltaOffset = (dataOffset - offset - 8) div 8
pack(p.buffer, offset,
(deltaOffset.uint64 shl 2) or
(info.dataSize.uint64 shl 32) or
(info.pointerCount.uint64 shl 48))
proc packCap(p: Packer, offset: int, value: CapServer) =
let rawValue = p.capToIndex(value)
case rawValue.kind:
of rawCapNumber:
let id = rawValue.number
if id == -1:
pack(p.buffer, offset, 0)
else:
pack(p.buffer, offset,
3.uint64 or (id.uint64 shl 32))
of rawCapValue:
pack(p.buffer, offset, rawValue.value)
proc packPointer*[T](p: Packer, offset: int, value: T) =
when value is (string|seq):
packList(p, offset, value)
elif T is CapServer:
packCap(p, offset, value)
elif T is SomeInterface:
packCap(p, offset, toCapServer(value))
elif T is object|ref object:
if value.isNil:
packNil(p, offset)
else:
packStruct(p, offset, value)
else:
mixin packPointerHook
packPointerHook(p, offset, value)
proc preprocessText(v: string): string =
if v == nil: return v
else: return v & "\0"
proc preprocessText[T](v: seq[T]): seq[T] =
if v == nil:
return v
else:
return v.map(x => preprocessText(x))
proc packText*[T](p: Packer, offset: int, value: T) =
packPointer(p, offset, preprocessText(value))
proc newPacker*(): Packer =
let capToIndex = proc(cap: CapServer): RawCapValue =
raise newException(Exception, "this packer doesn't support capabilities")
return Packer(
buffer: newZeroString(8),
capToIndex: capToIndex)
proc packPointer*[T](value: T): string =
let packer = newPacker()
packPointer(packer, 0, value)
return packer.buffer
proc packPointerIgnoringCaps*[T](value: T): string =
let packer = newPacker()
packer.capToIndex = proc(cap: CapServer): int = RawCapValue(kind: rawCapNumber, number: 0xBEEF)
packPointer(packer, 0, value)
return packer.buffer