mirror of
https://github.com/zap-proto/nim.git
synced 2026-10-30 15:04:34 +00:00
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.
54 lines
2.0 KiB
Nim
54 lines
2.0 KiB
Nim
## Sending/receiving capn'p messages over stream in a same format as C++ RPC implementation.
|
|
import zap, reactor, collections
|
|
|
|
proc readSegments*(stream: Input[byte]): Future[string] {.async.} =
|
|
let segmentCount = int(await stream.readItem(uint32, littleEndian))
|
|
if segmentCount > 512:
|
|
raise newException(ZapFormatError, "too many segments")
|
|
|
|
var s = zap.pack(segmentCount.uint32, littleEndian)
|
|
|
|
var dataLength = 0
|
|
for i in 0..segmentCount:
|
|
let words = int(await stream.readItem(uint32, littleEndian))
|
|
s &= zap.pack(words.uint32, littleEndian)
|
|
# FIXME: can this lead to DoS due to abort during overflow?
|
|
dataLength += words * 8
|
|
|
|
if segmentCount mod 2 == 1:
|
|
discard (await stream.readItem(uint32, littleEndian))
|
|
s &= "\0\0\0\0"
|
|
|
|
if dataLength > zap.bufferLimit:
|
|
raise newException(ZapFormatError, "message too long")
|
|
|
|
s &= await stream.read(dataLength)
|
|
when defined(caprpcPrintMessages):
|
|
echo "message ", s.encodeHex
|
|
return s
|
|
|
|
proc wrapByteInput*[T](stream: Input[byte], t: typedesc[T]): Input[T] {.asynciterator.} =
|
|
## Create a stream for receiving capn'p messages from byte stream.
|
|
while true:
|
|
let packed = await readSegments(stream)
|
|
let val = newUnpacker(packed).unpackPointer(0, T)
|
|
asyncYield val
|
|
|
|
proc pipeMsg[T](stream: Input[T], provider: ByteOutput) {.async.} =
|
|
asyncFor msg in stream:
|
|
let serialized = zap.packPointer(msg)
|
|
let lengthInWords = len(serialized) div 8
|
|
assert len(serialized) mod 8 == 0
|
|
let data = zap.pack(0.uint32, littleEndian) & zap.pack(lengthInWords.uint32, littleEndian) & serialized
|
|
# echo "send ", data.encodeHex
|
|
await provider.write(data)
|
|
|
|
proc wrapByteOutput*[T](byteprovider: ByteOutput, t: typedesc[T]): Output[T] =
|
|
## Create a provider for sending messages over byte provider.
|
|
let (stream, provider) = newInputOutputPair[T]()
|
|
pipeMsg(stream, byteprovider).onErrorClose(stream)
|
|
return provider
|
|
|
|
proc wrapBytePipe*[T](pipe: BytePipe, t: typedesc[T]): Pipe[T] =
|
|
return Pipe[T](input: wrapByteInput(pipe.input, T), output: wrapByteOutput(pipe.output, T))
|