Tinox

Eine kompilierte, objektorientierte Programmiersprache mit Java-ähnlicher Syntax, LLVM-Backend, integriertem HTTP-Server und vollständigem IDE-Tooling – entwickelt für performante Backend-Services.

Kompiliert LLVM Objektorientiert REST-API LSP High-Performance

Was ist Tinox?

Tinox ist eine eigenständig entwickelte, statisch typisierte Programmiersprache, die direkt zu nativen Maschinenbefehlen kompiliert wird. Die Sprache kombiniert eine Java-ähnliche, annotation-getriebene Syntax mit den Leistungsvorteilen eines LLVM-basierten Compilers und einem in C geschriebenen Laufzeitsystem.

Der Hauptfokus liegt auf der einfachen Entwicklung hochperformanter HTTP-REST-Dienste: Ein Controller-Handler benötigt nur wenige Zeilen Code, und der eingebaute HTTP-Server basiert auf epoll und SO_REUSEPORT – Technologien, die auch in produktionsreifen Webservern wie Nginx eingesetzt werden.

Kernidee: Die Produktivität eines frameworks wie Spring Boot oder Quarkus – ohne JVM-Overhead, Garbage-Collector-Pausen oder hohen Speicherverbrauch.
import tinox.core.http_server
import tinox.core.json

class SortController
{
    @POST
    @Path("/sort")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    fn sort(ctx: HttpContext) -> Nothing
    {
        const root: JsonValue = Json.parse(ctx.request.body)
        const numbers: List<Int64> = Json.intArrayFromJson(root.getField("numbers"))
        ctx.response.body = Json.intArrayWrap("sorted", numbers.sort())
    }
}

Dieser Code stellt einen vollständig funktionsfähigen REST-Endpunkt dar – kein Framework-Konfigurationsaufwand, keine XML-Dateien, kein Boilerplate.

Technische Grundlagen

LLVM als Compiler-Backend

Der Tinox-Compiler erzeugt keine nativen Maschinenbefehle direkt, sondern LLVM Intermediate Representation (LLVM IR) – eine plattformunabhängige, typisierte Assemblersprache. Das LLVM-Toolchain übernimmt dann die Optimierung und Übersetzung in Maschinencode.

Vorteil: Tinox profitiert automatisch von allen LLVM-Optimierungen (Loop-Unrolling, Inlining, Dead-Code-Elimination, Link-Time-Optimization) ohne eigene Optimierungslogik implementieren zu müssen.

LLVM wird auch von Clang (C/C++), Rust, Swift, Julia und Kotlin Native verwendet – es ist eines der ausgereiftesten Compiler-Backends der Industrie.

Rust als Implementierungssprache

Der gesamte Compiler – Lexer, Parser, Typchecker, Codegenerator und LSP-Server – ist in Rust implementiert. Das bringt folgende Vorteile:

C als Laufzeitsystem

Das Laufzeitsystem (runtime.c) ist in C geschrieben und stellt alle Betriebssystem-nahen Funktionen bereit: Speicherverwaltung, Strings, Arrays, Maps, Datei-I/O, Netzwerk und den HTTP-Server. C-Funktionen werden direkt aus dem LLVM IR heraus aufgerufen – kein ABI-Overhead durch Wrapper-Schichten.

Wichtige Konzepte

🔤

Statische Typisierung

Alle Typen werden zur Compile-Zeit geprüft. Keine Laufzeit-Typfehler möglich.

🔗

AOT-Kompilierung

Ahead-of-Time: Der Quellcode wird vor der Ausführung vollständig zu einem nativen Binary kompiliert. Kein Interpreter, kein JIT.

♻️

Boehm GC

Tinox verwendet den Boehm Garbage Collector – einen konservativen, thread-sicheren GC. Speicher wird automatisch freigegeben; der Entwickler muss nichts manuell verwalten.

🧵

1:1 Threading

Jeder spawn-Aufruf erstellt einen echten OS-Thread (pthread). Volle Parallelität auf mehreren CPU-Kernen.

Anweisungsende

Eine Anweisung endet am Zeilenende – es gibt kein Terminatorzeichen. Ein ;, das eine Anweisung beendet, ist ein harter Compilerfehler („semicolons were removed“); mehrere Anweisungen in einer Zeile sind nicht möglich. Das einzige verbleibende ; trennt die Teile eines C-artigen for (init; cond; update)-Kopfes.

Eine Anweisung geht in der nächsten Zeile nur weiter, wenn die Zeile mit einem binären Operator, einem Komma oder einer öffnenden Klammer endet oder die nächste Zeile mit . beginnt (Methodenketten). Innerhalb von ( ) und [ ] sind Zeilenumbrüche normale Leerzeichen. Eine Zeile, die direkt nach einem vollständigen Ausdruck mit (, [, - oder + beginnt, ist ein harter Fehler – der Compiler rät nie, ob sie die vorige Zeile fortsetzt. Enum-Varianten stehen je eine pro Zeile.

const total = first +
    second +
    third

const name = user
    .profile()
    .displayName()

Komponenten-Übersicht

Tinox besteht aus mehreren Rust-Crates (Bibliotheken), die zusammen den Compiler und das Tooling bilden, plus dem C-Laufzeitsystem und der in Tinox selbst geschriebenen Standardbibliothek.

Komponente Sprache Zweck Größe
tinox-commonRustGemeinsame Datentypen (Span, Error, AST-Knoten)~250 Zeilen
tinox-lexerRustTokenisierung (Lexer)~800 Zeilen
tinox-parserRustAST-Aufbau + Code-Formatter~2.500 Zeilen
tinox-typecheckRustTypprüfung + Annotation-Verarbeitung~2.000 Zeilen
tinox-codegenRustAST → LLVM IR~6.000 Zeilen
tinox (CLI)RustCompiler-Steuerung, REPL, Projektmanager~1.700 Zeilen
tinox-lspRustLanguage Server Protocol Server~1.000 Zeilen
tinox-eclipseJavaEclipse IDE Plugin~400 Zeilen
runtime.cCLaufzeitsystem: I/O, HTTP, Speicher, Strings~1.700 Zeilen
tinox-coreTinoxStandardbibliothek (55+ Module)~200 KB

Compiler-Pipeline

Der Tinox-Compiler transformiert eine .tnx-Quelldatei in mehreren Schritten zu einem ausführbaren nativen Binary:

1

Lexer (tinox-lexer)

Liest den UTF-8-Quelltext und zerlegt ihn in Tokens (kleinste bedeutungsvolle Einheiten): Schlüsselwörter, Bezeichner, Literale, Operatoren, Klammern. Jedes Token trägt präzise Positionsinformationen (Zeile, Spalte, Byte-Offset) für spätere Fehlermeldungen. Unterstützt String-Interpolation ("Hallo ${name}!").

2

Parser (tinox-parser)

Wandelt die Token-Sequenz in einen Abstract Syntax Tree (AST) um – eine Baumstruktur, die die syntaktische Struktur des Programms darstellt. Rekursiver Abstieg-Parser: Jede Grammatikregel entspricht einer Funktion. Erkennt Klassen, Methoden, Ausdrücke, Schleifen, Match-Ausdrücke etc.

3

Import-Auflösung (tinox CLI)

import-Deklarationen werden rekursiv aufgelöst: Die importierten .tnx-Dateien (aus der Standardbibliothek oder dem Projektverzeichnis) werden geladen, geparst und in den AST eingebettet. Das Ergebnis ist ein einziger, zusammengefasster AST mit allen benötigten Deklarationen.

4

Typchecker (tinox-typecheck)

Zwei-Pass-Verfahren: Erst werden alle Deklarationen (Klassen, Methoden, Funktionen) registriert, dann werden Ausdrücke und Anweisungen auf Typkorrektheit geprüft. Validiert auch Annotationen (@GET, @POST, @Produces usw.), extrahiert REST-Routen und erkennt DI-Komponenten.

5

Code-Generator (tinox-codegen)

Traversiert den AST direkt und erzeugt LLVM IR als Text. Generiert Vtable-Strukturen für Interface-Dispatch, monomorphisiert generische Funktionen/Klassen, alloziert Closure-Environments, erzeugt HTTP-Route-Handler. Kein Zwischenschritt – direkt AST → IR.

6

llc (LLVM Compiler)

Das externe LLVM-Werkzeug llc übersetzt das LLVM IR in Assembler. Im Release-Modus mit -O3 werden umfangreiche Optimierungen (Inlining, Loop-Optimierungen, Vektorisierung) durchgeführt.

7

Clang (C Compiler / Linker)

clang kompiliert runtime.c zu einem Objekt-File und verknüpft es mit dem generierten Assembler zu einem fertigen nativen Binary. Dabei wird auch -lm (Mathe-Bibliothek) gelinkt.

Ergebnis: Ein einzelnes, statisch verlinktes Binary ohne externe Abhängigkeiten (außer der C-Standardbibliothek). Startzeit: <1ms.

Runtime (C-Laufzeitsystem)

Das Herzstück des Tinox-Laufzeitsystems ist runtime/runtime.c (~1.700 Zeilen C-Code). Es stellt alle Primitive bereit, auf die der generierte LLVM IR direkt zugreift.

Speicherverwaltung

Tinox verwendet den Boehm Garbage Collector (libgc) für automatisches Speichermanagement. Der GC ist konservativ: Er scannt Stack und Register nach Zeigern und gibt nicht mehr erreichbare Objekte frei – ohne dass der Entwickler explizit free() aufrufen muss. Durch GC_THREADS ist er vollständig thread-sicher und funktioniert korrekt mit dem epoll-basierten HTTP-Server-Thread-Pool.

Für häufige Operationen (Sort, JSON-Parsing) werden thread-lokale statische Puffer verwendet, die einmal alloziert und dann wiederverwendet werden – das eliminiert den malloc-Overhead im Request-kritischen Pfad komplett.

Collections

TypImplementierungBesonderheit
ArrayDynamisches C-ArrayLänge bei Offset -1 gespeichert; wächst bei push
MapOpen-Addressing Hash-TableFNV-1a Hash, Load-Factor 0.75, Tombstones für Delete
StringC-String (char*)Immutable; Operationen erzeugen neues Objekt

Async & Concurrency

Das Concurrency-Modell basiert auf POSIX-Threads (pthreads). spawn erstellt einen echten OS-Thread. Channels nutzen intern eine Linked-List-Queue mit Mutex und Condition Variable.

Task-Lebensdauer, Deadlines und Abbruch

scope { ... } besitzt seine lexikalischen Spawns. Ein einfacher Spawn gehört zur Lebensdauer seines erzeugenden Threads; dieser wartet vor seinem Ende auf übrige Kinder. Beim Prozessende werden verbleibende joinbare Kinder abgebrochen und eingesammelt. Fehler werden durch await oder unbeobachtet am Scope-Ende weitergereicht; weitere Fehler erscheinen auf stderr. Der eigene Fehler des Blockrumpfs hat Vorrang. Wiederholtes Await liefert innerhalb der Lebensdauer denselben Wert oder Fehler. Nach Ende des besitzenden Scopes sind Handles ungültig; weitere Verwendung scheitert sichtbar. Tasks bleiben beim Besitzer; nach außen sollte der bereits erwartete Ergebniswert zurückgegeben werden.

try {
    scope {
        taskSetDeadline(250) // Duration from now, in monotonic milliseconds.
        const task: Task<String> = spawn Main::load()
        println(await task)
    }
} catch (e: String) { println(e) }
OperationVertrag
taskCancel(task: Task<T>)Fordert den kooperativen Abbruch eines Tasks und seiner Nachkommen an.
taskSetDeadline(ms: Int64)Setzt die Deadline des aktuellen Scopes. Deadlines übergeordneter Scopes gelten weiterhin; 0 läuft sofort ab. Negative Zeitspannen scheitern sichtbar.
taskCheckCancellation()Prüfpunkt für CPU-Schleifen. Wirft Task cancelled, Task deadline exceeded oder Task service shutdown requested.
taskWaitReadable(fd: Int64)Abbrechbares Warten auf einen echten Dateideskriptor; vor accept/read mit genau einem Leser verwenden.
taskDetach(task: Task<T>)Explizites Fire-and-forget. Nur der besitzende Thread darf einen noch nicht eingesammelten Task abkoppeln. Geerbte Abbruchanforderungen/Deadlines entfallen, Await ist unzulässig, unbehandelte Fehler beenden den Prozess sichtbar.
taskRequestShutdown()Prozessweite kooperative Stoppanforderung für Service-Lebenszyklus und Steuerung. Bootstrap-Awaits verlassen ihren Wartezustand; das Herunterfahren sammelt ausstehende joinbare Tasks ein und bewahrt echte Fehler.

Abbruch weckt auch sleep/sleep_ms, blockiertes Channel-recv und await (Prüfintervall etwa 10 ms, zuzüglich Scheduling). Tokens werden an Kind-Tasks und innere Scopes vererbt. Scope-Fehler brechen übrige Geschwister ab; der Fehler des Rumpfs bleibt vorrangig. Ein explizit abgebrochener Task meldet den Abbruch weiterhin, sofern er nicht per Await abgefangen wurde. Erwarteter Abbruch beim Prozessende gilt als erfolgreich.

Abbruch verwendet normales Exception-Unwinding: finally und defer laufen in der üblichen Reihenfolge; eine Transaktion im Kind wird vor Abschluss des Joins zurückgerollt. Transaktionen sind thread-lokal und werden nicht vererbt. Ein Scope innerhalb einer transaktionalen Methode wartet vor dem Commit auf Kinder. Ressourcen des Elternthreads sollten außerhalb des Scopes aufgeräumt werden, damit Kinder zuerst fertig werden.

Es gibt keine asynchrone Thread-Terminierung. Native blockierende I/O, Datenbankaufrufe und CPU-Schleifen ohne Prüfpunkt sind nicht automatisch unterbrechbar. Ressourcenspezifische Stopp-/Close-Operationen oder taskWaitReadable verwenden. Nach fünf Sekunden ignoriertem Abbruch beim Scope-Ende scheitert die Runtime sichtbar statt still zu hängen. Ein explizites Await eines abgebrochenen Tasks und eine Service-Stoppanforderung haben dieselbe Fünf-Sekunden-Grenze. Mit @Service gilt stattdessen das gemeinsame konfigurierte Shutdown-Budget; siehe den Service-Lebenszyklus unten. Abgekoppelte Worker werden ausdrücklich nicht strukturiert eingesammelt.

Speichern und Await bisheriger Int64-Handles bleiben für veröffentlichte Pakete vorübergehend kompatibel. Neuer Code und die Task-Steueroperationen verwenden Task<T>. Ungültige/abgelaufene Handles und zyklische Awaits scheitern mit Diagnose; Integer-Handles sind veraltet.

Koordinierter Service-Lebenszyklus

@Service(startupMs, shutdownMs) auf class Main aktiviert koordinierten Start, Health-Status und geordnetes Herunterfahren. Beide Angaben sind optionale positive Millisekunden (Standard: 30000 und 5000; Maximum: 3600000). Der Koordinator startet vor Datenbankinitialisierung und eager DI.

@ApplicationComponent
@Synchronized
class Resources {
    @OnStart
    fn start() -> Nothing { /* Initialize resources before listeners start. */ }
    @Readiness
    fn ready() -> Bool { return true }
    @Liveness
    fn live() -> Bool { return true }
    @OnStop
    fn stop() -> Nothing { /* Dispose resources after callbacks and tasks finish. */ }
}

@Service(30000, 5000)
class Main {
    fnc main() -> Int32 { return 0 }
}

Komponente und annotierte Controller/Endpoints aus der Einstiegsdatei importieren. Hooks sind synchrone Instanzmethoden ohne Parameter auf nichtgenerischen @ApplicationComponent/@Startup-Klassen ohne Vererbung. Start-Hooks folgen den injizierten Abhängigkeiten, danach Klassen-/Methodennamen; Stop-Hooks laufen umgekehrt. Jeder Hook joint seine gestarteten Kinder vor dem nächsten Schritt. Auch nach Startfehlern erhalten initialisierte Besitzer ihre Stop-Hooks. Cleanup läuft trotz Fehlern weiter; der erste Fehler bleibt erhalten, der Prozess endet mit Fehlerstatus. Gemeinsamen veränderlichen Zustand mit @Synchronized schützen.

Zustände: initializing → starting → ready, bei negativem Health-Beitrag unready, anschließend draining → stopping → stopped oder failed. Readiness verlangt abgeschlossene Initialisierung/Start-Hooks, gebundene Listener, Consumer-Handshakes und positive Readiness-/Liveness-Beiträge. Main ist keine Readiness-Barriere: Mit Listenern läuft der Dienst nach seiner Rückkehr weiter; ohne Listener fordert die Rückkehr das Herunterfahren an.

Generierte HTTP-/HTTP/3-Server reservieren GET /health/live und GET /health/ready: Status 200 oder 503 mit JSON für Zustand, Readiness, Liveness und monotone Zeitstempel startedMs, readyMs, shutdownMs. Start meint den Eintritt in den verwalteten Bootstrap, Ready den ersten erfolgreichen Health-Durchlauf, nicht Prozessstart oder Banner. Health-Methoden laufen regelmäßig im Koordinator und sollten kurz bleiben. Überschrittenes Startbudget beendet den Prozess sichtbar; spätere negative Health-Ergebnisse ändern den Status, starten den Prozess aber nicht neu.

serviceState(), serviceStatusJson(), serviceIsReady(), serviceIsLive(), serviceIsStopping() und serviceStartedMs()/serviceReadyMs()/serviceShutdownMs() liefern denselben Status. serviceAwaitReady(ms) liefert Bool; serviceBoundPort(index) den tatsächlichen Port eines Auto-Run-Tasks in Bootstrap-Plan-Reihenfolge (-1 vor Bind oder bei Brokern). tinox explain --bootstrap Main.tnx --json zeigt Budgets, Hook-Reihenfolge, Tasks und Shutdown-Vertrag.

SIGTERM, SIGINT, serviceRequestShutdown() und taskRequestShutdown() setzen den Dienst auf unready und stoppen neue Arbeit. Angenommene HTTP-Antworten und Broker-Bestätigungen werden beendet; WebSockets senden Close-Code 1001 und rufen OnClose auf. Listener und Worker werden gejoint, danach übrige strukturierte Kinder abgebrochen/gejoint, Stop-Hooks umgekehrt ausgeführt und Datenbank-Pools geschlossen. serviceAwaitShutdown() lässt Main auf die Stop-Anforderung warten. Ein gemeinsames Shutdown-Budget umfasst alle Schritte; bei Ablauf endet der Prozess mit Status 1, auch bei blockierendem nativen I/O oder Hooks. Keine asynchrone Thread-Terminierung; explizit detached gestartete Benutzer-Tasks bleiben außerhalb des strukturierten Join-Vertrags.

Container mit Exec-Entrypoint starten, damit Signale tinox erreichen. Startup-, Readiness- und Liveness-Probes auf die Health-Pfade richten; die Termination-Grace-Period muss länger als das Shutdown-Budget sein. @Command und @TinoxUIApp lassen sich derzeit nicht mit @Service kombinieren; der Compiler meldet dies als Fehler.

Gemeinsamer Anwendungszustand

@ApplicationComponent erzeugt pro Komponentenklasse einen lazy Singleton; @Startup erzeugt denselben Singleton-Typ vor main. Generierte Provider synchronisieren Erzeugung, Feld-Defaults, rekursive Dependency Injection und Veröffentlichung. Gleichzeitige Provider-Aufrufe erhalten dieselbe vollständig initialisierte Instanz. Benutzerkonstruktoren werden dabei nicht aufgerufen. Spätere Änderungen werden dadurch nicht gesperrt.

HTTP-Worker sowie unabhängig gestartete REST-, WebSocket- und AMQP-Komponenten können gleichzeitig laufen. Request-Daten gehören in @HttpRequestScoped-Besitzer, in die Anwendungsservices injiziert werden. Eine Request-Abhängigkeit in einem Singleton wird geteilt; das Erneuern eines injizierten Feldes im Singleton-Controller beim Dispatch bietet keine sichere Request-Isolation. Der Compiler warnt vor solchen Abhängigkeiten sowie öffentlichen oder ungeschützten veränderlichen Singleton-Feldern.

@ApplicationComponent
@Synchronized
class Counter {
    private var value: Int64 = 0
    fn increment() -> Int64 {
        this.value = this.value + 1
        return this.value
    }
}

@HttpRequestScoped
class Api {
    @Inject var counter: Counter
    @GET @Path("/count")
    fn count() -> Int64 { return this.counter.increment() }
}

@Synchronized an einer nicht-generischen Application-/Startup-Klasse schützt alle Instanzmethoden; an einer Instanzmethode nur diese Methode. Es gibt einen rekursiven Monitor pro deklarierender Klasse, den alle markierten Aufrufe und Instanzen dieser Klasse teilen. Verschachtelte Aufrufe desselben Threads sind erlaubt. Statische Methoden sind bei der Klassenannotation ausgenommen; explizit markierte statische/generische Methoden, Klassen ohne Singleton-Scope, Interface-/Trait-Methoden und Klassen mit Vererbung werden zurückgewiesen.

Der Monitor bleibt während defer/finally und Transaktions-Commit/Rollback gehalten und wird vor Rückgabe eines Werts oder Fehlers freigegeben. Abbruch beim Warten auf den Monitor wirft ohne Ausführung des Rumpfs. Abbruch im Rumpf führt das normale Unwinding und die Freigabe aus. Bereits aktive Fehler bleiben beim Aufräumen erhalten. Dev-UI-Zustandssnapshots nutzen den Klassenmonitor, sofern vorhanden.

Zustand privat halten und sowohl Lesen als auch Schreiben schützen. Zurückgegebene veränderliche Objekte, direkte Feldzugriffe sowie statische und unmarkierte Methoden umgehen den Monitor. Snapshots innerhalb einer geschützten Methode erstellen, statt interne Collections zurückzugeben. Unter gehaltenem Monitor kein Kind awaiten, das denselben Monitor benötigt; verschiedene Klassenmonitore in einheitlicher Reihenfolge betreten. Rekursion gilt für einen Thread, nicht für gespawnte Kinder. Für kleinere explizite kritische Abschnitte können Locks aus tinox.core.semaphore mit unmittelbar nach erfolgreicher Sperre registriertem defer dienen; diese nativen blockierenden Locks werden nicht abbrechbar. Annotationen und Diagnosen sind kein allgemeiner Beweis für Race-Freiheit.

HTTP-Server (Low Level)

Der HTTP-Server im Runtime verwendet:

Typsystem

Primitive Typen

Tinox-TypBitLLVM-IRBeschreibung
Int8 / Int16 / Int32 / Int648–64i8–i64Vorzeichenbehaftete Ganzzahlen
UInt8 / UInt16 / UInt32 / UInt648–64i8–i64Vorzeichenlose Ganzzahlen
Float32 / Float6432/64float/doubleIEEE 754 Fließkommazahlen
Bool1i1Wahrheitswert
Char32i32Unicode-Codepoint
String–i8*UTF-8-Zeichenkette (C-String)
Nothing–voidKein Rückgabewert (entspricht void)
Never–voidFunktion kehrt nie zurück (throw, exit)

s.chars() liefert List<String> mit einem Unicode-Skalarwert pro Element. Mit for ch in s.chars() lassen sich vollständige UTF-8-Zeichen verarbeiten. Kombinierende Zeichen und Emoji-Sequenzen können mehrere Elemente umfassen; Normalisierung und Graphemsegmentierung finden nicht statt. Leere Eingabe liefert eine leere Liste; ungültiges UTF-8 bricht mit dem Byte-Offset ab. Länge, Indizierung, charAt und direkte String-Iteration behalten ihre Byte-Semantik. Der Aufruf benötigt O(Bytes) Zeit und reserviert eine Liste sowie einen kurzen String pro Skalarwert.

Zusammengesetzte Typen

const numbers: List<Int64> = [1, 2, 3]                 // Array
const ages: Map<String, Int32> = @{"Alice" => 30}  // Map/Dictionary
const pair: (Int32, String) = (42, "hello")           // Tuple
const name: String? = null                             // Nullable
const fn add: fn(Int32, Int32) -> Int32 = |a, b| a + b // Funktionstyp

Generics

Tinox unterstützt parametrische Polymorphie mittels Monomorphisierung: Für jede konkrete Typisierung wird eine eigene Funktion erzeugt – wie in C++ Templates oder Rust. Es gibt keinen Laufzeit-Overhead durch Typ-Erasure (wie in Java).

fn max<T>(a: T, b: T) -> T
{
    if a > b { return a }
    return b
}

class Box<T>
{
    var value: T
}

Objektorientierung

Tinox ist eine hybride Sprache – objektorientiert mit Klassen, Vererbung und Interfaces, aber auch prozedurale freie Funktionen und funktionale Konzepte (Lambdas, Higher-Order Functions) sind möglich.

Klassen

class Animal
{
    var name: String
    var age:  Int32

    fn speak() -> String        // Instanzmethode
    {
        return "..."
    }

    fnc create(name: String) -> Animal  // statische Methode (fnc)
    {
        return Animal { name: name, age: 0 }
    }
}

class Dog extends Animal       // Vererbung
{
    fn speak() -> String
    {
        return "Wuff!"
    }
}

Interfaces & Polymorphismus

interface Serializable
{
    fn serialize() -> String
}

class User implements Serializable
{
    var name: String

    fn serialize() -> String
    {
        return "User:" + this.name
    }
}

Interface-Dispatch wird über Vtables implementiert – identisch zu C++ virtuelle Funktionen. Jedes Objekt, das ein Interface implementiert, bekommt einen Zeiger auf eine Tabelle von Funktionszeigern.

Abwesenheit und behandelbare Fehler

Neue Bibliotheks-APIs verwenden Option<T> für fehlende Werte und Result<T> für erwartete Fehler. Die Imports sind tinox.core.option und tinox.core.result. Some("") bedeutet vorhanden, aber leer; None bedeutet nicht vorhanden. Mit isSome()/isNone() prüfen oder mit unwrapOr() einen Ersatzwert wählen. Keine zusätzliche nullable T?-Variante derselben Suche einführen.

Result<T> behält seinen String-Fehler. Status mit isOk()/isErr() prüfen und Details über getError() lesen. Operation, Ziel und Ursache ohne geheime Daten erhalten; den Status nicht aus dem Meldungstext ableiten. Stabile Fehlerkategorien gehören in dokumentierte Ergebnisfelder. Ein generisches Result mit Fehlertyp und Änderungen der Speicherbelegung bleiben separater Arbeit vorbehalten.

Exceptions bleiben für dokumentierte Vorbedingungen, Programmier- und Einrichtungsfehler geeignet. Option.unwrap() wirft bei None "Option is None"; Result.unwrap() wirft bei Err den ursprünglichen Fehler. Exceptions aus Callbacks werden weitergereicht. finally dient der Bereinigung bei normalem und fehlerhaftem Verlassen eines Blocks.

Die ergänzenden HTTP-Methoden findHeader, findParam, findQuery und QueryString::find liefern Option<String>. Header ignorieren Groß-/Kleinschreibung, Pfad- und Query-Namen unterscheiden sie. Query-Werte bleiben roh; der letzte doppelte Parameter gewinnt, ein Schlüssel ohne Wert ist vorhanden und leer. Bestehende String-Getter behalten ihren leeren Ersatzwert ohne Deprecation-Warnungen. Eine Paketversion mit den optionalen Methoden oder zusammenpassende Workspace-Quellen verwenden. Beispiele und bestehende I/O-Grenzen stehen im API-Inventar und Migrationsleitfaden.

Der aktuelle Quellstand bietet außerdem geprüfte Dateisystem-Alternativen: Fs::tryReadFile, tryWriteFile, tryAppendFile, tryCopyFile, tryMoveFile und tryDeleteFile. Ihr IoResult<T> enthält Status und Nutzwert zusammen; failure() liefert Operation, Ziel, Betriebssystemcode und Ursache. Eine leere Datei ist ein Erfolg; Teil-Lesefehler und fehlgeschlagenes abschließendes Schreiben sind Fehler. Migration und Kompatibilität.

ByteStream und Http1Stream bieten begrenztes TCP-/TLS-Streaming mit Zeitlimits, expliziter Ressourcenübernahme und Bereinigung. HTTP/1.1-Streaming unterstützt einen Austausch mit Content-Length; die HTTP/2-/HTTP/3-Brücken erhalten Binärdaten, puffern aber weiterhin ganze Bodies. Binärdaten- und Streaming-Verträge.

Schlüsselwort-Übersicht

KeywordBedeutung
fnInstanzmethode (hat implizites this)
fncStatische Methode (kein this, Aufruf über Klassenname)
constUnveränderliche lokale Variable (let wird als veralteter Alias weiterhin akzeptiert)
varVeränderliche lokale Variable oder Feld
classKlassendeklaration
interfaceInterface-Deklaration (wie Java interface)
enumAufzählungstyp
extendsEinfachvererbung
implementsInterface-Implementierung
spawnNeuen Thread starten; der Ausdruck hat den Typ Task<T> (T = Rückgabetyp der Funktion)
scope { ... }Strukturierte Nebenläufigkeit: jeder spawn lexikalisch im Block gehört dazu, und beim Verlassen des Blocks (normal, per return/break oder throw) werden alle gejoint. Ein Kind-Fehler, den niemand awaitet hat, wird am Blockende erneut geworfen; hat der Block selbst geworfen, gewinnt dessen Fehler und Kind-Fehler werden auf stderr gemeldet. Ein throw in einer gespawnten Funktion wirft await erneut. scope ist kontextuell, kein reserviertes Wort. Ein spawn in einer Closure im Block gehört nicht dazu
awaitAuf einen Task<T> warten und dessen T liefern (String, Float64, Bool, Klassen, ... nicht nur Int64); erneutes await auf denselben Task liefert dasselbe Ergebnis. Task ist ein eingebauter Typname und kann nicht mehr für eigene Klassen verwendet werden
deferCode beim Verlassen des Scopes ausführen
matchPattern-Matching-Ausdruck

Annotationen

Das Annotationssystem ist eine der zentralen Stärken von Tinox. Annotationen steuern das Verhalten des Compilers, des HTTP-Servers und zukünftiger Frameworks – ähnlich wie in Java/Spring oder Quarkus.

HTTP-REST-Annotationen

Jeder Parameter eines @GET/@POST/…-Handlers braucht genau eine Bindungs-Annotation: @PathParam/@QueryParam binden per Parametername an das gleichnamige :name-Pfadsegment bzw. den Query-String-Key, @PostParam deserialisiert den kompletten JSON-Request-Body, @HttpContext gibt Zugriff auf Request/Response. Der Rückgabetyp entscheidet über den Antwortmodus: HttpContext bedeutet manueller Modus (die Methode baut ctx.response selbst auf), jeder andere Typ wird automatisch als JSON serialisiert.

// Auto-Serialize: Rückgabewert wird automatisch zu JSON
@GET
@Path("/users/:id")
@Produces(MediaType.APPLICATION_JSON)
@Auth("bearer")
fn getUser(@PathParam id: Int64) -> User { ... }

// Manueller Modus: dynamischer Statuscode (z.B. 404), @HttpContext + -> HttpContext
@GET
@Path("/users/:id")
fn getUserOrNotFound(@PathParam id: Int64, @HttpContext ctx: HttpContext) -> HttpContext
{
    // ... ctx.response.status(404).json(...) oder ctx.response.json(...)
    return ctx
}

Migration von der alten REST-API

Vor dieser Version bekam jeder Handler implizit genau einen ctx: HttpContext-Parameter ohne Annotation, mit Rückgabetyp Nothing, und musste Pfad-/Query-Parameter sowie den Request-Body manuell über ctx.request.getParam(...)/ctx.request.getQuery(...)/ Json::deserialize<T>(ctx.request.body) auslesen. Diese Form kompiliert nicht mehr — es gibt keine Abwärtskompatibilität, jeder Parameter braucht jetzt explizit eine der vier Bindungs-Annotationen.

VorherNachher
fn getUser(ctx: HttpContext) -> Nothing
{
  const id: Int64 = ctx.request.getParam("id").toInt();
  ctx.response.json(Json::serialize(user));
}
fn getUser(@PathParam id: Int64) -> User
{
  return user;
}
fn createUser(ctx: HttpContext) -> Nothing
{
  const u: User = Json::deserialize<User>(ctx.request.body);
  ctx.response.json(Json::serialize(u));
}
fn createUser(@PostParam u: User) -> User
{
  return u;
}
Dynamischer Statuscode (z.B. dieselbe Route mal 200, mal 404) lässt sich mit einem festen Rückgabetyp nicht ausdrücken — dafür bleibt der manuelle Modus: @HttpContext ctx: HttpContext als Parameter, -> HttpContext als Rückgabetyp, am Ende return ctx.

Serialisierung

@JsonSerializable
class User
{
    var id:   Int64

    @JsonField("first_name")   // Überschreibt den JSON-Key
    var firstName: String

    @Sensitive              // Im Log als "***" ausgegeben
    var password: String

    @DoNotSerialize         // Nicht in JSON aufgenommen
    var internalToken: String
}

Alle eingebauten Annotationen

AnnotationZielBeschreibung
@GET @POST @PUT @PATCH @DELETEMethodeHTTP-Verb für REST-Route
@Path("...")Klasse / MethodeURL-Pfad (unterstützt :param)
@Produces(MediaType.*)MethodeResponse-Content-Type
@Consumes(MediaType.*)MethodeRequest-Content-Type
@StatusCode(200)MethodeStandard-HTTP-Statuscode
@PathParamParameterBindet ein :name-Pfadsegment (gleicher Name wie der Parameter) — String/Int64/Int32/Bool/Float64/Float32
@QueryParamParameterBindet einen Query-String-Key (gleicher Name wie der Parameter) — gleiche Typen wie @PathParam
@PostParamParameterBindet den deserialisierten JSON-Request-Body — Typ muss @JsonSerializable sein
@HttpContextParameterBindet Request/Response-Handle — Typ muss HttpContext sein; Rückgabetyp HttpContext aktiviert den manuellen Response-Modus
@Auth("bearer")Klasse / MethodeAuthentifizierungspflicht
@JsonSerializableKlasseGeneriert toJson()/fromJson()
@JsonField("name")FeldAlternativer JSON-Key
@SensitiveFeldKomplett maskiert in Logs
@MaskedFeldTeilweise maskiert in Logs
@DoNotSerializeFeldFeld von JSON ausschließen
@LogKlasseAutomatisches log-Feld injizieren
@InjectFeldProvider-eigenes Injection-Feld; im Quellcode nicht neu zuweisbar
@Provider("name")KlasseBenennt einen konkreten DI-Provider mit explizitem Scope
@Qualifier("name")FeldWählt den Provider eines @Inject-Felds zur Compile-Zeit
@Required / @OptionalFeldPräsenz-/Null-Vertrag für JSON-Request-DTOs
@Length(min, max)FeldInklusive Grenzen für Unicode-Codepoints oder Listenelemente
@Range(min, max)FeldInklusive numerische Grenzen für Request-DTOs; @NotNull bleibt eine Datenbankannotation
@Config("key")FeldWert aus application.properties; Werte dürfen ${ENV} / ${ENV:default} enthalten (Umgebungsvariable, optional mit Default; \${ = literal)
@SynchronizedKlasse/MethodeRekursiver Klassenmonitor für Singleton-Instanzmethoden
@ApplicationComponentKlasseSingleton-Scope für DI
@HttpRequestScopedKlasseEine Instanz pro konkretem Provider und synchronem REST-Request-Kontext
@Test("desc")MethodeUnit-Test (tinox test)
@inlineFunktion / MethodeErzwungenes LLVM-Inlining
@deprecated("msg")AlleDeprecation-Warnung
@Command("name")KlasseCLI-Kommando
@annotationKlasseEigene Annotation definieren
@GetterKlasse / FeldGeneriert get<Feld>() — auf der Klasse für jedes Feld, auf einem einzelnen Feld nur für dieses; überschreibt keine bereits von Hand geschriebene Methode gleichen Namens
@SetterKlasse / FeldGeneriert set<Feld>(value) — gleiche Regeln wie @Getter
@WebsocketEndpoint("/path"[, port])KlasseWebSocket: generiert einen Accept/Message-Loop, gespawnt neben class Main (mehrere Klassen erlaubt, je eigener Port)
@OnOpenMethodeWebSocket: Aufruf bei neuer Verbindung, Signatur fn(conn: Int64) -> Nothing
@OnMessageMethodeWebSocket: Aufruf pro Text-Nachricht, Signatur fn(conn: Int64, msg: String) -> Nothing. AMQP-1.0: Aufruf pro empfangener Nachricht, Signatur fn(msg: Amqp10Message) -> Nothing. AMQP-0-9-1: Aufruf pro empfangener Nachricht, Signatur fn(msg: AmqpMessage091) -> Nothing
@OnCloseMethodeWebSocket: Aufruf beim Verbindungsende, Signatur fn(conn: Int64) -> Nothing
@Amqp10Consumer(host, port, user, pass, address)KlasseAMQP-1.0: generiert einen Connect/Attach/Receive-Loop, gespawnt neben class Main (mehrere Consumer-Klassen erlaubt, auch gegen denselben Broker mit unterschiedlichen Queues)
@Amqp091Consumer(host, port, vhost, user, pass, queue)KlasseAMQP-0-9-1: generiert einen Connect/Open/Qos/Consume/Receive-Loop, gespawnt neben class Main (mehrere Consumer-Klassen erlaubt); Prefetch ist v1 fest auf 1
@TinoxUIApp
@TinoxUIApp(httpPort)
@TinoxUIApp(httpPort, wsPort)
KlasseTinox-UI: generiert die HTTP-Shell/Client-JS auf httpPort und einen WebSocket-Accept-Loop auf wsPort, der die @View-Methode aufruft und den Component-Baum diff-basiert neu rendert (nur Patches werden gesendet, kein Voll-Resend); höchstens eine @TinoxUIApp-Klasse pro Programm, mit genau einer @View-Methode. Beide Ports sind optional: ohne Argument 8080/8081, mit einem Argument httpPort und httpPort + 1. Alle gebundenen Ports eines Programms werden zur Compile-Zeit auf Kollisionen geprüft — zwei Binder auf einem Port scheitern zur Laufzeit nicht hörbar (SO_REUSEPORT), sondern teilen den Traffic still unter sich auf
@ViewMethodeTinox-UI: markiert die Methode, die den Component-Baum der @TinoxUIApp baut/neu baut, Signatur fn() -> Component; dient zugleich als Fallback/404-Ansicht, wenn @Route verwendet wird und die aktuelle Route auf kein Muster passt
@Route("/path/:param")MethodeTinox-UI: registriert eine fn() -> Component-Methode innerhalb einer @TinoxUIApp-Klasse als Builder für diesen Pfad (Vaadin-artiges Routing). Erfordert ein Feld var currentRoute: String; gematchte :name-Pfadsegmente werden automatisch in ein gleichnamiges Feld der Klasse geschrieben, falls vorhanden

Aktuelle Verträge: DI-Provider und Request-Kontext, rekursive Request-Validierung und HTTP-400-Details.

Standardbibliothek (tinox-core)

Die Standardbibliothek besteht aus über 55 Modulen, die in Tinox selbst geschrieben sind. Import: import tinox.core.<modul>

HTTP & Web

http_server, rest, rest_framework, http, websocket – vollständiger HTTP/1.1-Server mit Routing, Middleware und Content-Negotiation, plus WebSocket-Server nach RFC 6455.

Messaging

amqp091 – AMQP-0-9-1-Client (kein Broker) für Message-Queue-Broker wie RabbitMQ: Connect, Channel, Publish/Consume. amqp10 – eigenständiger AMQP-1.0-Client mit Connection/Session/Link-Hierarchie und kreditbasierter Flow-Control.

Datenformate

json, xml, csv, toml, ini, base64, hex – Serialisierung und Deserialisierung gängiger Formate.

Mathematik

math, mathf, decimal, complex – trigonometrische Funktionen, Hochpräzisions-Arithmetik, komplexe Zahlen.

Collections

array, sort, set, queue, stack, heap, graph, trie – Algorithmen und Datenstrukturen.

Strings & Regex

string, regex, fmt, tpl – String-Manipulation, reguläre Ausdrücke, Formatierung, Templating.

I/O & System

io, fs, env, process, socket – Dateisystem, Umgebungsvariablen, Prozesse, Low-Level-Netzwerk.

Concurrency

cron, events, pool, semaphore, ratelimit, cache – Scheduling, Event-Bus, Thread-Pool, Synchronisierung.

Kryptographie

crypto, jwt, bcrypt – Hashing, JSON Web Tokens, Passwort-Hashing.

LSP – Grundlagen

Das Language Server Protocol (LSP) ist ein von Microsoft im Jahr 2016 entwickeltes offenes Protokoll, das die Kommunikation zwischen einem Sprachserver (der die Programmiersprache versteht) und einem Editor (der die Benutzeroberfläche zeigt) standardisiert.

Das Problem vor LSP

Früher musste jede IDE für jede Sprache eigene Plugins implementieren: Eclipse-Plugin für Java, VS-Code-Plugin für Java, IntelliJ-Plugin für Java – alles separate Implementierungen der gleichen Logik. Bei n Sprachen und m Editoren entstanden n × m Integrationen.

LSP-Lösung: n + m statt n × m

Mit LSP schreibt man einmal einen Sprachserver, und jeder LSP-kompatible Editor nutzt ihn automatisch. Der Sprachserver kommuniziert über JSON-RPC über stdio mit dem Editor.

Editor (Eclipse, VS Code, Neovim …)
      │
      │  JSON-RPC über stdin/stdout
      ▼
Language Server (tinox-lsp Binary)
      │
      │  Analysiert .tnx-Dateien
      ▼
Lex → Parse → Typecheck → Antworten an Editor

Wichtige LSP-Methoden

LSP-MethodeWas der Editor schicktWas der Server antwortet
textDocument/hoverCursor-PositionMarkdown mit Typ-Information
textDocument/completionCursor-Position nach .Liste von Vervollständigungs-Vorschlägen
textDocument/definitionCursor auf BezeichnerDatei + Zeile der Definition
textDocument/documentSymbolDokument-URIHierarchische Symbol-Liste (Outline)
textDocument/publishDiagnostics– (server push)Fehlerpositionen + Meldungen
textDocument/didChangeNeuer DateiinhaltLöst erneute Analyse aus
Vorteil: Der tinox-lsp Sprachserver funktioniert in Eclipse, VS Code, Neovim, Emacs und jedem anderen LSP-fähigen Editor – ohne sprachspezifische Plugin-Entwicklung pro Editor.

Tinox LSP-Server

tinox-lsp ist ein vollständiger LSP-Server, implementiert mit tower-lsp 0.20 (Rust) und Tokio als asynchronem Laufzeitsystem.

Eingebettete Standardbibliothek

Alle 26+ Standardbibliotheks-Module sind mittels include_str!() direkt in das LSP-Binary eingebettet. Der Server benötigt keine Installation oder Konfiguration von Dateipfaden – er funktioniert sofort nach dem Kopieren des Binaries.

Import-Auflösung für Diagnostics

Wenn eine .tnx-Datei geöffnet wird, löst der LSP-Server automatisch import-Deklarationen auf und lädt die entsprechenden Stdlib-ASTs als Prelude. So werden Json.parse() oder HttpContext nicht als "undefined" markiert.

Dot-Completion für Klassen

Wenn der Nutzer Json. tippt:

  1. Der Editor schickt textDocument/completion mit dem Offset nach dem Punkt
  2. Der LSP extrahiert die Dot-Chain (["Json"])
  3. Er prüft, ob Json eine Variable (Instanz) oder ein Klassenname ist
  4. Falls Klassenname: direkt die Methoden von Json aus der Registry laden
  5. Statische Methoden (fnc) werden mit dem Hinweis fnc parse(...) angezeigt

Technische Details

EigenschaftWert
ProtokollLanguage Server Protocol 3.17 (JSON-RPC über stdio)
Async-RuntimeTokio 1.x (multi-threaded)
Dokument-StorageDashMap (lock-free concurrent HashMap)
Stdlib eingebettetJa, via include_str!() at compile time
Diagnostik-ModusFull re-parse + typecheck bei jedem save/change
Unterstützte EditorenEclipse (LSP4E), VS Code, Neovim, Emacs, …

Eclipse-Plugin

Das Eclipse-Plugin tinox-eclipse verbindet den Eclipse IDE mit dem tinox-lsp Sprachserver über das LSP4E Framework – dem LSP-Client für Eclipse.

Features

🔴

Echtzeit-Fehlermarkierung

Syntaxfehler und Typfehler werden direkt im Editor unterstrichen, sobald die Datei gespeichert wird.

💡

Hover-Informationen

Maus über eine Funktion oder Variable: Typ-Signatur und Dokumentation erscheinen im Tooltip.

🔍

Go to Definition

Ctrl+Klick oder F3 springt zur Definition einer Klasse, Methode oder Funktion.

📝

Code-Vervollständigung

Ctrl+Leertaste oder nach dem Tippen eines Punktes erscheinen Methoden- und Feld-Vorschläge.

📋

Outline-Ansicht

Die Eclipse Outline View zeigt alle Klassen, Methoden und Felder der aktuellen Datei hierarchisch.

▶️

Ausführen (Ctrl+F11)

Direktes Kompilieren und Ausführen der aktuellen .tnx-Datei aus Eclipse heraus.

Architektur

Das Plugin ist ein OSGi-Bundle, das über plugin.xml die .tnx-Dateiendung registriert und LSP4E anweist, bei geöffneten .tnx-Dateien den tinox-lsp-Prozess zu starten. Die Kommunikation läuft über stdin/stdout in JSON-RPC.

CLI-Werkzeuge

Das tinox-Binary ist das zentrale Werkzeug für alle Entwicklungsaufgaben:

BefehlBeschreibung
tinox new <name>Neues Projekt scaffolden (Ordnerstruktur, tinox.toml, main.tnx)
tinox build <file>Zu nativem Binary kompilieren (Release: -O3 + LTO)
tinox run <file>Kompilieren und sofort ausführen
tinox dev <file>Hot-Reload-Modus: bei Dateiänderung automatisch neu kompilieren
tinox check <file>Nur Typprüfung, kein Binary erzeugen
tinox test <file>Alle @Test-annotierten Methoden ausführen
tinox fmt <file>Quellcode formatieren (AST → formatierter Text)
tinox docHTML-Dokumentation aus Docstrings generieren
tinox graph [file]Mermaid-Call-Graph-Diagramm aus REST/WebSocket/AMQP/CLI-Einstiegspunkten generieren
tinox replInteraktive REPL-Sitzung
tinox add …Paketabhängigkeit hinzufügen
tinox installAbhängigkeiten herunterladen und installieren

HTTP-Server & Performance

Der integrierte HTTP-Server ist auf maximale Durchsatzrate bei minimaler Latenz optimiert. Er verwendet Technologien, die auch in produktionsreifen Systemen wie Nginx und Envoy eingesetzt werden.

epoll – Event-Multiplexing

Statt für jede Verbindung einen Thread zu blockieren, registriert der Server alle aktiven Verbindungen in einem epoll-Dateideskriptor. Ein epoll_wait()-Aufruf liefert alle Verbindungen, die lesebereite Daten haben – in einem einzigen Systemaufruf.

// Konzept (vereinfacht):
epoll_fd = epoll_create1(0)
// Alle aktiven Verbindungen registrieren...
while (true)
{
    events = epoll_wait(epoll_fd, max_events, 50) // 50ms Timeout
    for event in events
    {
        if event.fd == server_socket { accept() }
        else { handle_request(event.fd) }
    }
}

SO_REUSEPORT – Kernel Load Balancing

Mit SO_REUSEPORT können mehrere Threads (oder Prozesse) gleichzeitig auf demselben Port lauschen. Der Linux-Kernel verteilt eingehende Verbindungen automatisch auf die verfügbaren Listener – ohne Locks, ohne Engpässe.

Thread-lokale Puffer

Jeder HTTP-Worker-Thread hat seinen eigenen statischen Puffer für Request-Daten, Response-Daten und JSON-Parsing. Diese Puffer werden nie freigegeben und wiederverwendet – das eliminiert malloc/free im kritischen Request-Pfad vollständig.

Nebenläufigkeits-Budgets & Overload-Behandlung

Worker- und Verbindungszahlen sind begrenzt und Container-bewusst. Die effektive CPU-Anzahl ist das Minimum aus CPU-Affinität (taskset, cpuset) und cgroup-CPU-Quota (v1 und v2, z. B. ergibt docker run --cpus=1.5 2), nicht die Kernzahl des Hosts. Jedes Limit lässt sich per Umgebungsvariable überschreiben; ein ungültiger Wert bricht den Start ab. Die effektiven Werte stehen im Startup-Banner (Limits: ...) und werden zusammen mit Open/Peak/Rejected-Zählern auf /metrics ausgegeben.

VariableDefaultVerhalten bei Erschöpfung
TINOX_CPUSerkannt–
TINOX_HTTP_WORKERSCPUs–
TINOX_MAX_CONNECTIONS4096 (max. 4096)HTTP/1: 503 Service Unavailable mit Retry-After: 1
TINOX_MAX_TASKS4096spawn liefert einen Task, dessen await (bzw. umgebender scope) "task budget exhausted" wirft
TINOX_MAX_WS_CONNECTIONS1024die WebSocket-Verbindung wird sofort geschlossen
TINOX_QUEUE_WAIT_MS0 (keine Warteschlange)wie lange spawn / ein WebSocket-Accept auf einen freien Slot warten darf, bevor abgewiesen wird

Jede Einstellung kann auch in application.properties als tinox.limits.<name> stehen (z. B. tinox.limits.max_tasks=256); die Umgebungsvariable hat Vorrang. Die Dev-UI-API liefert die effektiven Werte und Live-Zähler unter /limits. HTTP/1 weist sofort ab (die epoll-Worker dürfen nicht blockieren); die Accept-Schleifen von HTTP/2, HTTP/3 und TLS sind bewusst einthreadig, ihr Backlog ist durch die Kernel-Listen-Queue begrenzt und sie erzeugen keine Threads. AMQP-Verbindungen nutzen spawn und zählen daher gegen TINOX_MAX_TASKS. Abgewiesene Anfragen werden nie automatisch wiederholt. Bind-Adresse: Standardmäßig binden alle Listener (HttpServer, @TinoxUIApp, WebSocket, HTTP/2, SSE) auf 0.0.0.0. TINOX_BIND_ADDRESS=127.0.0.1 beschränkt sie auf eine IPv4-Adresse (z. B. nur Loopback für lokale Dashboards); ein ungültiger Wert bricht den Start hart ab.

Ergebnis: Bis zu 4.096 gleichzeitige Verbindungen pro Thread, lineares Skalieren über CPU-Kerne durch SO_REUSEPORT.

Benchmark: Tinox vs. Quarkus

Ein Sort-REST-Endpunkt wurde unter identischen Bedingungen gegen Quarkus (Java, native image) gemessen:

Metrik Tinox Quarkus Differenz
Durchsatz 26.700–27.000 req/s 24.867–25.541 req/s +5–7%
Latenz (mean) 0,44–0,45 ms 0,52–0,55 ms ~17–20% besser
Fehlerrate 0 % 0 % –

Testbedingungen: 5.000 Requests, Concurrency 20, Liste mit 20 Zahlen pro Request, Linux x86_64.

Hinweis: Quarkus wurde mit native-image kompiliert – also ebenfalls ohne JVM-Overhead. Der Vergleich ist daher fair: beide Systeme laufen als native Linux-Binary.

Vor- & Nachteile

✓ Vorteile

  • Sehr geringe Latenz und hoher Durchsatz dank LLVM + epoll
  • Boehm GC – automatisches Speichermanagement ohne Borrow-Checker-Komplexität
  • Geringe Startup-Zeit (<1ms) – ideal für Serverless/Lambda
  • Java-ähnliche Syntax – niedrige Einstiegshürde für Backend-Entwickler
  • Annotation-gesteuertes REST: minimaler Boilerplate
  • Vollständiges Tooling: LSP, Eclipse-Plugin, REPL, Formatter
  • Standardbibliothek in der Sprache selbst geschrieben
  • Kleiner Binärfüßabdruck – kein Framework als Laufzeitabhängigkeit
  • Eingebettete Stdlib im LSP – kein Konfigurationsaufwand
  • Cross-Platform durch LLVM (Linux, macOS, Windows möglich)

✗ Nachteile

  • Konservativer GC (Boehm) – kann bei Pointer-intensiven Workloads etwas langsamer sein als manuelles Management
  • Kleines Ökosystem – keine reifen Third-Party-Bibliotheken
  • Keine standardisierte Spezifikation – Sprache ändert sich schnell
  • Compiler-Fehler noch nicht so hilfreich wie bei Rust oder Go
  • Kein vollständiges Modulsystem mit echter Trennung zwischen Projekten
  • Keine Borrow-Checker-Sicherheit – Use-after-free theoretisch möglich (GC verhindert aber Leaks)
  • Lange Compile-Zeiten durch LLVM (bei großen Projekten)
  • Keine Windows-Unterstützung (epoll ist Linux-spezifisch)
  • Noch keine stabilen Releases – pre-1.0
  • Dünne Community und Dokumentation bisher

Vergleich: Speichersicherheit

Tinox gibt dem Entwickler volle Kontrolle über den Speicher – wie C –, bietet aber keine statischen Garantien wie Rusts Borrow-Checker. Das bedeutet:

SpracheSpeichermodellSicherheitPerformance
Java / KotlinGCHoch (kein UAF)GC-Pausen
RustOwnership + Borrow-CheckerSehr hoch (compile-time)Maximal
GoGC (concurrent)HochGut
TinoxBoehm GC (konservativ)GC verhindert LeaksSehr hoch
C/C++ManuellEntwicklerverantwortungMaximal

Einsatzgebiete

🌐

Hochperformante REST-APIs

Microservices mit sehr niedrigen Latenzanforderungen (Trading, Echtzeit-Systeme). Annotation-gesteuertes Routing, kein Framework-Overhead.

⚡

Serverless / Edge Computing

Kaltstart <1ms dank nativer Binary ohne JVM oder Node.js. Ideal für AWS Lambda, Cloudflare Workers (nativer Modus), Edge-Funktionen.

🔧

System-Werkzeuge & CLIs

Mit @Command/@Option/@Argument lassen sich CLI-Tools mit minimalem Aufwand entwickeln. Native Binary ohne Laufzeitabhängigkeiten.

📊

Datenverarbeitungs-Pipelines

Batch-Verarbeitung großer Datenmengen mit automatischem GC – kein manuelles Speichermanagement nötig, trotzdem hohe Performance.

🔒

Sicherheitskritische Dienste

Deterministisches Laufzeitverhalten durch konservativen GC mit vorhersehbarem Verhalten. Annotationen für Sensitivität und Maskierung eingebaut.

🎓

Lehre & Forschung

Kleine, lesbare Compiler-Codebasis. Ideal zum Erlernen von Compiler-Konstruktion, LLVM-IR und LSP-Entwicklung.

Weniger geeignet für…

Vergleich mit anderen Sprachen

Merkmal Tinox Java / Quarkus Go Rust C
Kompilierung AOT → nativ JVM / AOT (native image) AOT → nativ AOT → nativ AOT → nativ
Garbage Collector Ja (Boehm GC) Ja (G1GC, ZGC) Ja (concurrent) Nein (Ownership) Nein
Speichersicherheit GC-geschützt (keine Leaks) GC-geschützt GC-geschützt Compile-time Manuell
Syntax-Stil Java-ähnlich Java C-ähnlich C-ähnlich C
REST-Annotationen Eingebaut JAX-RS / Quarkus Kein Standard Kein Standard Keine
Startup-Zeit <1ms JVM: 1–3s; Native: ~10ms ~5ms <1ms <1ms
IDE-Tooling LSP + Eclipse Sehr reif Gut (gopls) Sehr gut (rust-analyzer) Mittel (clangd)
Ökosystem Klein (wachsend) Riesig (Maven) Gut Gut (crates.io) Groß (gereift)
Concurrency Threads + Channels Virtual Threads (Java 21) Goroutines async/await + Threads pthreads
Backend LLVM Ja Nein (JVM / Graal) Nein (eigener) Ja Optional (clang)
Positionierung: Tinox füllt eine Nische zwischen Go und C: produktivere Syntax als C/C++, ohne den GC-Overhead von Go oder Java, mit eingebautem REST-Framework das Spring Boot oder Quarkus in Bezug auf Entwicklerkomfort annähert – aber bei der Performance übertrifft.

http_server

Typisierte JSON-Antworten mit dynamischen Statuscodes sind als Response<T> verfügbar. Die Anleitung zu typisierten Antworten und OpenAPI-Verträgen (Englisch) beschreibt die Einrichtung aus dem Checkout und das annotationsbasierte CRUD-Beispiel.

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/http_server/1-0-4/docs.html

Obergrenze für den Request-Body: 4 MiB (TINOX_MAX_BODY in runtime.c). Überschreitet der Content-Length-Header diese Grenze, wird die Anfrage sofort mit 413 Payload Too Large abgelehnt und die Verbindung geschlossen — der Handler wird nicht aufgerufen, und der Body wird nie stillschweigend abgeschnitten und trotzdem als vollständig verarbeitet (Issue #174).

http2_server

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/http2_server/1-0-2/docs.html

http3_server

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/http3_server/1-0-2/docs.html

websocket

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/websocket/1-0-3/docs.html

sse

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/sse/1-0-1/docs.html

amqp091

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/amqp091/1-0-3/docs.html

amqp10

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/amqp10/1-0-2/docs.html

smtp

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/smtp/1-0-1/docs.html

redis

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/redis/1-0-1/docs.html

hpack

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/hpack/1-0-1/docs.html

http

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/http/1-0-3/docs.html

rest.client & rest.server

rest wird als ein einziges Paket veröffentlicht, das sowohl die Client- als auch die Server-API abdeckt. Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/rest/1-0-3/docs.html

socket

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/socket/1-0-0/docs.html

kubernetes

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/kubernetes/1-0-3/docs.html

ui

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/ui/1-0-13/docs.html

jwt

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/jwt/1-0-2/docs.html

oauth2

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/oauth2/1-0-2/docs.html

oidc

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/oidc/1-0-2/docs.html

json

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/json/1-0-0/docs.html

msgpack

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/msgpack/1-0-1/docs.html

xml

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/xml/1-0-1/docs.html

yaml

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/yaml/1-0-2/docs.html

csv

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/csv/1-0-1/docs.html

toml

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/toml/1-0-1/docs.html

ini

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/ini/1-0-1/docs.html

base64

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/base64/1-0-0/docs.html

hex

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/hex/1-0-0/docs.html

encoding

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/encoding/1-0-0/docs.html

unicode

Ein String bleibt eine Bytefolge (len(), s[i], substring() zählen Bytes). tinox.core.unicode ergänzt ausdrücklich und optional die Einheit Codepoint; Grapheme-Cluster und Normalisierung (NFC/NFD) gibt es nicht. Fehlerhaftes UTF-8 wird nie ersetzt, sondern löst einen fängbaren Fehler mit dem ersten ungültigen Byte-Offset aus.

AufrufBedeutung
Unicode::isValid(s)wohlgeformtes UTF-8 (keine Overlongs, Surrogate, Werte > U+10FFFF)
Unicode::codePointCount(s)Anzahl Codepoints; "e" + U+0301 sind zwei
Unicode::codePoints(s) / fromCodePoints(list)List<Int64> der Skalarwerte und zurück; NUL, Surrogate und Werte außerhalb des Bereichs werfen
Unicode::byteOffset(s, index)Byte-Offset des Codepoints index (für substring an Codepoint-Grenzen)
Unicode::toUpper(s) / toLower(s)volle Standardabbildung (ß → SS), ohne Locale; Groß-Sigma wird immer σ (keine Final-Sigma-Regel)

Kosten: jeder Aufruf ist O(Bytes) und validiert zuerst; codePoints belegt 8 Byte je Codepoint, toUpper/toLower bis zum 3-Fachen der Eingabe. Die Tabellen entsprechen Unicode::version() (16.0.0) und werden mit scripts/gen-unicode-case.py neu erzeugt.

array

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/array/1-0-0/docs.html

collections

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/collections/1-0-0/docs.html

heap

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/heap/1-0-0/docs.html

queue

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/queue/1-0-0/docs.html

set

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/set/1-0-0/docs.html

sort

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/sort/1-0-0/docs.html

iter

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/iter/1-0-0/docs.html

graph

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/graph/1-0-0/docs.html

trie

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/trie/1-0-0/docs.html

math

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/math/1-0-0/docs.html

mathf

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/mathf/1-0-0/docs.html

mathx

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/mathx/1-0-0/docs.html

complex

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/complex/1-0-0/docs.html

decimal

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/decimal/1-0-0/docs.html

random

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/random/1-0-0/docs.html

string

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/string/1-0-0/docs.html

fmt

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/fmt/1-0-0/docs.html

format

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/format/1-0-0/docs.html

regex

Aktueller Workspace: Regex-Methoden werfen bei ungültigen Mustern catchbare Fehler; isValid und WithOptions-Varianten sind verfügbar. Aktueller Regex-Vertrag. Die eingebettete Seite beschreibt die veröffentlichte Paketversion.

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/regex/1-0-0/docs.html

uri

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/uri/1-0-0/docs.html

Aktuelle Workspace-Ergänzungen: Metadaten, atomisches Umbenennen und sichere Verzeichnisoperationen.

fs

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/fs/1-0-0/docs.html

io

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/io/1-0-0/docs.html

env

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/env/1-0-0/docs.html

process

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/process/1-0-0/docs.html

debug

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/debug/1-0-0/docs.html

zip

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/zip/1-0-2/docs.html

compress

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/compress/1-0-1/docs.html

asm

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/asm/1-0-0/docs.html

bitmap

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/bitmap/1-0-1/docs.html

tpl

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/tpl/1-0-1/docs.html

crypto

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/crypto/1-0-2/docs.html

hash

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/hash/1-0-0/docs.html

validation

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/validation/1-0-0/docs.html

uuid

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/uuid/1-0-0/docs.html

semaphore

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/semaphore/1-0-0/docs.html

pool

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/pool/1-0-0/docs.html

ratelimit

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/ratelimit/1-0-1/docs.html

cron

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/cron/1-0-0/docs.html

events

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/events/1-0-0/docs.html

time

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/time/1-0-0/docs.html

cache

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/cache/1-0-0/docs.html

option

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/option/1-0-0/docs.html

result

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/result/1-0-0/docs.html

logger

Request correlation, structured logging and OTLP tracing (development reference)

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/logger/1-0-0/docs.html

db

Vollständige Referenz (Beschreibung, Dependencies, Beispiele, Klassen & Methoden) wird direkt aus der veröffentlichten tinox-central-Paketdokumentation geladen, damit sie nicht an zwei Stellen gepflegt werden muss: docs/tinox-core/db/1-0-0/docs.html

Nur die Query-Chain-Klassen (DB, EntityQuery) selbst — @Entity/@Table/@Column/@Id/@GeneratedValue, der Connection-Pool und @Transactional (seit Issue #191) sind Compiler-/Codegen-Features, keine Stdlib-Klassen; siehe den ORM & Lambda-Query-DSL-Leitfaden weiter unten für deren vollständige Referenz.

ORM & Lambda-Query-DSL

Tinox bietet ein typsicheres ORM nach dem JPA/QueryDSL-Prinzip. Klassen werden mit Annotations als Datenbank-Tabellen markiert. Abfragen laufen ausschließlich über ein Lambda-DSL — keine String-basierten Queries.

Annotations

AnnotationZielBeschreibung
@EntityKlasseMarkiert die Klasse als Datenbank-Entität
@Table("name")KlasseSetzt den Tabellennamen (Standard: lowercase Klassenname)
@IdFeldPrimärschlüssel
@Column("col")FeldMappt Feld auf DB-Spalte (Standard: Feldname)
@GeneratedValueFeldID wird von der DB generiert (AUTO_INCREMENT / SERIAL)
@NotNullFeldSpalte ist NOT NULL

Entity-Definition

@Entity
@Table("users")
class User {
    @Id @GeneratedValue
    var id: Int64
    @Column("name")
    var name: String
    @Column("age")
    var age: Int64
    @Column("active")
    var active: Bool
}

tinox.toml – [database]-Konfiguration

# PostgreSQL
[database]
driver = "postgres"
url    = "postgres://user:pass@localhost:5432/mydb"
pool   = 10

# MySQL / MariaDB
[database]
driver = "mysql"
url    = "mysql://user:pass@localhost:3306/mydb"

# SQLite (kein DB-Server nötig)
[database]
driver = "sqlite"
url    = "sqlite:///path/to/database.db"

Lambda-Query-DSL

// Alle Nutzer über 18, alphabetisch, max. 10 Ergebnisse
const adults: List<User> = DB::of(User)
    .filter(u -> u.age > 18)
    .orderBy(u -> u.name)
    .limit(10)
    .list()

// Einzelnen Nutzer lesen
const alice: User = DB::of(User)
    .filter(u -> u.name == "Alice")
    .first()

// Anzahl aktiver Nutzer
const n: Int64 = DB::of(User)
    .filter(u -> u.active == true)
    .count()

Lambda → SQL Übersetzungstabelle

Lambda-AusdruckSQL-Fragment
u.age > 18age > $1
u.name == "Alice"name = $1
u.name != "Bob"name != $1
u.name.startsWith("A")name LIKE 'A%'
u.name.endsWith("z")name LIKE '%z'
u.name.contains("oo")name LIKE '%oo%'
expr1 && expr2(pred1) AND (pred2)
expr1 || expr2(pred1) OR (pred2)
!exprNOT (pred)

Speichern, Ändern, Löschen

// INSERT (ID wird von der DB gesetzt)
const user: User = User {}
user.name = "Alice"
user.age  = 30
const saved: User = DB::of(User).save(user)

// DELETE
DB::of(User).delete(saved)

// Mehrere auf einmal speichern
const list: List<User> = List::new()
list.add(user1)
list.add(user2)
DB::of(User).saveAll(list)

Fehlerbehandlung

Datenbankfehler werden aktuell auf stderr protokolliert. Eine try/catch-Integration ist für eine spätere Version geplant. Verbindungsfehler beim Start beenden das Programm mit einer aussagekräftigen Fehlermeldung.