Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Audit icu4x — vers une réimplémentation Rust pur no_std sur Air

Note de travail (2026-06-30). Objectif : déterminer ce qui manque à Air pour réécrire icu4x en Rust pur no_std sur types/syscalls/composants Air uniquement. Base : version pinnée du projet (icu@2.2.0, calendar 2.2.1). Audit mené sur un clone unicode-org/icu4x (tag icu@2.2.0) sur carbon. Périmètre : les 6 composants pinnés + leur socle, runtime et datagen (frontière tracée).

0. Pourquoi cet audit

air-base-lib (couche 1) consomme icu4x → toute crate couche 1 qui dépend d’air-base-lib (air-memory/air-thread/air-process, via AirError) tire icu4x transitivement. Cela a soulevé, au moment de builder air-runtime pour *-linux-air, la question : icu4x est-il compatible no_std/cible custom, et que faudrait-il pour en avoir une version maîtrisée full-Rust ?

1. Périmètre

6 composants pinnés ([workspace.dependencies] du projet) : icu_normalizer, icu_casemap, icu_segmenter, icu_collator, icu_locale_core (2.2.0), icu_calendar (2.2.1) — tous en features = ["compiled_data", …] (données baked-in). Socle commun : zerovec, yoke, writeable, potential_utf, tinystr, litemap, et les crates d’infrastructure icu : icu_provider, icu_collections, icu_properties, icu_locale, calendrical_calculations.

2. Découverte centrale — le runtime icu4x est DÉJÀ no_std et de calcul pur

  • Les 10 crates auditées sont #![cfg_attr(not(any(test, doc)), no_std)] : no_std en production, std seulement pour tests/doc. icu4x est no_std-natif par conception.
  • Le runtime (consommation des tables baked) est du CALCUL PUR : aucune occurrence de std::{thread,fs,net,time,io,process}, SystemTime, Instant::now, File, getrandom, libc:: dans le code de production des 6 composants. (Seules traces : des commentaires décrivant un trait « no-std version of std::io::Write ».)
  • Besoins système du runtime = core + alloc. Rien d’autre. Zéro syscall, zéro horloge, zéro thread, zéro fichier. ⇒ Air n’a besoin de fournir aucun composant système (thread/fichier/horloge) au runtime icu4x. Air a déjà core + alloc.

Conséquence directe sur l’étape 6 / le « blocage » initial : la crainte qu’icu4x empêche le build *-linux-air est largement dissoute. icu4x étant no_std-pur-compute, il devrait compiler pour *-linux-air (core+alloc) modulo libm (cf. §5). Le « bloat binaire » est probablement moot (code mort éliminé par LTO si air-runtime n’utilise qu’AirError).

3. Le SEUL primitif système manquant — math flottante no_std (libm)

  • core_maths (façade no_std sur libm) est utilisé à deux endroits du périmètre :
    • icu_segmenter/src/complex/lstm/matrix.rs — math du modèle LSTM (segmentation par IA pour thaï/khmer/lao/birman ; activé par la feature auto/lstm, que le projet active).
    • calendrical_calculations (dép de icu_calendar) — math calendaire (astronomie des calendriers lunaires/solaires). Non contournable : le calendrier en a besoin.
  • C’est le vrai manque systémique d’Air : pas de libm no_std (exclu de la v1, ADR-046). Décision requise (cf. §8).

4. Inventaire par composant (runtime, default = compiled_data)

ComposantDeps icu/socleDeps externes (hors std/icu)Notes
icu_normalizericu_collections, icu_properties, icu_provider, zerovec, + icu_normalizer_datasmallvec, utf8_iter, utf16_iter, write16 ; harfbuzz-traits (optionnel)NFC/NFD/NFKC/NFKD
icu_casemapicu_collections, icu_properties, icu_provider, icu_locale_core, potential_utf, writeable, zerovec, + icu_casemap_data— (rien hors socle)upper/lower/title/fold
icu_segmentericu_collections, icu_locale, icu_provider, potential_utf, zerovec, + icu_segmenter_datacore_maths→libm (LSTM), utf8_itergrapheme/word/line/sentence ; LSTM=floats
icu_collatoricu_collections, icu_locale, icu_locale_core, icu_normalizer, icu_properties, icu_provider, zerovec, + icu_collator_datasmallvec, utf8_iter, utf16_itertri Unicode (UCA)
icu_locale_corezerovec, writeable, tinystr, litemapdisplaydoc (proc-macro host)identifiants de locale (BCP-47)
icu_calendaricu_locale, icu_locale_core, icu_provider, zerovec, tinystr, calendrical_calculations, + icu_calendar_datacalendrical_calculations→core_maths→libm ; ixdtf (optionnel) ; chrono/jiff/time (interop, optionnels)calendriers (grégorien, etc.)

Frontière nette : serde, databake, datagen = features non runtime (génération de données / chargement non-baked). chrono/jiff/time/ixdtf/harfbuzz-traits = optionnels (interop / parsing) — non requis pour le cœur.

5. Le socle icu (à réécrire aussi — tout no_std, calcul pur)

icu_provider (mécanique de fourniture de données : clés, requêtes, baked) · icu_collections (structures Unicode : code-point tries, inversion lists) · icu_properties (propriétés de caractères Unicode) · icu_locale (négociation de locale) · zerovec/yoke/writeable/ potential_utf (zéro-copie, vues empruntées sur les tables baked) · tinystr/litemap (petites chaînes ASCII / petites maps) · calendrical_calculations (math calendaire, →libm). C’est le gros du travail de réécriture — mais mécanique (no_std, calcul pur, zéro syscall).

6. Données / tables — format et provenance

  • Chaque icu_*_data est une crate #![no_std] qui include! un mod.rs généré = données Rust const baked (via databake) compilées dans le binaire (+ stubdata vide pour fournir les données autrement, + support ICU4X_DATA_DIR pour données custom).
  • Provenance (en-tête généré) : CLDR 48.2.0 + ICU 78.1 (icuexport) + LSTM v0.1.0.
  • ⇒ Les « tables » du modèle BDFL = données const Rust baked, dérivées des sources Unicode/CLDR. Posséder ce format (ou un équivalent Air) + savoir les régénérer = un chantier en soi.

7. Datagen / outils — host, indépendants de no_std-Air

  • provider/source (icu_provider_source : « data provider based on CLDR and ICU data ») lit CLDR + export ICU (UCD) ; provider/export bake en Rust const (databake) ou blobs.
  • Ce sont des outils de build host (std libre), indépendants d’une lib/pal Air — comme cbindgen/capnpc/le tool C++ capnp. Le no_std-Air ne les contraint pas (décision BDFL).
  • Dépendance amont inévitable : les sources CLDR + Unicode UCD (et modèles LSTM). Toute réimplémentation doit soit réutiliser le datagen icu4x pour produire nos tables, soit réimplémenter un datagen (std host) consommant les mêmes sources.

8. SYNTHÈSE — ce qui manque à Air (et décisions)

Runtime (consommateur de tables) :

  1. Rien côté système : core + alloc suffisent (Air les a). Aucun composant Air (thread/fichier/horloge) requis. ✅
  2. libm no_std = LE manque systémique (calendrical_calculations + LSTM segmenter). Décision : (a) fournir un libm Air (réécrit ou vendoré, no_std) ; ou (b) restreindre le périmètre (sans LSTM → segmentation par dictionnaire seul ; mais le calendrier garde besoin de libm → (b) ne suffit pas seul). → Air aura besoin d’un libm no_std.
  3. Petites crates utilitaires no_std à vendorer/réécrire (règle 80 %, ADR-024) : smallvec, tinystr, litemap, utf8_iter, utf16_iter, write16. Petites, no_std, zéro syscall. displaydoc = proc-macro host (ou remplacer par Display manuel).
  4. Socle icu (provider/collections/properties/locale + zerovec/yoke/writeable/ potential_utf/calendrical_calculations) : à réécrire (gros mais mécanique, no_std pur).

Données / outils : 5. Format de tables : adopter le baked-const Rust (databake-like) ou un équivalent Air. 6. Datagen (host, std libre) : réutiliser celui d’icu4x ou le réimplémenter ; dans les deux cas, dépendance aux sources CLDR + Unicode UCD (amont, non maîtrisable — c’est la donnée de référence mondiale). Outils produits indépendamment d’une lib/pal Air.

Ce qu’Air N’A PAS, en une ligne : un libm no_std, une poignée de petites crates utilitaires no_std (vendorables), et le pipeline de données (format de tables + datagen host sur sources CLDR/UCD). Le reste (runtime no_std, socle calcul-pur) est du portage Rust sans dépendance système.

9. Implication pour la décision A/B/C (couplage air-base-lib→icu4x, étape 6)

L’audit réduit fortement le risque qui motivait (A) : icu4x est no_std-pur-compute, donc le build *-linux-air d’air-runtime (qui ne fait que tirer AirError, code icu4x mort → éliminé) devrait passer modulo libm. Reste à vérifier empiriquement (build-std de la fermeture pour *-linux-air) — non fait, sur décision « on prend le temps ». La scission air-base-lib (A) reste un nettoyage d’archi sain (erreur transverse vs i18n lourde), mais probablement pas un bloqueur de l’étape 6.

✅ VÉRIFIÉ EMPIRIQUEMENT (2026-07-01, ADR-059). Sonde jetable (carbon) : les 6 composants (dont icu_segmenter avec LSTM auto) + tout le socle + calendrical_calculations/core_maths/libm COMPILENT pour x86_64-unknown-linux-air (build-std=[core,alloc], --release, EXIT=0, 33 s). Deux conditions triviales : (1) default-features = false sur les icu_* (évite la feature std par défaut = le « mur std ») ; (2) build --release (le seul extern crate std restant, dans icu_provider, est un repli logging eprintln debug_assertions-only — inactif en release). ⇒ la réimplémentation du socle est INUTILE ; décision Option V (ADR-059) : Air utilise icu4x, possède l’API air-string + les données. Le libm i18n vient du crate libm amont (via core_maths), pas de air-libm.

10. Questions ouvertes (décisions BDFL à venir)

  • libm : réécrire un libm Air no_std, ou vendorer libm (exception 80 % nommée) ? Quand (avant l’i18n, car bloquant pour calendar + LSTM) ?
  • Périmètre LSTM : garder la segmentation LSTM (besoin floats + données modèle) ou dictionnaire seul pour la v1 ?
  • Datagen : réutiliser le datagen icu4x pour produire des tables au format Air, ou réimplémenter ? (Réutiliser = pragmatique pour démarrer.)
  • Vendoring vs réécriture des petites crates utilitaires (smallvec/tinystr/…).
  • Quand : cet effort (i18n Air) est gros ; il se situe APRÈS le runtime/libc, ou en parallèle ?

Méthode : clone icu@2.2.0 sur carbon (~/icu4x-audit), inspection statique des Cargo.toml, features, no_std, usages système, format de données, datagen. Aucun build lancé.