std.edi.core
EDIFACT-Interchange-Engine — das Fundament aller std.edi.*-Units. Implementiert die vollständige Umschlag-Hierarchie (UNA / UNB / UNZ / UNG / UNE / UNH / UNT), den Segment-Tokenizer mit Escape-Behandlung, laufende Sequenznummern und Duplikatserkennung pro Partner.
Alle anderen EDI-Units importieren std.edi.core und setzen die hier definierten Structs und Konstanten voraus.
import std.edi.core;
import std.alloc;
fn main(): int64 {
// 1. Kontext anlegen - EdiCtxInit setzt die UNOA-Standardtrennzeichen
var ctx: int64 := alloc(EDI_CTX_SIZE);
EdiCtxInit(ctx);
// Absender/Empfaenger als Zeiger + Laenge (kein Kopieren)
poke64(ctx + EDI_CTX_SENDER, "4012345000009"c as int64);
poke64(ctx + EDI_CTX_SNDLEN, 13);
poke64(ctx + EDI_CTX_RCVR, "4098765000004"c as int64);
poke64(ctx + EDI_CTX_RCVLEN, 13);
poke64(ctx + EDI_CTX_ICREF, "IC000042"c as int64);
poke64(ctx + EDI_CTX_ICREFLEN, 8);
poke64(ctx + EDI_CTX_DATE, "20260813"c as int64);
poke64(ctx + EDI_CTX_TIME, "0930"c as int64);
// 2. State fuer Sequenznummern und Duplikatserkennung
var state: int64 := alloc(EDI_STATE_SIZE);
EdiStateInit(state);
var seq: int64 := EdiSeqNext(state, "4098765000004"c);
// 3. Umschlag schreiben
var out: int64 := alloc(4096);
var pos: int64 := EdiUnbWrite(ctx, out, 4096);
PrintLn(IntToStr(pos)); // 62 = Laenge des UNB-Segments
PrintLn(IntToStr(seq)); // 1 = erste Sequenznummer fuer diesen Partner
free(out, 4096);
free(state, EDI_STATE_SIZE);
free(ctx, EDI_CTX_SIZE);
return 0;
}
Imports
Konstanten & Standardtrennzeichen
Wenn kein UNA-Segment vorhanden ist, gelten folgende Standardwerte (UNOA-Zeichensatz):
| Konstante | Wert | Bedeutung |
EDI_DEF_COMP | : (58) | Komponenten-Trenner |
EDI_DEF_DATA | + (43) | Datenelement-Trenner |
EDI_DEF_DEC | . (46) | Dezimalzeichen |
EDI_DEF_REL | ? (63) | Release-Zeichen (Escape) |
EDI_DEF_SEG | (39) | Segmentabschluss |
Zeichensatz-Codes:
| Konstante | Bedeutung |
EDI_CHARSET_UNOA | A–Z, 0–9 + wenige Sonderzeichen |
EDI_CHARSET_UNOB | Latin-1 / ISO 8859-1 |
EDI_CHARSET_UNOC | UTF-8 |
Fehlercodes:
| Konstante | Bedeutung |
EDI_OK | Kein Fehler |
EDI_ERR_TRUNC | Puffer zu klein |
EDI_ERR_NOUNA | Kein UNA-Segment gefunden |
EDI_ERR_SYNTAX | Syntaxfehler im Segment |
EDI_ERR_DUP | Doppelte Interchange-Referenz erkannt |
EDI_ERR_OVERFLOW | Zu viele Partner-Slots belegt |
EDI_ERR_PARTNER | Partner nicht gefunden |
Structs
Alle Structs werden vom Caller alloziert. Größen sind über Konstanten verfügbar.
EdiUna (EDI_UNA_SIZE = 48 Bytes)
Hält die Trennzeichen aus dem UNA-Segment oder die Standardwerte.
| Offset-Konstante | Offset | Inhalt |
EDI_UNA_COMP | 0 | Komponenten-Trenner (int64) |
EDI_UNA_DATA | 8 | Datenelement-Trenner (int64) |
EDI_UNA_DEC | 16 | Dezimalzeichen (int64) |
EDI_UNA_REL | 24 | Release-Zeichen (int64) |
EDI_UNA_RES | 32 | Reserviert (int64) |
EDI_UNA_SEG | 40 | Segmentabschluss (int64) |
EdiCtx — Interchange-Kontext (EDI_CTX_SIZE = 128 Bytes)
| Offset-Konstante | Inhalt |
EDI_CTX_SENDER / EDI_CTX_SNDLEN | Absender-ID (Zeiger + Länge) |
EDI_CTX_RCVR / EDI_CTX_RCVLEN | Empfänger-ID (Zeiger + Länge) |
EDI_CTX_ICREF / EDI_CTX_ICREFLEN | Interchange-Referenz (ICR) |
EDI_CTX_DATE / EDI_CTX_TIME | Datum (YYYYMMDD oder YYMMDD) / Zeit (HHMM) |
EDI_CTX_CHARSET | Zeichensatz-Code (UNOA/UNOB/UNOC) |
EDI_CTX_TESTFLAG | 0 = Produktion, 1 = Testmodus |
EDI_CTX_COMP / DATA / DEC / REL / SEG | Trennzeichen (0 = Standard) |
EDI_CTX_MSGCOUNT | Anzahl Nachrichten im Interchange |
EdiSegCtx — Segment-Iterator (EDI_SEGCTX_SIZE = 40 Bytes)
Hält die aktuelle Position im Lesepuffer. Wird von EdiSegCtxInit initialisiert und von EdiSegmentNext fortgeschrieben.
EdiSeg — Segment-Ergebnis (EDI_SEG_SIZE = 16 Bytes)
| Offset-Konstante | Inhalt |
EDI_SEG_PTR | Zeiger in den Originalpuffer (Zero-Copy) |
EDI_SEG_LEN | Byte-Länge ohne Segmentabschluss |
EdiElem — Element/Komponenten-Slot (EDI_ELEM_SIZE = 16 Bytes)
| Offset-Konstante | Inhalt |
EDI_ELEM_PTR | Zeiger in den Originalpuffer |
EDI_ELEM_LEN | Byte-Länge des Elements/der Komponente |
EdiState — Partner-State (EDI_STATE_SIZE = 16 776 Bytes)
Speichert pro Partner (max. 16) die laufende Sequenznummer und einen Ring-Buffer von 128 ICR-Hashes zur Duplikatserkennung.
Funktionen
Initialisierung
| Signatur | Beschreibung |
EdiCtxInit(ctx: int64): void | Nullt alle Felder des Interchange-Kontexts |
EdiStateInit(state: int64): void | Nullt alle Felder des Partner-State |
UNA lesen / schreiben
| Signatur | Beschreibung |
EdiUnaRead(buf: int64, bufLen: int64, out: int64): int64 | Liest UNA-Segment aus buf; füllt out (EDI_UNA_SIZE). Kein UNA → Standardwerte. Gibt EDI_OK zurück. |
EdiUnaWrite(ctx: int64, out: int64, outMax: int64): int64 | Schreibt UNA-Segment mit den im Kontext gesetzten Trennzeichen; gibt geschriebene Bytes zurück. |
Segment-Tokenizer
| Signatur | Beschreibung |
EdiSegCtxInit(sctx: int64, una: int64, startPos: int64): void | Initialisiert Iterator ab Byte-Position startPos mit Trennzeichen aus una |
EdiSegmentNext(buf: int64, bufLen: int64, sctx: int64, seg: int64): int64 | Holt nächstes Segment. Gibt 1 (gefunden) oder 0 (Ende) zurück. Zeiger in seg zeigen in buf (Zero-Copy). |
EdiElementSplit(seg: int64, out: int64, maxOut: int64): int64 | Spaltet Segment in Datenelemente auf; befüllt out-Array (je EDI_ELEM_SIZE). Gibt Anzahl zurück. |
EdiComponentSplit(ptr: int64, len: int64, out: int64, maxOut: int64): int64 | Spaltet ein Datenelement in Komponenten auf. Gibt Anzahl zurück. |
EdiUnescape(src: int64, srcLen: int64, dst: int64, dstMax: int64): int64 | Entfernt Release-Zeichen (?) aus einem String. Gibt Länge des Ergebnisses zurück. |
UNB / UNZ — Interchange-Umschlag
| Signatur | Beschreibung |
EdiUnbRead(buf: int64, bufLen: int64, ctx: int64): int64 | Parst UNB-Segment; füllt Absender/Empfänger/ICR/Datum ins ctx. Gibt EDI_OK oder Fehlercode. |
EdiUnbWrite(ctx: int64, out: int64, outMax: int64): int64 | Schreibt UNB-Segment; gibt geschriebene Bytes zurück. |
EdiUnzWrite(ctx: int64, msgCount: int64, out: int64, outMax: int64): int64 | Schreibt UNZ-Trailer (Interchange-Ende). |
UNG / UNE — Gruppen-Umschlag
| Signatur | Beschreibung |
EdiUngWrite(ctx: int64, msgType: int64, grpRef: int64, out: int64, outMax: int64): int64 | Schreibt UNG-Gruppen-Header |
EdiUneWrite(ctx: int64, msgCount: int64, grpRef: int64, out: int64, outMax: int64): int64 | Schreibt UNE-Gruppen-Trailer |
UNH / UNT — Nachrichten-Umschlag
| Signatur | Beschreibung |
EdiUnhWrite(ctx: int64, msgRef: int64, msgType: int64, out: int64, outMax: int64): int64 | Schreibt UNH-Nachrichten-Header |
EdiUntWrite(ctx: int64, segCount: int64, msgRef: int64, out: int64, outMax: int64): int64 | Schreibt UNT-Nachrichten-Trailer |
Sequenznummern & Duplikatserkennung
| Signatur | Beschreibung |
EdiSeqNext(state: int64, partnerKey: int64): int64 | Erhöht Sequenznummer für diesen Partner; gibt nächste Nummer zurück |
EdiDuplicateCheck(state: int64, partnerKey: int64, icRef: int64): int64 | Prüft ob ICR bereits bekannt; gibt 1 (Duplikat) oder 0 (neu). Trägt neue ICRs automatisch ein (Ring-Buffer mit 128 Einträgen). |
Codebeispiel — eingehende Nachricht zerlegen
import std.edi.core;
import std.alloc;
import std.string;
fn Feld(ptr: int64, len: int64): pchar {
var b: int64 := alloc(len + 1);
var i: int64 := 0;
while (i < len) { poke8(b + i, peek8(ptr + i)); i := i + 1; }
poke8(b + len, 0);
return b as pchar;
}
fn main(): int64 {
var msg: pchar := "UNA:+.? 'UNB+UNOA:1+SENDER01+RCVR0001+20260813:0930+IC77'LIN+1++04012345678901:SRV'QTY+21:24'PRI+AAA:19.99:CP'UNZ+1+IC77'"c;
var buf: int64 := msg as int64;
var len: int64 := StrLen(msg);
// 1. Trennzeichen aus UNA - fehlt es, gelten die Standardwerte (EDI_ERR_NOUNA)
var una: int64 := alloc(EDI_UNA_SIZE);
var start: int64 := 0;
if (EdiUnaRead(buf, len, una) == EDI_OK) { start := 9; }
PrintLn(StrConcat("Datenelementtrenner: ", IntToStr(peek64(una + EDI_UNA_DATA))));
// 2. Segmente durchlaufen
var sctx: int64 := alloc(EDI_SEGCTX_SIZE);
EdiSegCtxInit(sctx, una, start);
var seg: int64 := alloc(EDI_SEG_SIZE);
var elems: int64 := alloc(16 * EDI_ELEM_SIZE);
var comps: int64 := alloc(8 * EDI_ELEM_SIZE);
while (EdiSegmentNext(buf, len, sctx, seg) == 1) {
var segPtr: int64 := peek64(seg + EDI_SEG_PTR);
var n: int64 := EdiElementSplit(seg, elems, 16);
// Das 3-Zeichen-Tag steht am Segmentanfang; EdiElementSplit ueberspringt es.
Print(StrConcat(Feld(segPtr, 3), ": "));
Print(StrConcat(IntToStr(n), " Datenelemente"));
if (peek8(segPtr) == 76 && peek8(segPtr + 1) == 73) {
// LIN+1++GTIN:SRV -> Element 2 traegt die GTIN als erste Komponente
var e2p: int64 := peek64(elems + 2 * EDI_ELEM_SIZE + EDI_ELEM_PTR);
var e2l: int64 := peek64(elems + 2 * EDI_ELEM_SIZE + EDI_ELEM_LEN);
var nc: int64 := EdiComponentSplit(e2p, e2l, comps, 8);
Print(StrConcat(" - GTIN ", Feld(peek64(comps + EDI_ELEM_PTR), peek64(comps + EDI_ELEM_LEN))));
}
PrintLn("");
}
return 0;
}
Datenelementtrenner: 43
UNB: 5 Datenelemente
LIN: 3 Datenelemente - GTIN 04012345678901
QTY: 1 Datenelemente
PRI: 1 Datenelemente
UNZ: 2 Datenelemente
Hinweise
Zero-Copy-Parsing: EdiSegmentNext, EdiElementSplit und EdiComponentSplit setzen Zeiger in den Originalpuffer — keine Kopien. Der Puffer muss während der gesamten Verarbeitung gültig bleiben.
Caller alloziert: Alle Structs (UNA, CTX, SEGCTX, SEG, Elem-Arrays) werden vom Caller alloziert und freigegeben. Die Größenkonstanten (EDI_*_SIZE) geben die benötigte Byte-Anzahl an.
Escape-Behandlung: EdiSegmentNext behandelt das Release-Zeichen (Standard: ?) korrekt — es schützt das darauffolgende Trennzeichen vor Interpretation. Explizite Daten müssen danach durch EdiUnescape bereinigt werden.
Duplikatserkennung: EdiDuplicateCheck nutzt einen Ring-Buffer mit 128 Hash-Einträgen pro Partner (max. 16 Partner). Bei mehr als 128 ICRs pro Partner kann ein alter ICR wieder als „neu“ erscheinen.
EdiUnbRead erwartet genau ein Segment. Der übergebene Bereich muss bei UNB beginnen und mit dem Segmentzeichen enden — sonst schleppt die Interchange-Referenz das Folgesegment mit. Praktisch: erst EdiSegmentNext, dann EdiUnbRead(peek64(seg + EDI_SEG_PTR), peek64(seg + EDI_SEG_LEN), ctx).
msgRef, msgType und grpRef sind Zeiger auf Text, obwohl sie als int64 deklariert sind: EdiUnhWrite(ctx, „1“c, „ORDERS“c, out, max). Eine echte Zahl an dieser Stelle wird als Adresse gelesen und stürzt ab.
Tag und Elemente: EdiElementSplit überspringt das Drei-Zeichen-Tag — Element 0 ist bereits das erste Datenelement. Das Tag selbst steht in den ersten drei Bytes ab EDI_SEG_PTR.
Eigene Trennzeichen wirken nur im Iterator. EdiSegmentNext arbeitet mit den Werten aus EdiSegCtxInit; EdiElementSplit und EdiComponentSplit rechnen dagegen fest mit +, : und ?. Bei abweichendem UNA müssen Elemente von Hand zerlegt werden.
EDI_ERR_NOUNA ist kein Abbruchgrund: EdiUnaRead hat dann die Standardtrennzeichen eingetragen; der Startversatz ist 0 statt 9.
Quelldatei
| Unit | Datei |
std.edi.core | std/edi/core.lyx |
Letzte Aktualisierung: 2026-08-13 — beide Codebeispiele lauffähig gemacht und ausgeführt (lyxc 1.0.17K); Hinweise zu EdiUnbRead (genau ein Segment), Zeiger-Parametern bei UNH/UNT/UNG, Tag-Behandlung in EdiElementSplit und den festen Trennzeichen der Split-Funktionen ergänzt
Codebeispiele geprüft: gegen lyxc 1.2.5C übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die pub fn-Signaturen in aurum/std gehalten, 0 Abweichungen).