std.buffer — Byte-Puffer

Zurück zur Unit-Übersicht

Operationen auf rohen Speicherbereichen: kopieren, füllen, umkehren, vergleichen, suchen, in Hex umwandeln und zurück. Die Unit allokiert nichts — jeder Puffer kommt vom Aufrufer, jede Funktion bekommt die Länge mitgeteilt.

Genau daraus folgt ihre wichtigste Eigenschaft: sie ist binärsicher. Nullbytes mitten in den Daten beenden nichts, Werte über 127 werden unverändert durchgereicht.

Einsatzbereiche: Protokollrahmen, Binärformate, Zwischenschicht für Netzwerk- und Krypto-Units.

Autor: Andreas Röne
Copyright: 2024–2025 Andreas Röne
Quelle: std/buffer.lyx

<WRAP info> Diese Unit ist vollständig geprüft und arbeitet fehlerfrei — einschließlich des Hex-Rundlaufs über alle Bytewerte von 0 bis 255 und der Suche in Daten mit eingebetteten Nullbytes. Zu beachten sind lediglich die Aufrufkonventionen unter Worauf zu achten ist. </WRAP>

Import

import std.buffer;


Konstanten

Name Wert Bedeutung
DEFAULT_BUFFER_CAPACITY 256 Vorschlag für eine Anfangsgröße
MIN_BUFFER_CAPACITY 16 Vorschlag für die kleinste sinnvolle Größe
GROWTH_FACTOR 2 Faktor, mit dem BufferCalculateCapacity vergrößert

Die ersten beiden sind reine Empfehlungen — keine Funktion der Unit verwendet sie.


Funktionen

Vergleichen und Suchen

Signatur Beschreibung
BufferEquals(buf1: pchar, len1: int64, buf2: pchar, len2: int64): bool Byteweiser Vergleich; unterschiedliche Längen ergeben sofort false
BufferFind(haystack: pchar, hlen: int64, needle: pchar, nlen: int64): int64 Position des ersten Vorkommens, sonst -1

BufferFind liefert auch dann -1, wenn die Nadel leer ist (nlen == 0) oder länger als der Heuhaufen — nicht 0, wie es manche Textsuchen tun.

Verändern

Signatur Beschreibung
BufferCopy(dest: pchar, src: pchar, len: int64): int64 Kopiert len Bytes, liefert len
BufferFill(buf: pchar, len: int64, byte: int64): void Füllt len Bytes mit einem Wert
BufferReverse(buf: pchar, len: int64): void Dreht die Bytefolge an Ort und Stelle um
BufferAppendString(buf: pchar, buf_len: int64, str: pchar, max_out: int64): int64 Hängt einen nullterminierten String an; liefert die neue Länge oder -1, wenn der Platz nicht reicht
BufferToString(buf: pchar, len: int64, output: pchar): int64 Kopiert len Bytes nach output und terminiert dort mit einem Nullbyte

BufferCopy setzt voraus, dass sich Quelle und Ziel nicht überlappen — es wird von vorn nach hinten kopiert.

Hex

Signatur Beschreibung
ByteToHex(byte: int64, output: pchar): int64 Schreibt zwei Großbuchstaben-Hexziffern, liefert 2. Terminiert nicht
BufferToHex(input: pchar, len: int64, output: pchar): int64 Wandelt len Bytes in 2·len Hexzeichen und terminiert
HexToBuffer(input: pchar, output: pchar): int64 Liest einen nullterminierten Hexstring; liefert die Bytezahl oder -1

HexToBuffer akzeptiert Groß- und Kleinschreibung und meldet -1 bei ungerader Länge oder Zeichen außerhalb von 0–9/A–F/a–f. Ein leerer String ergibt 0.

Kapazität und Größenrechner

Signatur Beschreibung
BufferCalculateCapacity(current, needed: int64): int64 Reicht current, bleibt es dabei; sonst wird verdoppelt, mindestens aber auf needed
Base64EncodedLen(input_len: int64): int64 Zeichenzahl einer Base64-Kodierung mit Auffüllzeichen
Base64DecodedLen(input_len: int64): int64 Obergrenze der Bytezahl beim Dekodieren

Die beiden Base64-Rechner geben nur Größen zurück; die Kodierung selbst liegt in std.base64, das mit EncodedLen eine namensgleiche Funktion mitbringt. Beide liefern dieselben Werte — geprüft für Eingabelängen 1 bis 8 gegen die tatsächliche Ausgabe von EncodeBytes.


Beispiel

import std.buffer;
import std.alloc;

fn main(): int64 {
    // Binaerdaten mit Nullbytes und Werten ueber 127
    var bin: int64 := alloc(8);
    poke8(bin, 0);     poke8(bin+1, 15);  poke8(bin+2, 16);  poke8(bin+3, 127);
    poke8(bin+4, 128); poke8(bin+5, 200); poke8(bin+6, 254); poke8(bin+7, 255);

    // Als Hex ausgeben
    var hex: int64 := alloc(32);
    var n: int64 := BufferToHex(bin as pchar, 8, hex as pchar);
    PrintLn("BufferToHex: ", IntToStr(n), " Zeichen -> ", hex as pchar);

    // und wieder zurueck
    var zurueck: int64 := alloc(16);
    var m: int64 := HexToBuffer(hex as pchar, zurueck as pchar);
    PrintLn("HexToBuffer: ", IntToStr(m), " Bytes, identisch? ",
            IntToStr(BufferEquals(bin as pchar, 8, zurueck as pchar, 8) as int64));

    // Suchen und Vergleichen arbeiten laengenbasiert, Nullbytes stoeren nicht
    var muster: int64 := alloc(4);
    poke8(muster, 128); poke8(muster+1, 200);
    PrintLn("Find(Muster 128 200) = ", IntToStr(BufferFind(bin as pchar, 8, muster as pchar, 2)));

    // Textpuffer zusammensetzen
    var buf: int64 := alloc(64);
    var len: int64 := BufferCopy(buf as pchar, "Hallo" as pchar, 5);
    len := BufferAppendString(buf as pchar, len, " Welt" as pchar, 64);
    PrintLn("nach Append: ", IntToStr(len), " Bytes");
    var text: int64 := alloc(64);
    BufferToString(buf as pchar, len, text as pchar);
    PrintLn("als String:  '", text as pchar, "'");

    // Wachstum planen
    PrintLn("CalculateCapacity(256, 300)  = ", IntToStr(BufferCalculateCapacity(256, 300)));
    PrintLn("CalculateCapacity(256, 5000) = ", IntToStr(BufferCalculateCapacity(256, 5000)));
    return 0;
}

Ausgabe:

BufferToHex: 16 Zeichen -> 000F107F80C8FEFF
HexToBuffer: 8 Bytes, identisch? 1
Find(Muster 128 200) = 4
nach Append: 10 Bytes
als String:  'Hallo Welt'
CalculateCapacity(256, 300)  = 512
CalculateCapacity(256, 5000) = 5000

Der Hex-Rundlauf umfasst hier bewusst das Nullbyte, die Grenzwerte 127/128 und 255 — alle acht Bytes kommen unverändert zurück.


Worauf zu achten ist

Keine Grenzprüfung

Keine Funktion kennt die Größe der übergebenen Puffer. BufferCopy(dest, src, 1000) schreibt tausend Bytes, gleich wie groß dest ist; BufferToHex braucht 2·len + 1 Bytes im Ziel, BufferToString len + 1. Diese Größen rechnet der Aufrufer aus.

Einzige Ausnahme ist BufferAppendString: dort begrenzt max_out das Ziel.

BufferAppendString terminiert nicht

Die Funktion hängt nur die Zeichen an und gibt die neue Länge zurück. Ein Nullbyte setzt sie nicht — dafür ist BufferToString da:

var len: int64 := BufferCopy(buf as pchar, "Hallo" as pchar, 5);
len := BufferAppendString(buf as pchar, len, " Welt" as pchar, 64);
BufferToString(buf as pchar, len, text as pchar);     // erst hier entsteht ein String

Die Platzprüfung lautet buf_len + str_len >= max_out — ein Byte bleibt also für die spätere Nullterminierung frei. Bei max_out = 13 und einem Ergebnis von genau 13 Bytes kommt deshalb -1 zurück.

ByteToHex hinterlässt keinen String

Es schreibt genau zwei Zeichen an den Anfang des Puffers. Was dahinter steht, bleibt stehen:

Puffer vorher 'XXX', ByteToHex(255, puffer)  ->  'FFX'

Für einen einzelnen Bytewert als Zeichenkette also selbst terminieren, oder BufferToHex mit len = 1 verwenden.

Überlappende Bereiche

BufferCopy kopiert vorwärts. Überlappen sich Quelle und Ziel so, dass das Ziel hinter der Quelle liegt, werden bereits kopierte Bytes erneut gelesen. Für Verschiebungen innerhalb eines Puffers zuerst in einen Zwischenpuffer kopieren.

Hex ist immer Großschreibung

BufferToHex und ByteToHex geben AF aus. HexToBuffer liest beide Schreibweisen.


Verwandte Units

  • std.base64 — die eigentliche Base64-Kodierung
  • std.alloc — Puffer beschaffen und freigeben
  • std.pack — Zahlen und Strings strukturiert in Puffer schreiben
  • std.string — Funktionen für nullterminierte Zeichenketten