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.
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.
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.
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:
- Sicherheit Kein Undefined Behavior, keine Speicherfehler im Compiler selbst
- Performance Schnelle Compile-Zeiten des Compilers (keine GC-Pausen)
- Parallelität Thread-sichere Datenstrukturen (DashMap) für den LSP-Server
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-common | Rust | Gemeinsame Datentypen (Span, Error, AST-Knoten) | ~250 Zeilen |
tinox-lexer | Rust | Tokenisierung (Lexer) | ~800 Zeilen |
tinox-parser | Rust | AST-Aufbau + Code-Formatter | ~2.500 Zeilen |
tinox-typecheck | Rust | Typprüfung + Annotation-Verarbeitung | ~2.000 Zeilen |
tinox-codegen | Rust | AST → LLVM IR | ~6.000 Zeilen |
tinox (CLI) | Rust | Compiler-Steuerung, REPL, Projektmanager | ~1.700 Zeilen |
tinox-lsp | Rust | Language Server Protocol Server | ~1.000 Zeilen |
tinox-eclipse | Java | Eclipse IDE Plugin | ~400 Zeilen |
runtime.c | C | Laufzeitsystem: I/O, HTTP, Speicher, Strings | ~1.700 Zeilen |
tinox-core | Tinox | Standardbibliothek (55+ Module) | ~200 KB |
Compiler-Pipeline
Der Tinox-Compiler transformiert eine .tnx-Quelldatei in mehreren
Schritten zu einem ausführbaren nativen Binary:
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}!").
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.
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.
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.
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.
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.
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.
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
| Typ | Implementierung | Besonderheit |
|---|---|---|
| Array | Dynamisches C-Array | Länge bei Offset -1 gespeichert; wächst bei push |
| Map | Open-Addressing Hash-Table | FNV-1a Hash, Load-Factor 0.75, Tombstones für Delete |
| String | C-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) }
| Operation | Vertrag |
|---|---|
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:
- SO_REUSEPORT Mehrere Threads binden den gleichen Port – der Kernel verteilt Verbindungen
- epoll Event-Multiplexing: ein Thread verwaltet bis zu 4.096 gleichzeitige Verbindungen
- TCP_NODELAY Nagle-Algorithmus deaktiviert – sofortiges Senden kleiner Pakete
- Keep-Alive HTTP/1.1 keep-alive, Idle-Timeout 500ms
Typsystem
Primitive Typen
| Tinox-Typ | Bit | LLVM-IR | Beschreibung |
|---|---|---|---|
Int8 / Int16 / Int32 / Int64 | 8–64 | i8–i64 | Vorzeichenbehaftete Ganzzahlen |
UInt8 / UInt16 / UInt32 / UInt64 | 8–64 | i8–i64 | Vorzeichenlose Ganzzahlen |
Float32 / Float64 | 32/64 | float/double | IEEE 754 Fließkommazahlen |
Bool | 1 | i1 | Wahrheitswert |
Char | 32 | i32 | Unicode-Codepoint |
String | – | i8* | UTF-8-Zeichenkette (C-String) |
Nothing | – | void | Kein Rückgabewert (entspricht void) |
Never | – | void | Funktion 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
| Keyword | Bedeutung |
|---|---|
fn | Instanzmethode (hat implizites this) |
fnc | Statische Methode (kein this, Aufruf über Klassenname) |
const | Unveränderliche lokale Variable (let wird als veralteter Alias weiterhin akzeptiert) |
var | Veränderliche lokale Variable oder Feld |
class | Klassendeklaration |
interface | Interface-Deklaration (wie Java interface) |
enum | Aufzählungstyp |
extends | Einfachvererbung |
implements | Interface-Implementierung |
spawn | Neuen 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 |
await | Auf 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 |
defer | Code beim Verlassen des Scopes ausführen |
match | Pattern-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.
| Vorher | Nachher |
|---|---|
fn getUser(ctx: HttpContext) -> Nothing |
fn getUser(@PathParam id: Int64) -> User |
fn createUser(ctx: HttpContext) -> Nothing |
fn createUser(@PostParam u: User) -> User |
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
| Annotation | Ziel | Beschreibung |
|---|---|---|
@GET @POST @PUT @PATCH @DELETE | Methode | HTTP-Verb für REST-Route |
@Path("...") | Klasse / Methode | URL-Pfad (unterstützt :param) |
@Produces(MediaType.*) | Methode | Response-Content-Type |
@Consumes(MediaType.*) | Methode | Request-Content-Type |
@StatusCode(200) | Methode | Standard-HTTP-Statuscode |
@PathParam | Parameter | Bindet ein :name-Pfadsegment (gleicher Name wie der Parameter) — String/Int64/Int32/Bool/Float64/Float32 |
@QueryParam | Parameter | Bindet einen Query-String-Key (gleicher Name wie der Parameter) — gleiche Typen wie @PathParam |
@PostParam | Parameter | Bindet den deserialisierten JSON-Request-Body — Typ muss @JsonSerializable sein |
@HttpContext | Parameter | Bindet Request/Response-Handle — Typ muss HttpContext sein; Rückgabetyp HttpContext aktiviert den manuellen Response-Modus |
@Auth("bearer") | Klasse / Methode | Authentifizierungspflicht |
@JsonSerializable | Klasse | Generiert toJson()/fromJson() |
@JsonField("name") | Feld | Alternativer JSON-Key |
@Sensitive | Feld | Komplett maskiert in Logs |
@Masked | Feld | Teilweise maskiert in Logs |
@DoNotSerialize | Feld | Feld von JSON ausschließen |
@Log | Klasse | Automatisches log-Feld injizieren |
@Inject | Feld | Provider-eigenes Injection-Feld; im Quellcode nicht neu zuweisbar |
@Provider("name") | Klasse | Benennt einen konkreten DI-Provider mit explizitem Scope |
@Qualifier("name") | Feld | Wählt den Provider eines @Inject-Felds zur Compile-Zeit |
@Required / @Optional | Feld | Präsenz-/Null-Vertrag für JSON-Request-DTOs |
@Length(min, max) | Feld | Inklusive Grenzen für Unicode-Codepoints oder Listenelemente |
@Range(min, max) | Feld | Inklusive numerische Grenzen für Request-DTOs; @NotNull bleibt eine Datenbankannotation |
@Config("key") | Feld | Wert aus application.properties; Werte dürfen ${ENV} / ${ENV:default} enthalten (Umgebungsvariable, optional mit Default; \${ = literal) |
@Synchronized | Klasse/Methode | Rekursiver Klassenmonitor für Singleton-Instanzmethoden |
@ApplicationComponent | Klasse | Singleton-Scope für DI |
@HttpRequestScoped | Klasse | Eine Instanz pro konkretem Provider und synchronem REST-Request-Kontext |
@Test("desc") | Methode | Unit-Test (tinox test) |
@inline | Funktion / Methode | Erzwungenes LLVM-Inlining |
@deprecated("msg") | Alle | Deprecation-Warnung |
@Command("name") | Klasse | CLI-Kommando |
@annotation | Klasse | Eigene Annotation definieren |
@Getter | Klasse / Feld | Generiert 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 |
@Setter | Klasse / Feld | Generiert set<Feld>(value) — gleiche Regeln wie @Getter |
@WebsocketEndpoint("/path"[, port]) | Klasse | WebSocket: generiert einen Accept/Message-Loop, gespawnt neben class Main (mehrere Klassen erlaubt, je eigener Port) |
@OnOpen | Methode | WebSocket: Aufruf bei neuer Verbindung, Signatur fn(conn: Int64) -> Nothing |
@OnMessage | Methode | WebSocket: 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 |
@OnClose | Methode | WebSocket: Aufruf beim Verbindungsende, Signatur fn(conn: Int64) -> Nothing |
@Amqp10Consumer(host, port, user, pass, address) | Klasse | AMQP-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) | Klasse | AMQP-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) | Klasse | Tinox-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 |
@View | Methode | Tinox-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") | Methode | Tinox-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-Methode | Was der Editor schickt | Was der Server antwortet |
|---|---|---|
textDocument/hover | Cursor-Position | Markdown mit Typ-Information |
textDocument/completion | Cursor-Position nach . | Liste von Vervollständigungs-Vorschlägen |
textDocument/definition | Cursor auf Bezeichner | Datei + Zeile der Definition |
textDocument/documentSymbol | Dokument-URI | Hierarchische Symbol-Liste (Outline) |
textDocument/publishDiagnostics | – (server push) | Fehlerpositionen + Meldungen |
textDocument/didChange | Neuer Dateiinhalt | Löst erneute Analyse aus |
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:
- Der Editor schickt
textDocument/completionmit dem Offset nach dem Punkt - Der LSP extrahiert die Dot-Chain (
["Json"]) - Er prüft, ob
Jsoneine Variable (Instanz) oder ein Klassenname ist - Falls Klassenname: direkt die Methoden von
Jsonaus der Registry laden - Statische Methoden (
fnc) werden mit dem Hinweisfnc parse(...)angezeigt
Technische Details
| Eigenschaft | Wert |
|---|---|
| Protokoll | Language Server Protocol 3.17 (JSON-RPC über stdio) |
| Async-Runtime | Tokio 1.x (multi-threaded) |
| Dokument-Storage | DashMap (lock-free concurrent HashMap) |
| Stdlib eingebettet | Ja, via include_str!() at compile time |
| Diagnostik-Modus | Full re-parse + typecheck bei jedem save/change |
| Unterstützte Editoren | Eclipse (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:
| Befehl | Beschreibung |
|---|---|
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 doc | HTML-Dokumentation aus Docstrings generieren |
tinox graph [file] | Mermaid-Call-Graph-Diagramm aus REST/WebSocket/AMQP/CLI-Einstiegspunkten generieren |
tinox repl | Interaktive REPL-Sitzung |
tinox add … | Paketabhängigkeit hinzufügen |
tinox install | Abhä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.
| Variable | Default | Verhalten bei Erschöpfung |
|---|---|---|
TINOX_CPUS | erkannt | – |
TINOX_HTTP_WORKERS | CPUs | – |
TINOX_MAX_CONNECTIONS | 4096 (max. 4096) | HTTP/1: 503 Service Unavailable mit Retry-After: 1 |
TINOX_MAX_TASKS | 4096 | spawn liefert einen Task, dessen await (bzw. umgebender scope) "task budget exhausted" wirft |
TINOX_MAX_WS_CONNECTIONS | 1024 | die WebSocket-Verbindung wird sofort geschlossen |
TINOX_QUEUE_WAIT_MS | 0 (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.
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.
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:
| Sprache | Speichermodell | Sicherheit | Performance |
|---|---|---|---|
| Java / Kotlin | GC | Hoch (kein UAF) | GC-Pausen |
| Rust | Ownership + Borrow-Checker | Sehr hoch (compile-time) | Maximal |
| Go | GC (concurrent) | Hoch | Gut |
| Tinox | Boehm GC (konservativ) | GC verhindert Leaks | Sehr hoch |
| C/C++ | Manuell | Entwicklerverantwortung | Maximal |
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…
- Frontend-Entwicklung (kein Browser-Target, kein WASM-Fokus)
- Datenwissenschaft / ML (kein NumPy-Äquivalent, keine Notebook-Integration)
- Mobile Apps (kein iOS/Android-Framework)
- Projekte, die ein riesiges Paket-Ökosystem benötigen (npm, Maven)
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) |
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.
| Aufruf | Bedeutung |
|---|---|
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
| Annotation | Ziel | Beschreibung |
|---|---|---|
@Entity | Klasse | Markiert die Klasse als Datenbank-Entität |
@Table("name") | Klasse | Setzt den Tabellennamen (Standard: lowercase Klassenname) |
@Id | Feld | Primärschlüssel |
@Column("col") | Feld | Mappt Feld auf DB-Spalte (Standard: Feldname) |
@GeneratedValue | Feld | ID wird von der DB generiert (AUTO_INCREMENT / SERIAL) |
@NotNull | Feld | Spalte 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-Ausdruck | SQL-Fragment |
|---|---|
u.age > 18 | age > $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) |
!expr | NOT (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.