The library, one page,
every method.

Four classes and a handful of free functions — that's the whole surface. Every signature below is copied from rpp/core.ring and rpp/idioms.ring, not a description that can drift from the code.

load "ringpp.ring"

oBuf = new RppBuffer(1024)
oBuf.PokeString(0, "hello")
? oBuf.Peek(0, 5)        # --> hello

That's the whole install story — clone the repository, then one load. Everything below is what's on the other side of it.

RppBuffer — bytes that cross a call by reference, not by copy

The type pillar one is built around. Build it with new RppBuffer(nBytes), the free function RppBuffer(nBytes), or RppBufferFromString(cStr) to wrap a string you already have.

Capacity(), Size() — the buffer's byte capacity (both return the same number; two names for two habits of thought).

Peek(nOffset, nLen) — an O(1) slice out. 0.09 µs where substr() costs 12.5 µs on a 500 KB string.

Byte(nOffset) — the single byte at nOffset, as a number.

Str() — the whole buffer as an ordinary Ring string. The one place a copy happens on purpose, named so it's visible at the call site.

Poke(nOffset, cBytes), PokeString(nOffset, cStr) — write bytes at an offset, bounds-checked. The two names are the same call.

Fill(nOffset, nLen, nByte) — repeat one byte nLen times, starting at nOffset.

PokeInt32(nOffset, n) / PeekInt32(nOffset), PokeDouble / PeekDouble, PokeFloat / PeekFloat — packed numeric fields, for binary record layouts.

View(nOffset, nLen) — a zero-copy RppView window onto part of the buffer. All() is View(0, Size()).

Grow(nNewBytes) — the only legal resize: allocates a new backing string, copies the old bytes in, returns the old capacity. Never resize any other way — the reasons are in the source comments, and they're not optional.

LoadFile(cPath), SaveFile(cPath, nLen) — read a file straight into the buffer (growing it if needed), or write nLen bytes of it back out.

AddressUnchecked(), PokeUnchecked(nOffset, cBytes) — the escape hatch, named so it stays visible: no bounds check, your bug becomes a crash with no line number instead of a raised error.

oBuf = RppBufferFromString("name=Mansour;role=maintainer")
oBuf.PokeInt32(900, 42)
? oBuf.PeekInt32(900)              # --> 42

RppView — a window, not a copy

Never constructed with new directly — get one from oBuf.View(nOffset, nLen) or oBuf.All(). It holds a live reference to its owner (ref() — plain assignment would copy the buffer and freeze a snapshot), so writes made through the buffer afterwards are visible through the view too.

Size(), Offset() — the view's length, and where it starts inside its owner.

Peek(nOffset, nCount), Byte(nOffset) — same shape as RppBuffer's, offsets relative to the view, not the owner.

Sub(nOffset, nCount) — a view of a view, no new copy.

Str() — the view's bytes as an ordinary Ring string.

Buffer() — the owning RppBuffer, if you need to step back out to it.

oView = oBuf.View(5, 7)
? oView.Str()                       # --> Mansour

RppIndexed — a phase, not a permanent state

Wraps ringvm_genarray() — worth roughly 95× on permuted reads of an append-built list, and up to 16× worse when mutations outnumber reads. So it's spelled as a phase you open and close, not a setting you leave on.

RppIndexed(aList) / new RppIndexed(aList) — opens the phase. Below 64 items it declines on its own (the cursor walk is already cheaper) rather than pay for an index nobody needs.

Applied() — whether the index was actually built.

Why() — one sentence explaining Applied()'s answer.

Release(aList) — closes the phase. Returns TRUE when the index stayed valid the whole time.

Caveat() — what Release() cannot see: sort() and reverse() invalidate the index without changing len(), so re-open the phase after sorting, always.

oIdx = RppIndexed(aRows)          # open, once, before the reads
for i = 1 to 50000
    x = aRows[ aKeys[i] ][3]        # random access, many times
next
oIdx.Release(aRows)                  # same list object, not a copy

The list must be passed to both calls, every time — storing it in an attribute would copy it by value, and Ring++ would index the copy instead of your list.

RppSandbox — a second interpreter, for containment

0.35 ms to create. A Ring error inside does not kill the host process. It buys containment, not speed — the same work runs roughly 1.75× slower in a fresh sub-state than in the host.

RppSandbox() / new RppSandbox() — opens a fresh Ring interpreter state.

Run(cCode) — runs a string of Ring source inside it.

Quiet() — suppresses the sandbox's own error output.

Var(cName), Has(cName) — read a variable back, or check it exists. (Not Getget is a Ring statement keyword and can never be a method name.)

SetVar(cName, vValue) — set a variable before running code that reads it.

Free(), IsOpen() — release the sub-state, or check whether it already has been.

oBox = RppSandbox()
oBox.Run("nTotal = 2 + 2")
? oBox.Var("nTotal")            # --> 4
oBox.Free()

The small helpers

RppRows(nRows, nCols) — a 2D list via list(rows, cols), roughly 6× faster to build than pushing sublists one at a time, with near-array random access as a side effect.

RppSyntaxOk(cCode), RppTokens(cCode) — Ring's own scanner, exposed as a service: TRUE/FALSE, or the actual token list, with no extension involved.

RppAdvise(), RppAdviseAdd(cWhere, cText), RppAdviceClear() — the log RppIndexed itself writes warnings to when a phase's list changed shape mid-flight. Call RppAdvise() to print what accumulated.

Which one, when

Holding a block of bytes, touched a lot

RppBuffer — a document, a file you're building, a parse buffer. Below ~512 bytes, plain Ring wins; this is for the big, repeatedly-touched case.

Slicing that buffer, without copying

RppView — records inside one big read, fields inside one big row. A window that stays live as the buffer changes.

Reading a big list many times, rarely changing it

RppIndexed — a lookup pass, a report, anything that reads far more than it writes. Open the phase, read, close it.

Running code you don't fully trust

RppSandbox — generated Ring, a plugin, anything where one bad statement should not take the host process down.

Next

Performant code — why RppBuffer is fast CLI.md — every command