tag:linuxfr.org,2005:/tags/archive/publicLinuxFr.org : les contenus étiquetés avec « archive »2024-03-07T13:12:59+01:00/favicon.pngtag:linuxfr.org,2005:Bookmark/80212024-03-07T13:12:59+01:002024-03-07T13:12:59+01:00Ordinateurs et apprentissages : efficacité et darwinisme pédagogiques [2002]<a href="https://edutice.hal.science/edutice-00276158/file/a0210a.htm">https://edutice.hal.science/edutice-00276158/file/a0210a.htm</a> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/135074/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/ysabeau/liens/ordinateurs-et-apprentissages-efficacite-et-darwinisme-pedagogiques-2002#comments">ouvrir dans le navigateur</a>
</p>
Ysabeau 🧶 🧦https://linuxfr.org/nodes/135074/comments.atomtag:linuxfr.org,2005:Bookmark/78902024-02-04T13:24:34+01:002024-02-04T13:24:34+01:00L’une des plus anciennes archives de logiciels va disparaître, plus de 30 ans après sa création <a href="https://www.01net.com/actualites/lune-des-plus-anciennes-archives-de-logiciels-va-disparaitre-30ans-creation.html">https://www.01net.com/actualites/lune-des-plus-anciennes-archives-de-logiciels-va-disparaitre-30ans-creation.html</a> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/134814/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/ysabeau/liens/l-une-des-plus-anciennes-archives-de-logiciels-va-disparaitre-plus-de-30-ans-apres-sa-creation#comments">ouvrir dans le navigateur</a>
</p>
Ysabeau 🧶 🧦https://linuxfr.org/nodes/134814/comments.atomtag:linuxfr.org,2005:News/412422022-11-04T21:36:18+01:002022-11-04T21:36:18+01:00Jubako et Arx, un conteneur universel et son format d’archiveLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><h2 id="toc-jubako-quezako">Jubako, quezako ?</h2>
<p><a href="https://en.wikipedia.org/wiki/J%C5%ABbako">重箱 (Jūbako)</a> est le nom japonais des boîtes à bento. Ce sont des boîtes compartimentées qui peuvent se composer en fonction de ce qu’il y a à stocker dedans (en général un repas).</p>
<p>Et ça tombe bien, parce que Jubako, c’est un format de conteneur qui permet de stocker différentes données et méta-données. J’ai tendance à parler de conteneurs plutôt que d’archives, en effet « archive » est un mot orienté qui fait penser aux archives de fichiers, alors que Jubako se veut généraliste : un conteneur Jubako pourrait être utilisé pour plein d’autres choses : empaquetage d’applications, pack de ressources dans un binaire, conteneur multimédia, etc.</p>
<p>Vous pouvez voir Jubako comme étant au stockage ce que XML est à la sérialisation. XML définit comment sérialiser du contenu (sous forme d’un arbre de nœuds avec des attributs) mais ne définit pas quels sont ces nœuds et attributs. Chaque cas d’usage a sa propre structure. Pour Jubako c’est pareil, il définit comment stocker des données dans un fichier « d’archive » mais il ne définit pas quelles sont ces données. Chaque cas d’usage aura sa propre structure de données.</p>
<p>Jubako et Arx sont sous licence MIT.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://framagit.org/jubako/spec" hreflang="en" href="https://linuxfr.org/redirect/111210">Les spécifications de Jubako</a></li><li>lien nᵒ 2 : <a title="https://framagit.org/jubako/jubako" hreflang="wq" href="https://linuxfr.org/redirect/111211">Les sources de la bibliothèque Jubako</a></li><li>lien nᵒ 3 : <a title="https://framagit.org/jubako/arx" hreflang="wq" href="https://linuxfr.org/redirect/111212">Les sources de l'outil d'archivage Arx</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-un-peu-de-contexte">Un peu de contexte</a></li>
<li><a href="#toc-le-format-jubako">Le format Jubako</a></li>
<li><a href="#toc-la-biblioth%C3%A8que-jubako">La bibliothèque Jubako</a></li>
<li>
<a href="#toc-loutil-arx">L’outil Arx</a><ul>
<li><a href="#toc-tar">Tar</a></li>
<li><a href="#toc-le-format-arx"> Le format Arx</a></li>
<li>
<a href="#toc-un-peu-de-comparaison">Un peu de comparaison</a><ul>
<li><a href="#toc-pour-la-partie-documentation-seulement-9194-entr%C3%A9es">Pour la partie documentation seulement (9194 entrées) :</a></li>
<li><a href="#toc-pour-la-partie-drivers-seulement-33056-entr%C3%A9es">Pour la partie drivers seulement (33056 entrées) :</a></li>
<li><a href="#toc-et-pour-les-sources-compl%C3%A8tes-81958-entr%C3%A9es">Et pour les sources complètes (81958 entrées) :</a></li>
<li><a href="#toc-la-taille">La taille</a></li>
<li><a href="#toc-le-temps-de-cr%C3%A9ation">Le temps de création</a></li>
<li><a href="#toc-le-temps-dextraction">Le temps d’extraction</a></li>
<li><a href="#toc-le-temps-de-listing">Le temps de listing</a></li>
<li><a href="#toc-le-temps-de-dumping">Le temps de dumping</a></li>
<li><a href="#toc-mount">Mount</a></li>
</ul>
</li>
<li><a href="#toc-utilisation-m%C3%A9moire">Utilisation mémoire</a></li>
</ul>
</li>
<li><a href="#toc-conclusion">Conclusion</a></li>
</ul>
<p>Dans cette dépêche on présente Jubako, un méta-format de conteneur et la bibliothèque associée pour lire et écrire ce format, ainsi qu’Arx, un outil se basant sur Jubako pour faire des archives de fichiers, un peu comme tar mais <s>en mieux</s> différemment. La partie Jubako peut être un peu longue, si vous êtes plus intéressé par la finalité du Jubako, vous pouvez directement sauter à <a href="#toc-loutil-arx">la partie Arx</a> pour voir l’usage qui en est fait.</p>
<h2 id="toc-un-peu-de-contexte">Un peu de contexte</h2>
<p>Mon métier, c’est développeur logiciel, en tant qu’indépendant.<br>
Mon client principal depuis six ans, c’est la fondation Kiwix pour qui je maintiens les outils de base de Kiwix, c’est-à-dire les outils bas niveau en C++ pour lire et créer les archives Zim (pour ce qui nous concerne ici). <br>
Les <a href="https://wiki.openzim.org/wiki/ZIM_file_format">archives Zim</a> sont des archives de « contenu » qui pourraient s’apparenter à des zip, mais avec un format interne qui permet une meilleure compression tout en permettant un accès en lecture quasiment en <em>random access.</em><br>
Le format ZIM fonctionne bien, mais il souffre de quelques erreurs de jeunesse. À la base, ZIM c’est « Zeno IMproved », et Zeno… je sais pas trop d’où ça vient. Il y a bien <a href="https://sourceforge.net/p/tntzenoreader">tntzenoreader</a> qui date de 2007. Mais s’il lit les fichiers Zeno, ce n’est pas lui a priori qui crée le format. (Soit dit en passant, c’est probablement la source de libzim vu que je reconnais pas mal de similitudes de code entre les deux projets). De mémoire, Zeno était associé à l’époque où la fondation Wikimedia parlait de mettre Wikipedia sur CD-ROM.</p>
<p>Enfin bon voilà, un peu de dette technique certes, mais surtout un format très orienté pour le cas d’usage de stocker des pages web. Par exemple, chaque entrée a obligatoirement une url, un titre et un mimetype.</p>
<p>Et puis un jour, celui qu’on ne nomme plus arriva avec son confinement généralisé. Durant cette période, j’ai commencé à travailler sur un nouveau format d’archive, inspiré du format ZIM mais avec comme objectif d’en faire un format généraliste qui pourrait être utilisé par d’autres projets.</p>
<p>Ainsi est né doucement le projet Jubako.</p>
<h2 id="toc-le-format-jubako">Le format Jubako</h2>
<p>Le format Jubako est donc un format (binaire) de fichiers pour des conteneurs. </p>
<p>Jubako est un meta-format. Comme XML, il ne définit pas la structure de données à stocker. Ce sera donc au « Vendeur » (qui utilise Jubako dans son projet) de définir la façon dont les données seront stockées. Il est conçu avec le cas d’usage suivant : un conteneur « read-only » créé une fois et distribué à un ensemble d’utilisateurs qui vont lire le conteneur. La lecture du conteneur doit pouvoir se faire sur des machines de relativement faible puissance et en temps « quasi » constant sans décompression préalable. À l’inverse, il est attendu que la machine sur laquelle est fait le conteneur soit un peu plus puissante (ou tout du moins, qu’il soit accepté que la création prenne plus de temps).</p>
<p>Un conteneur Jubako est composé de plusieurs sous-parties appelées pack :</p>
<ul>
<li>Les <code>contentPack</code>s, qui contiennent les données proprement dites. Les <code>contentPack</code>s sont les plus gros en taille et contiennent les données « brutes », sans métadonnées. C’est un peu un conteneur de blob si on reprend la terminologie de git.</li>
<li>Le <code>directoryPack</code> qui lui contient les entrées et métadonnées associées. C’est le pack le plus complexe et le plus « configurable ». Globalement, il stocke des entrées (<code>Entry</code>). Chaque entrée étant composée de métadonnées et pouvant pointer vers un, plusieurs ou aucun contenu (stocké dans les <code>contentPack</code>s).</li>
<li>Le <code>manifestPack</code> qui relie tous ces différents packs ensemble pour former un tout cohérent.</li>
</ul>
<p>Un conteneur Jubako est donc obligatoirement composé d’un <code>manifestPack</code> et d’un <code>directoryPack</code> et d’un ou plusieurs <code>contentPack</code>s (parfois aucun). Les packs peuvent être stockés séparément (sous forme de fichiers dans un dossier par exemple) ou concaténés ensemble pour ne former qu’un seul fichier.</p>
<p>Notez que si un conteneur est logiquement composé de plusieurs contentPack, il est normal que certains contentPack puissent être manquants lors de la lecture. Cela permet d’avoir un système d’extension de conteneur.<br>
Par exemple, pour un packaging d’application, l’application elle-même pourrait être stockée dans un contentPack toujours présent mais les traductions dans différentes langues serait stockées dans des packs distincts. Le conteneur Jubako référence toutes les entrées (et donc toutes les traductions) mais un utilisateur peut avoir en local un conteneur qui contient seulement le contentPack « application » et la traduction dans sa langue.<br>
Ou encore, le projet kiwix pourrait créer des archives avec les images dans des packs séparés, l’utilisateur téléchargerait des archives « sans » images. Et lorsqu’il a une bonne connexion, télécharge les packs manquants.</p>
<p>Bien sûr, c’est au vendeur de faire en sorte que son application sache quoi faire si un pack est manquant (planter, afficher un joli message d’erreur, proposer à l’utilisateur de télécharger le pack…)</p>
<p>Pour ce qui est des entrées stockées dans le directoryPack, c’est là aussi au vendeur de les définir. Le format des entrées est fixe pour chaque cas d’usage (ou alors il vous faudra gérer de la compatibilité entre les versions).<br>
Pour permettre à Jubako (le lecteur, pas le format) de lire le conteneur, le format des entrées est stocké dans le conteneur lui-même.<br>
Le schéma de données suit le principe suivant :</p>
<ul>
<li>Plusieurs types d’entrées peuvent être stockés dans un seul conteneur. Elles sont stockées (et peuvent être récupérées) séparément. Cela permet par exemple de séparer les données selon leur signification dans le conteneur. Par exemple, les fichiers stockés dans une archive de fichier versus les métadonnées de cette même archive (auteur, date de création…). </li>
<li>Un type d’entrée est composé d’un ou plusieurs variants. C’est l’équivalent d’une union pour celles et ceux qui font du c/c++. Par exemple, le type des entrées dans une archive de fichier sera composée de trois variants <code>Fichier</code>, <code>Dossier</code>, <code>Lien</code>.</li>
<li>Chaque variant est composé d’un ensemble d’attributs (l’équivalent d’un struct en c/c++). Chaque attribut a un type défini (entier signé, non signé, tableau de données (de taille fixe ou non), identifiant de contenu), ce qui permet à Jubako de savoir comment lire ces attributs.
Vous noterez que l’identifiant de contenu n’est qu’un attribut parmi d’autres. Une entrée peut donc pointer sur plusieurs contenus, ou aucun.</li>
</ul>
<p>Chaque variant a sa propre structure, mais il est tout de même conseillé d’avoir des attributs communs entre les variants pour pouvoir faire de la recherche d’entrées basée sur ces attributs.</p>
<p>Toutes les entrées d’un même type sont stockées ensemble dans un sous conteneur (tableau) appelé <code>EntryStore</code>. Comme toutes les entrées ont la même taille, les attributs de taille variable sont déportés dans un <code>ValueStore</code>.<br>
Enfin, des index permettent de pointer sur un sous-ensemble d’entrées dans un <code>EntryStore</code> en particulier. Ces index sont nommés et sont les points d’entrées pour la lecture des conteneurs.<br>
Une application va identifier l’index dont elle a besoin et va « suivre » le fil de la structure de donnée pour lire les entrées et accéder au contenu.</p>
<p>Il y a encore pas mal de choses spécifiées (plus ou moins) telles que des redirections ou des entrées qui étendent (rajoutent des attributs) à d’autres entrées. Mais d’une part, cette présentation est déjà bien trop longue et d’autre part, c’est pas encore implémenté, donc on verra plus tard si vous le voulez bien.</p>
<h2 id="toc-la-bibliothèque-jubako">La bibliothèque Jubako</h2>
<p>Il s’agit de l’implémentation de référence (et unique, pour le moment) pour le format Jubako.<br>
Elle est écrite en Rust parce que <s>c’est à la mode</s> c’est un des rares langages (à ma connaissance) qui soit en même temps de bas niveau (comme le C) et qui fournisse aussi des structures de haut niveau dans sa bibliothèque standard (comme le Python). Qui plus est, il fournit un certain nombre de garanties dans sa gestion mémoire qui permet d’avoir une certaine confiance sur un sujet aussi complexe/sensible que de générer/lire des fichiers binaires. (Ça n’empêche pas les bugs, je vous le garantis aussi :) )</p>
<p>(Je me devais de vous dire dans quel langage était écrit Jubako, mais ce n’est pas le plus important. Oui je sais qu’il y a plein de langages de qualité qui auraient pu être utilisés.)</p>
<p>Je ne vais pas trop m’étendre sur cette partie, l’API de la bibliothèque est pas encore sèche et la doc est inexistante pour le moment. Mais voici quand même un petit aperçu de la création et lecture d’un conteneur Jubako :</p>
<p>(Vous noterez une petite incohérence entre le nom des fonctions et des variables/définitions du format. C’est qu’en écrivant cette dépêche, je me suis rendu compte que la terminologie n’était pas bonne. J’ai écrit la dépêche avec une terminologie modifiée mais le code est encore avec « l’ancienne ».)</p>
<pre><code class="rust"><span class="k">use</span><span class="w"> </span><span class="n">jubako</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">jbk</span><span class="p">;</span><span class="w"></span>
<span class="k">use</span><span class="w"> </span><span class="n">jbk</span>::<span class="n">reader</span>::<span class="n">EntryTrait</span><span class="p">;</span><span class="w"></span>
<span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">error</span>::<span class="n">Error</span><span class="p">;</span><span class="w"></span>
<span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">rc</span>::<span class="n">Rc</span><span class="p">;</span><span class="w"></span>
<span class="k">use</span><span class="w"> </span><span class="n">typenum</span>::<span class="p">{</span><span class="n">U31</span><span class="p">,</span><span class="w"> </span><span class="n">U40</span><span class="p">,</span><span class="w"> </span><span class="n">U63</span><span class="p">};</span><span class="w"></span>
<span class="c1">// Cela vous permettra de différencier votre conteneur parmi la multitude de conteneurs jubako qui seront créés (oui, oui, j’y crois)</span>
<span class="k">const</span><span class="w"> </span><span class="n">VENDOR_ID</span>: <span class="kt">u32</span> <span class="o">=</span><span class="w"> </span><span class="mh">0x01_02_03_04</span><span class="p">;</span><span class="w"></span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span>-> <span class="nb">Result</span><span class="o"><</span><span class="p">(),</span><span class="w"> </span><span class="nb">Box</span><span class="o"><</span><span class="n">dyn</span><span class="w"> </span><span class="n">Error</span><span class="o">>></span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">content_pack</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">ContentPackCreator</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="s">"test.jbkc"</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">Id</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span><span class="w"> </span><span class="c1">// L’id du pack tel que référencé dans le reste du conteneur</span>
<span class="w"> </span><span class="n">VENDOR_ID</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">FreeData</span>::<span class="o"><</span><span class="n">U40</span><span class="o">></span>::<span class="n">clone_from_slice</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="mh">0x00</span><span class="p">;</span><span class="w"> </span><span class="mi">40</span><span class="p">]),</span><span class="w"> </span><span class="c1">// Mettez ce que vous voulez, c’est pour vous</span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">CompressionType</span>::<span class="n">Zstd</span><span class="p">,</span><span class="w"> </span><span class="c1">// L’algo de compression à utiliser</span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">content_pack</span><span class="p">.</span><span class="n">start</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">directory_pack</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">DirectoryPackCreator</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="s">"test.jbkd"</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">Id</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="n">VENDOR_ID</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">FreeData</span>::<span class="o"><</span><span class="n">U31</span><span class="o">></span>::<span class="n">clone_from_slice</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="mh">0x00</span><span class="p">;</span><span class="w"> </span><span class="mi">31</span><span class="p">]),</span><span class="w"></span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="c1">// Les entrées ont une taille fixe. Donc pour les valeurs de taille variable (chaînes de caractères), il nous faut un stockage particulier</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">value_store</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">directory_pack</span><span class="p">.</span><span class="n">create_key_store</span><span class="p">(</span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">KeyStoreKind</span>::<span class="n">Plain</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="c1">// On definit une entrée composée de deux variants</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">entry_def</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Entry</span>::<span class="n">new</span><span class="p">(</span><span class="n">vec</span><span class="o">!</span><span class="p">[</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Variant</span>::<span class="n">new</span><span class="p">(</span><span class="n">vec</span><span class="o">!</span><span class="p">[</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Key</span>::<span class="n">PString</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">Rc</span>::<span class="n">clone</span><span class="p">(</span><span class="o">&</span><span class="n">value_store</span><span class="p">)),</span><span class="w"> </span><span class="c1">// Une chaîne de caractères, à stocker dans value_store</span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Key</span>::<span class="n">new_int</span><span class="p">(),</span><span class="w"> </span><span class="c1">// Un entier</span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Key</span>::<span class="n">ContentAddress</span><span class="p">,</span><span class="w"> </span><span class="c1">// Un "pointeur" sur du contenu.</span>
<span class="w"> </span><span class="p">]),</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Variant</span>::<span class="n">new</span><span class="p">(</span><span class="n">vec</span><span class="o">!</span><span class="p">[</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Key</span>::<span class="n">PString</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">Rc</span>::<span class="n">clone</span><span class="p">(</span><span class="o">&</span><span class="n">value_store</span><span class="p">)),</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Key</span>::<span class="n">new_int</span><span class="p">(),</span><span class="w"> </span><span class="c1">//</span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Key</span>::<span class="n">new_int</span><span class="p">(),</span><span class="w"> </span><span class="c1">//</span>
<span class="w"> </span><span class="p">]),</span><span class="w"></span>
<span class="w"> </span><span class="p">]);</span><span class="w"></span>
<span class="w"> </span><span class="c1">// Le store qui contiendra nos entrées.</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">entry_store_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">directory_pack</span><span class="p">.</span><span class="n">create_entry_store</span><span class="p">(</span><span class="n">entry_def</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">entry_store</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">directory_pack</span><span class="p">.</span><span class="n">get_entry_store</span><span class="p">(</span><span class="n">entry_store_id</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="c1">// On ajoute le contenu de notre entrée :</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">content</span>: <span class="nb">Vec</span><span class="o"><</span><span class="kt">u8</span><span class="o">></span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"Du super contenu de qualité pour notre conteneur de test"</span><span class="p">.</span><span class="n">into</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">BufStream</span>::<span class="n">new</span><span class="p">(</span><span class="n">content</span><span class="p">,</span><span class="w"> </span><span class="n">jbk</span>::<span class="n">End</span>::<span class="nb">None</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">content_id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">content_pack</span><span class="p">.</span><span class="n">add_content</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="n">reader</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">entry_store</span><span class="p">.</span><span class="n">add_entry</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="c1">// On utilise le variant 0</span>
<span class="w"> </span><span class="n">vec</span><span class="o">!</span><span class="p">[</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Value</span>::<span class="n">Array</span><span class="p">(</span><span class="s">"Super"</span><span class="p">.</span><span class="n">into</span><span class="p">()),</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Value</span>::<span class="n">Unsigned</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Value</span>::<span class="n">Content</span><span class="p">(</span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Content</span>::<span class="n">from</span><span class="p">((</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">Id</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span><span class="w"> </span><span class="c1">// L'id de notre pack</span>
<span class="w"> </span><span class="n">content_id</span><span class="p">,</span><span class="w"> </span><span class="c1">// L'id du contenu dans le pack</span>
<span class="w"> </span><span class="p">))),</span><span class="w"></span>
<span class="w"> </span><span class="p">],</span><span class="w"></span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">entry_store</span><span class="p">.</span><span class="n">add_entry</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="c1">// On utilise le variant 1</span>
<span class="w"> </span><span class="n">vec</span><span class="o">!</span><span class="p">[</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Value</span>::<span class="n">Array</span><span class="p">(</span><span class="s">"Mega"</span><span class="p">.</span><span class="n">into</span><span class="p">()),</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Value</span>::<span class="n">Unsigned</span><span class="p">(</span><span class="mi">42</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Value</span>::<span class="n">Unsigned</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="p">],</span><span class="w"></span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">entry_store</span><span class="p">.</span><span class="n">add_entry</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="c1">// On utilise le variant 1</span>
<span class="w"> </span><span class="n">vec</span><span class="o">!</span><span class="p">[</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Value</span>::<span class="n">Array</span><span class="p">(</span><span class="s">"Hyper"</span><span class="p">.</span><span class="n">into</span><span class="p">()),</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Value</span>::<span class="n">Unsigned</span><span class="p">(</span><span class="mi">45</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">Value</span>::<span class="n">Unsigned</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="p">],</span><span class="w"></span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="c1">// On créé un index qui nous permettra de retrouver nos entrées.</span>
<span class="w"> </span><span class="n">directory_pack</span><span class="p">.</span><span class="n">create_index</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="s">"mon petit index a moi"</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">jubako</span>::<span class="n">ContentAddress</span>::<span class="n">new</span><span class="p">(</span><span class="mf">0.</span><span class="n">into</span><span class="p">(),</span><span class="w"> </span><span class="mf">0.</span><span class="n">into</span><span class="p">()),</span><span class="w"> </span><span class="c1">// Un pointeur vers du contenu qui peut servir à stocker ce que vous voulez. (Rien en l’occurrence ici)</span>
<span class="w"> </span><span class="mf">0.</span><span class="n">into</span><span class="p">(),</span><span class="w"> </span><span class="c1">// Notre index n'est pas trié</span>
<span class="w"> </span><span class="n">entry_store_id</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">jubako</span>::<span class="n">Count</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span><span class="w"> </span><span class="c1">// On a trois entrées</span>
<span class="w"> </span><span class="n">jubako</span>::<span class="n">Idx</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span><span class="w"> </span><span class="c1">// Et on commence à l'offset 0 dans le entry_store.</span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">directory_pack_info</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">directory_pack</span><span class="p">.</span><span class="n">finalize</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">content_pack_info</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">content_pack</span><span class="p">.</span><span class="n">finalize</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">manifest_creator</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">jbk</span>::<span class="n">creator</span>::<span class="n">ManifestPackCreator</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="s">"test.jbkm"</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">VENDOR_ID</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">FreeData</span>::<span class="o"><</span><span class="n">U63</span><span class="o">></span>::<span class="n">clone_from_slice</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="mh">0x00</span><span class="p">;</span><span class="w"> </span><span class="mi">63</span><span class="p">]),</span><span class="w"></span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">manifest_creator</span><span class="p">.</span><span class="n">add_pack</span><span class="p">(</span><span class="n">directory_pack_info</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">manifest_creator</span><span class="p">.</span><span class="n">add_pack</span><span class="p">(</span><span class="n">content_pack_info</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">manifest_creator</span><span class="p">.</span><span class="n">finalize</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="c1">// Vous avez maintenant 3 fichiers "test.jbkm", "test.jbkc" et "test.jbkd".</span>
<span class="w"> </span><span class="c1">// N'en faisons qu'un seul</span>
<span class="w"> </span><span class="n">jbk</span>::<span class="n">concat</span><span class="p">(</span><span class="o">&</span><span class="p">[</span><span class="s">"test.jbkm"</span><span class="p">,</span><span class="w"> </span><span class="s">"test.jbkc"</span><span class="p">,</span><span class="w"> </span><span class="s">"test.jbkd"</span><span class="p">],</span><span class="w"> </span><span class="s">"test.jbk"</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="c1">// On a maintenant un 4ème fichier "test.jbk" qui contient les trois autres.</span>
<span class="w"> </span><span class="c1">// Un peu de lecture</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">container</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">jbk</span>::<span class="n">reader</span>::<span class="n">Container</span>::<span class="n">new</span><span class="p">(</span><span class="s">"test.jbkm"</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"> </span><span class="c1">// ou "test.jbkm"</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">directory</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">container</span><span class="p">.</span><span class="n">get_directory_pack</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">index</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">directory</span><span class="p">.</span><span class="n">get_index_from_name</span><span class="p">(</span><span class="s">"mon petit index a moi"</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">resolver</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">directory</span><span class="p">.</span><span class="n">get_resolver</span><span class="p">();</span><span class="w"> </span><span class="c1">// C'est nécessaire pour retrouver les infos dans value_store</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">finder</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">index</span><span class="p">.</span><span class="n">get_finder</span><span class="p">(</span><span class="n">Rc</span>::<span class="n">clone</span><span class="p">(</span><span class="o">&</span><span class="n">resolver</span><span class="p">));</span><span class="w"> </span><span class="c1">// On va enfin pouvoir lire nos données.</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">entry</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">finder</span><span class="p">.</span><span class="n">get_entry</span><span class="p">(</span><span class="n">jbk</span>::<span class="n">Idx</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="n">entry</span><span class="p">.</span><span class="n">get_variant_id</span><span class="p">(),</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span><span class="w"> </span><span class="c1">// On a bien le variant 0</span>
<span class="w"> </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="n">resolver</span><span class="p">.</span><span class="n">resolve_to_vec</span><span class="p">(</span><span class="o">&</span><span class="n">entry</span><span class="p">.</span><span class="n">get_value</span><span class="p">(</span><span class="mf">0.</span><span class="n">into</span><span class="p">())</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nb">Vec</span>::<span class="n">from</span><span class="p">(</span><span class="s">"Super"</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="n">resolver</span><span class="p">.</span><span class="n">resolve_to_unsigned</span><span class="p">(</span><span class="o">&</span><span class="n">entry</span><span class="p">.</span><span class="n">get_value</span><span class="p">(</span><span class="mf">1.</span><span class="n">into</span><span class="p">())</span><span class="o">?</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="mi">50</span><span class="w"></span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">value_2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">entry</span><span class="p">.</span><span class="n">get_value</span><span class="p">(</span><span class="mf">2.</span><span class="n">into</span><span class="p">())</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">content_address</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">resolver</span><span class="p">.</span><span class="n">resolve_to_content</span><span class="p">(</span><span class="o">&</span><span class="n">value_2</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="c1">// On affiche le contenu sur la sortie standard</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">reader</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">container</span><span class="p">.</span><span class="n">get_reader</span><span class="p">(</span><span class="n">content_address</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">std</span>::<span class="n">io</span>::<span class="n">copy</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="n">reader</span><span class="p">.</span><span class="n">create_stream_all</span><span class="p">(),</span><span class="w"></span>
<span class="w"> </span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="n">std</span>::<span class="n">io</span>::<span class="n">stdout</span><span class="p">().</span><span class="n">lock</span><span class="p">(),</span><span class="w"></span>
<span class="w"> </span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">entry</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">finder</span><span class="p">.</span><span class="n">get_entry</span><span class="p">(</span><span class="n">jbk</span>::<span class="n">Idx</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="n">entry</span><span class="p">.</span><span class="n">get_variant_id</span><span class="p">(),</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w"> </span><span class="c1">// On a bien le variant 1</span>
<span class="w"> </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="n">resolver</span><span class="p">.</span><span class="n">resolve_to_vec</span><span class="p">(</span><span class="o">&</span><span class="n">entry</span><span class="p">.</span><span class="n">get_value</span><span class="p">(</span><span class="mf">0.</span><span class="n">into</span><span class="p">())</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nb">Vec</span>::<span class="n">from</span><span class="p">(</span><span class="s">"Mega"</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="n">resolver</span><span class="p">.</span><span class="n">resolve_to_unsigned</span><span class="p">(</span><span class="o">&</span><span class="n">entry</span><span class="p">.</span><span class="n">get_value</span><span class="p">(</span><span class="mf">1.</span><span class="n">into</span><span class="p">())</span><span class="o">?</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="mi">42</span><span class="w"></span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="n">resolver</span><span class="p">.</span><span class="n">resolve_to_unsigned</span><span class="p">(</span><span class="o">&</span><span class="n">entry</span><span class="p">.</span><span class="n">get_value</span><span class="p">(</span><span class="mf">2.</span><span class="n">into</span><span class="p">())</span><span class="o">?</span><span class="p">),</span><span class="w"> </span><span class="mi">5</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">entry</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">finder</span><span class="p">.</span><span class="n">get_entry</span><span class="p">(</span><span class="n">jbk</span>::<span class="n">Idx</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span><span class="o">?</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="n">entry</span><span class="p">.</span><span class="n">get_variant_id</span><span class="p">(),</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span><span class="w"> </span><span class="c1">// On a bien le variant 1</span>
<span class="w"> </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="n">resolver</span><span class="p">.</span><span class="n">resolve_to_vec</span><span class="p">(</span><span class="o">&</span><span class="n">entry</span><span class="p">.</span><span class="n">get_value</span><span class="p">(</span><span class="mf">0.</span><span class="n">into</span><span class="p">())</span><span class="o">?</span><span class="p">)</span><span class="o">?</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nb">Vec</span>::<span class="n">from</span><span class="p">(</span><span class="s">"Hyper"</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="n">resolver</span><span class="p">.</span><span class="n">resolve_to_unsigned</span><span class="p">(</span><span class="o">&</span><span class="n">entry</span><span class="p">.</span><span class="n">get_value</span><span class="p">(</span><span class="mf">1.</span><span class="n">into</span><span class="p">())</span><span class="o">?</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="mi">45</span><span class="w"></span>
<span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">assert_eq</span><span class="o">!</span><span class="p">(</span><span class="n">resolver</span><span class="p">.</span><span class="n">resolve_to_unsigned</span><span class="p">(</span><span class="o">&</span><span class="n">entry</span><span class="p">.</span><span class="n">get_value</span><span class="p">(</span><span class="mf">2.</span><span class="n">into</span><span class="p">())</span><span class="o">?</span><span class="p">),</span><span class="w"> </span><span class="mi">2</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"></span>
<span class="p">}</span><span class="w"></span></code></pre>
<h2 id="toc-loutil-arx">L’outil Arx</h2>
<blockquote>
<blockquote>
<p>Jubako c’est beau, mais franchement une lib… t’as pas mieux ? Un vrai cas d’usage ?</p>
</blockquote>
<p>Si ! Et c’est Arx, un outil pour faire des archives de fichiers un peu comme Tar ou Zip. Et en plus, ça sert de démonstrateur pour Jubako.</p>
</blockquote>
<h3 id="toc-tar">Tar</h3>
<p>Petite digression sur tar. Surtout si vous ne savez pas comment est structurée une archive tar.<br>
Tar c’est vieux, très vieux (1979). Ça date d’une époque où les disquettes 3.5 étaient une révolution (elles apparaissent en 1982)</p>
<p>Une archive tar, c’est des entrées (header + contenu) mises bout à bout. C’est tout.<br>
Un tar.gz, c’est un tar compressé avec gzip. Voilà.</p>
<p>Un tar ça marche bien pour du « streaming ». On crée l’archive simplement en parcourant le dossier à archiver et écrivant les entrées dans un « flux » (la sortie standard par exemple) dès qu’on les lit. On passe le flux à gzip et voilà.<br>
Pour décompresser on fait l’inverse.<br>
Et, comme on compresse toute l’archive « par l’extérieur », c’est probablement là qu’on a les meilleurs ratios de compression.<br>
Par contre… accéder à une entrée en particulier… Ben il faut parcourir toute l’archive pour trouver l’entrée. Et pour parcourir toute l’archive, il faut la décompresser. Et ça prend du temps.</p>
<h3 id="toc-le-format-arx"> Le format Arx</h3>
<p>Il n’y a qu’un seul type d’entrée dans Arx et il est composé de trois variants : </p>
<ul>
<li>Fichier</li>
<li>Dossier</li>
<li>Lien symbolique</li>
</ul>
<p>Pour le moment, aucune méta-donnée sur les fichiers n’est stockée. Donc pas de owner, group, droit d’accès ou attributs étendus. Il vous faudra attendre un peu pour ça :) ARX utilise une structure en arbre. Chaque dossier pointe vers un « range » d’entrées qu’il contient. Chaque entrée (y compris les dossiers) contient l’id de son dossier parent. Le nom des entrées est le « basename ». On ne stocke pas tout le chemin de l’entrée.</p>
<p>Cette structure en arbre permet d’accélérer la recherche d’entrée puisqu’on n’a pas besoin de faire une recherche « linéaire » sur toutes les entrées. Cela permet aussi de gagner de la place puisqu’on ne stocke pas le chemin complet. En contrepartie, trouver le chemin complet à partir d’une entrée nécessite de remonter tous ses parents. Mais c’est un cas de figure assez rare.</p>
<p>L’outil <code>arx</code> est relativement simple et permet cinq opérations :</p>
<ol>
<li>créer une archive à partir d’un ou plusieurs dossiers/fichiers,</li>
<li>extraire une archive dans un dossier,</li>
<li>lister les entrées dans une archive,</li>
<li>dumper (j’ai pas de traduction française) une entrée d’une archive,</li>
<li>monter l’archive dans un point de montage.</li>
</ol>
<p>Le code d’arx est assez simple (pour le moment). Il y a sept fichiers qui ne dépassent pas les 500 lignes de code chacun. Je vous invite vivement à aller le voir.</p>
<h3 id="toc-un-peu-de-comparaison">Un peu de comparaison</h3>
<blockquote>
<p>Mais du coup, arx, ça vaut quoi par rapport à tar ?</p>
</blockquote>
<p>Faisons donc un peu de tests. Pour tester arx, j’ai utilisé les sources du kernel Linux (on est sur linuxfr ou pas ?). J’en ai fait trois cas de test différents:</p>
<ul>
<li>Les sources au complet</li>
<li>Le dossier <code>Documentation</code> seulement</li>
<li>Le dossier <code>drivers</code> seulement</li>
</ul>
<p>De plus, arx utilise <a href="https://fr.wikipedia.org/wiki/Zstandard">zstd</a> comme algo de compression. Donc pour éviter de comparer des pommes avec des poires, il faut comparer à un tar.zst, pas un tar.gz. Il y a bien une option chez tar pour compresser en utilisant zstd mais ça utilise pas la même config (niveau de compression) que arx, qui prend le parti de compresser au maximum au détriment du temps de compression. Du coup, l’archive tar.zst est faite avec : <code>tar c linux-5.19 | zstd -19 -T8 -o linux.tar.zst</code>. (niveau de compression 19, 8 threads).<br>
Enfin, pour le contexte de test, les sources à compresser sont sur un SSD mais tout le reste c’est du tmpfs. Ce qui limite les entrées/sorties au minimum (mais en vrai ça change pas les ordres de grandeurs).</p>
<p>Voici les chiffres bruts, l’analyse arrive après :</p>
<h4 id="toc-pour-la-partie-documentation-seulement-9194-entrées">Pour la partie documentation seulement (9194 entrées) :</h4>
<table>
<thead>
<tr>
<th></th>
<th>Taille</th>
<th>Création</th>
<th>Extraction</th>
<th>Listing</th>
<th>Dump</th>
<th>Dump / entry</th>
<th>Mount diff</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Source</strong></td>
<td>58 MB</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>66ms</td>
</tr>
<tr>
<td><strong>Tar zstd</strong></td>
<td>7.8 MB</td>
<td>8s3</td>
<td>68ms</td>
<td>51ms</td>
<td>2m9s</td>
<td>43ms</td>
<td>2m38s</td>
</tr>
<tr>
<td><strong>Arx</strong></td>
<td>8.3 MB</td>
<td>8s7</td>
<td>100ms</td>
<td>5ms</td>
<td>8s9</td>
<td>3.4ms</td>
<td>324ms</td>
</tr>
<tr>
<td><strong><em>Ratios</em></strong></td>
<td><strong><em>1.06</em></strong></td>
<td><strong><em>1.05</em></strong></td>
<td><strong><em>1.47</em></strong></td>
<td><strong><em>0.1</em></strong></td>
<td><strong><em>0.07</em></strong></td>
<td></td>
<td><strong><em>0.002</em></strong></td>
</tr>
</tbody>
</table>
<h4 id="toc-pour-la-partie-drivers-seulement-33056-entrées">Pour la partie drivers seulement (33056 entrées) :</h4>
<table>
<thead>
<tr>
<th></th>
<th>Taille</th>
<th>Création</th>
<th>Extraction</th>
<th>Listing</th>
<th>Dump</th>
<th>Dump / entry</th>
<th>Mount diff</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Source</strong></td>
<td>865 MB</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>490ms</td>
</tr>
<tr>
<td><strong>Tar zstd</strong></td>
<td>73 MB</td>
<td>1m7</td>
<td>688ms</td>
<td>570ms</td>
<td>1h36</td>
<td>520ms</td>
<td>2h41m</td>
</tr>
<tr>
<td><strong>Arx</strong></td>
<td>80 MB</td>
<td>3m25</td>
<td>930ms</td>
<td>19ms</td>
<td>35s</td>
<td>3.1ms</td>
<td>1s75</td>
</tr>
<tr>
<td><strong><em>Ratios</em></strong></td>
<td><strong><em>1.09</em></strong></td>
<td><strong><em>3</em></strong></td>
<td><strong><em>1.35</em></strong></td>
<td><strong><em>0.03</em></strong></td>
<td><strong><em>0.006</em></strong></td>
<td></td>
<td><strong><em>0.00018</em></strong></td>
</tr>
</tbody>
</table>
<h4 id="toc-et-pour-les-sources-complètes-81958-entrées">Et pour les sources complètes (81958 entrées) :</h4>
<table>
<thead>
<tr>
<th></th>
<th>Taille</th>
<th>Création</th>
<th>Extraction</th>
<th>Listing</th>
<th>Dump</th>
<th>Dump / entry</th>
<th>Mount diff</th>
<th>Compilation</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Source</strong></td>
<td>1326 MB</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>880ms</td>
<td>32m</td>
</tr>
<tr>
<td><strong>Tar zstd</strong></td>
<td>129 MB</td>
<td>1m37s</td>
<td>1s130ms</td>
<td>900ms</td>
<td>6h20m</td>
<td>833ms</td>
<td></td>
<td></td>
</tr>
<tr>
<td><strong>Arx</strong></td>
<td>140 MB</td>
<td>4m45s</td>
<td>1s47ms</td>
<td>45ms</td>
<td>1m28s</td>
<td>4ms</td>
<td>4s2</td>
<td>48m</td>
</tr>
<tr>
<td><strong><em>Ratios</em></strong></td>
<td><strong><em>1.08</em></strong></td>
<td><strong><em>2.93</em></strong></td>
<td><strong><em>1.3</em></strong></td>
<td><strong><em>0.05</em></strong></td>
<td><strong><em>0.0045</em></strong></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<h4 id="toc-la-taille">La taille</h4>
<ul>
<li>Les sources décompressées du kernel font 1,3 Go.</li>
<li>L’archive tar.zst fait 129 Mo. (Un ratio de compression de 9,76%).</li>
<li>L’archive arx elle fait 140 Mo. (Un ratio de compression de 10,56%).</li>
</ul>
<p>Arx compresse moins bien. On a environ 8 % d’écart entre les deux archives. On s’y attendait au vu des structures de chaque archive, mais perso je trouve pas ça dégueu. Surtout comparé au 1,3 Go d’origine. Et pour info, le tar.gz utilisé par tout le monde, fait 200 Mo et ça a pas l’air de gêner grand monde alors bon, 140 Mo, ça va.</p>
<h4 id="toc-le-temps-de-création">Le temps de création</h4>
<p>La création de l’archive kernel.tar.zst se fait en 1m37s alors que l’arx se fait en 4m45s. Sans surprise ici, tar est bien plus rapide. On a globalement un rapport de 3 entre les temps de création. C’est en accord avec le cas d’usage de Jubako (temps de création plus lent, mais exploitation plus rapide) mais la création est probablement la partie la moins optimisée pour le moment. Il devrait être possible d’améliorer les perfs du côté de Jubako pour limiter l’écart (notamment compresser les contenus en parallèle).</p>
<h4 id="toc-le-temps-dextraction">Le temps d’extraction</h4>
<p>Le temps d’extraction se fait dans des temps relativement similaires : 1s1 pour tar et 1s5 pour arx. Là aussi, c’est en accord avec la structure des données des deux formats. Tar n’a qu’un seul flux à décompresser. Alors que Jubako doit parcourir les entrées et doit ensuite décompresser plusieurs flux internes (avec tout le temps d’initialisation associé). Mais, là encore, il y a probablement matière à amélioration du côté de Jubako/arx. </p>
<h4 id="toc-le-temps-de-listing">Le temps de listing</h4>
<p>Ici on n’extrait pas le contenu, mais on liste les fichiers dans l’archive. Il faut 10 à 20 fois moins de temps à arx pour lister le contenu de l’archive par rapport à tar. Là aussi, c’est cohérent avec la structure de données des deux formats. Jubako n’a aucun contenu à décompresser et il n’a que le <code>directoryPack</code> à lire, alors que tar doit décompresser toute l’archive.</p>
<p>On commence à toucher à des cas d’usage pour lesquels Jubako a été conçu : accéder aux données sans extraire toute l’archive.</p>
<h4 id="toc-le-temps-de-dumping">Le temps de dumping</h4>
<p>Par dumping j’entends extraire un seul fichier en particulier d’une archive.<br>
Le cas de test est d’extraire un fichier sur trois de l’archive.<br>
Pour le kernel au complet, ça veut dire extraire 27319 fichiers de l’archive (et donc lancer 27319 fois tar/arx)</p>
<p>Il faut 1m30 à arx pour faire le travail. Du côté de tar il faut… 6h20.<br>
Ça fait une moyenne de 4ms par entrée du côté d'arx et 0,8s pour tar.<br>
C’est un ratio de <strong>200</strong> !!</p>
<p>Si on réduit la taille des archives (ce qui limite la quantité de données à décompresser pour tar), le ratio baisse un peu, mais on reste quand même avec arx 12 fois plus rapide que tar pour la documentation. (3064 fichiers à dumper)</p>
<p>On voit ici clairement l’avantage de Jubako sur tar. C’est prévisible, rien de magique, on travaille juste avec un format de fichier fait pour ça, c’est normal que ça dépote.</p>
<p>Cela met aussi en évidence le fait qu’arx met un temps relativement constant pour extraire un fichier, quelle que soit la taille de l’archive.<br>
À l’inverse pour tar, les temps d’extraction augmentent avec la taille des archives.</p>
<h4 id="toc-mount">Mount</h4>
<p>Un mount tout seul ne prend pas de temps, il faut voir quand on veut lire les fichiers. Le test correspond donc à un mount suivi d’un <code>diff -r</code> entre ce qui a été monté et les sources d’origine. C’est le temps de diff qui est mesuré. L’archive tar est montée avec l’outil <code>archivemount</code>.</p>
<p>Bon, là c’est clair, arx est bien bien bien plus rapide que tar : un diff sur une archive arx monté est de <strong>490</strong> à <strong>5500</strong> fois plus rapide qu’un diff sur une archive tar. (Je n’ai pas osé faire le test sur le kernel complet, mais je vous laisse le faire si vous voulez). On notera quand même que le diff prend quatre à cinq fois plus de temps qu’un diff simple entre deux dossiers (sans mount).</p>
<p>Mais diff, c’est un cas vachement particulier, on parcourt certes tous les fichiers, mais c’est assez séquentiel et on y accède qu’une seule fois. Ça donnerait quoi avec un vrai cas d’usage ?</p>
<p>Du coup, compilons un kernel…<br>
La compilation du kernel simplement (configuration par défaut, <code>make -j8</code>, les sources extraites dans le fs, sur sdd) prend 32 minutes sur ma machine. La même compilation mais sur une archive montée (archive elle-même stockée sur le sdd, pas dans tmpfs) prend 48 minutes. Alors oui, ça prend 1,5 fois plus de temps, mais sachez que l’implémentation actuelle de <code>arx mount</code> est mono-threadée, donc le <code>make -j8</code> en prend un coup de base. Mais vous n’avez utilisé que 140 Mo d’espace disque au lieu de 1,3Go pour stocker les sources du kernel.</p>
<h3 id="toc-utilisation-mémoire">Utilisation mémoire</h3>
<p>Niveau utilisation mémoire, Jubako est, normalement, relativement sobre.</p>
<p>La partie index (stockée dans le directoryPack) est directement exploitable par Jubako. Rien n’est compressé et Jubako est conçu pour lire directement les données stockées dans le fichier. Bien sûr, ça engendre beaucoup d’I/O et c’est donc au détriment des performances. Pour réduire ça, différentes stratégies sont utilisées (bufferisation, mmap, cache…). Mais dans un contexte vraiment limité en mémoire, c’est désactivable (sur le principe, il y a pas vraiment d’options aujourd’hui dans l’implémentation). De toute façon, le directoryPack de l’archive de l’ensemble des sources du kernel fait 2 Mo. Donc on pourrait tout mettre en ram sans que ça ne pose de problème.</p>
<p>Au niveau des données compressées, ça nécessite un peu plus de données. Les données sont regroupées en « clusters » et les clusters sont compressés indépendamment. Donc pour accéder à un contenu, il faut décompresser au pire un cluster entier. L’implémentation actuelle crée des clusters de 4 Mo maximum. Donc sans cache, accéder à un contenu couterait 4 Mo max (plus les structures internes utilisées par les algorithmes de (dé)compression).</p>
<p>En pratique, il y a du cache mis en place (et pas obligatoirement de la manière la plus optimisée). Jubako fait de la décompression partielle : il commence à décompresser un cluster jusqu’aux données auxquelles on veut accéder. Mais pour éviter de décompresser à nouveau le début du cluster plus tard, on garde en mémoire le contexte de décompression en mémoire. Donc chaque cluster a certes 4 Mo maximum, mais on garde plus en mémoire. Et actuellement, on a un cache de 20 clusters en mémoire. Donc environ 80 Mo plus les 20 contextes de décompression (et je n’ai pas mesuré leur taille).</p>
<p>Au global, un <code>arx mount</code> pendant une compilation de kernel consomme 310 Mo au maximum (Maximum resident size). Il y a un peu de travail d’implémentation et d’ajustement pour avoir le meilleur compromis mémoire utilisée/performance. Et probablement de la configuration nécessaire pour s’adapter aux différents cas d’usage.</p>
<h2 id="toc-conclusion">Conclusion</h2>
<p>Voilà pour une première présentation de Jubako et Arx.</p>
<p>On est loin d’avoir un produit fini. Les specs de Jubako ne sont pas complètes. L’implémentation n’implémente pas tout ce qui est spécifié (et la spec <s>risque de</s> va changer avec l’implémentation).<br>
La bibliothèque Jubako elle-même est encore très jeune. Elle fonctionne, mais elle n’est probablement pas exempte de bugs, l’api est à améliorer, sans parler des performances.</p>
<p>Pour ce qui est de Arx, là aussi on en est au début. Arx stocke très peu de métadonnées sur les fichiers pour le moment, mais c’est une base et elle sert de très bon démonstrateur pour Jubako.</p>
<p>La différence entre ce qui est perdu (taille de l’archive, temps de compression) et ce qui est gagné (utilisation d’une archive sans la décompresser entièrement) est plus que raisonnable pour moi (surtout pour une première version).</p>
<p>Il y a encore pas mal de choses à faire mais c’est un premier jalon important pour moi de voir un projet imaginé il y a maintenant deux ans prendre forme et arriver à un résultat assez probant. (J’ai d’ailleurs eu du mal à me restreindre à faire une dépêche courte, j’en suis désolé… ou presque).</p>
<p>Jubako et Arx peuvent avoir leur utilité dans des cas d’usage particuliers. Pour ce qui est de la classique archive de fichiers qui sera extraite, il est fort probable que tar reste la référence. Mais Jubako ouvre une porte sur de toutes nouvelles façons de faire. Il serait possible de diffuser du contenu et de le lire <em>sans</em> le décompresser. Imaginez un peu :</p>
<ul>
<li>Une distribution Linux basée sur Jubako pour ses paquets. Des paquets qui ne seraient jamais décompressés mais montés à la demande…</li>
<li>Un système de sauvegarde (une sauvegarde incrémentale ne serait qu’une archive Jubako qui réutilise le contentPack des sauvegardes précédentes)…</li>
<li>Si python savait lire des archives Jubako…</li>
<li>Si on diffusait nos sites web statiques à coup de conteneur Jubako (ou de serveurs autonomes qui contiennent des conteneurs Jubako sous forme de ressources intégrées) …</li>
<li>Si les navigateurs web savaient lire du Jubako et qu’on packageait nos applis JavaScript à coup de Jubako…</li>
</ul>
<p>Comment ça je m’emballe ?!</p>
</div><div><a href="https://linuxfr.org/news/jubako-et-arx-un-conteneur-universel-et-son-format-d-archive.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/129067/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/jubako-et-arx-un-conteneur-universel-et-son-format-d-archive#comments">ouvrir dans le navigateur</a>
</p>
GaMaorfenorYves Bourguignonvmagninpalm123Ysabeau 🧶 🧦patrick_gLtrlgJulien Jorgehttps://linuxfr.org/nodes/129067/comments.atomtag:linuxfr.org,2005:Bookmark/27112021-02-22T07:43:58+01:002021-02-22T07:43:58+01:00Archive du web : service Europe ?<a href="https://archive.eu/">https://archive.eu/</a> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/123361/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/bubar/liens/archive-du-web-service-europe#comments">ouvrir dans le navigateur</a>
</p>
bubar🦥https://linuxfr.org/nodes/123361/comments.atomtag:linuxfr.org,2005:News/401502020-11-18T13:51:59+01:002020-11-19T10:25:23+01:00Lighten Mailbox : archivez vos courrielsLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><blockquote>
<p>À quand remonte la dernière fois que vous avez eu besoin de lire ce courriel du 15 septembre 2012 ? Ça fait longtemps, n’est‑ce pas ?</p>
</blockquote>
<h3 id="toc-préambule">Préambule</h3>
<p>Le stockage de ces courriels n’est pas sans impact énergétique. En effet, les serveurs qui hébergent ces vieux courriels sont allumés en permanence pour vous permettre, un jour peut‑être, de relire ce courriel du 15 septembre 2012 dont vous n’avez même plus le souvenir. Et ça s’entasse, s’entasse… Deux solutions peuvent s’offrir à vous pour diminuer cet impact :</p>
<ul>
<li>supprimer vos vieux courriels, la solution la plus sobre, radicale et efficace ;</li>
<li>archiver vos courriels sur une clef USB ou un disque dur externe, votre ordinateur ; en tout cas, un système d’archivage « froid », c’est‑à‑dire qui ne sera pas allumé en permanence.</li>
</ul>
</div><ul><li>lien nᵒ 1 : <a title="https://lighten-mailbox.zici.fr/" hreflang="fr" href="https://linuxfr.org/redirect/107314">Instance publique</a></li><li>lien nᵒ 2 : <a title="https://framagit.org/kepon/lighten-mailbox/" hreflang="fr" href="https://linuxfr.org/redirect/107315">Code source</a></li><li>lien nᵒ 3 : <a title="https://lighten-mailbox.zici.fr/archive/example/" hreflang="fr" href="https://linuxfr.org/redirect/107316">Exemple d’index d’archive</a></li><li>lien nᵒ 4 : <a title="https://vimeo.com/477628241" hreflang="fr" href="https://linuxfr.org/redirect/107317">Vidéo pour voir le processus</a></li></ul><div><h2 id="toc-lightenmailbox-un-service-en-ligne-opensource-pour-faireça">Lighten Mailbox : un service en ligne open source pour faire ça</h2>
<p><a href="http://lighten-mailbox.zici.fr/">Lighten Mailbox</a> est un projet libre (<a href="https://creativecommons.org/publicdomain/zero/1.0/deed.fr">CC0</a>) qui se présente sous la forme d’une interface Web qui permet de faire du ménage dans sa boîte de messagerie. Ce ménage se fait soit en supprimant des vieux messages, soit en les téléchargeant dans une archive.</p>
<p>En mode éclairé/expert vous pouvez choisir le format d’exportation de vos messages : HTML/TXT ou EML. Un fichier <code>index.html</code> est glissé dans l’archive ZIP avec vos messages, qui vous permet (grâce à <a href="http://tabulator.info/">tabulator</a>) de parcourir ou rechercher dans les métadonnées de vos messages (<a href="https://lighten-mailbox.zici.fr/archive/example/">exemple de rendu pour le <code>index.html</code></a>). Ces métadonnées sont stockées au format JSON (<a href="https://lighten-mailbox.zici.fr/archive/example/messages.js">exemple</a>), ce qui permet d’autres usages potentiels…</p>
<p>La sélection des messages se fait par critère de date (début/fin), puis en sélectionnant les dossiers IMAP souhaités.</p>
<p>Exemple d’utilisation : télécharger et archiver ces courriels qui datent de J - 2 ans à J - 1 an pour les enregistrer sur un disque dur externe, puis (quand vous vous êtes assuré de l’intégrité des données) supprimer ces messages.</p>
<h3 id="toc-sous-le-capot-">Sous le capot </h3>
<ul>
<li>rien de bien original : Apache, PHP et MySQL ;</li>
<li>un démon pour gérer la file d’attente qui génère les archives ;</li>
<li>une découverte totomatique des paramètres IMAP en mode débutant ;</li>
<li>des difficultés pour faire fonctionner le bazar avec <em>@gmail.com</em>… (OAuth est trop compliqué pour moi).</li>
</ul>
<p>P.‑S. — Pas taper, je ne veux pas sauver le monde avec une appli Web, juste l’alléger…</p>
</div><div><a href="https://linuxfr.org/news/lighten-mailbox-archivez-vos-courriels.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/122265/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/lighten-mailbox-archivez-vos-courriels#comments">ouvrir dans le navigateur</a>
</p>
DavidDavy Defaudpalm123Ysabeau 🧶 🧦Pierre Jarillonhttps://linuxfr.org/nodes/122265/comments.atomtag:linuxfr.org,2005:Diary/389142020-01-27T13:28:07+01:002020-01-28T01:39:27+01:00Utiliser Vim avec Android, un tuto avec de belles imagesLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<h2 id="toc-installation-de-vim">Installation de Vim</h2>
<p>Si vous n’avez pas déjà l’excellent <a href="https://termux.com/">Termux</a>, installez‑le.</p>
<p>Depuis Termux, lancez <code>pkg install vim</code></p>
<p>Le plus facile est fait.</p>
<h2 id="toc-installation-du-clavier">Installation du clavier</h2>
<h3 id="toc-ressortez-le-clavier-du-pentium90">Ressortez le clavier du Pentium 90</h3>
<p>Tiens, quel est donc ce connecteur ?<br>
Ça ressemble à ma vieille prise midi… que de souvenirs !<br>
<img src="//img.linuxfr.org/img/68747470733a2f2f746f662e63782f696d616765732f323032302f30312f32372f36393339393832663732356266656431633666613566636333326131346337642e6a7067/6939982f725bfed1c6fa5fcc32a14c7d.jpg" alt="Le retour de l’obra‑DIN" title="Source : https://tof.cx/images/2020/01/27/6939982f725bfed1c6fa5fcc32a14c7d.jpg"></p>
<h3 id="toc-fouillez-dans-les-cartons-de1998-pour-retrouver-votre-adaptateur-dinps2">Fouillez dans les cartons de 1998 pour retrouver votre adaptateur DIN‑PS/2</h3>
<p>Et notez qu’à ce stade, avec une carte mère « gamers », le problème serait réglé.<br>
Ça peut aider aussi lors de certains boots difficiles avec Linux, quand l’USB ne répond pas et qu’il faut rentrer la clé LUKS.<br>
<img src="//img.linuxfr.org/img/68747470733a2f2f746f662e63782f696d616765732f323032302f30312f32372f64386139326435326562306531333462373865356630383234323533626165362e6a7067/d8a92d52eb0e134b78e5f0824253bae6.jpg" alt="En avant vers la modernité" title="Source : https://tof.cx/images/2020/01/27/d8a92d52eb0e134b78e5f0824253bae6.jpg"></p>
<h3 id="toc-dans-le-sens-ps2-vers-usb-cest-un-peucompliqué">Dans le sens PS/2 vers USB, c’est un peu compliqué</h3>
<p>Mais avec le bon boîtier, rien d’impossible.<br>
Cet adaptateur induit une très légère latence, mais ça reste supportable.<br>
Vous ne l’utiliserez cependant pas comme clavier maître pour <a href="https://openmpt.org/">Open MPT</a><br>
<img src="//img.linuxfr.org/img/68747470733a2f2f746f662e63782f696d616765732f323032302f30312f32372f33366631613865363830376365383633356464613062373831316263363233352e6a7067/36f1a8e6807ce8635dda0b7811bc6235.jpg" alt="Ah, tout le monde est en USB ?" title="Source : https://tof.cx/images/2020/01/27/36f1a8e6807ce8635dda0b7811bc6235.jpg"></p>
<h3 id="toc-Évidemment-on-va-pas-brancher-ça-commeça">Évidemment on va pas brancher ça comme ça</h3>
<p>Les téléphones Android ont depuis un certain temps un connecteur USB‑C, ce qui va limiter les frais.<br>
<img src="//img.linuxfr.org/img/68747470733a2f2f746f662e63782f696d616765732f323032302f30312f32372f62643965623163643166636435323738373438613363616439323131633632642e6a7067/bd9eb1cd1fcd5278748a3cad9211c62d.jpg" alt="Mise à jour du connecteur USB" title="Source : https://tof.cx/images/2020/01/27/bd9eb1cd1fcd5278748a3cad9211c62d.jpg"></p>
<h3 id="toc-branchement-de-tout-ce-bel">Branchement de tout ce b***el</h3>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f746f662e63782f696d616765732f323032302f30312f32372f33643835663631306664353661303334643131303964663961383961663131392e6a7067/3d85f610fd56a034d1109df9a89af119.jpg" alt="Je suis sûr que ça va marcher" title="Source : https://tof.cx/images/2020/01/27/3d85f610fd56a034d1109df9a89af119.jpg"></p>
<h3 id="toc-et-ça-marche">Et ça marche !</h3>
<p>Le passage en AZERTY (ou en bépo) est laissé à titre d’exercice au lecteur.<br>
<img src="//img.linuxfr.org/img/68747470733a2f2f746f662e63782f696d616765732f323032302f30312f32372f63323264326531323530376237333566613439393332646365656462373131652e6a7067/c22d2e12507b735fa49932dceedb711e.jpg" alt="Oui çq ;qrche" title="Source : https://tof.cx/images/2020/01/27/c22d2e12507b735fa49932dceedb711e.jpg"></p>
<div><a href="https://linuxfr.org/users/jseb/journaux/utiliser-vim-avec-android-un-tuto-avec-de-belles-images.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/119259/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/jseb/journaux/utiliser-vim-avec-android-un-tuto-avec-de-belles-images#comments">ouvrir dans le navigateur</a>
</p>
jsebhttps://linuxfr.org/nodes/119259/comments.atomtag:linuxfr.org,2005:Diary/384712019-04-23T12:57:37+02:002019-04-23T12:57:37+02:00Historicalsource regroupe sur github plein de vieux codes sourcesLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<p>On viens de m'apprendre l'existence de <a href="https://github.com/historicalsource">https://github.com/historicalsource</a> qui regroupe à un endroit facile d'accès tout plein de code sources d'intérêt historique. On y trouve par exemple plein de jeux des années 90.</p>
<p>Je viens donc de découvrir que Glider 4.0 et Glider Pro (simulation d'avion en papier sur Macintosh) sont publiés sous licence GPL, et qu'il va falloir que quelqu'un s'occupe de les porter sur une plateforme moderne (tout comme Arashi, un clone de Tempest pour lequel il existe une version en Javascript).</p>
<p>On trouve également de nombreux jeux Infocom (aventures en mode texte), dont je n'ai pas vérifié la licence.</p>
<p>Si quelqu'un sait comment contacter historicalsource pour leur donner des dépôts à lister sur leur page, j'en ai quelque-uns à leur fournir.</p>
<div><a href="https://linuxfr.org/users/pulkomandy/journaux/historicalsource-regroupe-sur-github-plein-de-vieux-codes-sources.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/117019/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/pulkomandy/journaux/historicalsource-regroupe-sur-github-plein-de-vieux-codes-sources#comments">ouvrir dans le navigateur</a>
</p>
pulkomandyhttps://linuxfr.org/nodes/117019/comments.atomtag:linuxfr.org,2005:Bookmark/1122018-05-09T15:40:22+02:002018-05-09T15:40:22+02:00Archives Linux Journal<a href="https://secure2.linuxjournal.com/LJ-Archive-2017.zip">https://secure2.linuxjournal.com/LJ-Archive-2017.zip</a> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/114424/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/gbetous/liens/archives-linux-journal#comments">ouvrir dans le navigateur</a>
</p>
gUIhttps://linuxfr.org/nodes/114424/comments.atomtag:linuxfr.org,2005:News/331942012-08-03T01:17:15+02:002012-08-03T01:17:15+02:00Appel à contributions : Archives et contenus BBS et MinitelLicence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<div><p>Je profite du calme relatif de l'été pour poster cette dépêche, qui n'est certes pas directement liée au logiciel libre mais qui intéressera sans doute certains d'entre vous : LinuxFR comptait en effet, à ses débuts, de nombreux anciens des BBS français.</p>
<p>Le but de cette dépêche est avant tout de lancer un appel afin de récupérer, archiver et remettre un ligne tout un versant de l'histoire de la télématique dont hélas, il ne reste plus grand chose aujourd'hui. En ce qui concerne les BBS français, les quelques sites qui étaient apparus sur le net à la fin des années 90 étaient hébergés sur le vénérable mygale.org, ce qui explique en partie la rareté des contenus disponibles sur cette thématique.</p></div><ul><li>lien nᵒ 1 : <a title="http://www.goto10.fr" hreflang="fr" href="https://linuxfr.org/redirect/82903">Archives des BBS français et du Minitel</a></li><li>lien nᵒ 2 : <a title="http://www.minitel.org" hreflang="fr" href="https://linuxfr.org/redirect/82904">La mémoire du Minitel et des réseaux X.25</a></li></ul><div><p>Je recherche donc principalement :</p>
<ul><li>tous fichiers textes en rapport avec les BBS et le Minitel ;</li>
<li>des listes de fichiers disponibles sur ces BBS ;</li>
<li>des intros des BBS français, mais aussi du reste du monde francophone, Belgique principalement mais également Suisse romande et Québec ;</li>
<li>des <em>nodelists</em> des réseaux francophones basés sur la technologie Fidonet ;</li>
<li>les logiciels francophones uniquement (le reste étant déjà disponible sur de nombreux sites).</li>
</ul><p>Concernant le Minitel :</p>
<ul><li>tout lien en rapport direct avec le Minitel, les services commerciaux, les serveurs RTC amateurs, et le Videotex ;</li>
<li>tout logiciel (serveurs, émulateurs, utilitaires et logiciels de création Videotex).</li>
</ul><p>Le but étant de compléter ce qui a déjà été mis en ligne actuellement. Je vous invite donc à consulter les liens listés ci-dessus et à vous manifester si vous avez en votre possession des ressources pouvant y avoir leur place. Merci d'avance ! :)</p></div><div><a href="https://linuxfr.org/news/appel-a-contributions-archives-et-contenus-bbs-et-minitel.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/95053/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/appel-a-contributions-archives-et-contenus-bbs-et-minitel#comments">ouvrir dans le navigateur</a>
</p>
Frederic CambusLucas BonnetBenoît Sibaudbaud123https://linuxfr.org/nodes/95053/comments.atom