Skip to content

aowlspt/ctext

Source: aowl/src/aowlspt/ctext.nim — 95 lines.

ctext.nim -- getting TEXT out of C and into a nimony string, once.

THE PROBLEM THIS EXISTS FOR

Nimony has no cstring -> string conversion. Nim 2's $cstring is not available, and there is no newString+copyMem idiom that survives the bounds rules here. So every consumer of a C string coming out of the ABI -- a region participant's name, a refusal reason, a capability id -- hand-rolled the SAME copy-into-a-caller-buffer C helper, privately. There were three such copies before this file; this is the shared one, so there is not a fourth.

THE SHAPE, and why it is this shape

C never returns a string. What crosses the boundary is:

  • a cstring the C side owns and keeps alive, plus
  • a LENGTH obtained by a bounded scan on the C side.

The Nim side then allocates once and fills byte by byte. That is slower than a copyMem, and it is deliberate: a copyMem from a pointer whose length came from the same untrusted side is exactly the read-past-the-end this is meant to prevent, and these strings are tens of bytes read at human speed, not a hot loop.

EVERY READ IS BOUNDED. cTextLen scans at most MaxCText bytes and returns that cap if it finds no terminator, so an unterminated buffer costs a bounded read and yields a truncated string -- never a walk off the end of a page. A caller that needs to know truncation happened compares the result's length against MaxCText.

NULL IS NOT AN ERROR CODE HERE. A nil pointer yields "", because every caller so far wants "no text" and not an exception, and a "" that came from nil is indistinguishable from a "" that came from an empty buffer -- which is correct: both mean the same thing to a label.

Constants

MaxCText

nim
  MaxCText* = 4096

The hard ceiling on any single C string read through this module. It is a REFUSAL, not a preference: an unterminated or corrupt buffer must cost a bounded read. Region names are 32 bytes and reasons are 192, so this is three orders of magnitude of headroom over any real caller.

aowl/src/aowlspt/ctext.nim:37

Routines

cTextLen

nim
proc cTextLen*(s: cstring): int

The length of a C string, scanning at most MaxCText bytes. A result equal to MaxCText means the terminator was NOT found within the cap -- treat it as truncated, not as an exact length.

aowl/src/aowlspt/ctext.nim:66

cText

nim
proc cText*(s: cstring): string

A C string as a nimony string, bounded by MaxCText.

Returns "" for a nil or empty pointer; those two are the same answer to every caller this has, and inventing a distinction neither one carries would be a lie dressed as precision.

aowl/src/aowlspt/ctext.nim:72

cTextOr

nim
proc cTextOr*(s: cstring; dflt: string): string

cText, but with an explicit stand-in for "there was no text". Use this wherever an empty label on screen would read as a MEASUREMENT ("this participant has no name") rather than as an absence -- an empty string is the classic silently-wrong answer in this codebase.

aowl/src/aowlspt/ctext.nim:88

aoughwl — self-hosted platform for things n stuff. Contact / Support on Discord for access to the private backends.