[{"data":1,"prerenderedAt":67004},["ShallowReactive",2],{"archives":3},[4,1779,2087,3513,7752,15250,17381,23310,24899,26090,29164,33198,35065,35957,36711,37299,38766,39283,40793,41783,43617,43898,44387,45709,46022,49136,50348,52019,52692,55190,56371,56567,56686,56768,57201,57452,57486,57625,57952,58086,58729,58885,58985,60020,60051,60159,60330,61390,64240,64913],{"id":5,"title":6,"author":7,"body":8,"category":1682,"categorySlug":1683,"date":1684,"description":14,"excerpt":1685,"extension":1764,"location":1765,"meta":1766,"navigation":1767,"path":1768,"published":1767,"seo":1769,"slug":1770,"stem":1771,"tags":1772,"timeToRead":1777,"__hash__":1778},"posts\u002Fposts\u002FWoodstock\u002F2026-04-26_woodstock_v2.md","Woodstock Backup v2.0.0 - La réécriture complète en Rust","Ulrich Vandenhekke",{"type":9,"value":10,"toc":1645},"minimark",[11,15,18,21,115,118,127,132,137,140,147,151,159,166,169,173,180,188,191,195,202,206,209,212,216,223,227,231,238,245,249,252,340,354,358,366,394,397,404,408,419,429,433,436,439,443,447,453,456,459,462,465,469,472,476,479,485,488,492,502,505,528,531,542,548,552,556,559,781,784,818,822,825,879,882,886,889,896,899,906,909,916,919,926,943,950,953,957,961,964,970,985,988,992,995,999,1002,1021,1572,1575,1578,1581,1587,1594,1600,1607,1618,1622,1625,1628,1642],[12,13,14],"p",{},"Bonjour à tous,",[12,16,17],{},"Six ans. Il m'aura fallu six ans entre la première version de Woodstock Backup et cette v2. Si vous m'aviez dit en 2020\nque je passerais la moitié de la décennie à réécrire trois fois le même logiciel de sauvegarde... j'aurais quand même\nfoncé tête baissée. C'est ma façon de faire. Me voilà donc avec une version 2 stable,\nentièrement réécrite en Rust, qui tourne en production sur ma petite infrastructure depuis plus d'un an. Et je suis\nvraiment content du résultat. 😄",[12,19,20],{},"Pour ceux qui me lisent depuis longtemps, voici un récapitulatif des articles qui ont précédé celui-ci :",[22,23,24,40],"table",{},[25,26,27],"thead",{},[28,29,30,34,37],"tr",{},[31,32,33],"th",{},"Article",[31,35,36],{},"Date",[31,38,39],{},"Sujet",[41,42,43,59,73,87,101],"tbody",{},[28,44,45,53,56],{},[46,47,48],"td",{},[49,50,52],"a",{"href":51},"\u002Fpost\u002Fwoodstock","Woodstock Backup v1.0.0",[46,54,55],{},"2020-09-20",[46,57,58],{},"Présentation du projet, prototype TypeScript + rsync",[28,60,61,67,70],{},[46,62,63],{},[49,64,66],{"href":65},"\u002Fpost\u002Fwoodstock_brtfs","Woodstock Backup - Btrfs",[46,68,69],{},"2021-01-12",[46,71,72],{},"Abandon de Btrfs, écriture d'un pool custom",[28,74,75,81,84],{},[46,76,77],{},[49,78,80],{"href":79},"\u002Fpost\u002Fwoodstock_protocol_language_sauvegarde","Woodstock Backup - Protocole et Langage de sauvegarde",[46,82,83],{},"2021-04-18",[46,85,86],{},"Protocole gRPC maison",[28,88,89,95,98],{},[46,90,91],{},[49,92,94],{"href":93},"\u002Fpost\u002Fwoodstock_rust","Woodstock Backup - Optimiser Node.js avec Rust",[46,96,97],{},"2023-05-10",[46,99,100],{},"NAPI-RS et bindings Rust pour réduire la consommation mémoire",[28,102,103,109,112],{},[46,104,105],{},[49,106,108],{"href":107},"\u002Fpost\u002Fpr_backuppc_pool","Woodstock Backup - Reverse engineering de BackupPC",[46,110,111],{},"2024-05-07",[46,113,114],{},"Migration du pool BackupPC vers Woodstock",[12,116,117],{},"Pour les nouveaux, je résume : Woodstock Backup est mon logiciel de sauvegarde personnel, centralisé, qui sauvegarde\ntoutes les machines de mon réseau local et mes serveurs distants sur un NAS. L'idée de départ était simple. Le résultat\nest... un peu plus complexe. :)",[12,119,120],{},[121,122],"img",{"alt":123,"src":124,"className":125},"Pool of chunks","\u002FWoodstock\u002Fv2_splash.png",[126],"img-center",[128,129,131],"h2",{"id":130},"le-long-chemin-de-2020-à-2026","Le long chemin de 2020 à 2026",[133,134,136],"h3",{"id":135},"prototype-1-typescript-rsync-btrfs-2020","Prototype 1 : TypeScript + rsync + Btrfs (2020)",[12,138,139],{},"Tout a commencé avec un prototype en TypeScript qui utilisait rsync pour copier les fichiers et Btrfs pour les\nsnapshots incrémentaux. L'idée était élégante sur le papier : rsync calcule le delta, Btrfs déduplique, tout le monde\nest content.",[12,141,142,143,146],{},"En pratique, je me suis rapidement retrouvé avec des problèmes de stabilité de Btrfs lorsque le nombre de snapshots\ndevient élevé et que le système de fichiers approche de la saturation. J'en ai ",[49,144,145],{"href":65},"parlé en détail dans mon article sur\nBtrfs",". En résumé : abandonné.",[133,148,150],{"id":149},"prototype-2-typescript-grpc-pool-custom-2021","Prototype 2 : TypeScript + gRPC + pool custom (2021)",[12,152,153,154,158],{},"L'abandon de Btrfs m'a forcé à écrire mon propre pool de stockage basé sur le principe de ",[155,156,157],"strong",{},"Content-Addressable\nStorage"," (CAS) : chaque bloc de fichier (chunk) est identifié par son hash, et si deux fichiers partagent des blocs\nidentiques, ces blocs ne sont stockés qu'une seule fois. La déduplication est donc native.",[12,160,161,162,165],{},"Pour transférer les fichiers entre les machines à sauvegarder et le serveur, j'ai abandonné rsync et développé ",[49,163,164],{"href":79},"mon\npropre protocole basé sur gRPC",". gRPC s'appuie sur HTTP\u002F2, offre une\ncompression et un TLS natifs, et les bibliothèques existent pour tous les langages. Parfait.",[12,167,168],{},"Ce prototype, toujours en TypeScript, fonctionnait. Mais le moteur JavaScript de Node.js a ses limites. En particulier,\nla représentation en mémoire des objets JavaScript est beaucoup plus gourmande que celle d'un langage compilé. Pour un\noutil qui doit gérer des millions de fichiers, c'est un vrai problème.",[133,170,172],{"id":171},"lère-des-bindings-rust-2023","L'ère des bindings Rust (2023)",[12,174,175,176,179],{},"L'idée de cette phase était séduisante : garder toute la partie cœur en Rust pour les performances, et conserver le\nTypeScript pour la couche GraphQL et la logique applicative. Rust pour ce qui est critique, TypeScript pour ce qui est\nlisible et rapide à écrire. C'est ce que j'ai ",[49,177,178],{"href":93},"décrit dans mon article de 2023",".",[12,181,182,183,187],{},"Bien sûr, je me trompais. En pratique, il fallait des bindings d'exposition NAPI-RS pour chaque interface entre les\ndeux langages, des DTOs côté TypeScript qui doublonnaient les structures Rust, et surtout, plus le temps passait, plus\nle cœur métier migrait vers Rust — laissant de moins en moins de code côté TypeScript. La complexité était réelle :\ngérer des équivalents d'",[184,185,186],"code",{},"Observable"," ou du streaming en passant par des bindings NAPI-RS n'est pas trivial, même si\nles dernières versions de NAPI-RS ont depuis apporté des solutions à ces problèmes.",[12,189,190],{},"Le tout en restant performant malgré le coût du passage par les bindings TypeScript. C'était jouable, mais c'était\nde la jonglerie.",[133,192,194],{"id":193},"la-migration-du-pool-backuppc-2024","La migration du pool BackupPC (2024)",[12,196,197,198,201],{},"En parallèle, j'ai travaillé sur la migration de mon pool BackupPC existant vers le format Woodstock. La première\napproche envisagée consistait à monter les sauvegardes BackupPC via FUSE et à les lire comme un système de fichiers\nnormal. J'ai finalement opté pour une approche plus directe : ",[49,199,200],{"href":107},"lire directement le format interne de BackupPC",",\nce qui impliquait un travail de rétro-ingénierie non négligeable. La migration a été un succès, et j'ai pu enfin\nabandonner complètement BackupPC.",[133,203,205],{"id":204},"la-décision-de-tout-réécrire-en-rust-2024","La décision de tout réécrire en Rust (2024)",[12,207,208],{},"À ce stade, la situation était devenue évidente : la partie TypeScript avait tellement rétréci qu'il ne restait plus\ngrand-chose dedans. Un serveur NestJS (framework Node.js) pour l'API, une interface web en Vue.js, et quelques couches\nde glue — le reste était déjà en Rust. Maintenir deux écosystèmes pour si peu de code TypeScript n'avait plus aucun\nsens.",[12,210,211],{},"J'ai donc décidé de sauter le pas : supprimer les bindings, réécrire intégralement le backend en Rust. Plus de\nNAPI-RS, plus de DTOs en double, plus de jonglerie entre deux compilateurs. Du Rust pur, de bout en bout.",[133,213,215],{"id":214},"woodstock-backup-v2-la-version-stable-2026","Woodstock Backup v2 : la version stable (2026)",[12,217,218,219,222],{},"La version ",[184,220,221],{},"2.0.0"," est en production depuis plus d'un an maintenant, et je la considère comme stable. Les sauvegardes tournent tous les jours, les restaurations\nfonctionnent, et je dors tranquille.",[128,224,226],{"id":225},"architecture-de-woodstock-backup-v2","Architecture de Woodstock Backup v2",[133,228,230],{"id":229},"vue-densemble-le-modèle-pull","Vue d'ensemble : le modèle Pull",[12,232,233,234,237],{},"L'architecture reste fondée sur le ",[155,235,236],{},"modèle pull"," : c'est le serveur de sauvegarde qui initie les connexions vers les\npériphériques, et non l'inverse. Cela offre une garantie de sécurité importante : un périphérique compromis ne peut\npas écrire de données arbitraires dans le serveur de sauvegarde.",[239,240,241],"center",{},[121,242],{"alt":243,"src":244},"Architecture Pull - Woodstock v2","https:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fpng\u002FVLJDRjD04BxxAOPm81M47hZbK9N-g19AfLAZ7b2aQB8UnzkiPvsTtRJSE25nHJm0Hy9h-4tw9E3ysaxAnPPs_hxvlfav5O_EXzn4Btn6EK6Edfp6A1hRH-Z4vEOK72G4Wc5E4tG9TU3bG4yoVsO2HG05Eg-LBf0zYCee2OPSw_tUZaSFratt3CfeOZ_2Ge-agjMsDmm9UXoZ47G-sE0OpP2Vlls0QsITadZg00hShqmDznjh3Po_ZuVSFJCufNUlFujFZfR-XRKc8avWR1_NNT-K2wUBhFhE0a7vgzQystH_vOYuXVP1Hkk64gJSSWD4p5X8Pdq5mhjKZk_YU0L1rfQc-nVnvU-SXfmGf5fbcfmitLFPuMNh2UoSN8qfw4DCpeCX0KUpKFxn970NwEsz3BbxUnb_EhvoM6GV1sz0a0Km-EmeYjhmeNUoBn3q8VyqYE7fwqyGFM4qb1Dxi6mqqx5Dq-eVHTjHgBBiz8S-N9GBPOXLHc2mHksGkqB6CXG6cJLFZY9KNi_HKts0Qhbw9tkKGn_EBJCzQiimkRqvNw8TSbUpzhfLiLPWJxf3P6o4geguSa4GUFlMa7MNTwljDhPt0gb07mQaV71KxPPvXMvi7OaYLhGBJYLA1NhDofl1WDfSH0dLWo9ZRG4tw9GDJY0XsNd2FcMzja83BPuQOT0LxmZpfIe0JGtMA_UFbVAxSdhLLTXk8d6oGMI30vLXjLKg2po577aMaFrUpWEwNb2ErJ8aOHLyi9K6LLkATn4x6PuPdcSpjqcwvBdLzTJD1ggxKgdbhPhYX20f5qaeZ9w5Sj6wG_yXT7tee77dLepM9DyEMUrjRw0FmhlMiZnmLSMg5qUfie4RcMgxTqgSXDpy1G00",[133,246,248],{"id":247},"les-composants-rust","Les composants Rust",[12,250,251],{},"L'ensemble de la solution est écrit en Rust. Côté serveur, quatre microservices ; côté client, un démon déployé sur\nchaque machine à sauvegarder :",[22,253,254,267],{},[25,255,256],{},[28,257,258,261,264],{},[31,259,260],{},"Composant",[31,262,263],{},"Déployé sur",[31,265,266],{},"Rôle",[41,268,269,282,298,313,325],{},[28,270,271,276,279],{},[46,272,273],{},[184,274,275],{},"api_server",[46,277,278],{},"Serveur",[46,280,281],{},"API REST\u002FGraphQL pour l'interface Vue.js — hors du chemin de sauvegarde",[28,283,284,289,291],{},[46,285,286],{},[184,287,288],{},"client_api_server",[46,290,278],{},[46,292,293,294,297],{},"Reçoit les connexions mTLS de ",[184,295,296],{},"ws_client_daemon"," pour le signalement online\u002Foffline",[28,299,300,305,307],{},[46,301,302],{},[184,303,304],{},"job_worker",[46,306,278],{},[46,308,309,310,312],{},"Exécute les sauvegardes : connexion gRPC vers ",[184,311,296],{},", transfert des chunks, déduplication",[28,314,315,320,322],{},[46,316,317],{},[184,318,319],{},"scheduler",[46,321,278],{},[46,323,324],{},"Gère le planning, déclenche les jobs selon les règles définies",[28,326,327,331,334],{},[46,328,329],{},[184,330,296],{},[46,332,333],{},"Chaque périphérique",[46,335,336,337,339],{},"Reçoit les connexions de ",[184,338,304],{},", crée les snapshots, envoie les chunks",[12,341,342,343,349,350,353],{},"La gestion des jobs de sauvegarde est assurée par ",[49,344,348],{"href":345,"rel":346},"https:\u002F\u002Fgithub.com\u002Fgeofmureithi\u002Fapalis",[347],"nofollow","Apalis",", qui s'appuie sur\n",[155,351,352],{},"Redis\u002FValkey"," comme backend de file d'attente. Apalis remplace ici BullMQ, qui remplissait ce rôle dans l'ancienne\nversion NestJS. C'est une architecture simple et fiable, qui permet également de distribuer les workers si un jour\nl'infrastructure venait à grossir.",[133,355,357],{"id":356},"le-pool-de-stockage-cas-blake3zstd","Le pool de stockage : CAS Blake3+Zstd",[12,359,360,361,365],{},"Le cœur du système est le pool CAS (",[362,363,364],"em",{},"Content-Addressable Storage","). Son fonctionnement est le suivant :",[367,368,369,377,384,387],"ol",{},[370,371,372,373,376],"li",{},"Chaque fichier est découpé en ",[155,374,375],{},"chunks"," de taille variable.",[370,378,379,380,383],{},"Chaque chunk est haché avec ",[155,381,382],{},"Blake3"," (un algorithme de hachage moderne, très rapide).",[370,385,386],{},"Si le hash du chunk est déjà présent dans le pool, il n'est pas retransféré ni re-stocké.",[370,388,389,390,393],{},"Si le chunk est nouveau, il est compressé avec ",[155,391,392],{},"Zstd"," avant d'être écrit sur disque.",[12,395,396],{},"La déduplication est donc réalisée au niveau des chunks, et non au niveau des fichiers entiers. Cela signifie que si\nun fichier de 10 Go n'a été modifié qu'à 1 %, seul 1 % sera retransféré et stocké.",[12,398,399,400,403],{},"Le pool maintient également un ",[155,401,402],{},"compteur de références"," (refcount) par chunk : quand une sauvegarde est supprimée,\nles chunks qui ne sont plus référencés sont supprimés du pool. Ce mécanisme de garbage collection permet de garder le\npool propre sans intervention manuelle.",[133,405,407],{"id":406},"sauvegardes-windows-vss-natif","Sauvegardes Windows : VSS natif",[12,409,410,411,414,415,418],{},"Depuis la version ",[184,412,413],{},"alpha.57",", le client Windows utilise le ",[155,416,417],{},"Volume Shadow Copy Service (VSS)"," de Windows pour créer\nun snapshot cohérent du système de fichiers avant la sauvegarde. Cela permet de sauvegarder des fichiers verrouillés\n(comme les bases de données, les fichiers de profil Outlook, etc.) sans erreur.",[12,420,421,422,424,425,428],{},"Plus besoin de rsync, de Cygwin, ou d'outils tiers : le client ",[184,423,296],{}," est un binaire Rust natif pour\nWindows, compilé avec la cible ",[184,426,427],{},"x86_64-pc-windows-msvc",", qui utilise les APIs Win32 directement. C'est\nnettement plus propre que l'ancienne approche.",[133,430,432],{"id":431},"sauvegardes-linux-snapshots-btrfs","Sauvegardes Linux : snapshots Btrfs",[12,434,435],{},"Sur les machines Linux dont le système de fichiers est Btrfs, le client crée un snapshot en lecture seule avant de\nlancer la sauvegarde. Cela garantit la cohérence des données sauvegardées, même si des fichiers sont modifiés pendant\nla sauvegarde.",[12,437,438],{},"Contrairement au premier prototype qui utilisait Btrfs côté serveur (ce qui causait les problèmes évoqués plus haut),\nici les snapshots sont créés côté client et uniquement pour la durée de la sauvegarde. C'est une utilisation beaucoup\nplus conservative de Btrfs.",[128,440,442],{"id":441},"les-défis-techniques","Les défis techniques",[133,444,446],{"id":445},"le-refcounting-un-problème-de-cohérence","Le refcounting : un problème de cohérence",[12,448,449,450,452],{},"Le plus grand défi de cette réécriture a été l'implémentation correcte du ",[155,451,402],{}," du pool CAS.",[12,454,455],{},"Le problème est le suivant : lorsqu'une sauvegarde est en cours, des chunks sont ajoutés au pool et leur refcount est\nincrémenté. Si la sauvegarde est interrompue (coupure réseau, arrêt du serveur, etc.), le pool peut se retrouver dans\nun état incohérent : des chunks présents dans le pool sans être référencés par aucune sauvegarde complète.",[12,457,458],{},"C'est la solution la plus complexe et qui a nécessité le plus de travail pour être implémentée de manière fiable. En\neffet, il faut s'assurer que le comptage de référence est bon si on ne veux pas se retrouver à éliminer des chunks encore référencés.",[12,460,461],{},"Afin de garantir que le comptage de référence est bon, un outil de récupération a été développé : il analyse le pool et les manifestes des sauvegardes, et corrige les refcounts en cas d'incohérence. Il n'est normallement nécessaire qu'en cas de\ncrash du serveur (coupure de courant, etc.) pendant une sauvegarde.",[12,463,464],{},"Actuellement la structure du pool repose sur le système de fichiers. L'inconvénient est à aujourd'hui la durée\nd'execution du fsck qui peut être très longue (taille du pool). En échange les opérations de lecture\u002Fécriture sont très\nrapides.",[133,466,468],{"id":467},"les-breaking-changes","Les BREAKING CHANGES",[12,470,471],{},"Par rapport à la version 1, on est sur une réécriture complète. Vu le peu de monde qui utilise cette version 1, il n'y a\npas de migration prévue. La version 2 est un nouveau projet, avec une nouvelle API, et des changements majeurs dans la façon dont les sauvegardes sont gérées.",[133,473,475],{"id":474},"windows-sans-rsync-binaire-natif-cross-compilé","Windows sans rsync : binaire natif cross-compilé",[12,477,478],{},"Dans la version 1, le client Windows était un serveur rsyncd couplé à Cygwin. C'était fonctionnel, mais peu élégant,\ndifficile à installer, et les permissions NTFS n'étaient pas correctement sauvegardées.",[12,480,481,482,484],{},"Dans la version 2, le client Windows est un binaire Rust compilé en cross-compilation depuis Linux avec la cible\n",[184,483,427],{}," et le linker LLVM\u002FClang. Le binaire est distribué seul, sans dépendance. Il se connecte au\nserveur de sauvegarde via gRPC mTLS, crée un snapshot VSS, parcourt le système de fichiers et envoie les chunks.",[12,486,487],{},"Les ACLs NTFS, les attributs étendus, les points de jonction et les liens symboliques Windows sont correctement\ngérés. C'est un vrai progrès.",[133,489,491],{"id":490},"la-sécurité-mtls-de-bout-en-bout","La sécurité : mTLS de bout en bout",[12,493,494,495,497,498,501],{},"Toutes les communications impliquant ",[184,496,296],{}," sont chiffrées et authentifiées par ",[155,499,500],{},"mutual TLS (mTLS)",".\nChaque périphérique possède un certificat client signé par une autorité de certification interne au serveur Woodstock.",[12,503,504],{},"Deux canaux mTLS distincts :",[506,507,508,518],"ul",{},[370,509,510,517],{},[155,511,512,514,515],{},[184,513,304],{}," ↔ ",[184,516,296],{}," (gRPC) : le canal de sauvegarde, à l'initiative du serveur.",[370,519,520,527],{},[155,521,522,524,525],{},[184,523,296],{}," → ",[184,526,288],{}," (REST) : le canal de présence, à l'initiative du client, qui lui permet\nde signaler son statut online\u002Foffline.",[12,529,530],{},"Cela garantit que :",[506,532,533,536,539],{},[370,534,535],{},"Les données sauvegardées sont chiffrées en transit.",[370,537,538],{},"Seul le serveur Woodstock peut déclencher une sauvegarde sur un périphérique enregistré.",[370,540,541],{},"Un périphérique ne peut pas usurper l'identité d'un autre.",[12,543,544,545,547],{},"À noter : ",[184,546,275],{},", qui sert l'interface Vue.js, ne dispose pas encore d'authentification. C'est prévu, mais pas encore fait (voir plus bas).",[128,549,551],{"id":550},"en-production-depuis-plus-dun-an","En production depuis plus d'un an",[133,553,555],{"id":554},"les-machines-sauvegardées","Les machines sauvegardées",[12,557,558],{},"Voici un tableau récapitulatif de mes neuf machines sauvegardées au 26 avril 2026 :",[22,560,561,582],{},[25,562,563],{},[28,564,565,568,570,573,576,579],{},[31,566,567],{},"Machine",[31,569,266],{},[31,571,572],{},"Sauvegardes",[31,574,575],{},"Fichiers",[31,577,578],{},"Taille brute",[31,580,581],{},"Compressé",[41,583,584,606,628,650,672,694,716,737,759],{},[28,585,586,591,594,597,600,603],{},[46,587,588],{},[184,589,590],{},"localhost",[46,592,593],{},"NAS local (Debian)",[46,595,596],{},"43",[46,598,599],{},"13 346",[46,601,602],{},"1,3 Go",[46,604,605],{},"0,8 Go",[28,607,608,613,616,619,622,625],{},[46,609,610],{},[184,611,612],{},"pc-eve",[46,614,615],{},"PC principal de la famille (Windows)",[46,617,618],{},"211",[46,620,621],{},"563 333",[46,623,624],{},"473 Go",[46,626,627],{},"413 Go",[28,629,630,635,638,641,644,647],{},[46,631,632],{},[184,633,634],{},"pc-m-eve",[46,636,637],{},"Ordinateur portable familial (Windows)",[46,639,640],{},"26",[46,642,643],{},"146 243",[46,645,646],{},"21,6 Go",[46,648,649],{},"13,5 Go",[28,651,652,657,660,663,666,669],{},[46,653,654],{},[184,655,656],{},"pc-m-ulrich",[46,658,659],{},"Mon portable personnel (Linux)",[46,661,662],{},"53",[46,664,665],{},"43 185",[46,667,668],{},"125 Go",[46,670,671],{},"64,5 Go",[28,673,674,679,682,685,688,691],{},[46,675,676],{},[184,677,678],{},"pc-ulrich",[46,680,681],{},"Mon PC de bureau (Linux)",[46,683,684],{},"322",[46,686,687],{},"1 385 001",[46,689,690],{},"108 Go",[46,692,693],{},"76,7 Go",[28,695,696,701,704,707,710,713],{},[46,697,698],{},[184,699,700],{},"pc-alex-linux",[46,702,703],{},"PC Linux secondaire",[46,705,706],{},"4",[46,708,709],{},"210 006",[46,711,712],{},"25 Go",[46,714,715],{},"14,5 Go",[28,717,718,723,726,729,732,735],{},[46,719,720],{},[184,721,722],{},"pc-alex-windows",[46,724,725],{},"PC Windows secondaire",[46,727,728],{},"360",[46,730,731],{},"429 368",[46,733,734],{},"147 Go",[46,736,690],{},[28,738,739,744,747,750,753,756],{},[46,740,741],{},[184,742,743],{},"server",[46,745,746],{},"Serveur dédié OVH principal",[46,748,749],{},"450",[46,751,752],{},"243 074",[46,754,755],{},"810 Go",[46,757,758],{},"752 Go",[28,760,761,766,769,772,775,778],{},[46,762,763],{},[184,764,765],{},"server-ovh-6",[46,767,768],{},"Second serveur OVH",[46,770,771],{},"472",[46,773,774],{},"493 441",[46,776,777],{},"194 Go",[46,779,780],{},"173 Go",[12,782,783],{},"Quelques observations :",[506,785,786,798,805],{},[370,787,788,792,793,797],{},[155,789,790],{},[184,791,743],{}," et ",[155,794,795],{},[184,796,765],{}," ont le plus grand nombre de sauvegardes (450 et 472). Ce sont des serveurs qui\ntournent 24\u002F7, avec des données critiques.",[370,799,800,804],{},[155,801,802],{},[184,803,678],{}," cumule plus de 1,3 million de fichiers sauvegardés, ce qui en fait la machine avec le plus grand\nnombre de fichiers individuels. Mon répertoire de développement est manifestement très fragmenté. 😄",[370,806,807,808,812,813,817],{},"La compression Zstd est particulièrement efficace sur ",[155,809,810],{},[184,811,656],{}," (49 % d'économie) et beaucoup moins\nsur ",[155,814,815],{},[184,816,743],{}," (7 %), ce qui s'explique par la nature des données stockées : données de développement vs.\ndonnées déjà compressées (archives, images Docker, etc.).",[133,819,821],{"id":820},"le-pool-global","Le pool global",[12,823,824],{},"Le pool CAS central agrège toutes les sauvegardes :",[22,826,827,837],{},[25,828,829],{},[28,830,831,834],{},[31,832,833],{},"Statistique",[31,835,836],{},"Valeur",[41,838,839,847,855,863,871],{},[28,840,841,844],{},[46,842,843],{},"Chunks uniques",[46,845,846],{},"3 365 564",[28,848,849,852],{},[46,850,851],{},"Références totales",[46,853,854],{},"60 888 193",[28,856,857,860],{},[46,858,859],{},"Espace pool compressé",[46,861,862],{},"1,95 To",[28,864,865,868],{},[46,866,867],{},"Espace disque total",[46,869,870],{},"9,6 To",[28,872,873,876],{},[46,874,875],{},"Espace disque utilisé",[46,877,878],{},"6,4 To",[12,880,881],{},"Le ratio références\u002Fchunks (60,9 M \u002F 3,4 M ≈ 18x) illustre l'efficacité de la déduplication : chaque chunk unique est\nen moyenne référencé 18 fois par différentes sauvegardes. L'historique long des sauvegardes explique cette valeur\nélevée.",[133,883,885],{"id":884},"interface-web","Interface web",[12,887,888],{},"L'interface web Vue.js 3 \u002F Vuetify permet de visualiser l'état de l'infrastructure, du pool, et de l'historique des\nsauvegardes de chaque machine.",[12,890,891],{},[121,892],{"alt":893,"src":894,"className":895},"Page Devices - liste des machines sauvegardées","\u002FWoodstock\u002Fv2_devices.png",[126],[12,897,898],{},"La page principale liste les neuf machines avec leur état, le nombre de sauvegardes, la taille brute et compressée.",[12,900,901],{},[121,902],{"alt":903,"src":904,"className":905},"Page Pool - statistiques du pool CAS","\u002FWoodstock\u002Fv2_pool.png",[126],[12,907,908],{},"La page pool affiche les statistiques globales du stockage : occupation disque, nombre de chunks, nombre de\nréférences, et l'évolution depuis le mois précédent.",[12,910,911],{},[121,912],{"alt":913,"src":914,"className":915},"Page Backups - détail des sauvegardes d'une machine","\u002FWoodstock\u002Fv2_host_detail.png",[126],[12,917,918],{},"La page de détail d'une machine liste l'historique complet de ses sauvegardes avec la date, la durée, et la liste des\npartitions sauvegardées.",[12,920,921],{},[121,922],{"alt":923,"src":924,"className":925},"Liste des sauvegardes avec état, durée et politique de rétention","\u002FWoodstock\u002Fv2_backup_list.png",[126],[12,927,928,929,932,933,932,936,939,940,179],{},"Chaque entrée de la liste indique le numéro de sauvegarde, la date de démarrage, la durée, le nombre de fichiers\ntotaux et nouveaux, les fichiers modifiés et supprimés, le compteur d'erreurs, et la politique de rétention\nappliquée : ",[155,930,931],{},"Horaire",", ",[155,934,935],{},"Quotidien",[155,937,938],{},"Hebdo"," ou ",[155,941,942],{},"Mensuel",[12,944,945],{},[121,946],{"alt":947,"src":948,"className":949},"Navigation dans les fichiers d'une sauvegarde","\u002FWoodstock\u002Fv2_backup_browse.png",[126],[12,951,952],{},"En cliquant sur une sauvegarde, on accède à la vue détaillée : statistiques complètes (fichiers, tailles, durée,\nvitesse), partitions sauvegardées avec leur type (Btrfs ou VSS), et un explorateur de fichiers permettant de\nnaviguer dans l'arborescence et de télécharger ou restaurer individuellement n'importe quel fichier.",[128,954,956],{"id":955},"perspectives","Perspectives",[133,958,960],{"id":959},"archivage-hors-site-sur-disque-usb","Archivage hors-site sur disque USB",[12,962,963],{},"Une fonctionnalité que je veux mettre en place depuis la v1 est l'archivage de la dernière version des sauvegardes\nsur un disque dur USB, qui est ensuite stocké hors-site. L'idée est d'avoir une copie physique des données dans un\nautre lieu en cas de sinistre (incendie, vol, etc.).",[12,965,966,967,179],{},"Dans la version 1 avec BackupPC, j'avais un script qui utilisait le connecteur FUSE de BackupPC pour monter les\nsauvegardes et les synchroniser avec rsync vers le disque USB. Ce script posait des problèmes avec les gros fichiers\net les permissions Windows, comme je l'avais mentionné ",[49,968,969],{"href":51},"dans le tout premier article",[12,971,972,973,976,977,980,981,984],{},"Dans Woodstock v2, l'outil en ligne de commande ",[184,974,975],{},"ws_console"," dispose d'une commande ",[184,978,979],{},"mount"," qui permet de monter\nune sauvegarde comme un système de fichiers FUSE. Il sera donc possible de faire un ",[184,982,983],{},"rsync"," depuis ce point de\nmontage vers le disque USB, avec une gestion correcte des permissions et des gros fichiers.",[12,986,987],{},"Ce n'est pas encore automatisé, mais c'est la prochaine chose que je veux mettre en place.",[133,989,991],{"id":990},"ajout-dun-format-de-stockage","Ajout d'un format de stockage",[12,993,994],{},"J'envisage également de pouvoir stocker directement le pool sur un bucket S3 ou compatible (SeaweedFS, RustFS). Avant de me lancer dans cette implémentation, je veux d'abord tester\nles performances d'un tel choix.",[128,996,998],{"id":997},"comparaison-avec-les-solutions-existantes","Comparaison avec les solutions existantes",[12,1000,1001],{},"Avant de conclure, voici une comparaison honnête avec les alternatives réalistes. Si un autre outil correspond mieux à vos besoins, utilisez-le. Sans rancune.",[12,1003,1004,1005,932,1008,932,1011,932,1014,792,1017,1020],{},"Les outils couverts : ",[155,1006,1007],{},"Restic",[155,1009,1010],{},"BorgBackup",[155,1012,1013],{},"BackupPC",[155,1015,1016],{},"URBackup",[155,1018,1019],{},"Kopia",". Ils représentent les principales solutions open-source pour une infrastructure self-hostée multi-machines — ce qui correspond grosso modo au problème que Woodstock cherche à résoudre.",[22,1022,1023,1043],{},[25,1024,1025],{},[28,1026,1027,1030,1033,1035,1037,1039,1041],{},[31,1028,1029],{},"Critère",[31,1031,1032],{},"Woodstock v2",[31,1034,1007],{},[31,1036,1010],{},[31,1038,1013],{},[31,1040,1016],{},[31,1042,1019],{},[41,1044,1045,1069,1094,1117,1142,1166,1190,1214,1238,1261,1285,1309,1333,1358,1383,1408,1427,1447,1469,1490,1510,1530,1549],{},[28,1046,1047,1052,1055,1058,1061,1064,1067],{},[46,1048,1049],{},[155,1050,1051],{},"Langage",[46,1053,1054],{},"Rust",[46,1056,1057],{},"Go",[46,1059,1060],{},"Python + C",[46,1062,1063],{},"Perl",[46,1065,1066],{},"C++",[46,1068,1057],{},[28,1070,1071,1076,1079,1082,1085,1088,1091],{},[46,1072,1073],{},[155,1074,1075],{},"Licence",[46,1077,1078],{},"MIT",[46,1080,1081],{},"BSD-2",[46,1083,1084],{},"BSD",[46,1086,1087],{},"GPL v2+",[46,1089,1090],{},"AGPLv3+",[46,1092,1093],{},"Apache 2.0",[28,1095,1096,1101,1104,1107,1110,1113,1115],{},[46,1097,1098],{},[155,1099,1100],{},"Développement actif",[46,1102,1103],{},"✅ 2026",[46,1105,1106],{},"✅ 2025",[46,1108,1109],{},"✅ 2024",[46,1111,1112],{},"❌ 2020¹",[46,1114,1103],{},[46,1116,1106],{},[28,1118,1119,1124,1127,1130,1133,1136,1139],{},[46,1120,1121],{},[155,1122,1123],{},"Modèle de synchronisation",[46,1125,1126],{},"Pull (initié par le serveur)",[46,1128,1129],{},"Push (le client pousse)",[46,1131,1132],{},"Push (SSH ou local)",[46,1134,1135],{},"Pull (rsync\u002Ftar\u002FSMB)",[46,1137,1138],{},"Pull-like (découverte LAN)",[46,1140,1141],{},"Push \u002F mode serveur",[28,1143,1144,1149,1152,1155,1158,1161,1163],{},[46,1145,1146],{},[155,1147,1148],{},"Agent requis sur le client",[46,1150,1151],{},"✅ daemon",[46,1153,1154],{},"❌ binaire unique",[46,1156,1157],{},"❌ accès SSH",[46,1159,1160],{},"❌ rsync\u002FSMB",[46,1162,1151],{},[46,1164,1165],{},"❌ \u002F ✅ serveur optionnel",[28,1167,1168,1173,1176,1179,1182,1185,1187],{},[46,1169,1170],{},[155,1171,1172],{},"Planificateur intégré",[46,1174,1175],{},"✅ (Apalis + Redis)",[46,1177,1178],{},"❌ (cron\u002Fsystemd)",[46,1180,1181],{},"❌ (cron\u002FBorgmatic)",[46,1183,1184],{},"✅",[46,1186,1184],{},[46,1188,1189],{},"✅ (mode serveur)",[28,1191,1192,1197,1200,1203,1206,1209,1211],{},[46,1193,1194],{},[155,1195,1196],{},"Backends cloud\u002Fdistants",[46,1198,1199],{},"❌ self-hosted uniquement",[46,1201,1202],{},"✅ S3, B2, SFTP, Azure, GCS, rclone…",[46,1204,1205],{},"⚠️ SSH \u002F BorgBase",[46,1207,1208],{},"❌ disque local uniquement",[46,1210,1199],{},[46,1212,1213],{},"✅ S3, Azure, GCS, B2, SFTP, rclone…",[28,1215,1216,1221,1224,1227,1230,1233,1236],{},[46,1217,1218],{},[155,1219,1220],{},"Format de stockage",[46,1222,1223],{},"Pool CAS, fichiers sur disque",[46,1225,1226],{},"Pack files CAS",[46,1228,1229],{},"Journal de segments + index",[46,1231,1232],{},"Pool MD5 (niveau fichier) + reverse deltas",[46,1234,1235],{},"Snapshots de fichiers + images de blocs",[46,1237,1226],{},[28,1239,1240,1245,1248,1250,1253,1256,1259],{},[46,1241,1242],{},[155,1243,1244],{},"Granularité de déduplication",[46,1246,1247],{},"Chunk (CDC)",[46,1249,1247],{},[46,1251,1252],{},"Chunk (BUZHASH CDC)",[46,1254,1255],{},"Fichier (MD5 fichier complet, sans hardlinks en v4)",[46,1257,1258],{},"Fichier",[46,1260,1247],{},[28,1262,1263,1268,1271,1274,1277,1280,1283],{},[46,1264,1265],{},[155,1266,1267],{},"Dédup cross-machines",[46,1269,1270],{},"✅ (un pool partagé)",[46,1272,1273],{},"⚠️ uniquement si dépôt partagé",[46,1275,1276],{},"⚠️ uniquement au sein d'un dépôt",[46,1278,1279],{},"✅ (pool MD5 partagé)",[46,1281,1282],{},"✅ (niveau fichier)",[46,1284,1276],{},[28,1286,1287,1292,1294,1297,1300,1303,1306],{},[46,1288,1289],{},[155,1290,1291],{},"Compression",[46,1293,392],{},[46,1295,1296],{},"Zstd (depuis 0.14)",[46,1298,1299],{},"lz4, zstd, zlib, lzma",[46,1301,1302],{},"gzip \u002F bzip2",[46,1304,1305],{},"lzo \u002F zstd (optionnel)",[46,1307,1308],{},"zstd, lz4, gzip…",[28,1310,1311,1316,1319,1322,1325,1327,1330],{},[46,1312,1313],{},[155,1314,1315],{},"Chiffrement au repos",[46,1317,1318],{},"❌ pool en clair",[46,1320,1321],{},"✅ AES-256-CTR + Poly1305 (obligatoire)",[46,1323,1324],{},"✅ AES-256-CTR + HMAC (optionnel)",[46,1326,1318],{},[46,1328,1329],{},"⚠️ optionnel",[46,1331,1332],{},"✅ AES-256-GCM ou ChaCha20 (optionnel)",[28,1334,1335,1340,1343,1346,1349,1352,1355],{},[46,1336,1337],{},[155,1338,1339],{},"Chiffrement en transit",[46,1341,1342],{},"✅ mTLS (gRPC + REST)",[46,1344,1345],{},"⚠️ dépend du backend",[46,1347,1348],{},"⚠️ SSH ou local",[46,1350,1351],{},"⚠️ SSH ou SMB (optionnel)",[46,1353,1354],{},"⚠️ TLS optionnel",[46,1356,1357],{},"⚠️ TLS en mode serveur (optionnel)",[28,1359,1360,1365,1368,1371,1374,1377,1380],{},[46,1361,1362],{},[155,1363,1364],{},"Vérification d'intégrité",[46,1366,1367],{},"Hash Blake3 par chunk",[46,1369,1370],{},"SHA-256 par blob",[46,1372,1373],{},"HMAC-SHA256 \u002F BLAKE2b",[46,1375,1376],{},"MD5 fichier complet (v4+)",[46,1378,1379],{},"Hash",[46,1381,1382],{},"BLAKE2B ou SHA256",[28,1384,1385,1390,1393,1396,1399,1402,1405],{},[46,1386,1387],{},[155,1388,1389],{},"Modèle d'authentification",[46,1391,1392],{},"mTLS par appareil (CA interne)",[46,1394,1395],{},"Mot de passe + fichiers clés",[46,1397,1398],{},"Clés SSH + passphrase",[46,1400,1401],{},"Clés SSH",[46,1403,1404],{},"Certificats clients optionnels",[46,1406,1407],{},"Mot de passe + TLS optionnel",[28,1409,1410,1415,1417,1419,1421,1423,1425],{},[46,1411,1412],{},[155,1413,1414],{},"Linux",[46,1416,1184],{},[46,1418,1184],{},[46,1420,1184],{},[46,1422,1184],{},[46,1424,1184],{},[46,1426,1184],{},[28,1428,1429,1434,1437,1439,1441,1443,1445],{},[46,1430,1431],{},[155,1432,1433],{},"macOS",[46,1435,1436],{},"❌²",[46,1438,1184],{},[46,1440,1184],{},[46,1442,1184],{},[46,1444,1184],{},[46,1446,1184],{},[28,1448,1449,1454,1457,1459,1462,1465,1467],{},[46,1450,1451],{},[155,1452,1453],{},"Windows (natif)",[46,1455,1456],{},"✅ (binaire MSVC)",[46,1458,1184],{},[46,1460,1461],{},"❌ (WSL2 uniquement)",[46,1463,1464],{},"⚠️ (rsync\u002FCygwin ou SMB)",[46,1466,1184],{},[46,1468,1184],{},[28,1470,1471,1476,1478,1481,1484,1486,1488],{},[46,1472,1473],{},[155,1474,1475],{},"Snapshots VSS (Windows)",[46,1477,1184],{},[46,1479,1480],{},"✅ (depuis 0.12)",[46,1482,1483],{},"❌",[46,1485,1483],{},[46,1487,1184],{},[46,1489,1184],{},[28,1491,1492,1497,1499,1502,1504,1506,1508],{},[46,1493,1494],{},[155,1495,1496],{},"Snapshots Btrfs (Linux)",[46,1498,1184],{},[46,1500,1501],{},"❌ (hooks manuels)",[46,1503,1501],{},[46,1505,1483],{},[46,1507,1483],{},[46,1509,1483],{},[28,1511,1512,1517,1519,1521,1523,1525,1528],{},[46,1513,1514],{},[155,1515,1516],{},"Sauvegarde image \u002F bare-metal",[46,1518,1483],{},[46,1520,1483],{},[46,1522,1483],{},[46,1524,1483],{},[46,1526,1527],{},"✅ (Windows + Linux)",[46,1529,1483],{},[28,1531,1532,1537,1539,1541,1543,1545,1547],{},[46,1533,1534],{},[155,1535,1536],{},"Montage FUSE",[46,1538,1184],{},[46,1540,1184],{},[46,1542,1184],{},[46,1544,1483],{},[46,1546,1483],{},[46,1548,1184],{},[28,1550,1551,1555,1558,1561,1564,1567,1569],{},[46,1552,1553],{},[155,1554,885],{},[46,1556,1557],{},"✅ (sans auth pour l'instant)",[46,1559,1560],{},"❌ (tiers : Restic Browser)",[46,1562,1563],{},"❌ (tiers : Vorta)",[46,1565,1566],{},"✅ (CGI\u002FPerl)",[46,1568,1184],{},[46,1570,1571],{},"✅ (KopiaUI)",[12,1573,1574],{},"¹ La dernière version de BackupPC date de juin 2020. La base de code est stable mais n'est plus maintenue.",[12,1576,1577],{},"² La prise en charge de macOS n'est pas encore implémentée dans Woodstock.",[12,1579,1580],{},"Quelques points à souligner :",[12,1582,1583,1584,1586],{},"Le ",[155,1585,236],{}," (Woodstock, BackupPC, URBackup) signifie qu'un client compromis ne peut pas écrire de données arbitraires sur le serveur de sauvegarde. Le modèle push (Restic, Borg, Kopia) est plus simple à mettre en place, mais accorde davantage de confiance à chaque client.",[12,1588,1589,1590,1593],{},"La ",[155,1591,1592],{},"déduplication au niveau des chunks"," signifie que si un fichier de 10 Go est modifié d'1 Ko, seul cet 1 Ko est transféré et stocké. La déduplication au niveau fichier (BackupPC, URBackup) signifie que si un fichier change, le nouveau fichier entier est stocké dans le pool. Pour BackupPC spécifiquement, rsync gère le transfert de manière efficiente — seuls les blocs modifiés transitent sur le réseau — mais comme la correspondance dans le pool est basée sur un MD5 du fichier complet, un fichier modifié génère toujours une nouvelle entrée complète dans le pool. Pour les gros fichiers mutables — bases de données, disques de machines virtuelles, fichiers PST Outlook — la différence d'efficacité de stockage est considérable.",[12,1595,1589,1596,1599],{},[155,1597,1598],{},"déduplication cross-machines"," est moins courante. Si dix machines possèdent chacune une copie du même installeur de 500 Mo, Woodstock ne le stocke qu'une seule fois. Restic et Borg ne dédupliquent qu'au sein d'un même dépôt — pour obtenir une déduplication cross-machines, il faudrait mettre toutes les machines dans le même dépôt, ce qui crée des problèmes de contention de verrous et de contrôle d'accès.",[12,1601,1602,1603,1606],{},"La principale faiblesse de Woodstock : ",[155,1604,1605],{},"le pool n'est pas chiffré au repos",". Si quelqu'un obtient un accès physique ou système à votre NAS, il peut lire vos données de sauvegarde directement. L'approche de Restic — tout chiffrer, de manière obligatoire — est clairement plus solide si quelqu'un met la main sur votre disque. Sur un NAS de LAN privé que vous contrôlez physiquement, c'est un compromis que j'accepte. Pour un stockage distant ou dans le cloud, c'est une vraie limitation. Il est possible de contrer ce problème en chiffrant le disque lui-même (LUKS, BitLocker, etc.), mais c'est une couche supplémentaire à gérer.",[12,1608,1609,1610,1613,1614,1617],{},"Si vous avez besoin d'une ",[155,1611,1612],{},"restauration bare-metal",", URBackup est le seul outil de cette liste qui le fait nativement. Si vous avez besoin de ",[155,1615,1616],{},"BorgBackup sur Windows",", c'est impossible sans WSL2.",[128,1619,1621],{"id":1620},"conclusion","Conclusion",[12,1623,1624],{},"Ce projet m'a appris énormément de choses : Rust, gRPC, mTLS, la gestion de pools CAS, le VSS Windows, les snapshots\nBtrfs, la gestion de jobs distribués avec Redis, et probablement une dizaine d'autres sujets que j'ai oubliés depuis.",[12,1626,1627],{},"Est-ce que c'était raisonnable de passer six ans à construire son propre logiciel de sauvegarde alors qu'il en existe\ndes dizaines ? Bien sur que oui 😄.",[12,1629,1630,1631,1636,1637,179],{},"Le logiciel est disponible sur ",[49,1632,1635],{"href":1633,"rel":1634},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002Fwoodstock-backup",[347],"mon instance Gitea"," et la\ndocumentation est sur ",[49,1638,1641],{"href":1639,"rel":1640},"https:\u002F\u002Fwoodstock.shadoware.org",[347],"woodstock.shadoware.org",[12,1643,1644],{},"À bientôt !",{"title":1646,"searchDepth":1647,"depth":1647,"links":1648},"",2,[1649,1658,1665,1671,1676,1680,1681],{"id":130,"depth":1647,"text":131,"children":1650},[1651,1653,1654,1655,1656,1657],{"id":135,"depth":1652,"text":136},3,{"id":149,"depth":1652,"text":150},{"id":171,"depth":1652,"text":172},{"id":193,"depth":1652,"text":194},{"id":204,"depth":1652,"text":205},{"id":214,"depth":1652,"text":215},{"id":225,"depth":1647,"text":226,"children":1659},[1660,1661,1662,1663,1664],{"id":229,"depth":1652,"text":230},{"id":247,"depth":1652,"text":248},{"id":356,"depth":1652,"text":357},{"id":406,"depth":1652,"text":407},{"id":431,"depth":1652,"text":432},{"id":441,"depth":1647,"text":442,"children":1666},[1667,1668,1669,1670],{"id":445,"depth":1652,"text":446},{"id":467,"depth":1652,"text":468},{"id":474,"depth":1652,"text":475},{"id":490,"depth":1652,"text":491},{"id":550,"depth":1647,"text":551,"children":1672},[1673,1674,1675],{"id":554,"depth":1652,"text":555},{"id":820,"depth":1652,"text":821},{"id":884,"depth":1652,"text":885},{"id":955,"depth":1647,"text":956,"children":1677},[1678,1679],{"id":959,"depth":1652,"text":960},{"id":990,"depth":1652,"text":991},{"id":997,"depth":1647,"text":998},{"id":1620,"depth":1647,"text":1621},"Woodstock","woodstock","2026-04-26",{"type":9,"value":1686},[1687,1689,1691,1693,1757,1759],[12,1688,14],{},[12,1690,17],{},[12,1692,20],{},[22,1694,1695,1705],{},[25,1696,1697],{},[28,1698,1699,1701,1703],{},[31,1700,33],{},[31,1702,36],{},[31,1704,39],{},[41,1706,1707,1717,1727,1737,1747],{},[28,1708,1709,1713,1715],{},[46,1710,1711],{},[49,1712,52],{"href":51},[46,1714,55],{},[46,1716,58],{},[28,1718,1719,1723,1725],{},[46,1720,1721],{},[49,1722,66],{"href":65},[46,1724,69],{},[46,1726,72],{},[28,1728,1729,1733,1735],{},[46,1730,1731],{},[49,1732,80],{"href":79},[46,1734,83],{},[46,1736,86],{},[28,1738,1739,1743,1745],{},[46,1740,1741],{},[49,1742,94],{"href":93},[46,1744,97],{},[46,1746,100],{},[28,1748,1749,1753,1755],{},[46,1750,1751],{},[49,1752,108],{"href":107},[46,1754,111],{},[46,1756,114],{},[12,1758,117],{},[12,1760,1761],{},[121,1762],{"alt":123,"src":124,"className":1763},[126],"md","Lille, France",{"planet":1767},true,"\u002Fpost\u002Fwoodstock_v2",{"title":6,"description":14},"woodstock_v2","posts\u002FWoodstock\u002F2026-04-26_woodstock_v2",[1683,1773,1774,1775,1776],"backup","sauvegarde","rust","grpc",22,"gu0ISxCDyWPA2zrYBtgiI1k0uw4o8BK0cd-5g09z838",{"id":1780,"title":1781,"author":7,"body":1782,"category":2057,"categorySlug":2058,"date":2059,"description":14,"excerpt":2060,"extension":1764,"location":1765,"meta":2074,"navigation":1767,"path":2075,"published":1767,"seo":2076,"slug":2077,"stem":2078,"tags":2079,"timeToRead":2085,"__hash__":2086},"posts\u002Fposts\u002FProgrammation\u002F2026-01-29-sportequipement-vibe-coding.md","SportEquipement - Application de suivi de matériel sportif avec Vibe Coding",{"type":9,"value":1783,"toc":2049},[1784,1786,1789,1792,1795,1801,1805,1808,1811,1825,1829,1832,1839,1842,1845,1852,1855,1858,1865,1875,1879,1882,1885,1899,1902,1925,1928,1931,1938,1941,1944,1947,1950,1953,1956,1960,1963,1966,1969,1974,1977,1980,1983,1986,1990,1993,1996,1999,2002,2009,2011,2014,2017,2020,2027,2034,2037,2040,2043,2046],[12,1785,14],{},[12,1787,1788],{},"Pour ceux qui me connaissent, vous savez que je fais un peu de vélo. Et comme tout cycliste moderne, j'utilise Strava.\nC'est l'outil de référence pour suivre ses sorties, comparer ses segments et analyser sa forme. Strava propose bien une\nfonctionnalité de \"Suivi d'équipement\" (pour savoir combien de kilomètres ont vos chaussures ou votre cadre), mais...\nc'est sommaire.",[12,1790,1791],{},"Si la version Web permet d'ajouter des composants (chaîne, cassette, etc.), l'application mobile a tout simplement\n\"oublié\" cette fonctionnalité.",[12,1793,1794],{},"J'ai eu quelques problèmes avec ma chaîne qui s'use en 2 000 km ou des plaquettes de frein à changer. Bref j'avais envie\nd'avoir des stats un peu précises sur le suivi de mon matériel. De la même manière je cours régulièrement et je voulais\nsavoir quand mes baskets atteignaient leur durée de vie maximale (ça, ça marche bien sur Strava).",[12,1796,1797,1798,179],{},"Pour améliorer ce suivi j'ai décidé de créer ma propre solution : ",[155,1799,1800],{},"SportEquipement",[128,1802,1804],{"id":1803},"sportequipement-cest-quoi","SportEquipement : C'est quoi ?",[12,1806,1807],{},"L'idée est simple : une application mobile, connectée à Strava et à Android Health Connect, qui récupère mes activités\net incrémente l'usure de chaque composant défini.",[12,1809,1810],{},"Pour la première version, je voulais aussi qu'elle soit \"Local First\". L'application tourne entièrement sur le téléphone.\nPas de serveur, application open source, et mes données restent chez moi. (Bon, j'ajouterai peut-être dans un futur un\nserveur pour synchroniser mes différents appareils et sécuriser les tokens Strava, mais chaque chose en son temps).",[12,1812,1813,1814,1819,1820,179],{},"L'application est d'ailleurs disponible sur le ",[49,1815,1818],{"href":1816,"rel":1817},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=org.shadoware.sportequipment&hl=fr",[347],"Play Store"," et le code source est sur mon ",[49,1821,1824],{"href":1822,"rel":1823},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment",[347],"Gitea perso",[128,1826,1828],{"id":1827},"quelques-écrans","Quelques écrans",[12,1830,1831],{},"Cet écran montre la liste des équipements qui ont été ajoutés. On peut voir l'état d'usure de chaque équipement et si\nl'équipement est usé ou un de ces composants est usé on a une alerte visuelle.",[12,1833,1834],{},[121,1835],{"alt":1836,"className":1837,"src":1838},"Liste des équipements",[126],"\u002FProgrammation\u002Fsportequipement\u002Fimage1.png",[12,1840,1841],{},"Sur l'écran détail d'un équipement, on y voit les informations détaillées de l'équipement mais aussi la liste des\ncomposants et l'usure des composants.",[12,1843,1844],{},"Un composant peut être usé par le kilométrage (ex: chaîne, pneu) ou par le temps (ex: liquide de frein).",[12,1846,1847],{},[121,1848],{"alt":1849,"className":1850,"src":1851},"Liste des composants \u002F Détail",[126],"\u002FProgrammation\u002Fsportequipement\u002Fimage2.png",[12,1853,1854],{},"Sur l'écran d'activité on récupère la liste des activités synchronisées depuis Strava ou Health Connect (pour l'instant\nune fois par jour). On peut créer des activités manuellement aussi si besoin. C'est l'ajout ou la suppression d'une activité\nqui impacte l'usure des équipements.",[12,1856,1857],{},"Les activités sont rattachées aux équipements via des types d'activités (l'équipement est attaché à un type d'activité,\net l'activité aussi).",[12,1859,1860],{},[121,1861],{"alt":1862,"className":1863,"src":1864},"Liste des activités",[126],"\u002FProgrammation\u002Fsportequipement\u002Fimage3.png",[12,1866,1867,1868,1871,1872,179],{},"Mais si je vous écris aujourd'hui, ce n'est pas seulement pour vous présenter l'outil. C'est surtout pour vous raconter\n",[362,1869,1870],{},"comment"," je l'ai développé. J'ai voulu tester le fameux ",[155,1873,1874],{},"\"Vibe Coding\"",[128,1876,1878],{"id":1877},"développement-en-vibe-coding","Développement en Vibe Coding",[12,1880,1881],{},"Je souhaitais développer mon application rapidement. Je voulais également avoir ce que les agents IA avaient dans leurs\nneurones. En effet, j'ai une licence Github Copilot que j'utilisais principalement en mode auto-completion de code, un\npeu pour discuter, et parfois pour modifier du code sur un projet déjà existant. J'ai déjà \"Vibe Codé\" des scripts sans\nimpact dont le but était de faire des exécutions one-shot.",[12,1883,1884],{},"Sur ce projet, je me suis dit :",[1886,1887,1888,1896],"blockquote",{},[12,1889,1890,1891,1895],{},"Je ne suis plus développeur, je suis le chef de projet et le product owner. Je décris la fonctionnalité que je veux, et\nl'IA code pour moi. Je teste et si ça ne compile pas ou si ça ne marche pas comme je veux je fais un retour ",[1892,1893,1894],"del",{},"au\ndeveloppeur"," à l'IA.",[12,1897,1898],{},"A la fin je regarderai le code généré et je verrai ce que l'IA vaut.",[12,1900,1901],{},"J'ai donc commencé à mettre différents types d'instructions :",[506,1903,1904,1911,1918],{},[370,1905,1906],{},[49,1907,1910],{"href":1908,"rel":1909},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment\u002Fsrc\u002Fbranch\u002Fmain\u002F.github\u002Fcopilot-instructions.md",[347],"Expert en Kotlin et développement Android",[370,1912,1913],{},[49,1914,1917],{"href":1915,"rel":1916},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment\u002Fsrc\u002Fbranch\u002Fmain\u002F.github\u002Fprompts\u002Fui_ux.prompt.md",[347],"Agent expert en UI\u002FUX",[370,1919,1920],{},[49,1921,1924],{"href":1922,"rel":1923},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment\u002Fsrc\u002Fbranch\u002Fmain\u002F.github\u002Fprompts\u002Fdocumentation.prompt.md",[347],"Expert en documentation technique",[12,1926,1927],{},"Ces différents experts sont inspirés de prompts trouvés sur Internet et adaptés, modifiés et\u002Fou générés à l'aide de Github\nCopilot également.",[12,1929,1930],{},"Ensuite j'ai décrit mon projet en quelques lignes dans le prompt. Je lui ai demandé de me faire un plan, puis de le\ndévelopper.",[12,1932,1933,1934,1937],{},"Les deux premières semaines ont été grisantes. J'ai développé ",[155,1935,1936],{},"80% du logiciel"," en un temps record. Je regardais l'IA\ncoder, je cliquais sur le bouton continuer ou approuver (les outils MCP, le build, ...) et je jouais aux jeux vidéo pendant ce\ntemps.",[12,1939,1940],{},"Pour info les MCP c'est \"Model Context Protocol\", cela permet à Github Copilot d'accéder à des outils comme\nl'execution des commandes, la recherche sur internet, ...",[12,1942,1943],{},"Ma routine était simple : je demandais une fonctionnalité, l'IA générait le code, je compilais (pendant ce temps je\nfaisais autre chose : jeux vidéos, un autre programme ...). Une fois terminé, je lançais android studio, je compilais,\nje testais.",[12,1945,1946],{},"Si ça marchait, je passais à la fonctionnalité suivante. Si ça ne marchait pas, je donnais un retour à l'IA en lui\nexpliquant le problème (erreur de compilation, bug, comportement non conforme) et c'était reparti pour un tour.",[12,1948,1949],{},"Régulièrement je devais réinitialiser la conversation, je demandais donc à l'IA de mettre à jour le plan pour reprendre\ndans une nouvelle conversation. Généralement, je faisais cela quand l'IA commençait à faire n'importe quoi (oubli de ce\nsur quoi on travaillait initialement, pataugeait dans la semoule, ...). Les prémisses du pétage de plombs de l'IA passaient\nsouvent par un passage du français à l'anglais dans les réponses. La signification était probablement la perte du\ncontexte initial et des instructions de démarrage.",[12,1951,1952],{},"Au début, Copilot m'a pondu une architecture logicielle complexe, découpée en couches bien distinctes. Sur le papier,\nc'était beau (ça vient des instructions expert en développement kotlin qui demande une Clean architecture avec une\nséparation : Domain, Data, Feature, Core).",[12,1954,1955],{},"De mon coté, pour un projet perso, si j'avais dû développer le projet moi-même de zéro, j'aurais probablement fait\nbeaucoup plus simple et direct. Probablement une application avec des vues, une couche service et une couche DAO dans le\nmême projet. Mais là, c'était l'IA qui gérait l'architecture, donc je laissais faire. J'avançais à l'aveugle.",[128,1957,1959],{"id":1958},"et-là-cest-le-drame","Et là, c'est le drame",[12,1961,1962],{},"Au bout de deux semaines, patatras. Je reçois une notification : je n'ai plus de crédits GitHub Copilot pour les\nrequêtes premium. Panne sèche.",[12,1964,1965],{},"J'ai bien tenté de reprendre avec un GPT4.1 ou GPT4o qui sont inclus en illimité, mais quand on a testé Gemini 3 ou\nClaude Sonnet 4.... qu'est que GPT4 est con....",[12,1967,1968],{},"Je me retrouve seul face à mon IDE, sans l'aide de copilot. J'étais le 15 du mois et il me restait 2 semaines avant le\nrenouvellement. Je me suis dit :",[1886,1970,1971],{},[12,1972,1973],{},"Bon, c'est l'occasion de regarder un peu ce qu'il m'a écrit en détail et de faire une revue de code.",[12,1975,1976],{},"J'ai ouvert les fichiers. Et là... j'ai eu du sang qui m'a coulé des yeux, la cervelle qui m'est sortie par les oreilles ...",[12,1978,1979],{},"Le MCD (Modèle Conceptuel de Données) était inutilement complexe. Il y avait de la duplication de code partout. Pour\nfaire la même chose, l'IA avait parfois utilisé trois méthodes différentes à trois endroits du code, sans aucune\ncohérence globale. Il y avait du code mort, des variables inutilisées, des détours techniques incompréhensibles.",[12,1981,1982],{},"C'était fonctionnel, oui. Mais c'était sale. Très sale.",[12,1984,1985],{},"J'ai donc commencé à lister tous les problèmes architecturaux que j'ai vus dans le code (et attention il y en a\nprobablement d'autres). La suppression du code mort, je l'ai faite moi-même car à chaque fois l'IA n'était pas capable\nde le détecter si je ne lui donne pas le nom de la méthode.",[128,1987,1989],{"id":1988},"le-grand-nettoyage","Le grand nettoyage",[12,1991,1992],{},"Dès que mes crédits ont été renouvelés, j'ai voulu tout nettoyer et donner à l'IA l'ensemble de ma revue de code.\nOptimiste, j'ai donné le grand listing de tout ce qui n'allait pas et de demander à l'IA une correction globale.",[12,1994,1995],{},"Échec total. C'était trop pour elle. Elle se perdait dans les corrections, me disait que tout était fait, mais seule une\npartie était terminée....",[12,1997,1998],{},"J'ai dû changer de stratégie. J'ai repris mon \"Vibe Coding\", mais cette fois-ci pour réparer les dégâts, problème par\nproblème, couche par couche (domaine, puis data, puis feature, puis core).",[12,2000,2001],{},"Puis je suis repassé en mode petite itération pour terminer les dernières fonctionnalités, corriger les derniers bugs. Et\nj'ai parfois dû mettre les mains dans le cambouis moi-même pour corriger des problèmes que l'IA n'arrivait pas à\nrésoudre.",[12,2003,2004,2005,2008],{},"Résultat des courses : j'avais fait 80% du projet (le prototype sale) en 2 semaines. Il m'a fallu ",[155,2006,2007],{},"un mois entier","\npour faire les 20% restants et nettoyer la base de code pour la rendre un peu plus propre.",[128,2010,1621],{"id":1620},[12,2012,2013],{},"Aujourd'hui, l'application est sur le store Google, elle fonctionne bien, et elle répond à mon besoin initial.",[12,2015,2016],{},"Mais je ressors de cette expérience avec un sentiment mitigé. J'ai un étrange détachement vis-à-vis de ce projet.",[12,2018,2019],{},"J'ai l'impression d'avoir codé avec un stagiaire qui a de très bonnes connaissances théoriques sur l'architecture logicielle\nmais qui sans contrôle te crée une dette technique immense. Par contre il a l'avantage d'écrire très vite. Ce projet qui\na été fait en un mois - un mois et demi, m'aurait pris beaucoup plus de temps (sachant que je ne développe en perso que\nle soir et le week-end).",[12,2021,2022,2023,2026],{},"D'habitude, quand je code un logiciel, c'est mon \"bébé\". Je connais chaque ligne, chaque astuce, chaque défaut. Là...\nj'ai l'impression que ce n'est pas ",[155,2024,2025],{},"mon"," projet. Je me sens comme le Chef de Projet ou le client qui a passé commande.",[12,2028,2029,2030,2033],{},"Pour les petits scripts, les projets ",[155,2031,2032],{},"utilitaires",", l'intelligence artificielle est super pratique. Elle permet de\nconstruire rapidement un prototype fonctionnel. Mais sans maîtrise, le code n'est pas maintenable dans le temps (même\npar l'IA elle-même). Et je ne parle pas non plus des problématiques de sécurité, si j'avais un serveur sur cette\napplication.",[12,2035,2036],{},"Pour les projets de passionnés, ceux qu'on fait \"pour l'art\" ou pour le plaisir d'apprendre, je ne suis pas sûr de\nréitérer l'expérience aussi poussée. J'aime développer et c'est important pour moi de toucher la ligne de code.",[12,2038,2039],{},"J'ai aussi une autre inquiétude par rapport à l'avenir du métier de développeur. Un bon développeur pour l'instant est\ntoujours nécessaire pour superviser l'intelligence artificielle.",[12,2041,2042],{},"Mais les juniors qui arrivent sur le marché du travail vont-ils apprendre les bonnes pratiques de développement s'ils\nse reposent trop sur l'IA pour coder à leur place ? Est-ce que les juniors vont trouver du travail, même s'ils sont\ncompétents ?",[12,2044,2045],{},"Actuellement un développeur qui n'utilise pas l'IA risque de se retrouver à un moment dépassé sur le marché du travail.\nCe que j'aimais avec le développement c'est qu'avant n'importe qui pouvait faire du développement avec n'importe quel\nPC. Il y avait un coût d'apprentissage, tout le monde n'est pas fait pour faire du développement, mais c'était accessible\nfinancièrement aux personnes motivées.",[12,2047,2048],{},"Maintenant, il faut soit une très grosse carte graphique (et même là ce ne sont pas des modèles ultra performants), ou\nun abonnement payant. Il y a quelques versions gratuites mais elles sont là pour attirer. L'IA rend le développement plus\naccessible au niveau apprentissage, mais moins accessible financièrement et surtout dépendant d'un fournisseur.",{"title":1646,"searchDepth":1647,"depth":1647,"links":2050},[2051,2052,2053,2054,2055,2056],{"id":1803,"depth":1647,"text":1804},{"id":1827,"depth":1647,"text":1828},{"id":1877,"depth":1647,"text":1878},{"id":1958,"depth":1647,"text":1959},{"id":1988,"depth":1647,"text":1989},{"id":1620,"depth":1647,"text":1621},"Programmation","programmation","2026-01-29",{"type":9,"value":2061},[2062,2064,2066,2068,2070],[12,2063,14],{},[12,2065,1788],{},[12,2067,1791],{},[12,2069,1794],{},[12,2071,1797,2072,179],{},[155,2073,1800],{},{"planet":1767},"\u002Fpost\u002Fsportequipement-vibe-coding",{"title":1781,"description":14},"sportequipement-vibe-coding","posts\u002FProgrammation\u002F2026-01-29-sportequipement-vibe-coding",[2080,2081,2082,2083,2084],"developpement","ia","vibe coding","rex","android",10,"UjPK0msj1uJoiDCVdG4PiDDZUq_PLpB0i2SzFIOesPI",{"id":2088,"title":2089,"author":7,"body":2090,"category":2057,"categorySlug":2058,"date":3363,"description":14,"excerpt":3364,"extension":1764,"location":1765,"meta":3504,"navigation":1767,"path":3505,"published":1767,"seo":3506,"slug":3507,"stem":3508,"tags":3509,"timeToRead":2241,"__hash__":3512},"posts\u002Fposts\u002FProgrammation\u002F2024-06-14-max-old-space.md","Quelle est la valeur par défaut de max-old-space-size dans NodeJS ?",{"type":9,"value":2091,"toc":3361},[2092,2094,2101,2104,2285,2288,2294,2309,2312,2315,2351,2354,2359,2368,2371,2374,2385,2600,2609,2618,2788,2796,2938,2954,3251,3254,3280,3286,3289,3292,3295,3354,3357],[12,2093,14],{},[12,2095,2096,2097,2100],{},"Lors de nos développements en NodeJS, il arrive parfois que l'on se retrouve confronté à des erreurs. Certaines de ces\nerreurs ne se reproduisent pas en local, mais uniquement sur un environnement distant. C'est ce qui nous est arrivé\nrécemment lors de l'exécution de la commande ",[184,2098,2099],{},"npm ci"," sur la chaîne de déploiement continue.",[12,2102,2103],{},"L'erreur que nous avons rencontrée est la suivante :",[2105,2106,2110],"pre",{"className":2107,"code":2108,"language":2109,"meta":1646,"style":1646},"language-shell shiki shiki-themes one-dark-pro","\u003C--- Last few GCs --->\n\n[14040:0x56930a0]    96150 ms: Mark-Compact 2012.5 (2093.0) -> 2011.8 (2092.0) MB, 902.72 \u002F 17.13 ms  (average mu = 0.416, current mu = 0.217) allocation failure; scavenge might not succeed\n[14040:0x56930a0]    97002 ms: Mark-Compact 2019.6 (2092.0) -> 2019.1 (2112.8) MB, 843.10 \u002F 0.00 ms  (average mu = 0.251, current mu = 0.011) allocation failure; scavenge might not succeed\n\n\u003C--- JS stacktrace --->\n\nFATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory\n","shell",[184,2111,2112,2121,2126,2183,2228,2233,2239,2244],{"__ignoreMap":1646},[2113,2114,2117],"span",{"class":2115,"line":2116},"line",1,[2113,2118,2120],{"class":2119},"sn6KH","\u003C--- Last few GCs --->\n",[2113,2122,2123],{"class":2115,"line":1647},[2113,2124,2125],{"emptyLinePlaceholder":1767},"\n",[2113,2127,2128,2131,2135,2138,2141,2144,2147,2151,2154,2157,2160,2162,2164,2168,2171,2174,2177,2180],{"class":2115,"line":1652},[2113,2129,2130],{"class":2119},"[14040:0x56930a0]    96150 ms: Mark-Compact 2012.5 (",[2113,2132,2134],{"class":2133},"sVbv2","2093.0",[2113,2136,2137],{"class":2119},") -> 2011.8 (",[2113,2139,2140],{"class":2133},"2092.0",[2113,2142,2143],{"class":2119},") MB, 902.72 \u002F 17.13 ms  (",[2113,2145,2146],{"class":2133},"average",[2113,2148,2150],{"class":2149},"subq3"," mu",[2113,2152,2153],{"class":2149}," =",[2113,2155,2156],{"class":2149}," 0.416,",[2113,2158,2159],{"class":2149}," current",[2113,2161,2150],{"class":2149},[2113,2163,2153],{"class":2149},[2113,2165,2167],{"class":2166},"sVC51"," 0.217",[2113,2169,2170],{"class":2119},") allocation failure; ",[2113,2172,2173],{"class":2133},"scavenge",[2113,2175,2176],{"class":2149}," might",[2113,2178,2179],{"class":2149}," not",[2113,2181,2182],{"class":2149}," succeed\n",[2113,2184,2186,2189,2191,2194,2197,2200,2202,2204,2206,2209,2211,2213,2215,2218,2220,2222,2224,2226],{"class":2115,"line":2185},4,[2113,2187,2188],{"class":2119},"[14040:0x56930a0]    97002 ms: Mark-Compact 2019.6 (",[2113,2190,2140],{"class":2133},[2113,2192,2193],{"class":2119},") -> 2019.1 (",[2113,2195,2196],{"class":2133},"2112.8",[2113,2198,2199],{"class":2119},") MB, 843.10 \u002F 0.00 ms  (",[2113,2201,2146],{"class":2133},[2113,2203,2150],{"class":2149},[2113,2205,2153],{"class":2149},[2113,2207,2208],{"class":2149}," 0.251,",[2113,2210,2159],{"class":2149},[2113,2212,2150],{"class":2149},[2113,2214,2153],{"class":2149},[2113,2216,2217],{"class":2166}," 0.011",[2113,2219,2170],{"class":2119},[2113,2221,2173],{"class":2133},[2113,2223,2176],{"class":2149},[2113,2225,2179],{"class":2149},[2113,2227,2182],{"class":2149},[2113,2229,2231],{"class":2115,"line":2230},5,[2113,2232,2125],{"emptyLinePlaceholder":1767},[2113,2234,2236],{"class":2115,"line":2235},6,[2113,2237,2238],{"class":2119},"\u003C--- JS stacktrace --->\n",[2113,2240,2242],{"class":2115,"line":2241},7,[2113,2243,2125],{"emptyLinePlaceholder":1767},[2113,2245,2247,2250,2253,2256,2259,2262,2265,2268,2271,2274,2276,2279,2282],{"class":2115,"line":2246},8,[2113,2248,2249],{"class":2133},"FATAL",[2113,2251,2252],{"class":2149}," ERROR:",[2113,2254,2255],{"class":2149}," Reached",[2113,2257,2258],{"class":2149}," heap",[2113,2260,2261],{"class":2149}," limit",[2113,2263,2264],{"class":2149}," Allocation",[2113,2266,2267],{"class":2149}," failed",[2113,2269,2270],{"class":2149}," -",[2113,2272,2273],{"class":2149}," JavaScript",[2113,2275,2258],{"class":2149},[2113,2277,2278],{"class":2149}," out",[2113,2280,2281],{"class":2149}," of",[2113,2283,2284],{"class":2149}," memory\n",[12,2286,2287],{},"Cette erreur se produit lorsque la mémoire allouée pour NodeJS est insuffisante (le garbage collector de NodeJS n'arrive\npas à nettoyer la mémoire). Pour résoudre ce problème, l'une des possibilités est d'augmenter la mémoire allouée à NodeJS.",[12,2289,2290,2291,2293],{},"Une autre possibilité est de comprendre pourquoi le programme a besoin d'autant de mémoire et de le corriger, car\nle problème peut venir d'une fuite de mémoire. Dans notre cas, c'est la commande ",[184,2292,2099],{}," qui est en cause. Et la\nconsommation de mémoire dépend du nombre de paquets que nous avons en dépendance (et qui eux-mêmes en ont).",[12,2295,2296,2297,2300,2301,2304,2305,2308],{},"Pour augmenter la mémoire allouée à NodeJS, il faut ajouter l'option ",[184,2298,2299],{},"--max-old-space-size"," à la commande ",[184,2302,2303],{},"node",".\nOn peut également modifier la variable d'environnement ",[184,2306,2307],{},"NODE_OPTIONS"," pour définir la taille de la mémoire allouée à\nNodeJS.",[12,2310,2311],{},"Lors de la modification de la mémoire allouée à NodeJS, il est important de prendre en compte la mémoire disponible sur\nla machine et la mémoire utilisée par les autres processus.",[12,2313,2314],{},"Voici un exemple pour augmenter la mémoire allouée à NodeJS à 4 Go :",[2105,2316,2320],{"className":2317,"code":2318,"language":2319,"meta":1646,"style":1646},"language-bash shiki shiki-themes one-dark-pro","export NODE_OPTIONS=--max-old-space-size=4096\nnpm ci\n","bash",[184,2321,2322,2343],{"__ignoreMap":1646},[2113,2323,2324,2328,2332,2336,2338,2340],{"class":2115,"line":2116},[2113,2325,2327],{"class":2326},"seHd6","export",[2113,2329,2331],{"class":2330},"sVyAn"," NODE_OPTIONS",[2113,2333,2335],{"class":2334},"sjrmR","=",[2113,2337,2299],{"class":2330},[2113,2339,2335],{"class":2334},[2113,2341,2342],{"class":2166},"4096\n",[2113,2344,2345,2348],{"class":2115,"line":1647},[2113,2346,2347],{"class":2133},"npm",[2113,2349,2350],{"class":2149}," ci\n",[12,2352,2353],{},"Mais avant d'augmenter la mémoire allouée à NodeJS, nous allons nous poser une question :",[1886,2355,2356],{},[12,2357,2358],{},"Pourquoi ai-je le problème sur la chaîne de déploiement et pas sur mon poste en local, malgré le fait que la version\nde NodeJS soit identique ?",[12,2360,2361,2362,2367],{},"Pour répondre à cette question, nous avons commencé par une petite recherche sur le grand réseau. Nous avons trouvé un\narticle très intéressant sur le site ",[49,2363,2366],{"href":2364,"rel":2365},"https:\u002F\u002Fmedium.com\u002Fgeekculture\u002Fnode-js-default-memory-settings-3c0fe8a9ba1",[347],"Medium",".\nDans cet article, une expérimentation a été faite pour déterminer la mémoire allouée par défaut à NodeJS en fonction de\nla version de NodeJS.",[12,2369,2370],{},"Cela montre que dans la version de NodeJS 20 que nous utilisons, la limite devrait être de 4Go par défaut. Mais\npourquoi avons-nous cette erreur, sachant que dans le message d'erreur, la mémoire allouée est de 2Go ?",[12,2372,2373],{},"Pour trouver plus d'informations, nous avons dû nous plonger dans le code source de NodeJS, qui embarque lui-même le code\nsource du moteur V8. Nous allons nous concentrer sur la dernière version de NodeJS.",[12,2375,2376,2377,2380,2381,2384],{},"Pour trouver plus d'informations, nous avons essayé de rechercher dans le code les endroits qui parlent de max_old_space_size. Cette\nrecherche m'a amené à la fonction ",[184,2378,2379],{},"MaxOldGenerationSize"," dans le fichier ",[184,2382,2383],{},"deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.cc"," :",[2105,2386,2390],{"className":2387,"code":2388,"language":2389,"meta":1646,"style":1646},"language-cpp shiki shiki-themes one-dark-pro","\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.cc\n\n\u002F\u002F line 362\nsize_t Heap::MaxOldGenerationSize(uint64_t physical_memory) {\n  size_t max_size = V8HeapTrait::kMaxSize;\n  \u002F\u002F Increase the heap size from 2GB to 4GB for 64-bit systems with physical\n  \u002F\u002F memory at least 16GB. The theshold is set to 15GB to accomodate for some\n  \u002F\u002F memory being reserved by the hardware.\n  constexpr bool x64_bit = Heap::kHeapLimitMultiplier >= 2;\n  if (v8_flags.huge_max_old_generation_size && x64_bit &&\n      (physical_memory \u002F GB) >= 15) {\n    DCHECK_EQ(max_size \u002F GB, 2u);\n    max_size *= 2;\n  }\n  return std::min(max_size, AllocatorLimitOnMaxOldGenerationSize());\n}\n","cpp",[184,2391,2392,2398,2402,2407,2434,2447,2452,2457,2462,2488,2512,2531,2554,2567,2573,2594],{"__ignoreMap":1646},[2113,2393,2394],{"class":2115,"line":2116},[2113,2395,2397],{"class":2396},"sV9Aq","\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.cc\n",[2113,2399,2400],{"class":2115,"line":1647},[2113,2401,2125],{"emptyLinePlaceholder":1767},[2113,2403,2404],{"class":2115,"line":1652},[2113,2405,2406],{"class":2396},"\u002F\u002F line 362\n",[2113,2408,2409,2412,2416,2419,2421,2424,2427,2431],{"class":2115,"line":2185},[2113,2410,2411],{"class":2326},"size_t",[2113,2413,2415],{"class":2414},"sU0A5"," Heap",[2113,2417,2418],{"class":2119},"::",[2113,2420,2379],{"class":2133},[2113,2422,2423],{"class":2119},"(",[2113,2425,2426],{"class":2326},"uint64_t",[2113,2428,2430],{"class":2429},"s_ZVi"," physical_memory",[2113,2432,2433],{"class":2119},") {\n",[2113,2435,2436,2439,2442,2444],{"class":2115,"line":2230},[2113,2437,2438],{"class":2326},"  size_t",[2113,2440,2441],{"class":2119}," max_size ",[2113,2443,2335],{"class":2326},[2113,2445,2446],{"class":2119}," V8HeapTrait::kMaxSize;\n",[2113,2448,2449],{"class":2115,"line":2235},[2113,2450,2451],{"class":2396},"  \u002F\u002F Increase the heap size from 2GB to 4GB for 64-bit systems with physical\n",[2113,2453,2454],{"class":2115,"line":2241},[2113,2455,2456],{"class":2396},"  \u002F\u002F memory at least 16GB. The theshold is set to 15GB to accomodate for some\n",[2113,2458,2459],{"class":2115,"line":2246},[2113,2460,2461],{"class":2396},"  \u002F\u002F memory being reserved by the hardware.\n",[2113,2463,2465,2468,2471,2474,2476,2479,2482,2485],{"class":2115,"line":2464},9,[2113,2466,2467],{"class":2326},"  constexpr",[2113,2469,2470],{"class":2326}," bool",[2113,2472,2473],{"class":2119}," x64_bit ",[2113,2475,2335],{"class":2326},[2113,2477,2478],{"class":2119}," Heap::kHeapLimitMultiplier ",[2113,2480,2481],{"class":2326},">=",[2113,2483,2484],{"class":2166}," 2",[2113,2486,2487],{"class":2119},";\n",[2113,2489,2490,2493,2496,2499,2501,2504,2507,2509],{"class":2115,"line":2085},[2113,2491,2492],{"class":2326},"  if",[2113,2494,2495],{"class":2119}," (",[2113,2497,2498],{"class":2414},"v8_flags",[2113,2500,179],{"class":2119},[2113,2502,2503],{"class":2330},"huge_max_old_generation_size",[2113,2505,2506],{"class":2334}," &&",[2113,2508,2473],{"class":2119},[2113,2510,2511],{"class":2334},"&&\n",[2113,2513,2515,2518,2521,2524,2526,2529],{"class":2115,"line":2514},11,[2113,2516,2517],{"class":2119},"      (physical_memory ",[2113,2519,2520],{"class":2326},"\u002F",[2113,2522,2523],{"class":2119}," GB) ",[2113,2525,2481],{"class":2326},[2113,2527,2528],{"class":2166}," 15",[2113,2530,2433],{"class":2119},[2113,2532,2534,2537,2540,2542,2545,2548,2551],{"class":2115,"line":2533},12,[2113,2535,2536],{"class":2133},"    DCHECK_EQ",[2113,2538,2539],{"class":2119},"(max_size ",[2113,2541,2520],{"class":2326},[2113,2543,2544],{"class":2119}," GB, ",[2113,2546,2547],{"class":2166},"2",[2113,2549,2550],{"class":2330},"u",[2113,2552,2553],{"class":2119},");\n",[2113,2555,2557,2560,2563,2565],{"class":2115,"line":2556},13,[2113,2558,2559],{"class":2119},"    max_size ",[2113,2561,2562],{"class":2326},"*=",[2113,2564,2484],{"class":2166},[2113,2566,2487],{"class":2119},[2113,2568,2570],{"class":2115,"line":2569},14,[2113,2571,2572],{"class":2119},"  }\n",[2113,2574,2576,2579,2582,2585,2588,2591],{"class":2115,"line":2575},15,[2113,2577,2578],{"class":2326},"  return",[2113,2580,2581],{"class":2119}," std::",[2113,2583,2584],{"class":2133},"min",[2113,2586,2587],{"class":2119},"(max_size, ",[2113,2589,2590],{"class":2133},"AllocatorLimitOnMaxOldGenerationSize",[2113,2592,2593],{"class":2119},"());\n",[2113,2595,2597],{"class":2115,"line":2596},16,[2113,2598,2599],{"class":2119},"}\n",[12,2601,2602,2603,792,2606,179],{},"Pour pouvoir rassembler les morceaux, nous devons donc connaître ",[184,2604,2605],{},"V8HeapTrait::kMaxSize",[184,2607,2608],{},"Heap::kHeapLimitMultiplier",[12,2610,2611,2612,2614,2615,2384],{},"La valeur de ",[184,2613,2605],{}," est définie dans le fichier ",[184,2616,2617],{},"deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap-controller.h",[2105,2619,2621],{"className":2387,"code":2620,"language":2389,"meta":1646,"style":1646},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap-controller.h\n\n\u002F\u002F line 16\nstruct BaseControllerTrait {\n  static constexpr size_t kMinSize = 128u * Heap::kHeapLimitMultiplier * MB;\n  static constexpr size_t kMaxSize = 1024u * Heap::kHeapLimitMultiplier * MB;\n\n  static constexpr double kMinGrowingFactor = 1.1;\n  static constexpr double kMaxGrowingFactor = 4.0;\n  static constexpr double kConservativeGrowingFactor = 1.3;\n  static constexpr double kTargetMutatorUtilization = 0.97;\n};\n",[184,2622,2623,2628,2632,2637,2648,2680,2706,2710,2729,2747,2765,2783],{"__ignoreMap":1646},[2113,2624,2625],{"class":2115,"line":2116},[2113,2626,2627],{"class":2396},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap-controller.h\n",[2113,2629,2630],{"class":2115,"line":1647},[2113,2631,2125],{"emptyLinePlaceholder":1767},[2113,2633,2634],{"class":2115,"line":1652},[2113,2635,2636],{"class":2396},"\u002F\u002F line 16\n",[2113,2638,2639,2642,2645],{"class":2115,"line":2185},[2113,2640,2641],{"class":2326},"struct",[2113,2643,2644],{"class":2414}," BaseControllerTrait",[2113,2646,2647],{"class":2119}," {\n",[2113,2649,2650,2653,2656,2659,2662,2664,2667,2669,2672,2674,2677],{"class":2115,"line":2230},[2113,2651,2652],{"class":2326},"  static",[2113,2654,2655],{"class":2326}," constexpr",[2113,2657,2658],{"class":2326}," size_t",[2113,2660,2661],{"class":2119}," kMinSize ",[2113,2663,2335],{"class":2326},[2113,2665,2666],{"class":2166}," 128",[2113,2668,2550],{"class":2330},[2113,2670,2671],{"class":2326}," *",[2113,2673,2478],{"class":2119},[2113,2675,2676],{"class":2326},"*",[2113,2678,2679],{"class":2119}," MB;\n",[2113,2681,2682,2684,2686,2688,2691,2693,2696,2698,2700,2702,2704],{"class":2115,"line":2235},[2113,2683,2652],{"class":2326},[2113,2685,2655],{"class":2326},[2113,2687,2658],{"class":2326},[2113,2689,2690],{"class":2119}," kMaxSize ",[2113,2692,2335],{"class":2326},[2113,2694,2695],{"class":2166}," 1024",[2113,2697,2550],{"class":2330},[2113,2699,2671],{"class":2326},[2113,2701,2478],{"class":2119},[2113,2703,2676],{"class":2326},[2113,2705,2679],{"class":2119},[2113,2707,2708],{"class":2115,"line":2241},[2113,2709,2125],{"emptyLinePlaceholder":1767},[2113,2711,2712,2714,2716,2719,2722,2724,2727],{"class":2115,"line":2246},[2113,2713,2652],{"class":2326},[2113,2715,2655],{"class":2326},[2113,2717,2718],{"class":2326}," double",[2113,2720,2721],{"class":2119}," kMinGrowingFactor ",[2113,2723,2335],{"class":2326},[2113,2725,2726],{"class":2166}," 1.1",[2113,2728,2487],{"class":2119},[2113,2730,2731,2733,2735,2737,2740,2742,2745],{"class":2115,"line":2464},[2113,2732,2652],{"class":2326},[2113,2734,2655],{"class":2326},[2113,2736,2718],{"class":2326},[2113,2738,2739],{"class":2119}," kMaxGrowingFactor ",[2113,2741,2335],{"class":2326},[2113,2743,2744],{"class":2166}," 4.0",[2113,2746,2487],{"class":2119},[2113,2748,2749,2751,2753,2755,2758,2760,2763],{"class":2115,"line":2085},[2113,2750,2652],{"class":2326},[2113,2752,2655],{"class":2326},[2113,2754,2718],{"class":2326},[2113,2756,2757],{"class":2119}," kConservativeGrowingFactor ",[2113,2759,2335],{"class":2326},[2113,2761,2762],{"class":2166}," 1.3",[2113,2764,2487],{"class":2119},[2113,2766,2767,2769,2771,2773,2776,2778,2781],{"class":2115,"line":2514},[2113,2768,2652],{"class":2326},[2113,2770,2655],{"class":2326},[2113,2772,2718],{"class":2326},[2113,2774,2775],{"class":2119}," kTargetMutatorUtilization ",[2113,2777,2335],{"class":2326},[2113,2779,2780],{"class":2166}," 0.97",[2113,2782,2487],{"class":2119},[2113,2784,2785],{"class":2115,"line":2533},[2113,2786,2787],{"class":2119},"};\n",[12,2789,2790,2791,2614,2793,2384],{},"Enfin, la valeur de ",[184,2792,2608],{},[184,2794,2795],{},"deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.h",[2105,2797,2799],{"className":2387,"code":2798,"language":2389,"meta":1646,"style":1646},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.h\n\n\u002F\u002F line 306\n#if V8_OS_ANDROID\n  \u002F\u002F Don't apply pointer multiplier on Android since it has no swap space and\n  \u002F\u002F should instead adapt it's heap size based on available physical memory.\n  static const int kPointerMultiplier = 1;\n  static const int kHeapLimitMultiplier = 1;\n#else\n  static const int kPointerMultiplier = kTaggedSize \u002F 4;\n  \u002F\u002F The heap limit needs to be computed based on the system pointer size\n  \u002F\u002F because we want a pointer-compressed heap to have larger limit than\n  \u002F\u002F an ordinary 32-bit which that is constrained by 2GB virtual address space.\n  static const int kHeapLimitMultiplier = kSystemPointerSize \u002F 4;\n#endif\n",[184,2800,2801,2806,2810,2815,2823,2828,2833,2853,2870,2875,2897,2902,2907,2912,2933],{"__ignoreMap":1646},[2113,2802,2803],{"class":2115,"line":2116},[2113,2804,2805],{"class":2396},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.h\n",[2113,2807,2808],{"class":2115,"line":1647},[2113,2809,2125],{"emptyLinePlaceholder":1767},[2113,2811,2812],{"class":2115,"line":1652},[2113,2813,2814],{"class":2396},"\u002F\u002F line 306\n",[2113,2816,2817,2820],{"class":2115,"line":2185},[2113,2818,2819],{"class":2326},"#if",[2113,2821,2822],{"class":2133}," V8_OS_ANDROID\n",[2113,2824,2825],{"class":2115,"line":2230},[2113,2826,2827],{"class":2396},"  \u002F\u002F Don't apply pointer multiplier on Android since it has no swap space and\n",[2113,2829,2830],{"class":2115,"line":2235},[2113,2831,2832],{"class":2396},"  \u002F\u002F should instead adapt it's heap size based on available physical memory.\n",[2113,2834,2835,2837,2840,2843,2846,2848,2851],{"class":2115,"line":2241},[2113,2836,2652],{"class":2326},[2113,2838,2839],{"class":2326}," const",[2113,2841,2842],{"class":2326}," int",[2113,2844,2845],{"class":2119}," kPointerMultiplier ",[2113,2847,2335],{"class":2326},[2113,2849,2850],{"class":2166}," 1",[2113,2852,2487],{"class":2119},[2113,2854,2855,2857,2859,2861,2864,2866,2868],{"class":2115,"line":2246},[2113,2856,2652],{"class":2326},[2113,2858,2839],{"class":2326},[2113,2860,2842],{"class":2326},[2113,2862,2863],{"class":2119}," kHeapLimitMultiplier ",[2113,2865,2335],{"class":2326},[2113,2867,2850],{"class":2166},[2113,2869,2487],{"class":2119},[2113,2871,2872],{"class":2115,"line":2464},[2113,2873,2874],{"class":2326},"#else\n",[2113,2876,2877,2879,2881,2883,2885,2887,2890,2892,2895],{"class":2115,"line":2085},[2113,2878,2652],{"class":2326},[2113,2880,2839],{"class":2326},[2113,2882,2842],{"class":2326},[2113,2884,2845],{"class":2119},[2113,2886,2335],{"class":2326},[2113,2888,2889],{"class":2119}," kTaggedSize ",[2113,2891,2520],{"class":2326},[2113,2893,2894],{"class":2166}," 4",[2113,2896,2487],{"class":2119},[2113,2898,2899],{"class":2115,"line":2514},[2113,2900,2901],{"class":2396},"  \u002F\u002F The heap limit needs to be computed based on the system pointer size\n",[2113,2903,2904],{"class":2115,"line":2533},[2113,2905,2906],{"class":2396},"  \u002F\u002F because we want a pointer-compressed heap to have larger limit than\n",[2113,2908,2909],{"class":2115,"line":2556},[2113,2910,2911],{"class":2396},"  \u002F\u002F an ordinary 32-bit which that is constrained by 2GB virtual address space.\n",[2113,2913,2914,2916,2918,2920,2922,2924,2927,2929,2931],{"class":2115,"line":2569},[2113,2915,2652],{"class":2326},[2113,2917,2839],{"class":2326},[2113,2919,2842],{"class":2326},[2113,2921,2863],{"class":2119},[2113,2923,2335],{"class":2326},[2113,2925,2926],{"class":2119}," kSystemPointerSize ",[2113,2928,2520],{"class":2326},[2113,2930,2894],{"class":2166},[2113,2932,2487],{"class":2119},[2113,2934,2935],{"class":2115,"line":2575},[2113,2936,2937],{"class":2326},"#endif\n",[12,2939,2611,2940,2943,2944,2947,2948,2951,2952,2384],{},[184,2941,2942],{},"kHeapLimitMultiplier"," dépend elle-même de ",[184,2945,2946],{},"kSystemPointerSize",", qui n'est pas définie dans ce fichier. C'est\ndans le fichier ",[184,2949,2950],{},"deps\u002Fv8\u002Fsrc\u002Fbase\u002Fplatform\u002Fplatform.h"," que nous allons trouver la valeur de ",[184,2953,2946],{},[2105,2955,2957],{"className":2387,"code":2956,"language":2389,"meta":1646,"style":1646},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fbase\u002Fplatform\u002Fplatform.h\n\n\u002F\u002F line 84\nV8_INLINE intptr_t InternalGetExistingThreadLocal(intptr_t index) {\n  const intptr_t kTibInlineTlsOffset = 0xE10;\n  const intptr_t kTibExtraTlsOffset = 0xF94;\n  const intptr_t kMaxInlineSlots = 64;\n  const intptr_t kMaxSlots = kMaxInlineSlots + 1024;\n  const intptr_t kSystemPointerSize = sizeof(void*);\n  DCHECK(0 \u003C= index && index \u003C kMaxSlots);\n  USE(kMaxSlots);\n  if (index \u003C kMaxInlineSlots) {\n    return static_cast\u003Cintptr_t>(\n        __readfsdword(kTibInlineTlsOffset + kSystemPointerSize * index));\n  }\n  intptr_t extra = static_cast\u003Cintptr_t>(__readfsdword(kTibExtraTlsOffset));\n  if (!extra) return 0;\n  return *reinterpret_cast\u003Cintptr_t*>(extra + kSystemPointerSize *\n                                                  (index - kMaxInlineSlots));\n}\n",[184,2958,2959,2964,2968,2973,2991,3012,3030,3046,3066,3086,3113,3121,3133,3147,3164,3168,3190,3211,3234,3246],{"__ignoreMap":1646},[2113,2960,2961],{"class":2115,"line":2116},[2113,2962,2963],{"class":2396},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fbase\u002Fplatform\u002Fplatform.h\n",[2113,2965,2966],{"class":2115,"line":1647},[2113,2967,2125],{"emptyLinePlaceholder":1767},[2113,2969,2970],{"class":2115,"line":1652},[2113,2971,2972],{"class":2396},"\u002F\u002F line 84\n",[2113,2974,2975,2978,2981,2984,2986,2988],{"class":2115,"line":2185},[2113,2976,2977],{"class":2119},"V8_INLINE ",[2113,2979,2980],{"class":2326},"intptr_t",[2113,2982,2983],{"class":2133}," InternalGetExistingThreadLocal",[2113,2985,2423],{"class":2119},[2113,2987,2980],{"class":2326},[2113,2989,2990],{"class":2119}," index) {\n",[2113,2992,2993,2996,2999,3002,3004,3007,3010],{"class":2115,"line":2230},[2113,2994,2995],{"class":2326},"  const",[2113,2997,2998],{"class":2326}," intptr_t",[2113,3000,3001],{"class":2119}," kTibInlineTlsOffset ",[2113,3003,2335],{"class":2326},[2113,3005,3006],{"class":2330}," 0x",[2113,3008,3009],{"class":2166},"E10",[2113,3011,2487],{"class":2119},[2113,3013,3014,3016,3018,3021,3023,3025,3028],{"class":2115,"line":2235},[2113,3015,2995],{"class":2326},[2113,3017,2998],{"class":2326},[2113,3019,3020],{"class":2119}," kTibExtraTlsOffset ",[2113,3022,2335],{"class":2326},[2113,3024,3006],{"class":2330},[2113,3026,3027],{"class":2166},"F94",[2113,3029,2487],{"class":2119},[2113,3031,3032,3034,3036,3039,3041,3044],{"class":2115,"line":2241},[2113,3033,2995],{"class":2326},[2113,3035,2998],{"class":2326},[2113,3037,3038],{"class":2119}," kMaxInlineSlots ",[2113,3040,2335],{"class":2326},[2113,3042,3043],{"class":2166}," 64",[2113,3045,2487],{"class":2119},[2113,3047,3048,3050,3052,3055,3057,3059,3062,3064],{"class":2115,"line":2246},[2113,3049,2995],{"class":2326},[2113,3051,2998],{"class":2326},[2113,3053,3054],{"class":2119}," kMaxSlots ",[2113,3056,2335],{"class":2326},[2113,3058,3038],{"class":2119},[2113,3060,3061],{"class":2326},"+",[2113,3063,2695],{"class":2166},[2113,3065,2487],{"class":2119},[2113,3067,3068,3070,3072,3074,3076,3079,3081,3084],{"class":2115,"line":2464},[2113,3069,2995],{"class":2326},[2113,3071,2998],{"class":2326},[2113,3073,2926],{"class":2119},[2113,3075,2335],{"class":2326},[2113,3077,3078],{"class":2326}," sizeof",[2113,3080,2423],{"class":2119},[2113,3082,3083],{"class":2326},"void*",[2113,3085,2553],{"class":2119},[2113,3087,3088,3091,3093,3096,3099,3102,3105,3107,3110],{"class":2115,"line":2085},[2113,3089,3090],{"class":2133},"  DCHECK",[2113,3092,2423],{"class":2119},[2113,3094,3095],{"class":2166},"0",[2113,3097,3098],{"class":2326}," \u003C=",[2113,3100,3101],{"class":2119}," index ",[2113,3103,3104],{"class":2334},"&&",[2113,3106,3101],{"class":2119},[2113,3108,3109],{"class":2326},"\u003C",[2113,3111,3112],{"class":2119}," kMaxSlots);\n",[2113,3114,3115,3118],{"class":2115,"line":2514},[2113,3116,3117],{"class":2133},"  USE",[2113,3119,3120],{"class":2119},"(kMaxSlots);\n",[2113,3122,3123,3125,3128,3130],{"class":2115,"line":2533},[2113,3124,2492],{"class":2326},[2113,3126,3127],{"class":2119}," (index ",[2113,3129,3109],{"class":2326},[2113,3131,3132],{"class":2119}," kMaxInlineSlots) {\n",[2113,3134,3135,3138,3141,3144],{"class":2115,"line":2556},[2113,3136,3137],{"class":2326},"    return",[2113,3139,3140],{"class":2119}," static_cast",[2113,3142,3143],{"class":2326},"\u003Cintptr_t>",[2113,3145,3146],{"class":2119},"(\n",[2113,3148,3149,3152,3155,3157,3159,3161],{"class":2115,"line":2569},[2113,3150,3151],{"class":2133},"        __readfsdword",[2113,3153,3154],{"class":2119},"(kTibInlineTlsOffset ",[2113,3156,3061],{"class":2326},[2113,3158,2926],{"class":2119},[2113,3160,2676],{"class":2326},[2113,3162,3163],{"class":2119}," index));\n",[2113,3165,3166],{"class":2115,"line":2575},[2113,3167,2572],{"class":2119},[2113,3169,3170,3173,3176,3178,3180,3182,3184,3187],{"class":2115,"line":2596},[2113,3171,3172],{"class":2326},"  intptr_t",[2113,3174,3175],{"class":2119}," extra ",[2113,3177,2335],{"class":2326},[2113,3179,3140],{"class":2119},[2113,3181,3143],{"class":2326},[2113,3183,2423],{"class":2119},[2113,3185,3186],{"class":2133},"__readfsdword",[2113,3188,3189],{"class":2119},"(kTibExtraTlsOffset));\n",[2113,3191,3193,3195,3197,3200,3203,3206,3209],{"class":2115,"line":3192},17,[2113,3194,2492],{"class":2326},[2113,3196,2495],{"class":2119},[2113,3198,3199],{"class":2334},"!",[2113,3201,3202],{"class":2119},"extra) ",[2113,3204,3205],{"class":2326},"return",[2113,3207,3208],{"class":2166}," 0",[2113,3210,2487],{"class":2119},[2113,3212,3214,3216,3218,3221,3224,3227,3229,3231],{"class":2115,"line":3213},18,[2113,3215,2578],{"class":2326},[2113,3217,2671],{"class":2326},[2113,3219,3220],{"class":2119},"reinterpret_cast",[2113,3222,3223],{"class":2326},"\u003Cintptr_t*>",[2113,3225,3226],{"class":2119},"(extra ",[2113,3228,3061],{"class":2326},[2113,3230,2926],{"class":2119},[2113,3232,3233],{"class":2326},"*\n",[2113,3235,3237,3240,3243],{"class":2115,"line":3236},19,[2113,3238,3239],{"class":2119},"                                                  (index ",[2113,3241,3242],{"class":2326},"-",[2113,3244,3245],{"class":2119}," kMaxInlineSlots));\n",[2113,3247,3249],{"class":2115,"line":3248},20,[2113,3250,2599],{"class":2119},[12,3252,3253],{},"Nous avons ainsi tous les éléments pour reconstituer le puzzle :",[506,3255,3256,3261,3270],{},[370,3257,3258,3260],{},[184,3259,2946],{}," permet de définir la taille d'un pointeur. Sur un système 64 bits, la taille d'un pointeur est\nde 8 octets, alors que sur un système 32 bits, la taille d'un pointeur est de 4 octets.",[370,3262,3263,3265,3266,3269],{},[184,3264,2942],{}," possède la valeur de 1 sur Android, mais pour les autres systèmes, la valeur est de\n",[184,3267,3268],{},"kSystemPointerSize \u002F 4",", soit 2 sur un système 64 bits et 1 sur un système 32 bits.",[370,3271,3272,3275,3276,3279],{},[184,3273,3274],{},"kMaxSize"," vaut ",[184,3277,3278],{},"1024 * kHeapLimitMultiplier * MB",", soit 1024 MB sur un système 32 bits et 2048 MB sur un système 64\nbits.",[12,3281,3282,3283,3285],{},"Enfin, en lisant le code de la fonction ",[184,3284,2379],{},", on comprend que la mémoire allouée à NodeJS est de 2Go\npar défaut.",[12,3287,3288],{},"Mais si le système est un système 64 bits (pointeur de 8 octets) et que la mémoire disponible sur la machine est\nsupérieure à 15Go, alors la mémoire allouée est multipliée par 2, soit 4Go.",[12,3290,3291],{},"Cela explique pourquoi sur nos postes de développement surpuissants, nous n'avons pas l'erreur car la mémoire allouée est de\n4Go par défaut. Mais sur la chaîne de déploiement, la mémoire allouée est de 2Go par défaut, car la machine ne possède\nque 8Go (ce qui est largement suffisant).",[12,3293,3294],{},"Voici un petit tableau récapitulatif:",[22,3296,3297,3313],{},[25,3298,3299],{},[28,3300,3301,3304,3307,3310],{},[31,3302,3303],{},"Système",[31,3305,3306],{},"Taille du pointeur",[31,3308,3309],{},"Mémoire disponible",[31,3311,3312],{},"Mémoire allouée par défaut",[41,3314,3315,3329,3342],{},[28,3316,3317,3320,3323,3326],{},[46,3318,3319],{},"32 bits",[46,3321,3322],{},"4 octets",[46,3324,3325],{},"OSEF",[46,3327,3328],{},"2Go",[28,3330,3331,3334,3337,3340],{},[46,3332,3333],{},"64 bits",[46,3335,3336],{},"8 octets",[46,3338,3339],{},"\u003C 15Go",[46,3341,3328],{},[28,3343,3344,3346,3348,3351],{},[46,3345,3333],{},[46,3347,3336],{},[46,3349,3350],{},">= 15Go",[46,3352,3353],{},"4Go",[12,3355,3356],{},"J'espère que cet article vous a permis de faire un petit voyage dans le code source de NodeJS et de\ncomprendre comment est calculée la valeur par défaut de max-old-space-size.",[3358,3359,3360],"style",{},"html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}",{"title":1646,"searchDepth":1647,"depth":1647,"links":3362},[],"2024-06-14",{"type":9,"value":3365},[3366,3368,3372,3374,3502],[12,3367,14],{},[12,3369,2096,3370,2100],{},[184,3371,2099],{},[12,3373,2103],{},[2105,3375,3376],{"className":2107,"code":2108,"language":2109,"meta":1646,"style":1646},[184,3377,3378,3382,3386,3424,3462,3466,3470,3474],{"__ignoreMap":1646},[2113,3379,3380],{"class":2115,"line":2116},[2113,3381,2120],{"class":2119},[2113,3383,3384],{"class":2115,"line":1647},[2113,3385,2125],{"emptyLinePlaceholder":1767},[2113,3387,3388,3390,3392,3394,3396,3398,3400,3402,3404,3406,3408,3410,3412,3414,3416,3418,3420,3422],{"class":2115,"line":1652},[2113,3389,2130],{"class":2119},[2113,3391,2134],{"class":2133},[2113,3393,2137],{"class":2119},[2113,3395,2140],{"class":2133},[2113,3397,2143],{"class":2119},[2113,3399,2146],{"class":2133},[2113,3401,2150],{"class":2149},[2113,3403,2153],{"class":2149},[2113,3405,2156],{"class":2149},[2113,3407,2159],{"class":2149},[2113,3409,2150],{"class":2149},[2113,3411,2153],{"class":2149},[2113,3413,2167],{"class":2166},[2113,3415,2170],{"class":2119},[2113,3417,2173],{"class":2133},[2113,3419,2176],{"class":2149},[2113,3421,2179],{"class":2149},[2113,3423,2182],{"class":2149},[2113,3425,3426,3428,3430,3432,3434,3436,3438,3440,3442,3444,3446,3448,3450,3452,3454,3456,3458,3460],{"class":2115,"line":2185},[2113,3427,2188],{"class":2119},[2113,3429,2140],{"class":2133},[2113,3431,2193],{"class":2119},[2113,3433,2196],{"class":2133},[2113,3435,2199],{"class":2119},[2113,3437,2146],{"class":2133},[2113,3439,2150],{"class":2149},[2113,3441,2153],{"class":2149},[2113,3443,2208],{"class":2149},[2113,3445,2159],{"class":2149},[2113,3447,2150],{"class":2149},[2113,3449,2153],{"class":2149},[2113,3451,2217],{"class":2166},[2113,3453,2170],{"class":2119},[2113,3455,2173],{"class":2133},[2113,3457,2176],{"class":2149},[2113,3459,2179],{"class":2149},[2113,3461,2182],{"class":2149},[2113,3463,3464],{"class":2115,"line":2230},[2113,3465,2125],{"emptyLinePlaceholder":1767},[2113,3467,3468],{"class":2115,"line":2235},[2113,3469,2238],{"class":2119},[2113,3471,3472],{"class":2115,"line":2241},[2113,3473,2125],{"emptyLinePlaceholder":1767},[2113,3475,3476,3478,3480,3482,3484,3486,3488,3490,3492,3494,3496,3498,3500],{"class":2115,"line":2246},[2113,3477,2249],{"class":2133},[2113,3479,2252],{"class":2149},[2113,3481,2255],{"class":2149},[2113,3483,2258],{"class":2149},[2113,3485,2261],{"class":2149},[2113,3487,2264],{"class":2149},[2113,3489,2267],{"class":2149},[2113,3491,2270],{"class":2149},[2113,3493,2273],{"class":2149},[2113,3495,2258],{"class":2149},[2113,3497,2278],{"class":2149},[2113,3499,2281],{"class":2149},[2113,3501,2284],{"class":2149},[3358,3503,3360],{},{"planet":1767},"\u002Fpost\u002Fmax-old-space",{"title":2089,"description":14},"max-old-space","posts\u002FProgrammation\u002F2024-06-14-max-old-space",[3510,3511],"javascript","nodejs","rKoBJuRBkLYyVgc8etB7XZPEzRbfpMzHtNOUEWqjpL8",{"id":3514,"title":108,"author":7,"body":3515,"category":2057,"categorySlug":2058,"date":111,"description":7729,"excerpt":7730,"extension":1764,"location":1765,"meta":7746,"navigation":1767,"path":107,"published":1767,"seo":7747,"slug":7748,"stem":7749,"tags":7750,"timeToRead":4961,"__hash__":7751},"posts\u002Fposts\u002FWoodstock\u002F2024-03-14_pr_backuppc_pool.md",{"type":9,"value":3516,"toc":7711},[3517,3526,3529,3532,3538,3542,3545,3554,3562,3571,3579,3582,3585,3588,3591,3594,3597,3600,3604,3610,3618,3634,3643,3647,3650,3715,3719,3722,3730,3733,3737,3753,3760,3768,3771,3780,3783,3790,3793,3800,3803,3806,3810,3816,3819,3822,3827,3830,3834,3847,3853,4060,4063,4072,4078,4087,4090,4093,4356,4374,4382,4407,4670,4673,5323,5345,5351,5365,5835,5841,6030,6034,6037,6040,6055,6423,6430,6437,6440,6444,6447,6466,6474,6477,6481,6484,6492,6495,6498,6512,6521,6531,6591,6594,6665,6668,6956,6966,6973,7105,7112,7259,7268,7271,7278,7281,7288,7291,7475,7478,7482,7485,7494,7497,7501,7508,7517,7640,7646,7649,7660,7663,7679,7683,7686,7694,7700,7703,7705,7708],[12,3518,3519,3520,3525],{},"Une partie de cet article a été publiée sur ",[49,3521,3524],{"href":3522,"rel":3523},"https:\u002F\u002Flinuxfr.org\u002Fusers\u002Fsan_guickoo\u002Fjournaux\u002Fpullrequest-d-une-application-en-rust",[347],"LinuxFR",".\nAprès avoir reçu quelques retours, j'ai décidé de publier une version modifiée et améliorée de cet article sur mon blog.",[12,3527,3528],{},"Je remercie donc la communauté de LinuxFR pour ses retours. 😄",[12,3530,3531],{},"Les commentaires sur le code ou sur l'article sont les bienvenus.",[12,3533,3534],{},[121,3535],{"src":3536,"alt":1646,"className":3537},"\u002FWoodstock\u002Fhdd_unsplash.jpg",[126],[128,3539,3541],{"id":3540},"le-commencement","Le commencement",[12,3543,3544],{},"Actuellement, j'utilise BackupPC pour sauvegarder mes données. BackupPC est un logiciel de sauvegarde qui se connecte à\ndifférents ordinateurs via SSH et utilise rsync pour sauvegarder les données. Il fonctionne parfaitement avec des\nordinateurs Linux et un peu moins bien sur des ordinateurs Windows où il faut installer un rsyncd\u002FCygwin (les données ne\nsont pas protégées par SSH dans ce cas).",[12,3546,3547,3548,3553],{},"Par ailleurs, je développe ",[49,3549,3552],{"href":3550,"rel":3551},"https:\u002F\u002Fshadoware.org\u002Fpost\u002Fwoodstock",[347],"mon propre logiciel de sauvegarde",". C'est un défi\npersonnel que je me suis lancé pour répondre à mes propres besoins (et aussi pour le plaisir).",[12,3555,3556,3557,179],{},"Mon premier prototype est écrit en TypeScript et utilise rsync couplé avec Btrfs pour faire des sauvegardes\nincrémentales. Malheureusement, quelques problèmes liés à l'utilisation de Btrfs m'ont fait abandonner ce prototype\n(problème avec la création d'un grand nombre de snapshots et un système de fichier un peu trop plein). J'en parle dans\n",[49,3558,3561],{"href":3559,"rel":3560},"https:\u002F\u002Fshadoware.org\u002Fpost\u002Fwoodstock_brtfs",[347],"un article sur mon blog",[12,3563,3564,3565,3570],{},"Je me suis donc orienté vers l'écriture de mon propre pool de stockage de données. L'écriture dans ce pool de stockage\nm'oblige à écrire mon propre logiciel de synchronisation. BackupPC a fait le choix de rester sur rsync et donc à créer\nun ",[49,3566,3569],{"href":3567,"rel":3568},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Frsync-bpc",[347],"fork de rsync"," capable de se connecter en rsync sur les machines à\nsauvegarder, tout en étant capable d'écrire le résultat dans le pool de backuppc, au format de backuppc. Pour ma part,\nj'ai choisi de développer mon protocole basé sur GRPC (et donc HTTP\u002F2).",[12,3572,3573,3574,179],{},"J'ai donc fait un second prototype, toujours en TypeScript, pour tester mon idée. Je suis satisfait du résultat, mais\nles limites du moteur JavaScript font de ce prototype un simple prototype. Là aussi, j'en parle dans\n",[49,3575,3578],{"href":3576,"rel":3577},"https:\u002F\u002Fshadoware.org\u002Fpost\u002Fwoodstock_rust",[347],"un autre article de mon blog",[12,3580,3581],{},"Je vais donc réécrire la partie la plus importante de ce programme en Rust. Pourquoi Rust ? Dans ma jeunesse (enfin,\nj'avais entre 20 et 30 ans), j'adorais programmer en C\u002FC++. Je me souviens d'avoir programmé un petit IDE en C++ avec\nQt. Lors du développement en C++, il m'arrivait parfois de me retrouver avec des fuites de mémoire, des erreurs de\nsegmentation, ainsi que des problèmes de concurrence d'accès aux données.",[12,3583,3584],{},"C++ m'a beaucoup appris sur le fonctionnement d'une machine, la gestion de la mémoire, la gestion du multithreading,\netc. Tout le monde devrait commencer par ce langage 😄.",[12,3586,3587],{},"Rust est un langage qui a été conçu pour éviter ces problèmes. Après avoir lu la documentation, j'ai adoré le concept.\nDu coup, j'ai décidé que ce serait une très bonne idée d'apprendre à l'utiliser. (Surtout qu'on en entend beaucoup\nparler en ce moment).",[12,3589,3590],{},"Vous trouvez que je digresse beaucoup ? C'est possible.",[12,3592,3593],{},"Revenons à notre programme.",[12,3595,3596],{},"Je me suis dit qu'avant d'écrire la gestion de mon pool de stockage en Rust, je voulais faire un script qui me permette\nde migrer le contenu de mon pool de stockage de BackupPC vers mon nouveau pool de stockage. Et de le faire en Rust.",[12,3598,3599],{},"Pour cela, j'ai besoin de comprendre comment fonctionne le pool de stockage de BackupPC. Le faire avec le code source\nde BackupPC sera amusant et pas trop compliqué.",[128,3601,3603],{"id":3602},"description-du-pool-de-stockage-de-backuppc","Description du pool de stockage de BackupPC",[12,3605,3606],{},[121,3607],{"src":3608,"alt":3609},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002Fimages\u002Flogos\u002Flogo.svg","BackupPC Logo",[12,3611,1589,3612,3617],{},[49,3613,3616],{"href":3614,"rel":3615},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002FBackupPC.html",[347],"documentation de BackupPC"," décrit déjà pas mal de choses :",[506,3619,3620,3627],{},[370,3621,3622],{},[49,3623,3626],{"href":3624,"rel":3625},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002FBackupPC.html#Storage-layout",[347],"Comment sont stockés les fichiers dans le pool de stockage ?",[370,3628,3629],{},[49,3630,3633],{"href":3631,"rel":3632},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002FBackupPC.html#Compressed-file-format",[347],"Une description succincte du format des fichiers compressés",[12,3635,3636,3637,3642],{},"Ensuite, pour obtenir les détails, il faut aller lire le ",[49,3638,3641],{"href":3639,"rel":3640},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs",[347],"code source en C"," qui\nsert de liaison avec le code en Perl. BackupPC est originellement écrit en Perl (entièrement, si je ne me trompe pas,\npour la version 3). La version 4 a été partiellement réécrite en C pour la partie qui gère le pool de stockage. Cette\npartie est utilisée par le rsync modifié et par la partie Perl à travers la bibliothèque de bindings.",[133,3644,3646],{"id":3645},"la-constitution-du-pool","La constitution du pool",[12,3648,3649],{},"Le pool de stockage de BackupPC est principalement constitué de plusieurs dossiers :",[506,3651,3652,3700,3706],{},[370,3653,3654,3657,3658],{},[184,3655,3656],{},"pc"," : Ce dossier répertorie les sauvegardes des différentes machines, organisées par machine puis par sauvegarde.\n",[506,3659,3660,3665],{},[370,3661,3662],{},[184,3663,3664],{},"host1",[370,3666,3667,3670,3671,3673,3674],{},[184,3668,3669],{},"host2"," : Le dossier de la machine ",[184,3672,3669],{},".\n",[506,3675,3676,3682,3688,3692,3697],{},[370,3677,3678,3681],{},[184,3679,3680],{},"backups"," : Un fichier au format TSV (Tab Separated Values) qui liste les sauvegardes de la machine avec leurs\ninformations.",[370,3683,3684,3687],{},[184,3685,3686],{},"1"," : La sauvegarde où les dossiers sont représentés à l'aide du système de fichiers, et les fichiers sont listés\ndans des fichiers d'attributs. Le hash du fichier d'attribut dans le pool est contenu dans le nom du fichier\n(exemple : attrib_33fe8f9ae2f5cedbea63b9d3ea767ac0).",[370,3689,3690],{},[184,3691,2547],{},[370,3693,3694],{},[184,3695,3696],{},"3",[370,3698,3699],{},"...",[370,3701,3702,3705],{},[184,3703,3704],{},"pool"," : Dans ce dossier, les fichiers sont stockés en utilisant leur hash MD5. Les fichiers sont répartis sur deux\nniveaux dans 128 dossiers dont le nom est constitué des 7 premiers bits des deux premiers octets. Si deux fichiers ont\nle même MD5, un nombre est accolé au hash pour les différencier.",[370,3707,3708,3711,3712,3714],{},[184,3709,3710],{},"cpool"," : La structure de ce dossier est la même que celle du dossier ",[184,3713,3704],{},". Il s'agit d'un pool de stockage\ncompressé.",[133,3716,3718],{"id":3717},"le-format-des-fichiers-compressés","Le format des fichiers compressés",[12,3720,3721],{},"Voici la description du fichier compressé selon la documentation (traduction libre) :",[1886,3723,3724,3727],{},[12,3725,3726],{},"Le format de fichier compressé est généré par Compress::Zlib::deflate avec une modification mineure, mais\nimportante. Comme Compress::Zlib::inflate gonfle entièrement son argument en mémoire, il pourrait consommer de\ngrandes quantités de mémoire s'il décompressait un fichier très compressé. Par exemple, un fichier de 200 Mo de 0x0\nbytes se compresse à environ 200 Ko. Si Compress::Zlib::inflate était appelé avec ce seul tampon de 200 Ko, il\naurait besoin d'allouer 200 Mo de mémoire pour retourner le résultat.",[12,3728,3729],{},"BackupPC surveille l'efficacité de la compression d'un fichier. Si un gros fichier a un taux de compression très élevé\n(ce qui signifie qu'il utilisera beaucoup de mémoire lorsqu'il sera décompressé), BackupPC appelle la méthode flush(), qui\ntermine proprement la compression en cours. BackupPC commence alors une nouvelle section et ajoute simplement le\nfichier de sortie. Ainsi, le format de fichier compressé de BackupPC est une ou plusieurs sections\u002Fflushes\nconcaténées. Les ratios spécifiques que BackupPC utilise sont que si un morceau de 6 Mo se compresse à moins de 64\nKo, alors un flush sera effectué.",[12,3731,3732],{},"Donc, dans notre cas, nous devons être capables de lire un fichier compressé comme une suite de fichiers compressés\nconcaténés les uns après les autres.",[133,3734,3736],{"id":3735},"le-format-des-fichiers-dattributs","Le format des fichiers d'attributs",[12,3738,3739,3744,3745,3748,3749,3752],{},[49,3740,3743],{"href":3741,"rel":3742},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002FBackupPC.html#Attribute-file-format",[347],"Dans la documentation",", il est expliqué que\ndans la version 4, on retrouve un fichier ",[184,3746,3747],{},"attrib_33fe8f9ae2f5cedbea63b9d3ea767ac0"," dans les différents dossiers de la\nmachine (",[184,3750,3751],{},"__TOPDIR__\u002Fpc\u002Fhost1",").\nLe nom du fichier contient le hash du fichier d'attributs que l'on peut retrouver dans le pool de stockage.",[12,3754,3755,3756,3759],{},"Pour accéder à un fichier du pool de stockage, il faut aller dans le dossier ",[184,3757,3758],{},"__TOPDIR__\u002Fpc"," et lire le nom du fichier\nd'attribut pour retrouver le hash du fichier compressé correspondant au nom du dossier que l'on veut lire. Enfin, il\nfaut lire le fichier compressé pour obtenir les données.",[12,3761,3762,3763,179],{},"Le contenu du fichier d'attribut n'est pas décrit. Cependant, on peut le retrouver dans le fichier\n",[49,3764,3767],{"href":3765,"rel":3766},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_attrib.c#L626",[347],"bpc_attribs.c",[12,3769,3770],{},"Le fichier d'attribut est encodé en binaire. Il s'agit d'une suite de varint (entier encodé sur un nombre variable\nd'octets).",[12,3772,3773,3774,3779],{},"Un ",[49,3775,3778],{"href":3776,"rel":3777},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FVariable-length_quantity",[347],"Varint"," est un entier qui est encodé sur un nombre variable\nd'octets. Le premier bit de chaque octet indique si l'entier est terminé ou s'il faut lire un autre octet. Le reste des\nbits de l'octet représente l'entier.",[12,3781,3782],{},"Voici une représentation d'un Varint depuis Wikipedia :",[12,3784,3785],{},[121,3786],{"src":3787,"alt":3788,"className":3789},"https:\u002F\u002Fupload.wikimedia.org\u002Fwikipedia\u002Fcommons\u002Fthumb\u002Fc\u002Fc6\u002FUintvar_coding.svg\u002F1920px-Uintvar_coding.svg.png","Image représentant un Varint (Source : Wikipedia)",[126],[12,3791,3792],{},"D'après le code source de BackupPC, le fichier d'attribut est encodé de la manière suivante :",[12,3794,3795,3796,3799],{},"Le fichier commence par un numéro magique ",[184,3797,3798],{},"0x17565353",". Ensuite, pour chaque fichier, on retrouve les attributs du\nfichier. Pour chaque attribut, on retrouve les xattrs.",[121,3801],{"src":3802},"https:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fpng\u002FXL8nJWCn4Epl5TC11H8eEf2kIa530HqIhHDlxbRosSUkVKgUb7VaOyW7KKWuGsLxPpIptjcbHYDnPtfNTMGiIOExxVrZ_L0lwaLbOXHUfQXD1TnYDC8-Dd31jucIm2RuqETZm-kEIIe0q2ZissOEEYhuqA-4OA_8HpdiIM49SJSGjjdpZ3kLBStgM1EfvD47ItXxVNul4HBR4jIMeMZOkQ8f--nw0g4cZTGQiOVz-GHu99FolzQX7uHKEVZ_naLmJ5unT3lbZqGAJG8tFvkVANL6kqlnLTfiSjuN6AvVdcgao8xriCSGlXo4eeGqai0QhxaoXE1k9gKfisQw_hac17SC_9jywg98-Ar6S0QZSST7KNEEioHlyxf_0W00",[12,3804,3805],{},"Il ne me reste donc plus qu'à reproduire tout cela en Rust.",[128,3807,3809],{"id":3808},"le-développement-en-rust","Le développement en Rust",[12,3811,3812],{},[121,3813],{"src":3814,"alt":1054,"className":3815},"\u002FWoodstock\u002Frust_unsplash.jpg",[126],[12,3817,3818],{},"Pour le développement de ce programme, j'ai établi certaines limites. Je me suis concentré uniquement sur la lecture des\nfichiers du pool qui sont en version 4. Ma version de BackupPC est la version 4, et j'ai migré l'intégralité du pool de\nstockage depuis la version 3.",[12,3820,3821],{},"Je n'ai donc pas de données en version 3 pour tester mon programme.",[12,3823,3824],{},[155,3825,3826],{},"Si vous souhaitez tester mon programme ou l'utiliser, sachez qu'il n'est pas compatible avec la version 3 de BackupPC,\nni avec la version 4 si votre pool est un mélange de versions V3 et V4.",[12,3828,3829],{},"Si le besoin se fait sentir plus tard, il sera toujours possible d'améliorer tout cela.",[133,3831,3833],{"id":3832},"les-fichiers-compressés","Les fichiers compressés",[12,3835,3836,3837,3840,3841,3844,3845,179],{},"J'ai donc commencé par développer un programme qui, à partir d'un hash, est capable de décompresser un fichier de ce\npool. Pour décompresser un fichier BackupPC compressé avec ",[184,3838,3839],{},"zlib",", j'ai choisi d'utiliser la bibliothèque ",[184,3842,3843],{},"flate2",", qui\nest une alternative Rust à la bibliothèque standard ",[184,3846,3839],{},[12,3848,3849,3850,3852],{},"La bibliothèque ",[184,3851,3843],{}," permet de décompresser un fichier en utilisant la notion de BufReader en Rust. La version\nsimplifiée pour décompresser un fichier du pool est donc la suivante :",[2105,3854,3857],{"className":3855,"code":3856,"language":1775,"meta":1646,"style":1646},"language-rust shiki shiki-themes one-dark-pro","use flate2::bufread::ZlibDecoder;\nuse std::fs::File;\n\nfn main() {\n    let f = File::open(\"33fe8f9ae2f5cedbea63b9d3ea767ac0\").unwrap();\n    let b = BufReader::new(f);\n    let mut z = ZlibEncoder::new(b);\n    let mut buffer = Vec::new();\n    z.read_to_end(&mut buffer).unwrap();\n    println!(\"{:?}\", buffer);\n}\n",[184,3858,3859,3879,3898,3902,3913,3945,3969,3995,4015,4039,4056],{"__ignoreMap":1646},[2113,3860,3861,3864,3867,3869,3872,3874,3877],{"class":2115,"line":2116},[2113,3862,3863],{"class":2326},"use",[2113,3865,3866],{"class":2414}," flate2",[2113,3868,2418],{"class":2119},[2113,3870,3871],{"class":2414},"bufread",[2113,3873,2418],{"class":2119},[2113,3875,3876],{"class":2414},"ZlibDecoder",[2113,3878,2487],{"class":2119},[2113,3880,3881,3883,3886,3888,3891,3893,3896],{"class":2115,"line":1647},[2113,3882,3863],{"class":2326},[2113,3884,3885],{"class":2414}," std",[2113,3887,2418],{"class":2119},[2113,3889,3890],{"class":2414},"fs",[2113,3892,2418],{"class":2119},[2113,3894,3895],{"class":2414},"File",[2113,3897,2487],{"class":2119},[2113,3899,3900],{"class":2115,"line":1652},[2113,3901,2125],{"emptyLinePlaceholder":1767},[2113,3903,3904,3907,3910],{"class":2115,"line":2185},[2113,3905,3906],{"class":2326},"fn",[2113,3908,3909],{"class":2133}," main",[2113,3911,3912],{"class":2119},"() {\n",[2113,3914,3915,3918,3921,3923,3926,3928,3931,3933,3936,3939,3942],{"class":2115,"line":2230},[2113,3916,3917],{"class":2326},"    let",[2113,3919,3920],{"class":2330}," f",[2113,3922,2153],{"class":2334},[2113,3924,3925],{"class":2414}," File",[2113,3927,2418],{"class":2119},[2113,3929,3930],{"class":2133},"open",[2113,3932,2423],{"class":2119},[2113,3934,3935],{"class":2149},"\"33fe8f9ae2f5cedbea63b9d3ea767ac0\"",[2113,3937,3938],{"class":2119},").",[2113,3940,3941],{"class":2133},"unwrap",[2113,3943,3944],{"class":2119},"();\n",[2113,3946,3947,3949,3952,3954,3957,3959,3962,3964,3967],{"class":2115,"line":2235},[2113,3948,3917],{"class":2326},[2113,3950,3951],{"class":2330}," b",[2113,3953,2153],{"class":2334},[2113,3955,3956],{"class":2414}," BufReader",[2113,3958,2418],{"class":2119},[2113,3960,3961],{"class":2133},"new",[2113,3963,2423],{"class":2119},[2113,3965,3966],{"class":2330},"f",[2113,3968,2553],{"class":2119},[2113,3970,3971,3973,3976,3979,3981,3984,3986,3988,3990,3993],{"class":2115,"line":2241},[2113,3972,3917],{"class":2326},[2113,3974,3975],{"class":2326}," mut",[2113,3977,3978],{"class":2330}," z",[2113,3980,2153],{"class":2334},[2113,3982,3983],{"class":2414}," ZlibEncoder",[2113,3985,2418],{"class":2119},[2113,3987,3961],{"class":2133},[2113,3989,2423],{"class":2119},[2113,3991,3992],{"class":2330},"b",[2113,3994,2553],{"class":2119},[2113,3996,3997,3999,4001,4004,4006,4009,4011,4013],{"class":2115,"line":2246},[2113,3998,3917],{"class":2326},[2113,4000,3975],{"class":2326},[2113,4002,4003],{"class":2330}," buffer",[2113,4005,2153],{"class":2334},[2113,4007,4008],{"class":2414}," Vec",[2113,4010,2418],{"class":2119},[2113,4012,3961],{"class":2133},[2113,4014,3944],{"class":2119},[2113,4016,4017,4020,4022,4025,4028,4031,4033,4035,4037],{"class":2115,"line":2464},[2113,4018,4019],{"class":2330},"    z",[2113,4021,179],{"class":2119},[2113,4023,4024],{"class":2133},"read_to_end",[2113,4026,4027],{"class":2119},"(&",[2113,4029,4030],{"class":2326},"mut",[2113,4032,4003],{"class":2330},[2113,4034,3938],{"class":2119},[2113,4036,3941],{"class":2133},[2113,4038,3944],{"class":2119},[2113,4040,4041,4044,4046,4049,4051,4054],{"class":2115,"line":2085},[2113,4042,4043],{"class":2133},"    println!",[2113,4045,2423],{"class":2119},[2113,4047,4048],{"class":2149},"\"{:?}\"",[2113,4050,932],{"class":2119},[2113,4052,4053],{"class":2330},"buffer",[2113,4055,2553],{"class":2119},[2113,4057,4058],{"class":2115,"line":2514},[2113,4059,2599],{"class":2119},[12,4061,4062],{},"Malheureusement, ce code ne fonctionne pas à tous les coups. Pour les plus petits fichiers de mon pool, cela fonctionne\ntrès bien, mais quand on se retrouve avec les fichiers volumineux que BackupPC a décidé de découper, cela ne fonctionne\nplus.",[12,4064,4065,4066,4071],{},"De plus, dans certains cas, BackupPC ",[49,4067,4070],{"href":4068,"rel":4069},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_fileZIO.c#L214",[347],"remplace certains octets","\npour indiquer que l'on est au début d'un nouveau bloc de données zlib, ou pour indiquer que l'on a ajouté à la fin une\nchecksum md4.",[12,4073,4074,4075,4077],{},"Comme certains fichiers peuvent être volumineux, je ne peux pas faire un ",[184,4076,4024],{}," pour lire le fichier en\nmémoire et le modifier.",[12,4079,4080,4081,4086],{},"Le fichier ",[49,4082,4085],{"href":4083,"rel":4084},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_fileZIO.c",[347],"bpc_fileZIO.c"," permet de comprendre\ncomment sont encodés les fichiers compressés.",[12,4088,4089],{},"Lors de la lecture d'un fichier compressé, si la lecture de la compression s'arrête et qu'il reste encore des octets\nà lire, alors il faut recommencer une nouvelle décompression.",[12,4091,4092],{},"On peut aussi voir le bout de code suivant :",[2105,4094,4098],{"className":4095,"code":4096,"language":4097,"meta":1646,"style":1646},"language-c shiki shiki-themes one-dark-pro","\u002F\u002F https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_fileZIO.c#L219C15-L237C18\nif ( fd->strm.next_in[0] == 0xd6 || fd->strm.next_in[0] == 0xd7 ) {\n    \u002F*\n     * Flag 0xd6 or 0xd7 means this is a compressed file with\n     * appended md4 block checksums for rsync.  Change\n     * the first byte back to 0x78 and proceed.\n     *\u002F\n    fd->strm.next_in[0] = 0x78;\n} else if ( fd->strm.next_in[0] == 0xb3 ) {\n    \u002F*\n     * Flag 0xb3 means this is the start of the rsync\n     * block checksums, so consider this as EOF for\n     * the compressed file.  Also seek the file so\n     * it is positioned at the 0xb3.\n     *\u002F\n    fd->eof = 1;\n    \u002F* TODO: check return status *\u002F\n    lseek(fd->fd, -fd->strm.avail_in, SEEK_CUR);\n    fd->strm.avail_in = 0;\n}\n","c",[184,4099,4100,4105,4166,4171,4176,4181,4186,4191,4220,4254,4258,4263,4268,4273,4278,4282,4297,4302,4334,4352],{"__ignoreMap":1646},[2113,4101,4102],{"class":2115,"line":2116},[2113,4103,4104],{"class":2396},"\u002F\u002F https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_fileZIO.c#L219C15-L237C18\n",[2113,4106,4107,4110,4113,4116,4119,4122,4125,4127,4130,4133,4135,4138,4141,4144,4146,4148,4150,4152,4154,4156,4158,4160,4163],{"class":2115,"line":1647},[2113,4108,4109],{"class":2326},"if",[2113,4111,4112],{"class":2119}," ( fd",[2113,4114,4115],{"class":2326},"->",[2113,4117,4118],{"class":2119},"strm.",[2113,4120,4121],{"class":2330},"next_in",[2113,4123,4124],{"class":2119},"[",[2113,4126,3095],{"class":2166},[2113,4128,4129],{"class":2119},"] ",[2113,4131,4132],{"class":2326},"==",[2113,4134,3006],{"class":2330},[2113,4136,4137],{"class":2166},"d6",[2113,4139,4140],{"class":2334}," ||",[2113,4142,4143],{"class":2119}," fd",[2113,4145,4115],{"class":2326},[2113,4147,4118],{"class":2119},[2113,4149,4121],{"class":2330},[2113,4151,4124],{"class":2119},[2113,4153,3095],{"class":2166},[2113,4155,4129],{"class":2119},[2113,4157,4132],{"class":2326},[2113,4159,3006],{"class":2330},[2113,4161,4162],{"class":2166},"d7",[2113,4164,4165],{"class":2119}," ) {\n",[2113,4167,4168],{"class":2115,"line":1652},[2113,4169,4170],{"class":2396},"    \u002F*\n",[2113,4172,4173],{"class":2115,"line":2185},[2113,4174,4175],{"class":2396},"     * Flag 0xd6 or 0xd7 means this is a compressed file with\n",[2113,4177,4178],{"class":2115,"line":2230},[2113,4179,4180],{"class":2396},"     * appended md4 block checksums for rsync.  Change\n",[2113,4182,4183],{"class":2115,"line":2235},[2113,4184,4185],{"class":2396},"     * the first byte back to 0x78 and proceed.\n",[2113,4187,4188],{"class":2115,"line":2241},[2113,4189,4190],{"class":2396},"     *\u002F\n",[2113,4192,4193,4196,4198,4201,4203,4205,4207,4209,4211,4213,4215,4218],{"class":2115,"line":2246},[2113,4194,4195],{"class":2414},"    fd",[2113,4197,4115],{"class":2119},[2113,4199,4200],{"class":2414},"strm",[2113,4202,179],{"class":2119},[2113,4204,4121],{"class":2330},[2113,4206,4124],{"class":2119},[2113,4208,3095],{"class":2166},[2113,4210,4129],{"class":2119},[2113,4212,2335],{"class":2326},[2113,4214,3006],{"class":2330},[2113,4216,4217],{"class":2166},"78",[2113,4219,2487],{"class":2119},[2113,4221,4222,4225,4228,4231,4233,4235,4237,4239,4241,4243,4245,4247,4249,4252],{"class":2115,"line":2464},[2113,4223,4224],{"class":2119},"} ",[2113,4226,4227],{"class":2326},"else",[2113,4229,4230],{"class":2326}," if",[2113,4232,4112],{"class":2119},[2113,4234,4115],{"class":2326},[2113,4236,4118],{"class":2119},[2113,4238,4121],{"class":2330},[2113,4240,4124],{"class":2119},[2113,4242,3095],{"class":2166},[2113,4244,4129],{"class":2119},[2113,4246,4132],{"class":2326},[2113,4248,3006],{"class":2330},[2113,4250,4251],{"class":2166},"b3",[2113,4253,4165],{"class":2119},[2113,4255,4256],{"class":2115,"line":2085},[2113,4257,4170],{"class":2396},[2113,4259,4260],{"class":2115,"line":2514},[2113,4261,4262],{"class":2396},"     * Flag 0xb3 means this is the start of the rsync\n",[2113,4264,4265],{"class":2115,"line":2533},[2113,4266,4267],{"class":2396},"     * block checksums, so consider this as EOF for\n",[2113,4269,4270],{"class":2115,"line":2556},[2113,4271,4272],{"class":2396},"     * the compressed file.  Also seek the file so\n",[2113,4274,4275],{"class":2115,"line":2569},[2113,4276,4277],{"class":2396},"     * it is positioned at the 0xb3.\n",[2113,4279,4280],{"class":2115,"line":2575},[2113,4281,4190],{"class":2396},[2113,4283,4284,4286,4288,4291,4293,4295],{"class":2115,"line":2596},[2113,4285,4195],{"class":2414},[2113,4287,4115],{"class":2119},[2113,4289,4290],{"class":2330},"eof",[2113,4292,2153],{"class":2326},[2113,4294,2850],{"class":2166},[2113,4296,2487],{"class":2119},[2113,4298,4299],{"class":2115,"line":3192},[2113,4300,4301],{"class":2396},"    \u002F* TODO: check return status *\u002F\n",[2113,4303,4304,4307,4309,4312,4314,4316,4318,4320,4322,4324,4326,4328,4331],{"class":2115,"line":3213},[2113,4305,4306],{"class":2133},"    lseek",[2113,4308,2423],{"class":2119},[2113,4310,4311],{"class":2414},"fd",[2113,4313,4115],{"class":2119},[2113,4315,4311],{"class":2330},[2113,4317,932],{"class":2119},[2113,4319,3242],{"class":2326},[2113,4321,4311],{"class":2414},[2113,4323,4115],{"class":2119},[2113,4325,4200],{"class":2414},[2113,4327,179],{"class":2119},[2113,4329,4330],{"class":2330},"avail_in",[2113,4332,4333],{"class":2119},", SEEK_CUR);\n",[2113,4335,4336,4338,4340,4342,4344,4346,4348,4350],{"class":2115,"line":3236},[2113,4337,4195],{"class":2414},[2113,4339,4115],{"class":2119},[2113,4341,4200],{"class":2414},[2113,4343,179],{"class":2119},[2113,4345,4330],{"class":2330},[2113,4347,2153],{"class":2326},[2113,4349,3208],{"class":2166},[2113,4351,2487],{"class":2119},[2113,4353,4354],{"class":2115,"line":3248},[2113,4355,2599],{"class":2119},[12,4357,4358,4359,939,4362,4365,4366,4369,4370,4373],{},"Si, en début de flux, on trouve ",[184,4360,4361],{},"0xd6",[184,4363,4364],{},"0xd7",", il faut changer le premier octet en ",[184,4367,4368],{},"0x78"," et continuer la lecture. En\nfin de flux, si on trouve ",[184,4371,4372],{},"0xb3",", on considère que c'est la fin du fichier compressé. (De notre côté, nous ne nous\nintéresserons pas à la partie checksum md4).",[12,4375,3849,4376,4378,4379,4381],{},[184,4377,3843],{}," ne permet pas de faire cela simplement. J'ai donc dû lire le code de ",[184,4380,3843],{}," pour comprendre\ncomment je pouvais agir pour lire un fichier compressé de BackupPC.",[12,4383,4384,4391,4392,4394,4395,4398,4399,4402,4403,4406],{},[49,4385,4388,4389],{"href":4386,"rel":4387},"https:\u002F\u002Fgithub.com\u002Frust-lang\u002Fflate2-rs\u002Fblob\u002Fmain\u002Fsrc\u002Fbufreader.rs#L73",[347],"Le code suivant de la bibliothèque ",[184,4390,3843],{},"\npermet de voir que ",[184,4393,3843],{}," a besoin, dans son constructeur, d'un ",[184,4396,4397],{},"BufRead"," pour lire un fichier compressé. Il utilise\nla méthode ",[184,4400,4401],{},"fill_buf"," pour remplir un tampon et la méthode ",[184,4404,4405],{},"consume"," pour consommer le tampon.",[2105,4408,4410],{"className":3855,"code":4409,"language":1775,"meta":1646,"style":1646},"\u002F\u002F https:\u002F\u002Fgithub.com\u002Frust-lang\u002Fflate2-rs\u002Fblob\u002Fmain\u002Fsrc\u002Fbufreader.rs#L73\nimpl\u003CR: Read> Read for BufReader\u003CR> {\n    fn read(&mut self, buf: &mut [u8]) -> io::Result\u003Cusize> {\n        \u002F\u002F If we don't have any buffered data and we're doing a massive read\n        \u002F\u002F (larger than our internal buffer), bypass our internal buffer\n        \u002F\u002F entirely.\n        if self.pos == self.cap && buf.len() >= self.buf.len() {\n            return self.inner.read(buf);\n        }\n        let nread = {\n            let mut rem = self.fill_buf()?;\n            rem.read(buf)?\n        };\n        self.consume(nread);\n        Ok(nread)\n    }\n}\n",[184,4411,4412,4417,4450,4499,4504,4509,4514,4555,4574,4579,4591,4612,4628,4633,4649,4661,4666],{"__ignoreMap":1646},[2113,4413,4414],{"class":2115,"line":2116},[2113,4415,4416],{"class":2396},"\u002F\u002F https:\u002F\u002Fgithub.com\u002Frust-lang\u002Fflate2-rs\u002Fblob\u002Fmain\u002Fsrc\u002Fbufreader.rs#L73\n",[2113,4418,4419,4422,4424,4427,4430,4433,4436,4438,4441,4443,4445,4447],{"class":2115,"line":1647},[2113,4420,4421],{"class":2326},"impl",[2113,4423,3109],{"class":2119},[2113,4425,4426],{"class":2414},"R",[2113,4428,4429],{"class":2119},": ",[2113,4431,4432],{"class":2414},"Read",[2113,4434,4435],{"class":2119},"> ",[2113,4437,4432],{"class":2414},[2113,4439,4440],{"class":2326}," for",[2113,4442,3956],{"class":2414},[2113,4444,3109],{"class":2119},[2113,4446,4426],{"class":2414},[2113,4448,4449],{"class":2119},"> {\n",[2113,4451,4452,4455,4458,4460,4462,4465,4467,4470,4473,4475,4478,4481,4484,4487,4489,4492,4494,4497],{"class":2115,"line":1652},[2113,4453,4454],{"class":2326},"    fn",[2113,4456,4457],{"class":2133}," read",[2113,4459,4027],{"class":2119},[2113,4461,4030],{"class":2326},[2113,4463,4464],{"class":2414}," self",[2113,4466,932],{"class":2119},[2113,4468,4469],{"class":2330},"buf",[2113,4471,4472],{"class":2119},": &",[2113,4474,4030],{"class":2326},[2113,4476,4477],{"class":2119}," [",[2113,4479,4480],{"class":2414},"u8",[2113,4482,4483],{"class":2119},"]) -> ",[2113,4485,4486],{"class":2414},"io",[2113,4488,2418],{"class":2119},[2113,4490,4491],{"class":2414},"Result",[2113,4493,3109],{"class":2119},[2113,4495,4496],{"class":2414},"usize",[2113,4498,4449],{"class":2119},[2113,4500,4501],{"class":2115,"line":2185},[2113,4502,4503],{"class":2396},"        \u002F\u002F If we don't have any buffered data and we're doing a massive read\n",[2113,4505,4506],{"class":2115,"line":2230},[2113,4507,4508],{"class":2396},"        \u002F\u002F (larger than our internal buffer), bypass our internal buffer\n",[2113,4510,4511],{"class":2115,"line":2235},[2113,4512,4513],{"class":2396},"        \u002F\u002F entirely.\n",[2113,4515,4516,4519,4521,4524,4526,4528,4531,4533,4536,4538,4541,4544,4546,4548,4551,4553],{"class":2115,"line":2241},[2113,4517,4518],{"class":2326},"        if",[2113,4520,4464],{"class":2414},[2113,4522,4523],{"class":2119},".pos ",[2113,4525,4132],{"class":2334},[2113,4527,4464],{"class":2414},[2113,4529,4530],{"class":2119},".cap ",[2113,4532,3104],{"class":2334},[2113,4534,4535],{"class":2330}," buf",[2113,4537,179],{"class":2119},[2113,4539,4540],{"class":2133},"len",[2113,4542,4543],{"class":2119},"() ",[2113,4545,2481],{"class":2334},[2113,4547,4464],{"class":2414},[2113,4549,4550],{"class":2119},".buf.",[2113,4552,4540],{"class":2133},[2113,4554,3912],{"class":2119},[2113,4556,4557,4560,4562,4565,4568,4570,4572],{"class":2115,"line":2246},[2113,4558,4559],{"class":2326},"            return",[2113,4561,4464],{"class":2414},[2113,4563,4564],{"class":2119},".inner.",[2113,4566,4567],{"class":2133},"read",[2113,4569,2423],{"class":2119},[2113,4571,4469],{"class":2330},[2113,4573,2553],{"class":2119},[2113,4575,4576],{"class":2115,"line":2464},[2113,4577,4578],{"class":2119},"        }\n",[2113,4580,4581,4584,4587,4589],{"class":2115,"line":2085},[2113,4582,4583],{"class":2326},"        let",[2113,4585,4586],{"class":2330}," nread",[2113,4588,2153],{"class":2334},[2113,4590,2647],{"class":2119},[2113,4592,4593,4596,4598,4601,4603,4605,4607,4609],{"class":2115,"line":2514},[2113,4594,4595],{"class":2326},"            let",[2113,4597,3975],{"class":2326},[2113,4599,4600],{"class":2330}," rem",[2113,4602,2153],{"class":2334},[2113,4604,4464],{"class":2414},[2113,4606,179],{"class":2119},[2113,4608,4401],{"class":2133},[2113,4610,4611],{"class":2119},"()?;\n",[2113,4613,4614,4617,4619,4621,4623,4625],{"class":2115,"line":2533},[2113,4615,4616],{"class":2330},"            rem",[2113,4618,179],{"class":2119},[2113,4620,4567],{"class":2133},[2113,4622,2423],{"class":2119},[2113,4624,4469],{"class":2330},[2113,4626,4627],{"class":2119},")?\n",[2113,4629,4630],{"class":2115,"line":2556},[2113,4631,4632],{"class":2119},"        };\n",[2113,4634,4635,4638,4640,4642,4644,4647],{"class":2115,"line":2569},[2113,4636,4637],{"class":2414},"        self",[2113,4639,179],{"class":2119},[2113,4641,4405],{"class":2133},[2113,4643,2423],{"class":2119},[2113,4645,4646],{"class":2330},"nread",[2113,4648,2553],{"class":2119},[2113,4650,4651,4654,4656,4658],{"class":2115,"line":2575},[2113,4652,4653],{"class":2414},"        Ok",[2113,4655,2423],{"class":2119},[2113,4657,4646],{"class":2330},[2113,4659,4660],{"class":2119},")\n",[2113,4662,4663],{"class":2115,"line":2596},[2113,4664,4665],{"class":2119},"    }\n",[2113,4667,4668],{"class":2115,"line":3192},[2113,4669,2599],{"class":2119},[12,4671,4672],{},"J'ai donc commencé par développer un adaptateur qui s'intercale entre le fichier et le décompresseur. Cet adaptateur\na pour objectif de reproduire le comportement de BackupPC et de remplacer les octets en début de fichier.",[2105,4674,4676],{"className":3855,"code":4675,"language":1775,"meta":1646,"style":1646},"struct InterpretAdapter\u003CR: BufRead> {\n    inner: R,\n    first: bool,\n    temp: Option\u003CVec\u003Cu8>>,\n}\n\nimpl\u003CR: BufRead> InterpretAdapter\u003CR> {\n    fn new(inner: R) -> Self {\n        Self {\n            inner,\n            first: true,\n            temp: None,\n        }\n    }\n\n    fn reset(&mut self) {\n        self.first = true;\n        self.temp = None;\n    }\n}\n...\nimpl\u003CR: BufRead> BufRead for InterpretAdapter\u003CR> {\n    fn fill_buf(&mut self) -> io::Result\u003C&[u8]> {\n        if self.temp.is_none() {\n            let buf = self.inner.fill_buf()?;\n            let mut buf = buf.to_vec();\n\n            if self.first && !buf.is_empty() {\n                self.first = false;\n\n                if buf[0] == 0xd6 || buf[0] == 0xd7 {\n                    buf[0] = 0x78;\n                } else if buf[0] == 0xb3 {\n                    \u002F\u002F EOF\n                    buf = Vec::new();\n                }\n            }\n\n            self.temp = Some(buf);\n        }\n\n        Ok(self.temp.as_ref().unwrap())\n    }\n\n    fn consume(&mut self, amt: usize) {\n        if amt > 0 {\n            self.temp = None;\n            self.inner.consume(amt);\n        }\n    }\n}\n",[184,4677,4678,4695,4707,4719,4741,4745,4749,4772,4796,4803,4810,4822,4834,4838,4842,4846,4861,4875,4889,4893,4897,4903,4929,4959,4974,4991,5011,5016,5040,5055,5060,5096,5115,5140,5146,5161,5167,5173,5178,5197,5202,5207,5230,5235,5240,5265,5280,5293,5308,5313,5318],{"__ignoreMap":1646},[2113,4679,4680,4682,4685,4687,4689,4691,4693],{"class":2115,"line":2116},[2113,4681,2641],{"class":2326},[2113,4683,4684],{"class":2414}," InterpretAdapter",[2113,4686,3109],{"class":2119},[2113,4688,4426],{"class":2414},[2113,4690,4429],{"class":2119},[2113,4692,4397],{"class":2414},[2113,4694,4449],{"class":2119},[2113,4696,4697,4700,4702,4704],{"class":2115,"line":1647},[2113,4698,4699],{"class":2330},"    inner",[2113,4701,4429],{"class":2119},[2113,4703,4426],{"class":2414},[2113,4705,4706],{"class":2119},",\n",[2113,4708,4709,4712,4714,4717],{"class":2115,"line":1652},[2113,4710,4711],{"class":2330},"    first",[2113,4713,4429],{"class":2119},[2113,4715,4716],{"class":2414},"bool",[2113,4718,4706],{"class":2119},[2113,4720,4721,4724,4726,4729,4731,4734,4736,4738],{"class":2115,"line":2185},[2113,4722,4723],{"class":2330},"    temp",[2113,4725,4429],{"class":2119},[2113,4727,4728],{"class":2414},"Option",[2113,4730,3109],{"class":2119},[2113,4732,4733],{"class":2414},"Vec",[2113,4735,3109],{"class":2119},[2113,4737,4480],{"class":2414},[2113,4739,4740],{"class":2119},">>,\n",[2113,4742,4743],{"class":2115,"line":2230},[2113,4744,2599],{"class":2119},[2113,4746,4747],{"class":2115,"line":2235},[2113,4748,2125],{"emptyLinePlaceholder":1767},[2113,4750,4751,4753,4755,4757,4759,4761,4763,4766,4768,4770],{"class":2115,"line":2241},[2113,4752,4421],{"class":2326},[2113,4754,3109],{"class":2119},[2113,4756,4426],{"class":2414},[2113,4758,4429],{"class":2119},[2113,4760,4397],{"class":2414},[2113,4762,4435],{"class":2119},[2113,4764,4765],{"class":2414},"InterpretAdapter",[2113,4767,3109],{"class":2119},[2113,4769,4426],{"class":2414},[2113,4771,4449],{"class":2119},[2113,4773,4774,4776,4779,4781,4784,4786,4788,4791,4794],{"class":2115,"line":2246},[2113,4775,4454],{"class":2326},[2113,4777,4778],{"class":2133}," new",[2113,4780,2423],{"class":2119},[2113,4782,4783],{"class":2330},"inner",[2113,4785,4429],{"class":2119},[2113,4787,4426],{"class":2414},[2113,4789,4790],{"class":2119},") -> ",[2113,4792,4793],{"class":2414},"Self",[2113,4795,2647],{"class":2119},[2113,4797,4798,4801],{"class":2115,"line":2464},[2113,4799,4800],{"class":2414},"        Self",[2113,4802,2647],{"class":2119},[2113,4804,4805,4808],{"class":2115,"line":2085},[2113,4806,4807],{"class":2330},"            inner",[2113,4809,4706],{"class":2119},[2113,4811,4812,4815,4817,4820],{"class":2115,"line":2514},[2113,4813,4814],{"class":2330},"            first",[2113,4816,4429],{"class":2119},[2113,4818,4819],{"class":2166},"true",[2113,4821,4706],{"class":2119},[2113,4823,4824,4827,4829,4832],{"class":2115,"line":2533},[2113,4825,4826],{"class":2330},"            temp",[2113,4828,4429],{"class":2119},[2113,4830,4831],{"class":2414},"None",[2113,4833,4706],{"class":2119},[2113,4835,4836],{"class":2115,"line":2556},[2113,4837,4578],{"class":2119},[2113,4839,4840],{"class":2115,"line":2569},[2113,4841,4665],{"class":2119},[2113,4843,4844],{"class":2115,"line":2575},[2113,4845,2125],{"emptyLinePlaceholder":1767},[2113,4847,4848,4850,4853,4855,4857,4859],{"class":2115,"line":2596},[2113,4849,4454],{"class":2326},[2113,4851,4852],{"class":2133}," reset",[2113,4854,4027],{"class":2119},[2113,4856,4030],{"class":2326},[2113,4858,4464],{"class":2414},[2113,4860,2433],{"class":2119},[2113,4862,4863,4865,4868,4870,4873],{"class":2115,"line":3192},[2113,4864,4637],{"class":2414},[2113,4866,4867],{"class":2119},".first ",[2113,4869,2335],{"class":2334},[2113,4871,4872],{"class":2166}," true",[2113,4874,2487],{"class":2119},[2113,4876,4877,4879,4882,4884,4887],{"class":2115,"line":3213},[2113,4878,4637],{"class":2414},[2113,4880,4881],{"class":2119},".temp ",[2113,4883,2335],{"class":2334},[2113,4885,4886],{"class":2414}," None",[2113,4888,2487],{"class":2119},[2113,4890,4891],{"class":2115,"line":3236},[2113,4892,4665],{"class":2119},[2113,4894,4895],{"class":2115,"line":3248},[2113,4896,2599],{"class":2119},[2113,4898,4900],{"class":2115,"line":4899},21,[2113,4901,4902],{"class":2119},"...\n",[2113,4904,4905,4907,4909,4911,4913,4915,4917,4919,4921,4923,4925,4927],{"class":2115,"line":1777},[2113,4906,4421],{"class":2326},[2113,4908,3109],{"class":2119},[2113,4910,4426],{"class":2414},[2113,4912,4429],{"class":2119},[2113,4914,4397],{"class":2414},[2113,4916,4435],{"class":2119},[2113,4918,4397],{"class":2414},[2113,4920,4440],{"class":2326},[2113,4922,4684],{"class":2414},[2113,4924,3109],{"class":2119},[2113,4926,4426],{"class":2414},[2113,4928,4449],{"class":2119},[2113,4930,4932,4934,4937,4939,4941,4943,4945,4947,4949,4951,4954,4956],{"class":2115,"line":4931},23,[2113,4933,4454],{"class":2326},[2113,4935,4936],{"class":2133}," fill_buf",[2113,4938,4027],{"class":2119},[2113,4940,4030],{"class":2326},[2113,4942,4464],{"class":2414},[2113,4944,4790],{"class":2119},[2113,4946,4486],{"class":2414},[2113,4948,2418],{"class":2119},[2113,4950,4491],{"class":2414},[2113,4952,4953],{"class":2119},"\u003C&[",[2113,4955,4480],{"class":2414},[2113,4957,4958],{"class":2119},"]> {\n",[2113,4960,4962,4964,4966,4969,4972],{"class":2115,"line":4961},24,[2113,4963,4518],{"class":2326},[2113,4965,4464],{"class":2414},[2113,4967,4968],{"class":2119},".temp.",[2113,4970,4971],{"class":2133},"is_none",[2113,4973,3912],{"class":2119},[2113,4975,4977,4979,4981,4983,4985,4987,4989],{"class":2115,"line":4976},25,[2113,4978,4595],{"class":2326},[2113,4980,4535],{"class":2330},[2113,4982,2153],{"class":2334},[2113,4984,4464],{"class":2414},[2113,4986,4564],{"class":2119},[2113,4988,4401],{"class":2133},[2113,4990,4611],{"class":2119},[2113,4992,4994,4996,4998,5000,5002,5004,5006,5009],{"class":2115,"line":4993},26,[2113,4995,4595],{"class":2326},[2113,4997,3975],{"class":2326},[2113,4999,4535],{"class":2330},[2113,5001,2153],{"class":2334},[2113,5003,4535],{"class":2330},[2113,5005,179],{"class":2119},[2113,5007,5008],{"class":2133},"to_vec",[2113,5010,3944],{"class":2119},[2113,5012,5014],{"class":2115,"line":5013},27,[2113,5015,2125],{"emptyLinePlaceholder":1767},[2113,5017,5019,5022,5024,5026,5028,5031,5033,5035,5038],{"class":2115,"line":5018},28,[2113,5020,5021],{"class":2326},"            if",[2113,5023,4464],{"class":2414},[2113,5025,4867],{"class":2119},[2113,5027,3104],{"class":2334},[2113,5029,5030],{"class":2334}," !",[2113,5032,4469],{"class":2330},[2113,5034,179],{"class":2119},[2113,5036,5037],{"class":2133},"is_empty",[2113,5039,3912],{"class":2119},[2113,5041,5043,5046,5048,5050,5053],{"class":2115,"line":5042},29,[2113,5044,5045],{"class":2414},"                self",[2113,5047,4867],{"class":2119},[2113,5049,2335],{"class":2334},[2113,5051,5052],{"class":2166}," false",[2113,5054,2487],{"class":2119},[2113,5056,5058],{"class":2115,"line":5057},30,[2113,5059,2125],{"emptyLinePlaceholder":1767},[2113,5061,5063,5066,5068,5070,5072,5074,5076,5079,5081,5083,5085,5087,5089,5091,5094],{"class":2115,"line":5062},31,[2113,5064,5065],{"class":2326},"                if",[2113,5067,4535],{"class":2330},[2113,5069,4124],{"class":2119},[2113,5071,3095],{"class":2166},[2113,5073,4129],{"class":2119},[2113,5075,4132],{"class":2334},[2113,5077,5078],{"class":2166}," 0xd6",[2113,5080,4140],{"class":2334},[2113,5082,4535],{"class":2330},[2113,5084,4124],{"class":2119},[2113,5086,3095],{"class":2166},[2113,5088,4129],{"class":2119},[2113,5090,4132],{"class":2334},[2113,5092,5093],{"class":2166}," 0xd7",[2113,5095,2647],{"class":2119},[2113,5097,5099,5102,5104,5106,5108,5110,5113],{"class":2115,"line":5098},32,[2113,5100,5101],{"class":2330},"                    buf",[2113,5103,4124],{"class":2119},[2113,5105,3095],{"class":2166},[2113,5107,4129],{"class":2119},[2113,5109,2335],{"class":2334},[2113,5111,5112],{"class":2166}," 0x78",[2113,5114,2487],{"class":2119},[2113,5116,5118,5121,5123,5125,5127,5129,5131,5133,5135,5138],{"class":2115,"line":5117},33,[2113,5119,5120],{"class":2119},"                } ",[2113,5122,4227],{"class":2326},[2113,5124,4230],{"class":2326},[2113,5126,4535],{"class":2330},[2113,5128,4124],{"class":2119},[2113,5130,3095],{"class":2166},[2113,5132,4129],{"class":2119},[2113,5134,4132],{"class":2334},[2113,5136,5137],{"class":2166}," 0xb3",[2113,5139,2647],{"class":2119},[2113,5141,5143],{"class":2115,"line":5142},34,[2113,5144,5145],{"class":2396},"                    \u002F\u002F EOF\n",[2113,5147,5149,5151,5153,5155,5157,5159],{"class":2115,"line":5148},35,[2113,5150,5101],{"class":2330},[2113,5152,2153],{"class":2334},[2113,5154,4008],{"class":2414},[2113,5156,2418],{"class":2119},[2113,5158,3961],{"class":2133},[2113,5160,3944],{"class":2119},[2113,5162,5164],{"class":2115,"line":5163},36,[2113,5165,5166],{"class":2119},"                }\n",[2113,5168,5170],{"class":2115,"line":5169},37,[2113,5171,5172],{"class":2119},"            }\n",[2113,5174,5176],{"class":2115,"line":5175},38,[2113,5177,2125],{"emptyLinePlaceholder":1767},[2113,5179,5181,5184,5186,5188,5191,5193,5195],{"class":2115,"line":5180},39,[2113,5182,5183],{"class":2414},"            self",[2113,5185,4881],{"class":2119},[2113,5187,2335],{"class":2334},[2113,5189,5190],{"class":2414}," Some",[2113,5192,2423],{"class":2119},[2113,5194,4469],{"class":2330},[2113,5196,2553],{"class":2119},[2113,5198,5200],{"class":2115,"line":5199},40,[2113,5201,4578],{"class":2119},[2113,5203,5205],{"class":2115,"line":5204},41,[2113,5206,2125],{"emptyLinePlaceholder":1767},[2113,5208,5210,5212,5214,5217,5219,5222,5225,5227],{"class":2115,"line":5209},42,[2113,5211,4653],{"class":2414},[2113,5213,2423],{"class":2119},[2113,5215,5216],{"class":2414},"self",[2113,5218,4968],{"class":2119},[2113,5220,5221],{"class":2133},"as_ref",[2113,5223,5224],{"class":2119},"().",[2113,5226,3941],{"class":2133},[2113,5228,5229],{"class":2119},"())\n",[2113,5231,5233],{"class":2115,"line":5232},43,[2113,5234,4665],{"class":2119},[2113,5236,5238],{"class":2115,"line":5237},44,[2113,5239,2125],{"emptyLinePlaceholder":1767},[2113,5241,5243,5245,5248,5250,5252,5254,5256,5259,5261,5263],{"class":2115,"line":5242},45,[2113,5244,4454],{"class":2326},[2113,5246,5247],{"class":2133}," consume",[2113,5249,4027],{"class":2119},[2113,5251,4030],{"class":2326},[2113,5253,4464],{"class":2414},[2113,5255,932],{"class":2119},[2113,5257,5258],{"class":2330},"amt",[2113,5260,4429],{"class":2119},[2113,5262,4496],{"class":2414},[2113,5264,2433],{"class":2119},[2113,5266,5268,5270,5273,5276,5278],{"class":2115,"line":5267},46,[2113,5269,4518],{"class":2326},[2113,5271,5272],{"class":2330}," amt",[2113,5274,5275],{"class":2334}," >",[2113,5277,3208],{"class":2166},[2113,5279,2647],{"class":2119},[2113,5281,5283,5285,5287,5289,5291],{"class":2115,"line":5282},47,[2113,5284,5183],{"class":2414},[2113,5286,4881],{"class":2119},[2113,5288,2335],{"class":2334},[2113,5290,4886],{"class":2414},[2113,5292,2487],{"class":2119},[2113,5294,5296,5298,5300,5302,5304,5306],{"class":2115,"line":5295},48,[2113,5297,5183],{"class":2414},[2113,5299,4564],{"class":2119},[2113,5301,4405],{"class":2133},[2113,5303,2423],{"class":2119},[2113,5305,5258],{"class":2330},[2113,5307,2553],{"class":2119},[2113,5309,5311],{"class":2115,"line":5310},49,[2113,5312,4578],{"class":2119},[2113,5314,5316],{"class":2115,"line":5315},50,[2113,5317,4665],{"class":2119},[2113,5319,5321],{"class":2115,"line":5320},51,[2113,5322,2599],{"class":2119},[12,5324,5325,5326,5329,5330,5333,5334,5336,5337,5339,5340,792,5342,5344],{},"Cet adaptateur s'intercale entre le ",[184,5327,5328],{},"BufReader"," et le ",[184,5331,5332],{},"ZLibDecoder",". Du point de vue du ",[184,5335,5332],{},", il doit\nse comporter comme un ",[184,5338,5328],{}," et donc implémenter les méthodes ",[184,5341,4401],{},[184,5343,4405],{},". Dans ces méthodes, je dois\nretourner un tampon.",[12,5346,5347,5348,5350],{},"Je ne peux pas modifier directement le tampon du ",[184,5349,5328],{}," car ce dernier est une référence en lecture seule. Je\ncopie donc le contenu (je n'ai pas trouvé de meilleure manière de faire cela).",[12,5352,5353,5354,5357,5358,5361,5362,5364],{},"Enfin, on peut développer le ",[184,5355,5356],{},"Reader"," qui sera capable de lire les fichiers compressés. Pour cela, j'ai d'abord\nimplémenté une méthode ",[184,5359,5360],{},"read_some_bytes"," qui permet de lire un certain nombre d'octets. Si jamais on arrive à la fin\nd'une section, alors la méthode peut retourner moins que la taille du tampon. Généralement, cela arrive en fin de\nfichier. Quand j'ai utilisé directement cette méthode en tant que méthode ",[184,5363,4567],{},", je me suis retrouvé avec des\ncorruptions.",[2105,5366,5368],{"className":3855,"code":5367,"language":1775,"meta":1646,"style":1646},"pub struct BackupPCReader\u003CR: Read> {\n    decoder: Option\u003CZlibDecoder\u003CInterpretAdapter\u003CBufReader\u003CR>>>>,\n}\n...\nfn read_some_bytes(&mut self, buf: &mut [u8]) -> io::Result\u003Cusize> {\n    loop {\n        let decoder = self.decoder.as_mut();\n        if decoder.is_none() {\n            return Ok(0);\n        }\n\n        let decoder_read_result = decoder.unwrap().read(buf);\n\n        let count = match decoder_read_result {\n            Ok(count) => {\n                count\n            }\n            Err(e) => {\n                return Err(e);\n            }\n        };\n\n        if count != 0 {\n            return Ok(count);\n        }\n\n        if count == 0 {\n            let decoder = self.decoder.take();\n            if let Some(decoder) = decoder {\n                let mut reader = decoder.into_inner();\n                \u002F\u002F S'il reste encore des octets à lire dans reader alors on continue, sinon on s'arrête\n                if reader.fill_buf()?.is_empty() {\n                    return Ok(0);\n                }\n                reader.reset();\n\n                self.decoder = Some(ZlibDecoder::new(reader));\n            }\n        }\n    }\n}\n",[184,5369,5370,5391,5419,5423,5427,5466,5473,5492,5504,5517,5521,5525,5550,5554,5570,5583,5588,5592,5604,5618,5622,5626,5630,5643,5655,5659,5663,5676,5693,5716,5737,5742,5759,5772,5776,5788,5792,5819,5823,5827,5831],{"__ignoreMap":1646},[2113,5371,5372,5375,5378,5381,5383,5385,5387,5389],{"class":2115,"line":2116},[2113,5373,5374],{"class":2326},"pub",[2113,5376,5377],{"class":2326}," struct",[2113,5379,5380],{"class":2414}," BackupPCReader",[2113,5382,3109],{"class":2119},[2113,5384,4426],{"class":2414},[2113,5386,4429],{"class":2119},[2113,5388,4432],{"class":2414},[2113,5390,4449],{"class":2119},[2113,5392,5393,5396,5398,5400,5402,5404,5406,5408,5410,5412,5414,5416],{"class":2115,"line":1647},[2113,5394,5395],{"class":2330},"    decoder",[2113,5397,4429],{"class":2119},[2113,5399,4728],{"class":2414},[2113,5401,3109],{"class":2119},[2113,5403,3876],{"class":2414},[2113,5405,3109],{"class":2119},[2113,5407,4765],{"class":2414},[2113,5409,3109],{"class":2119},[2113,5411,5328],{"class":2414},[2113,5413,3109],{"class":2119},[2113,5415,4426],{"class":2414},[2113,5417,5418],{"class":2119},">>>>,\n",[2113,5420,5421],{"class":2115,"line":1652},[2113,5422,2599],{"class":2119},[2113,5424,5425],{"class":2115,"line":2185},[2113,5426,4902],{"class":2119},[2113,5428,5429,5431,5434,5436,5438,5440,5442,5444,5446,5448,5450,5452,5454,5456,5458,5460,5462,5464],{"class":2115,"line":2230},[2113,5430,3906],{"class":2326},[2113,5432,5433],{"class":2133}," read_some_bytes",[2113,5435,4027],{"class":2119},[2113,5437,4030],{"class":2326},[2113,5439,4464],{"class":2414},[2113,5441,932],{"class":2119},[2113,5443,4469],{"class":2330},[2113,5445,4472],{"class":2119},[2113,5447,4030],{"class":2326},[2113,5449,4477],{"class":2119},[2113,5451,4480],{"class":2414},[2113,5453,4483],{"class":2119},[2113,5455,4486],{"class":2414},[2113,5457,2418],{"class":2119},[2113,5459,4491],{"class":2414},[2113,5461,3109],{"class":2119},[2113,5463,4496],{"class":2414},[2113,5465,4449],{"class":2119},[2113,5467,5468,5471],{"class":2115,"line":2235},[2113,5469,5470],{"class":2326},"    loop",[2113,5472,2647],{"class":2119},[2113,5474,5475,5477,5480,5482,5484,5487,5490],{"class":2115,"line":2241},[2113,5476,4583],{"class":2326},[2113,5478,5479],{"class":2330}," decoder",[2113,5481,2153],{"class":2334},[2113,5483,4464],{"class":2414},[2113,5485,5486],{"class":2119},".decoder.",[2113,5488,5489],{"class":2133},"as_mut",[2113,5491,3944],{"class":2119},[2113,5493,5494,5496,5498,5500,5502],{"class":2115,"line":2246},[2113,5495,4518],{"class":2326},[2113,5497,5479],{"class":2330},[2113,5499,179],{"class":2119},[2113,5501,4971],{"class":2133},[2113,5503,3912],{"class":2119},[2113,5505,5506,5508,5511,5513,5515],{"class":2115,"line":2464},[2113,5507,4559],{"class":2326},[2113,5509,5510],{"class":2414}," Ok",[2113,5512,2423],{"class":2119},[2113,5514,3095],{"class":2166},[2113,5516,2553],{"class":2119},[2113,5518,5519],{"class":2115,"line":2085},[2113,5520,4578],{"class":2119},[2113,5522,5523],{"class":2115,"line":2514},[2113,5524,2125],{"emptyLinePlaceholder":1767},[2113,5526,5527,5529,5532,5534,5536,5538,5540,5542,5544,5546,5548],{"class":2115,"line":2533},[2113,5528,4583],{"class":2326},[2113,5530,5531],{"class":2330}," decoder_read_result",[2113,5533,2153],{"class":2334},[2113,5535,5479],{"class":2330},[2113,5537,179],{"class":2119},[2113,5539,3941],{"class":2133},[2113,5541,5224],{"class":2119},[2113,5543,4567],{"class":2133},[2113,5545,2423],{"class":2119},[2113,5547,4469],{"class":2330},[2113,5549,2553],{"class":2119},[2113,5551,5552],{"class":2115,"line":2556},[2113,5553,2125],{"emptyLinePlaceholder":1767},[2113,5555,5556,5558,5561,5563,5566,5568],{"class":2115,"line":2569},[2113,5557,4583],{"class":2326},[2113,5559,5560],{"class":2330}," count",[2113,5562,2153],{"class":2334},[2113,5564,5565],{"class":2326}," match",[2113,5567,5531],{"class":2330},[2113,5569,2647],{"class":2119},[2113,5571,5572,5575,5577,5580],{"class":2115,"line":2575},[2113,5573,5574],{"class":2414},"            Ok",[2113,5576,2423],{"class":2119},[2113,5578,5579],{"class":2330},"count",[2113,5581,5582],{"class":2119},") => {\n",[2113,5584,5585],{"class":2115,"line":2596},[2113,5586,5587],{"class":2330},"                count\n",[2113,5589,5590],{"class":2115,"line":3192},[2113,5591,5172],{"class":2119},[2113,5593,5594,5597,5599,5602],{"class":2115,"line":3213},[2113,5595,5596],{"class":2414},"            Err",[2113,5598,2423],{"class":2119},[2113,5600,5601],{"class":2330},"e",[2113,5603,5582],{"class":2119},[2113,5605,5606,5609,5612,5614,5616],{"class":2115,"line":3236},[2113,5607,5608],{"class":2326},"                return",[2113,5610,5611],{"class":2414}," Err",[2113,5613,2423],{"class":2119},[2113,5615,5601],{"class":2330},[2113,5617,2553],{"class":2119},[2113,5619,5620],{"class":2115,"line":3248},[2113,5621,5172],{"class":2119},[2113,5623,5624],{"class":2115,"line":4899},[2113,5625,4632],{"class":2119},[2113,5627,5628],{"class":2115,"line":1777},[2113,5629,2125],{"emptyLinePlaceholder":1767},[2113,5631,5632,5634,5636,5639,5641],{"class":2115,"line":4931},[2113,5633,4518],{"class":2326},[2113,5635,5560],{"class":2330},[2113,5637,5638],{"class":2334}," !=",[2113,5640,3208],{"class":2166},[2113,5642,2647],{"class":2119},[2113,5644,5645,5647,5649,5651,5653],{"class":2115,"line":4961},[2113,5646,4559],{"class":2326},[2113,5648,5510],{"class":2414},[2113,5650,2423],{"class":2119},[2113,5652,5579],{"class":2330},[2113,5654,2553],{"class":2119},[2113,5656,5657],{"class":2115,"line":4976},[2113,5658,4578],{"class":2119},[2113,5660,5661],{"class":2115,"line":4993},[2113,5662,2125],{"emptyLinePlaceholder":1767},[2113,5664,5665,5667,5669,5672,5674],{"class":2115,"line":5013},[2113,5666,4518],{"class":2326},[2113,5668,5560],{"class":2330},[2113,5670,5671],{"class":2334}," ==",[2113,5673,3208],{"class":2166},[2113,5675,2647],{"class":2119},[2113,5677,5678,5680,5682,5684,5686,5688,5691],{"class":2115,"line":5018},[2113,5679,4595],{"class":2326},[2113,5681,5479],{"class":2330},[2113,5683,2153],{"class":2334},[2113,5685,4464],{"class":2414},[2113,5687,5486],{"class":2119},[2113,5689,5690],{"class":2133},"take",[2113,5692,3944],{"class":2119},[2113,5694,5695,5697,5700,5702,5704,5707,5710,5712,5714],{"class":2115,"line":5042},[2113,5696,5021],{"class":2326},[2113,5698,5699],{"class":2326}," let",[2113,5701,5190],{"class":2414},[2113,5703,2423],{"class":2119},[2113,5705,5706],{"class":2330},"decoder",[2113,5708,5709],{"class":2119},") ",[2113,5711,2335],{"class":2334},[2113,5713,5479],{"class":2330},[2113,5715,2647],{"class":2119},[2113,5717,5718,5721,5723,5726,5728,5730,5732,5735],{"class":2115,"line":5057},[2113,5719,5720],{"class":2326},"                let",[2113,5722,3975],{"class":2326},[2113,5724,5725],{"class":2330}," reader",[2113,5727,2153],{"class":2334},[2113,5729,5479],{"class":2330},[2113,5731,179],{"class":2119},[2113,5733,5734],{"class":2133},"into_inner",[2113,5736,3944],{"class":2119},[2113,5738,5739],{"class":2115,"line":5062},[2113,5740,5741],{"class":2396},"                \u002F\u002F S'il reste encore des octets à lire dans reader alors on continue, sinon on s'arrête\n",[2113,5743,5744,5746,5748,5750,5752,5755,5757],{"class":2115,"line":5098},[2113,5745,5065],{"class":2326},[2113,5747,5725],{"class":2330},[2113,5749,179],{"class":2119},[2113,5751,4401],{"class":2133},[2113,5753,5754],{"class":2119},"()?.",[2113,5756,5037],{"class":2133},[2113,5758,3912],{"class":2119},[2113,5760,5761,5764,5766,5768,5770],{"class":2115,"line":5117},[2113,5762,5763],{"class":2326},"                    return",[2113,5765,5510],{"class":2414},[2113,5767,2423],{"class":2119},[2113,5769,3095],{"class":2166},[2113,5771,2553],{"class":2119},[2113,5773,5774],{"class":2115,"line":5142},[2113,5775,5166],{"class":2119},[2113,5777,5778,5781,5783,5786],{"class":2115,"line":5148},[2113,5779,5780],{"class":2330},"                reader",[2113,5782,179],{"class":2119},[2113,5784,5785],{"class":2133},"reset",[2113,5787,3944],{"class":2119},[2113,5789,5790],{"class":2115,"line":5163},[2113,5791,2125],{"emptyLinePlaceholder":1767},[2113,5793,5794,5796,5799,5801,5803,5805,5807,5809,5811,5813,5816],{"class":2115,"line":5169},[2113,5795,5045],{"class":2414},[2113,5797,5798],{"class":2119},".decoder ",[2113,5800,2335],{"class":2334},[2113,5802,5190],{"class":2414},[2113,5804,2423],{"class":2119},[2113,5806,3876],{"class":2414},[2113,5808,2418],{"class":2119},[2113,5810,3961],{"class":2133},[2113,5812,2423],{"class":2119},[2113,5814,5815],{"class":2330},"reader",[2113,5817,5818],{"class":2119},"));\n",[2113,5820,5821],{"class":2115,"line":5175},[2113,5822,5172],{"class":2119},[2113,5824,5825],{"class":2115,"line":5180},[2113,5826,4578],{"class":2119},[2113,5828,5829],{"class":2115,"line":5199},[2113,5830,4665],{"class":2119},[2113,5832,5833],{"class":2115,"line":5204},[2113,5834,2599],{"class":2119},[12,5836,5837,5838,5840],{},"J'ai donc rédigé une méthode ",[184,5839,4567],{}," qui remplit le tampon tant que le nombre d'octets lus est inférieur à la taille du\ntampon. On ne retourne 0 que dans le cas où la lecture du fichier est terminée.",[2105,5842,5844],{"className":3855,"code":5843,"language":1775,"meta":1646,"style":1646},"fn read(&mut self, buf: &mut [u8]) -> io::Result\u003Cusize> {\n    let mut total_bytes_read = 0;\n\n    while total_bytes_read \u003C buf.len() {\n        let bytes_to_read = &mut buf[total_bytes_read..];\n        let bytes_read = self.read_some_bytes(bytes_to_read)?;\n        total_bytes_read += bytes_read;\n\n        if bytes_read == 0 {\n            break;\n        }\n    }\n\n    Ok(total_bytes_read)\n}\n",[184,5845,5846,5884,5899,5903,5921,5945,5968,5980,5984,5996,6003,6007,6011,6015,6026],{"__ignoreMap":1646},[2113,5847,5848,5850,5852,5854,5856,5858,5860,5862,5864,5866,5868,5870,5872,5874,5876,5878,5880,5882],{"class":2115,"line":2116},[2113,5849,3906],{"class":2326},[2113,5851,4457],{"class":2133},[2113,5853,4027],{"class":2119},[2113,5855,4030],{"class":2326},[2113,5857,4464],{"class":2414},[2113,5859,932],{"class":2119},[2113,5861,4469],{"class":2330},[2113,5863,4472],{"class":2119},[2113,5865,4030],{"class":2326},[2113,5867,4477],{"class":2119},[2113,5869,4480],{"class":2414},[2113,5871,4483],{"class":2119},[2113,5873,4486],{"class":2414},[2113,5875,2418],{"class":2119},[2113,5877,4491],{"class":2414},[2113,5879,3109],{"class":2119},[2113,5881,4496],{"class":2414},[2113,5883,4449],{"class":2119},[2113,5885,5886,5888,5890,5893,5895,5897],{"class":2115,"line":1647},[2113,5887,3917],{"class":2326},[2113,5889,3975],{"class":2326},[2113,5891,5892],{"class":2330}," total_bytes_read",[2113,5894,2153],{"class":2334},[2113,5896,3208],{"class":2166},[2113,5898,2487],{"class":2119},[2113,5900,5901],{"class":2115,"line":1652},[2113,5902,2125],{"emptyLinePlaceholder":1767},[2113,5904,5905,5908,5910,5913,5915,5917,5919],{"class":2115,"line":2185},[2113,5906,5907],{"class":2326},"    while",[2113,5909,5892],{"class":2330},[2113,5911,5912],{"class":2334}," \u003C",[2113,5914,4535],{"class":2330},[2113,5916,179],{"class":2119},[2113,5918,4540],{"class":2133},[2113,5920,3912],{"class":2119},[2113,5922,5923,5925,5928,5930,5933,5935,5937,5939,5942],{"class":2115,"line":2230},[2113,5924,4583],{"class":2326},[2113,5926,5927],{"class":2330}," bytes_to_read",[2113,5929,2153],{"class":2334},[2113,5931,5932],{"class":2119}," &",[2113,5934,4030],{"class":2326},[2113,5936,4535],{"class":2330},[2113,5938,4124],{"class":2119},[2113,5940,5941],{"class":2330},"total_bytes_read",[2113,5943,5944],{"class":2119},"..];\n",[2113,5946,5947,5949,5952,5954,5956,5958,5960,5962,5965],{"class":2115,"line":2235},[2113,5948,4583],{"class":2326},[2113,5950,5951],{"class":2330}," bytes_read",[2113,5953,2153],{"class":2334},[2113,5955,4464],{"class":2414},[2113,5957,179],{"class":2119},[2113,5959,5360],{"class":2133},[2113,5961,2423],{"class":2119},[2113,5963,5964],{"class":2330},"bytes_to_read",[2113,5966,5967],{"class":2119},")?;\n",[2113,5969,5970,5973,5976,5978],{"class":2115,"line":2241},[2113,5971,5972],{"class":2330},"        total_bytes_read",[2113,5974,5975],{"class":2334}," +=",[2113,5977,5951],{"class":2330},[2113,5979,2487],{"class":2119},[2113,5981,5982],{"class":2115,"line":2246},[2113,5983,2125],{"emptyLinePlaceholder":1767},[2113,5985,5986,5988,5990,5992,5994],{"class":2115,"line":2464},[2113,5987,4518],{"class":2326},[2113,5989,5951],{"class":2330},[2113,5991,5671],{"class":2334},[2113,5993,3208],{"class":2166},[2113,5995,2647],{"class":2119},[2113,5997,5998,6001],{"class":2115,"line":2085},[2113,5999,6000],{"class":2326},"            break",[2113,6002,2487],{"class":2119},[2113,6004,6005],{"class":2115,"line":2514},[2113,6006,4578],{"class":2119},[2113,6008,6009],{"class":2115,"line":2533},[2113,6010,4665],{"class":2119},[2113,6012,6013],{"class":2115,"line":2556},[2113,6014,2125],{"emptyLinePlaceholder":1767},[2113,6016,6017,6020,6022,6024],{"class":2115,"line":2569},[2113,6018,6019],{"class":2414},"    Ok",[2113,6021,2423],{"class":2119},[2113,6023,5941],{"class":2330},[2113,6025,4660],{"class":2119},[2113,6027,6028],{"class":2115,"line":2575},[2113,6029,2599],{"class":2119},[133,6031,6033],{"id":6032},"les-fichiers-dattributs","Les fichiers d'attributs",[12,6035,6036],{},"Les fichiers d'attributs sont d'abord des fichiers compressés en utilisant la méthode décrite précédemment. J'ai donc\nutilisé le Reader que j'ai écrit précédemment pour décompresser les fichiers au fur et à mesure de leur lecture.",[12,6038,6039],{},"Je me suis ensuite attaqué au décodage des fichiers d'attributs. En réécrivant le code, j'ai découvert que certains\nnombres étaient mal encodés, ce qui provoquait des erreurs de décodage que j'ai dû gérer avec plus de flexibilité.",[12,6041,6042,6043,6045,6046,6048,6049,6054],{},"Pour lire un ",[184,6044,3778],{},", j'ai ajouté un trait au trait ",[184,6047,4432],{},". Je me suis basé sur le ",[49,6050,6053],{"href":6051,"rel":6052},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_attrib.c#L548",[347],"code C de BackupPC",",\ntout en modifiant la philosophie pour répondre au fonctionnement d'un Reader Rust.",[2105,6056,6058],{"className":3855,"code":6057,"language":1775,"meta":1646,"style":1646},"pub trait VarintRead: Read {\n    fn read_varint(&mut self) -> io::Result\u003Cu64> {\n        let mut result = 0;\n        let mut shift = 0;\n\n        loop {\n            let mut buf: [u8; 1] = [0u8; 1];\n            self.read_exact(&mut buf)?;\n\n            let byte = buf[0];\n            let val = (byte & 0x7F) as u64;\n            if shift >= 64 || val \u003C\u003C shift >> shift != val {\n                eprintln!(\"Varint too large: probably corrupted data\");\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    \"Varint too large: probably corrupted data\",\n                ));\n            }\n\n            result |= val \u003C\u003C shift;\n            if byte & 0x80 == 0 {\n                return Ok(result);\n            }\n            shift += 7;\n        }\n    }\n}\n",[184,6059,6060,6076,6104,6119,6134,6138,6145,6180,6197,6201,6218,6248,6279,6291,6312,6329,6336,6341,6345,6349,6365,6382,6395,6399,6411,6415,6419],{"__ignoreMap":1646},[2113,6061,6062,6064,6067,6070,6072,6074],{"class":2115,"line":2116},[2113,6063,5374],{"class":2326},[2113,6065,6066],{"class":2326}," trait",[2113,6068,6069],{"class":2414}," VarintRead",[2113,6071,4429],{"class":2119},[2113,6073,4432],{"class":2414},[2113,6075,2647],{"class":2119},[2113,6077,6078,6080,6083,6085,6087,6089,6091,6093,6095,6097,6099,6102],{"class":2115,"line":1647},[2113,6079,4454],{"class":2326},[2113,6081,6082],{"class":2133}," read_varint",[2113,6084,4027],{"class":2119},[2113,6086,4030],{"class":2326},[2113,6088,4464],{"class":2414},[2113,6090,4790],{"class":2119},[2113,6092,4486],{"class":2414},[2113,6094,2418],{"class":2119},[2113,6096,4491],{"class":2414},[2113,6098,3109],{"class":2119},[2113,6100,6101],{"class":2414},"u64",[2113,6103,4449],{"class":2119},[2113,6105,6106,6108,6110,6113,6115,6117],{"class":2115,"line":1652},[2113,6107,4583],{"class":2326},[2113,6109,3975],{"class":2326},[2113,6111,6112],{"class":2330}," result",[2113,6114,2153],{"class":2334},[2113,6116,3208],{"class":2166},[2113,6118,2487],{"class":2119},[2113,6120,6121,6123,6125,6128,6130,6132],{"class":2115,"line":2185},[2113,6122,4583],{"class":2326},[2113,6124,3975],{"class":2326},[2113,6126,6127],{"class":2330}," shift",[2113,6129,2153],{"class":2334},[2113,6131,3208],{"class":2166},[2113,6133,2487],{"class":2119},[2113,6135,6136],{"class":2115,"line":2230},[2113,6137,2125],{"emptyLinePlaceholder":1767},[2113,6139,6140,6143],{"class":2115,"line":2235},[2113,6141,6142],{"class":2326},"        loop",[2113,6144,2647],{"class":2119},[2113,6146,6147,6149,6151,6153,6156,6158,6161,6163,6165,6167,6169,6171,6173,6175,6177],{"class":2115,"line":2241},[2113,6148,4595],{"class":2326},[2113,6150,3975],{"class":2326},[2113,6152,4535],{"class":2330},[2113,6154,6155],{"class":2119},": [",[2113,6157,4480],{"class":2414},[2113,6159,6160],{"class":2119},"; ",[2113,6162,3686],{"class":2166},[2113,6164,4129],{"class":2119},[2113,6166,2335],{"class":2334},[2113,6168,4477],{"class":2119},[2113,6170,3095],{"class":2166},[2113,6172,4480],{"class":2414},[2113,6174,6160],{"class":2119},[2113,6176,3686],{"class":2166},[2113,6178,6179],{"class":2119},"];\n",[2113,6181,6182,6184,6186,6189,6191,6193,6195],{"class":2115,"line":2246},[2113,6183,5183],{"class":2414},[2113,6185,179],{"class":2119},[2113,6187,6188],{"class":2133},"read_exact",[2113,6190,4027],{"class":2119},[2113,6192,4030],{"class":2326},[2113,6194,4535],{"class":2330},[2113,6196,5967],{"class":2119},[2113,6198,6199],{"class":2115,"line":2464},[2113,6200,2125],{"emptyLinePlaceholder":1767},[2113,6202,6203,6205,6208,6210,6212,6214,6216],{"class":2115,"line":2085},[2113,6204,4595],{"class":2326},[2113,6206,6207],{"class":2330}," byte",[2113,6209,2153],{"class":2334},[2113,6211,4535],{"class":2330},[2113,6213,4124],{"class":2119},[2113,6215,3095],{"class":2166},[2113,6217,6179],{"class":2119},[2113,6219,6220,6222,6225,6227,6229,6232,6235,6238,6240,6243,6246],{"class":2115,"line":2514},[2113,6221,4595],{"class":2326},[2113,6223,6224],{"class":2330}," val",[2113,6226,2153],{"class":2334},[2113,6228,2495],{"class":2119},[2113,6230,6231],{"class":2330},"byte",[2113,6233,6234],{"class":2119}," & ",[2113,6236,6237],{"class":2166},"0x7F",[2113,6239,5709],{"class":2119},[2113,6241,6242],{"class":2326},"as",[2113,6244,6245],{"class":2414}," u64",[2113,6247,2487],{"class":2119},[2113,6249,6250,6252,6254,6257,6259,6261,6263,6266,6268,6271,6273,6275,6277],{"class":2115,"line":2533},[2113,6251,5021],{"class":2326},[2113,6253,6127],{"class":2330},[2113,6255,6256],{"class":2334}," >=",[2113,6258,3043],{"class":2166},[2113,6260,4140],{"class":2334},[2113,6262,6224],{"class":2330},[2113,6264,6265],{"class":2334}," \u003C\u003C",[2113,6267,6127],{"class":2330},[2113,6269,6270],{"class":2334}," >>",[2113,6272,6127],{"class":2330},[2113,6274,5638],{"class":2334},[2113,6276,6224],{"class":2330},[2113,6278,2647],{"class":2119},[2113,6280,6281,6284,6286,6289],{"class":2115,"line":2556},[2113,6282,6283],{"class":2133},"                eprintln!",[2113,6285,2423],{"class":2119},[2113,6287,6288],{"class":2149},"\"Varint too large: probably corrupted data\"",[2113,6290,2553],{"class":2119},[2113,6292,6293,6295,6297,6299,6301,6303,6306,6308,6310],{"class":2115,"line":2569},[2113,6294,5608],{"class":2326},[2113,6296,5611],{"class":2414},[2113,6298,2423],{"class":2119},[2113,6300,4486],{"class":2414},[2113,6302,2418],{"class":2119},[2113,6304,6305],{"class":2414},"Error",[2113,6307,2418],{"class":2119},[2113,6309,3961],{"class":2133},[2113,6311,3146],{"class":2119},[2113,6313,6314,6317,6319,6322,6324,6327],{"class":2115,"line":2575},[2113,6315,6316],{"class":2414},"                    io",[2113,6318,2418],{"class":2119},[2113,6320,6321],{"class":2414},"ErrorKind",[2113,6323,2418],{"class":2119},[2113,6325,6326],{"class":2414},"InvalidData",[2113,6328,4706],{"class":2119},[2113,6330,6331,6334],{"class":2115,"line":2596},[2113,6332,6333],{"class":2149},"                    \"Varint too large: probably corrupted data\"",[2113,6335,4706],{"class":2119},[2113,6337,6338],{"class":2115,"line":3192},[2113,6339,6340],{"class":2119},"                ));\n",[2113,6342,6343],{"class":2115,"line":3213},[2113,6344,5172],{"class":2119},[2113,6346,6347],{"class":2115,"line":3236},[2113,6348,2125],{"emptyLinePlaceholder":1767},[2113,6350,6351,6354,6357,6359,6361,6363],{"class":2115,"line":3248},[2113,6352,6353],{"class":2330},"            result",[2113,6355,6356],{"class":2334}," |=",[2113,6358,6224],{"class":2330},[2113,6360,6265],{"class":2334},[2113,6362,6127],{"class":2330},[2113,6364,2487],{"class":2119},[2113,6366,6367,6369,6371,6373,6376,6378,6380],{"class":2115,"line":4899},[2113,6368,5021],{"class":2326},[2113,6370,6207],{"class":2330},[2113,6372,6234],{"class":2119},[2113,6374,6375],{"class":2166},"0x80",[2113,6377,5671],{"class":2334},[2113,6379,3208],{"class":2166},[2113,6381,2647],{"class":2119},[2113,6383,6384,6386,6388,6390,6393],{"class":2115,"line":1777},[2113,6385,5608],{"class":2326},[2113,6387,5510],{"class":2414},[2113,6389,2423],{"class":2119},[2113,6391,6392],{"class":2330},"result",[2113,6394,2553],{"class":2119},[2113,6396,6397],{"class":2115,"line":4931},[2113,6398,5172],{"class":2119},[2113,6400,6401,6404,6406,6409],{"class":2115,"line":4961},[2113,6402,6403],{"class":2330},"            shift",[2113,6405,5975],{"class":2334},[2113,6407,6408],{"class":2166}," 7",[2113,6410,2487],{"class":2119},[2113,6412,6413],{"class":2115,"line":4976},[2113,6414,4578],{"class":2119},[2113,6416,6417],{"class":2115,"line":4993},[2113,6418,4665],{"class":2119},[2113,6420,6421],{"class":2115,"line":5013},[2113,6422,2599],{"class":2119},[12,6424,6425,6426,6429],{},"Au cours de mes tests pour lire les fichiers d'attributs du pool, j'ai rencontré de nombreuses erreurs de décodage (avec\nle message ci-dessus : ",[362,6427,6428],{},"Varint too large: probably corrupted data","). Ce qui est rassurant, c'est que cela n'est pas lié\nà mon algorithme. BackupPC rencontre le même problème lors du décodage de ses propres fichiers (ce qui se traduit dans\nson cas par une date en janvier 1970) :",[12,6431,6432],{},[121,6433],{"src":6434,"alt":6435,"className":6436},"\u002FWoodstock\u002F2024-03-14_pr_backuppc_pool_1.png","Fichier corrompu",[126],[12,6438,6439],{},"La question est donc : mes données sont-elles corrompues ou s'agit-il d'un bug de BackupPC ? Je ne vais pas obtenir de\nréponse à cette question.",[133,6441,6443],{"id":6442},"la-lecture-des-machines-et-des-sauvegardes","La lecture des machines et des sauvegardes",[12,6445,6446],{},"La partie la plus ardue est terminée.",[506,6448,6449,6454,6460],{},[370,6450,6451,6452,179],{},"Pour lister les machines, il suffit de lister les dossiers du répertoire ",[184,6453,3758],{},[370,6455,6456,6457,179],{},"Pour lister les sauvegardes, il faut lire le fichier texte (tsv) ",[184,6458,6459],{},"__TOPDIR__\u002Fpc\u002F\u003Cmachine>\u002Fbackups",[370,6461,6462,6463,179],{},"Pour lister les dossiers partagés, il faut lister les dossiers du répertoire ",[184,6464,6465],{},"__TOPDIR__\u002Fpc\u002F\u003Cmachine>\u002F\u003Cbackup>",[12,6467,6468,6469,179],{},"J'ai écrit ces trois méthodes dans ce fichier, que je vous invite à consulter. Il n'est pas très complexe : ",[49,6470,6473],{"href":6471,"rel":6472},"https:\u002F\u002Fgogs.shadoware.org\u002Fphoenix\u002Fbackuppc_pool\u002Fsrc\u002Fbranch\u002Fmaster\u002Fsrc\u002Fhosts.rs",[347],"hosts.rs",[12,6475,6476],{},"Je peux donc affirmer que j'ai réussi à lire les fichiers du pool de BackupPC. Pour mieux visualiser le résultat, j'ai\ndécidé de me lancer un défi supplémentaire : écrire un pilote FUSE pour visualiser le pool de stockage.",[133,6478,6480],{"id":6479},"le-driver-fuse","Le driver FUSE",[12,6482,6483],{},"L'étape suivante consiste donc à développer un pilote FUSE qui intègre tout cela. Il s'agit de lire les fichiers\nd'attributs pour reconstituer la structure du système de fichiers (qui sont dans le pool et compressés), et de lire les\nfichiers compressés pour les données.",[12,6485,6486,6487,179],{},"Pour gérer la partie du système de fichiers, j'ai utilisé la bibliothèque ",[49,6488,6491],{"href":6489,"rel":6490},"https:\u002F\u002Fdocs.rs\u002Ffuser\u002Flatest\u002Ffuser\u002Findex.html",[347],"fuser",[12,6493,6494],{},"Le cœur du système ayant été développé, la partie système de fichiers consiste principalement à décoder le chemin fourni\npar FUSE, à décompressé et lire le fichier d'attributs. Dans le cas où l'utilisateur souhaite lire le contenu d'un\nfichier, il faut décompresser le fichier compressé.",[12,6496,6497],{},"J'ai donc la partie système de fichiers qui gère les requêtes FUSE et les transmet à la partie vue (avec un système de\ncache), et la partie vue qui prend un chemin et le transforme dans le format suivant en appelant les différentes\nméthodes précédentes :",[506,6499,6500,6503,6506,6509],{},[370,6501,6502],{},"nom de l'hôte",[370,6504,6505],{},"numéro de sauvegarde",[370,6507,6508],{},"chemin du partage",[370,6510,6511],{},"chemin",[12,6513,6514,6515,6520],{},"Le décodage est réalisé dans le fichier ",[49,6516,6519],{"href":6517,"rel":6518},"https:\u002F\u002Fgogs.shadoware.org\u002Fphoenix\u002Fbackuppc_pool\u002Fsrc\u002Fbranch\u002Fmaster\u002Fsrc\u002Fview.rs",[347],"view.rs",".\nPour cette partie, j'ai ajouté des tests unitaires. Ces tests unitaires m'ont permis d'itérer plus rapidement pour\nconstruire le système de fichiers (sans avoir à monter ce dernier à chaque fois).",[12,6522,6523,6524,6526,6527,6530],{},"On appelle la méthode avec le chemin découpé à chaque ",[184,6525,2520],{}," pour obtenir les différents éléments du chemin (sous forme de\n",[184,6528,6529],{},"&[&str]",").:",[2105,6532,6534],{"className":3855,"code":6533,"language":1775,"meta":1646,"style":1646},"pub fn list(&self, path: &[&str]) -> Result\u003CVec\u003CFileAttributes>> {\n    match path.len() {\n",[184,6535,6536,6577],{"__ignoreMap":1646},[2113,6537,6538,6540,6543,6546,6548,6550,6552,6555,6558,6561,6563,6565,6567,6569,6571,6574],{"class":2115,"line":2116},[2113,6539,5374],{"class":2326},[2113,6541,6542],{"class":2326}," fn",[2113,6544,6545],{"class":2133}," list",[2113,6547,4027],{"class":2119},[2113,6549,5216],{"class":2414},[2113,6551,932],{"class":2119},[2113,6553,6554],{"class":2330},"path",[2113,6556,6557],{"class":2119},": &[&",[2113,6559,6560],{"class":2414},"str",[2113,6562,4483],{"class":2119},[2113,6564,4491],{"class":2414},[2113,6566,3109],{"class":2119},[2113,6568,4733],{"class":2414},[2113,6570,3109],{"class":2119},[2113,6572,6573],{"class":2414},"FileAttributes",[2113,6575,6576],{"class":2119},">> {\n",[2113,6578,6579,6582,6585,6587,6589],{"class":2115,"line":1647},[2113,6580,6581],{"class":2326},"    match",[2113,6583,6584],{"class":2330}," path",[2113,6586,179],{"class":2119},[2113,6588,4540],{"class":2133},[2113,6590,3912],{"class":2119},[12,6592,6593],{},"On commence d'abord par examiner le nombre d'éléments dans le chemin. Si nous n'avons aucun élément, cela signifie que\nnous sommes à la racine et que nous souhaitons obtenir la liste des machines.",[2105,6595,6597],{"className":3855,"code":6596,"language":1775,"meta":1646,"style":1646},"        0 => {\n            let hosts = self.hosts.list_hosts()?;\n            Ok(hosts.into_iter().map(FileAttributes::from_host).collect())\n        }\n",[184,6598,6599,6607,6626,6661],{"__ignoreMap":1646},[2113,6600,6601,6604],{"class":2115,"line":2116},[2113,6602,6603],{"class":2166},"        0",[2113,6605,6606],{"class":2119}," => {\n",[2113,6608,6609,6611,6614,6616,6618,6621,6624],{"class":2115,"line":1647},[2113,6610,4595],{"class":2326},[2113,6612,6613],{"class":2330}," hosts",[2113,6615,2153],{"class":2334},[2113,6617,4464],{"class":2414},[2113,6619,6620],{"class":2119},".hosts.",[2113,6622,6623],{"class":2133},"list_hosts",[2113,6625,4611],{"class":2119},[2113,6627,6628,6630,6632,6635,6637,6640,6642,6645,6647,6649,6651,6654,6656,6659],{"class":2115,"line":1652},[2113,6629,5574],{"class":2414},[2113,6631,2423],{"class":2119},[2113,6633,6634],{"class":2330},"hosts",[2113,6636,179],{"class":2119},[2113,6638,6639],{"class":2133},"into_iter",[2113,6641,5224],{"class":2119},[2113,6643,6644],{"class":2133},"map",[2113,6646,2423],{"class":2119},[2113,6648,6573],{"class":2414},[2113,6650,2418],{"class":2119},[2113,6652,6653],{"class":2330},"from_host",[2113,6655,3938],{"class":2119},[2113,6657,6658],{"class":2133},"collect",[2113,6660,5229],{"class":2119},[2113,6662,6663],{"class":2115,"line":2185},[2113,6664,4578],{"class":2119},[12,6666,6667],{},"Si nous avons un seul élément, alors c'est le nom de la machine. Nous souhaitons donc récupérer la liste des sauvegardes\nassociées à cette machine.",[2105,6669,6671],{"className":3855,"code":6670,"language":1775,"meta":1646,"style":1646},"        1 => {\n            let backups = self.hosts.list_backups(path[0]);\n            match backups {\n                Ok(backups) => Ok(backups\n                    .into_iter()\n                    .map(|a| FileAttributes::from_backup(&a))\n                    .collect()),\n                Err(err) => {\n                    \u002F\u002F If the file isn't found, it's because we should return empty vec\n                    if let Some(io_err) = err.downcast_ref::\u003Cstd::io::Error>() {\n                        if io_err.kind() == std::io::ErrorKind::NotFound {\n                            Ok(Vec::new())\n                        } else {\n                            Err(err)\n                        }\n                    } else {\n                        Err(err)\n                    }\n                }\n            }\n        }\n",[184,6672,6673,6680,6707,6716,6736,6746,6776,6785,6797,6802,6845,6879,6894,6903,6914,6919,6928,6939,6944,6948,6952],{"__ignoreMap":1646},[2113,6674,6675,6678],{"class":2115,"line":2116},[2113,6676,6677],{"class":2166},"        1",[2113,6679,6606],{"class":2119},[2113,6681,6682,6684,6687,6689,6691,6693,6696,6698,6700,6702,6704],{"class":2115,"line":1647},[2113,6683,4595],{"class":2326},[2113,6685,6686],{"class":2330}," backups",[2113,6688,2153],{"class":2334},[2113,6690,4464],{"class":2414},[2113,6692,6620],{"class":2119},[2113,6694,6695],{"class":2133},"list_backups",[2113,6697,2423],{"class":2119},[2113,6699,6554],{"class":2330},[2113,6701,4124],{"class":2119},[2113,6703,3095],{"class":2166},[2113,6705,6706],{"class":2119},"]);\n",[2113,6708,6709,6712,6714],{"class":2115,"line":1652},[2113,6710,6711],{"class":2326},"            match",[2113,6713,6686],{"class":2330},[2113,6715,2647],{"class":2119},[2113,6717,6718,6721,6723,6725,6728,6731,6733],{"class":2115,"line":2185},[2113,6719,6720],{"class":2414},"                Ok",[2113,6722,2423],{"class":2119},[2113,6724,3680],{"class":2330},[2113,6726,6727],{"class":2119},") => ",[2113,6729,6730],{"class":2414},"Ok",[2113,6732,2423],{"class":2119},[2113,6734,6735],{"class":2330},"backups\n",[2113,6737,6738,6741,6743],{"class":2115,"line":2230},[2113,6739,6740],{"class":2119},"                    .",[2113,6742,6639],{"class":2133},[2113,6744,6745],{"class":2119},"()\n",[2113,6747,6748,6750,6752,6754,6757,6759,6761,6764,6766,6769,6771,6773],{"class":2115,"line":2235},[2113,6749,6740],{"class":2119},[2113,6751,6644],{"class":2133},[2113,6753,2423],{"class":2119},[2113,6755,6756],{"class":2334},"|",[2113,6758,49],{"class":2330},[2113,6760,6756],{"class":2334},[2113,6762,6763],{"class":2414}," FileAttributes",[2113,6765,2418],{"class":2119},[2113,6767,6768],{"class":2133},"from_backup",[2113,6770,4027],{"class":2119},[2113,6772,49],{"class":2330},[2113,6774,6775],{"class":2119},"))\n",[2113,6777,6778,6780,6782],{"class":2115,"line":2241},[2113,6779,6740],{"class":2119},[2113,6781,6658],{"class":2133},[2113,6783,6784],{"class":2119},"()),\n",[2113,6786,6787,6790,6792,6795],{"class":2115,"line":2246},[2113,6788,6789],{"class":2414},"                Err",[2113,6791,2423],{"class":2119},[2113,6793,6794],{"class":2330},"err",[2113,6796,5582],{"class":2119},[2113,6798,6799],{"class":2115,"line":2464},[2113,6800,6801],{"class":2396},"                    \u002F\u002F If the file isn't found, it's because we should return empty vec\n",[2113,6803,6804,6807,6809,6811,6813,6816,6818,6820,6823,6825,6828,6831,6834,6836,6838,6840,6842],{"class":2115,"line":2085},[2113,6805,6806],{"class":2326},"                    if",[2113,6808,5699],{"class":2326},[2113,6810,5190],{"class":2414},[2113,6812,2423],{"class":2119},[2113,6814,6815],{"class":2330},"io_err",[2113,6817,5709],{"class":2119},[2113,6819,2335],{"class":2334},[2113,6821,6822],{"class":2330}," err",[2113,6824,179],{"class":2119},[2113,6826,6827],{"class":2133},"downcast_ref",[2113,6829,6830],{"class":2119},"::\u003C",[2113,6832,6833],{"class":2414},"std",[2113,6835,2418],{"class":2119},[2113,6837,4486],{"class":2414},[2113,6839,2418],{"class":2119},[2113,6841,6305],{"class":2414},[2113,6843,6844],{"class":2119},">() {\n",[2113,6846,6847,6850,6853,6855,6858,6860,6862,6864,6866,6868,6870,6872,6874,6877],{"class":2115,"line":2514},[2113,6848,6849],{"class":2326},"                        if",[2113,6851,6852],{"class":2330}," io_err",[2113,6854,179],{"class":2119},[2113,6856,6857],{"class":2133},"kind",[2113,6859,4543],{"class":2119},[2113,6861,4132],{"class":2334},[2113,6863,3885],{"class":2414},[2113,6865,2418],{"class":2119},[2113,6867,4486],{"class":2414},[2113,6869,2418],{"class":2119},[2113,6871,6321],{"class":2414},[2113,6873,2418],{"class":2119},[2113,6875,6876],{"class":2414},"NotFound",[2113,6878,2647],{"class":2119},[2113,6880,6881,6884,6886,6888,6890,6892],{"class":2115,"line":2533},[2113,6882,6883],{"class":2414},"                            Ok",[2113,6885,2423],{"class":2119},[2113,6887,4733],{"class":2414},[2113,6889,2418],{"class":2119},[2113,6891,3961],{"class":2133},[2113,6893,5229],{"class":2119},[2113,6895,6896,6899,6901],{"class":2115,"line":2556},[2113,6897,6898],{"class":2119},"                        } ",[2113,6900,4227],{"class":2326},[2113,6902,2647],{"class":2119},[2113,6904,6905,6908,6910,6912],{"class":2115,"line":2569},[2113,6906,6907],{"class":2414},"                            Err",[2113,6909,2423],{"class":2119},[2113,6911,6794],{"class":2330},[2113,6913,4660],{"class":2119},[2113,6915,6916],{"class":2115,"line":2575},[2113,6917,6918],{"class":2119},"                        }\n",[2113,6920,6921,6924,6926],{"class":2115,"line":2596},[2113,6922,6923],{"class":2119},"                    } ",[2113,6925,4227],{"class":2326},[2113,6927,2647],{"class":2119},[2113,6929,6930,6933,6935,6937],{"class":2115,"line":3192},[2113,6931,6932],{"class":2414},"                        Err",[2113,6934,2423],{"class":2119},[2113,6936,6794],{"class":2330},[2113,6938,4660],{"class":2119},[2113,6940,6941],{"class":2115,"line":3213},[2113,6942,6943],{"class":2119},"                    }\n",[2113,6945,6946],{"class":2115,"line":3236},[2113,6947,5166],{"class":2119},[2113,6949,6950],{"class":2115,"line":3248},[2113,6951,5172],{"class":2119},[2113,6953,6954],{"class":2115,"line":4899},[2113,6955,4578],{"class":2119},[12,6957,6958,6959,6962,6963,6965],{},"Si nous avons plus de deux éléments, alors nous souhaitons retourner au moins le partage, voire les fichiers à\nsauvegarder. Le problème est que le nom du partage ",[184,6960,6961],{},"share"," peut lui-même contenir des ",[184,6964,2520],{},". Il peut donc être composé de\nplusieurs éléments.",[12,6967,6968,6969,6972],{},"Le but de la méthode ",[184,6970,6971],{},"list_shares_of"," est de retourner le partage si ce dernier est un partage valide, ainsi que la\npartie du chemin qui suit le partage.",[2105,6974,6976],{"className":3855,"code":6975,"language":1775,"meta":1646,"style":1646},"        _ => {\n            let (shares, selected_share, share_size) =\n                self.list_shares_of(path[0], path[1].parse::\u003Cu32>().unwrap_or(0), &path[2..])?;\n\n            let shares = shares.into_iter().map(FileAttributes::from_share).collect();\n",[184,6977,6978,6985,7009,7067,7071],{"__ignoreMap":1646},[2113,6979,6980,6983],{"class":2115,"line":2116},[2113,6981,6982],{"class":2330},"        _",[2113,6984,6606],{"class":2119},[2113,6986,6987,6989,6991,6994,6996,6999,7001,7004,7006],{"class":2115,"line":1647},[2113,6988,4595],{"class":2326},[2113,6990,2495],{"class":2119},[2113,6992,6993],{"class":2330},"shares",[2113,6995,932],{"class":2119},[2113,6997,6998],{"class":2330},"selected_share",[2113,7000,932],{"class":2119},[2113,7002,7003],{"class":2330},"share_size",[2113,7005,5709],{"class":2119},[2113,7007,7008],{"class":2334},"=\n",[2113,7010,7011,7013,7015,7017,7019,7021,7023,7025,7028,7030,7032,7034,7037,7040,7042,7045,7048,7051,7053,7055,7058,7060,7062,7064],{"class":2115,"line":1652},[2113,7012,5045],{"class":2414},[2113,7014,179],{"class":2119},[2113,7016,6971],{"class":2133},[2113,7018,2423],{"class":2119},[2113,7020,6554],{"class":2330},[2113,7022,4124],{"class":2119},[2113,7024,3095],{"class":2166},[2113,7026,7027],{"class":2119},"], ",[2113,7029,6554],{"class":2330},[2113,7031,4124],{"class":2119},[2113,7033,3686],{"class":2166},[2113,7035,7036],{"class":2119},"].",[2113,7038,7039],{"class":2133},"parse",[2113,7041,6830],{"class":2119},[2113,7043,7044],{"class":2414},"u32",[2113,7046,7047],{"class":2119},">().",[2113,7049,7050],{"class":2133},"unwrap_or",[2113,7052,2423],{"class":2119},[2113,7054,3095],{"class":2166},[2113,7056,7057],{"class":2119},"), &",[2113,7059,6554],{"class":2330},[2113,7061,4124],{"class":2119},[2113,7063,2547],{"class":2166},[2113,7065,7066],{"class":2119},"..])?;\n",[2113,7068,7069],{"class":2115,"line":2185},[2113,7070,2125],{"emptyLinePlaceholder":1767},[2113,7072,7073,7075,7078,7080,7082,7084,7086,7088,7090,7092,7094,7096,7099,7101,7103],{"class":2115,"line":2230},[2113,7074,4595],{"class":2326},[2113,7076,7077],{"class":2330}," shares",[2113,7079,2153],{"class":2334},[2113,7081,7077],{"class":2330},[2113,7083,179],{"class":2119},[2113,7085,6639],{"class":2133},[2113,7087,5224],{"class":2119},[2113,7089,6644],{"class":2133},[2113,7091,2423],{"class":2119},[2113,7093,6573],{"class":2414},[2113,7095,2418],{"class":2119},[2113,7097,7098],{"class":2330},"from_share",[2113,7100,3938],{"class":2119},[2113,7102,6658],{"class":2133},[2113,7104,3944],{"class":2119},[12,7106,7107,7108,7111],{},"Une fois que nous avons le nom de la machine, le numéro de la sauvegarde et le partage, nous pouvons appeler la méthode\n",[184,7109,7110],{},"list_file_from_dir"," pour récupérer les fichiers du sous-dossier du partage.",[2105,7113,7115],{"className":3855,"code":7114,"language":1775,"meta":1646,"style":1646},"            match selected_share {\n                None => Ok(shares),\n                Some(selected_share) => self.list_file_from_dir(\n                    path[0],\n                    path[1].parse::\u003Cu32>().unwrap_or(0),\n                    &selected_share,\n                    &path[(2 + share_size)..].join(\"\u002F\"),\n                ),\n            }\n        }\n    }\n}\n",[184,7116,7117,7126,7143,7162,7174,7200,7209,7238,7243,7247,7251,7255],{"__ignoreMap":1646},[2113,7118,7119,7121,7124],{"class":2115,"line":2116},[2113,7120,6711],{"class":2326},[2113,7122,7123],{"class":2330}," selected_share",[2113,7125,2647],{"class":2119},[2113,7127,7128,7131,7134,7136,7138,7140],{"class":2115,"line":1647},[2113,7129,7130],{"class":2414},"                None",[2113,7132,7133],{"class":2119}," => ",[2113,7135,6730],{"class":2414},[2113,7137,2423],{"class":2119},[2113,7139,6993],{"class":2330},[2113,7141,7142],{"class":2119},"),\n",[2113,7144,7145,7148,7150,7152,7154,7156,7158,7160],{"class":2115,"line":1652},[2113,7146,7147],{"class":2414},"                Some",[2113,7149,2423],{"class":2119},[2113,7151,6998],{"class":2330},[2113,7153,6727],{"class":2119},[2113,7155,5216],{"class":2414},[2113,7157,179],{"class":2119},[2113,7159,7110],{"class":2133},[2113,7161,3146],{"class":2119},[2113,7163,7164,7167,7169,7171],{"class":2115,"line":2185},[2113,7165,7166],{"class":2330},"                    path",[2113,7168,4124],{"class":2119},[2113,7170,3095],{"class":2166},[2113,7172,7173],{"class":2119},"],\n",[2113,7175,7176,7178,7180,7182,7184,7186,7188,7190,7192,7194,7196,7198],{"class":2115,"line":2230},[2113,7177,7166],{"class":2330},[2113,7179,4124],{"class":2119},[2113,7181,3686],{"class":2166},[2113,7183,7036],{"class":2119},[2113,7185,7039],{"class":2133},[2113,7187,6830],{"class":2119},[2113,7189,7044],{"class":2414},[2113,7191,7047],{"class":2119},[2113,7193,7050],{"class":2133},[2113,7195,2423],{"class":2119},[2113,7197,3095],{"class":2166},[2113,7199,7142],{"class":2119},[2113,7201,7202,7205,7207],{"class":2115,"line":2235},[2113,7203,7204],{"class":2119},"                    &",[2113,7206,6998],{"class":2330},[2113,7208,4706],{"class":2119},[2113,7210,7211,7213,7215,7218,7220,7223,7225,7228,7231,7233,7236],{"class":2115,"line":2241},[2113,7212,7204],{"class":2119},[2113,7214,6554],{"class":2330},[2113,7216,7217],{"class":2119},"[(",[2113,7219,2547],{"class":2166},[2113,7221,7222],{"class":2119}," + ",[2113,7224,7003],{"class":2330},[2113,7226,7227],{"class":2119},")..].",[2113,7229,7230],{"class":2133},"join",[2113,7232,2423],{"class":2119},[2113,7234,7235],{"class":2149},"\"\u002F\"",[2113,7237,7142],{"class":2119},[2113,7239,7240],{"class":2115,"line":2246},[2113,7241,7242],{"class":2119},"                ),\n",[2113,7244,7245],{"class":2115,"line":2464},[2113,7246,5172],{"class":2119},[2113,7248,7249],{"class":2115,"line":2085},[2113,7250,4578],{"class":2119},[2113,7252,7253],{"class":2115,"line":2514},[2113,7254,4665],{"class":2119},[2113,7256,7257],{"class":2115,"line":2533},[2113,7258,2599],{"class":2119},[12,7260,7261,7262,7267],{},"Enfin, pour la partie FUSE, je vous invite à consulter le fichier ",[49,7263,7266],{"href":7264,"rel":7265},"https:\u002F\u002Fgogs.shadoware.org\u002Fphoenix\u002Fbackuppc_pool\u002Fsrc\u002Fbranch\u002Fmaster\u002Fsrc\u002Ffilesystem.rs",[347],"filesystem.rs",".\nCe fichier fait simplement le lien entre la partie FUSE et la partie vue.",[12,7269,7270],{},"Sur la partie FUSE, j'ai ajouté un cache pour éviter de lire plusieurs fois le même fichier (d'attributs) dans le pool.",[12,7272,7273,7274,7277],{},"Une fois le système de fichiers construit, il était nécessaire de le tester avec des tests réels. Le premier test a\nconsisté à parcourir l'ensemble du système de fichiers pour vérifier si la partie attribut fonctionnait correctement.\nPour cela, j'ai utilisé le programme ",[184,7275,7276],{},"filelight",". Ce programme permet de parcourir le système de fichiers et de\nvisualiser la taille des fichiers.",[12,7279,7280],{},"Voici un exemple sur une partie de la sauvegarde :",[12,7282,7283],{},[121,7284],{"src":7285,"alt":7286,"className":7287},"\u002FWoodstock\u002Ffilelight_localhost.png","Filelight",[126],[12,7289,7290],{},"Enfin, pour tester la capacité du programme à lire tous les fichiers du système de fichiers, j'ai écrit un petit script\nshell.",[2105,7292,7294],{"className":2317,"code":7293,"language":2319,"meta":1646,"style":1646},"#!\u002Fbin\u002Fbash\n\n# Fichiers de sortie\noutput_file=\"md5sums.txt\"\nerror_file=\"errors.txt\"\n\n# Parcourir tous les fichiers du système de fichiers\nfind \u002Fhome\u002Fphoenix\u002Ftmp\u002Ftest -type f -print0 | while IFS= read -r -d '' file; do\n    # Essayer de calculer le hash MD5 du fichier\n    if md5sum \"$file\" >> \"$output_file\"; then\n        echo \"Processed $file\"\n    else\n        # Si le fichier ne peut pas être lu, écrire le nom du fichier dans le fichier d'erreur\n        echo \"Failed to process $file\" >> \"$error_file\"\n    fi\ndone\n",[184,7295,7296,7301,7305,7310,7320,7330,7334,7339,7385,7390,7422,7435,7440,7445,7465,7470],{"__ignoreMap":1646},[2113,7297,7298],{"class":2115,"line":2116},[2113,7299,7300],{"class":2396},"#!\u002Fbin\u002Fbash\n",[2113,7302,7303],{"class":2115,"line":1647},[2113,7304,2125],{"emptyLinePlaceholder":1767},[2113,7306,7307],{"class":2115,"line":1652},[2113,7308,7309],{"class":2396},"# Fichiers de sortie\n",[2113,7311,7312,7315,7317],{"class":2115,"line":2185},[2113,7313,7314],{"class":2330},"output_file",[2113,7316,2335],{"class":2334},[2113,7318,7319],{"class":2149},"\"md5sums.txt\"\n",[2113,7321,7322,7325,7327],{"class":2115,"line":2230},[2113,7323,7324],{"class":2330},"error_file",[2113,7326,2335],{"class":2334},[2113,7328,7329],{"class":2149},"\"errors.txt\"\n",[2113,7331,7332],{"class":2115,"line":2235},[2113,7333,2125],{"emptyLinePlaceholder":1767},[2113,7335,7336],{"class":2115,"line":2241},[2113,7337,7338],{"class":2396},"# Parcourir tous les fichiers du système de fichiers\n",[2113,7340,7341,7344,7347,7350,7352,7355,7358,7361,7364,7366,7368,7371,7374,7377,7380,7382],{"class":2115,"line":2246},[2113,7342,7343],{"class":2133},"find",[2113,7345,7346],{"class":2149}," \u002Fhome\u002Fphoenix\u002Ftmp\u002Ftest",[2113,7348,7349],{"class":2166}," -type",[2113,7351,3920],{"class":2149},[2113,7353,7354],{"class":2166}," -print0",[2113,7356,7357],{"class":2119}," | ",[2113,7359,7360],{"class":2326},"while",[2113,7362,7363],{"class":2330}," IFS",[2113,7365,2335],{"class":2334},[2113,7367,4457],{"class":2334},[2113,7369,7370],{"class":2166}," -r",[2113,7372,7373],{"class":2166}," -d",[2113,7375,7376],{"class":2149}," ''",[2113,7378,7379],{"class":2149}," file",[2113,7381,6160],{"class":2119},[2113,7383,7384],{"class":2326},"do\n",[2113,7386,7387],{"class":2115,"line":2464},[2113,7388,7389],{"class":2396},"    # Essayer de calculer le hash MD5 du fichier\n",[2113,7391,7392,7395,7398,7401,7404,7407,7410,7412,7415,7417,7419],{"class":2115,"line":2085},[2113,7393,7394],{"class":2326},"    if",[2113,7396,7397],{"class":2133}," md5sum",[2113,7399,7400],{"class":2149}," \"",[2113,7402,7403],{"class":2330},"$file",[2113,7405,7406],{"class":2149},"\"",[2113,7408,7409],{"class":2119}," >> ",[2113,7411,7406],{"class":2149},[2113,7413,7414],{"class":2330},"$output_file",[2113,7416,7406],{"class":2149},[2113,7418,6160],{"class":2119},[2113,7420,7421],{"class":2326},"then\n",[2113,7423,7424,7427,7430,7432],{"class":2115,"line":2514},[2113,7425,7426],{"class":2334},"        echo",[2113,7428,7429],{"class":2149}," \"Processed ",[2113,7431,7403],{"class":2330},[2113,7433,7434],{"class":2149},"\"\n",[2113,7436,7437],{"class":2115,"line":2533},[2113,7438,7439],{"class":2326},"    else\n",[2113,7441,7442],{"class":2115,"line":2556},[2113,7443,7444],{"class":2396},"        # Si le fichier ne peut pas être lu, écrire le nom du fichier dans le fichier d'erreur\n",[2113,7446,7447,7449,7452,7454,7456,7458,7460,7463],{"class":2115,"line":2569},[2113,7448,7426],{"class":2334},[2113,7450,7451],{"class":2149}," \"Failed to process ",[2113,7453,7403],{"class":2330},[2113,7455,7406],{"class":2149},[2113,7457,7409],{"class":2119},[2113,7459,7406],{"class":2149},[2113,7461,7462],{"class":2330},"$error_file",[2113,7464,7434],{"class":2149},[2113,7466,7467],{"class":2115,"line":2575},[2113,7468,7469],{"class":2326},"    fi\n",[2113,7471,7472],{"class":2115,"line":2596},[2113,7473,7474],{"class":2326},"done\n",[12,7476,7477],{},"J'ai récemment commencé à développer un autre programme en Rust qui utilise la notion de multithreading.",[133,7479,7481],{"id":7480},"les-avantages-de-rust","Les avantages de Rust",[12,7483,7484],{},"En travaillant sur ce petit programme Rust, j'ai apprécié plusieurs aspects du langage. Rust offre la possibilité\nd'écrire du code de bas niveau sans avoir à gérer la mémoire comme en C, ce qui m'a procuré un sentiment de sécurité. De\nplus, la documentation de Rust est bien conçue et m'a permis de progresser rapidement.",[12,7486,7487,7488,7493],{},"J'ai également trouvé la syntaxe de Rust très lisible, à condition de ne pas utiliser les durées de vie. La présence de\nnombreux packages (crates) sur ",[49,7489,7492],{"href":7490,"rel":7491},"https:\u002F\u002Fcrates.io\u002F",[347],"crates.io"," facilite le développement rapide de fonctionnalités.",[12,7495,7496],{},"Clippy, un outil de vérification du code Rust, m'a été très utile. Il donne des conseils pour améliorer le code, ce qui\nm'a aidé à progresser, même s'il me reste encore du chemin à parcourir.",[133,7498,7500],{"id":7499},"les-défis-rencontrés-avec-rust","Les défis rencontrés avec Rust",[12,7502,7503,7504,7507],{},"Malgré ces avantages, j'ai rencontré quelques difficultés, probablement dues à ma méconnaissance du langage Rust. Par\nexemple, pour certaines parties du code, je me suis retrouvé avec du code contenant le mot ",[362,7505,7506],{},"unsafe",". J'ai refusé de\nl'utiliser, ce qui a rendu mon code plus complexe.",[12,7509,7510,7511,7514,7515,2384],{},"J'ai un cas particulier pour lequel je n'ai pas trouvé de solution. Dans mon logiciel de sauvegarde, je lis des noms de\nfichier que je stocke en tant que ",[184,7512,7513],{},"[u8]"," en Protobuf dans un fichier. Si je traite ces noms de fichier en tant que\nchaîne de caractères, je me retrouve avec des erreurs potentielles de conversion si le nom de fichier n'est pas en UTF-8\nsous Linux et en UTF-16 sous Windows. Pour stocker donc sous forme d'octet, j'utilise ces méthodes qui m'obligent à\nutiliser ",[184,7516,7506],{},[2105,7518,7520],{"className":3855,"code":7519,"language":1775,"meta":1646,"style":1646},"#[must_use]\npub fn osstr_to_vec(path: &OsStr) -> Vec\u003Cu8> {\n    path.as_encoded_bytes().to_vec()\n}\n\n#[must_use]\npub fn vec_to_osstr(vec: &[u8]) -> OsString {\n    unsafe { OsString::from_encoded_bytes_unchecked(vec.to_owned()) }\n}\n",[184,7521,7522,7527,7555,7571,7575,7579,7583,7609,7636],{"__ignoreMap":1646},[2113,7523,7524],{"class":2115,"line":2116},[2113,7525,7526],{"class":2119},"#[must_use]\n",[2113,7528,7529,7531,7533,7536,7538,7540,7542,7545,7547,7549,7551,7553],{"class":2115,"line":1647},[2113,7530,5374],{"class":2326},[2113,7532,6542],{"class":2326},[2113,7534,7535],{"class":2133}," osstr_to_vec",[2113,7537,2423],{"class":2119},[2113,7539,6554],{"class":2330},[2113,7541,4472],{"class":2119},[2113,7543,7544],{"class":2414},"OsStr",[2113,7546,4790],{"class":2119},[2113,7548,4733],{"class":2414},[2113,7550,3109],{"class":2119},[2113,7552,4480],{"class":2414},[2113,7554,4449],{"class":2119},[2113,7556,7557,7560,7562,7565,7567,7569],{"class":2115,"line":1652},[2113,7558,7559],{"class":2330},"    path",[2113,7561,179],{"class":2119},[2113,7563,7564],{"class":2133},"as_encoded_bytes",[2113,7566,5224],{"class":2119},[2113,7568,5008],{"class":2133},[2113,7570,6745],{"class":2119},[2113,7572,7573],{"class":2115,"line":2185},[2113,7574,2599],{"class":2119},[2113,7576,7577],{"class":2115,"line":2230},[2113,7578,2125],{"emptyLinePlaceholder":1767},[2113,7580,7581],{"class":2115,"line":2235},[2113,7582,7526],{"class":2119},[2113,7584,7585,7587,7589,7592,7594,7597,7600,7602,7604,7607],{"class":2115,"line":2241},[2113,7586,5374],{"class":2326},[2113,7588,6542],{"class":2326},[2113,7590,7591],{"class":2133}," vec_to_osstr",[2113,7593,2423],{"class":2119},[2113,7595,7596],{"class":2330},"vec",[2113,7598,7599],{"class":2119},": &[",[2113,7601,4480],{"class":2414},[2113,7603,4483],{"class":2119},[2113,7605,7606],{"class":2414},"OsString",[2113,7608,2647],{"class":2119},[2113,7610,7611,7614,7617,7619,7621,7624,7626,7628,7630,7633],{"class":2115,"line":2246},[2113,7612,7613],{"class":2326},"    unsafe",[2113,7615,7616],{"class":2119}," { ",[2113,7618,7606],{"class":2414},[2113,7620,2418],{"class":2119},[2113,7622,7623],{"class":2133},"from_encoded_bytes_unchecked",[2113,7625,2423],{"class":2119},[2113,7627,7596],{"class":2330},[2113,7629,179],{"class":2119},[2113,7631,7632],{"class":2133},"to_owned",[2113,7634,7635],{"class":2119},"()) }\n",[2113,7637,7638],{"class":2115,"line":2464},[2113,7639,2599],{"class":2119},[12,7641,7642,7643,7645],{},"Le problème est que dans l'interface, des noms Windows ou Linux peuvent être affichés. Je ne souhaite pas que le\nprogramme plante, mais qu'au pire des cas, il affiche un nom de fichier incorrect. Je ne suis pas sûr que le code écrit\nsoit la meilleure solution. (D'après mes tests, il fonctionne). Une meilleure solution pour convertir un ",[184,7644,7544],{}," en\nString en fonction de l'encodage actuel (la variable d'environnement LANG sous Linux) aurait été pratique.",[12,7647,7648],{},"La gestion de la mémoire, bien que semblant simple au premier abord, devient rapidement complexe lorsqu'on veut faire du\nmultithreading. On se retrouve à mettre des structures dans des Box, des Mutex et des Arc, ce qui complique la syntaxe.",[12,7650,7651,7652,7655,7656,7659],{},"L'introduction de dynamisme, notamment pour la gestion des erreurs, nécessite l'utilisation du mot-clé ",[184,7653,7654],{},"dyn",", ce qui\npeut également devenir complexe. Je me retrouve avec presque toutes mes méthodes qui retournent un\n",[184,7657,7658],{},"Result\u003C???, Box\u003Cdyn Error>>",". Là aussi, je me demande si c'est une bonne pratique.",[12,7661,7662],{},"De plus, Rust semble souvent nous obliger à cloner des objets, alors que j'aurais parfois préféré utiliser simplement la\nréférence de l'objet. Mais comme potentiellement Rust n'arrive pas à déterminer si dans un cas de multithreading la\nvariable sera toujours valide, on doit la cloner. Parfois dans ces cas, je me dis qu'en C, je sais quand j'alloue, quand\nje désalloue, qui a le droit de lire et d'écrire. De mes souvenirs du C et du C++, j'avais plus de contrôle, le code me\nparaissait plus facile à écrire. Maintenant, le moindre oubli, petite erreur, peut être fatal, et Rust nous protège de\ncela. Bref, je pense simplement que je dois encore prendre le temps de me familiariser avec la philosophie de Rust.",[12,7664,7665,7666,932,7669,932,7672,939,7675,7678],{},"Enfin, les erreurs de compilation ont été un gros défi. J'ai passé des heures à essayer de comprendre certaines erreurs,\nà ajuster les durées de vie ou à ajouter ",[184,7667,7668],{},"static",[184,7670,7671],{},"Send",[184,7673,7674],{},"Sync",[184,7676,7677],{},"Clone"," sans savoir quelle était la bonne façon de\nfaire.",[133,7680,7682],{"id":7681},"les-tests-unitaires","Les tests unitaires",[12,7684,7685],{},"J'ai réalisé des tests unitaires pour une partie du développement. Cependant, l'écriture de ces tests a été difficile,\nsurtout en comparaison avec des bibliothèques comme Jest en JavaScript.",[12,7687,7688,7689,179],{},"La création de mocks pour les interfaces et les autres fichiers n'est pas native à Rust et n'est pas une tâche simple.\nJ'ai dû utiliser une bibliothèque dédiée, ",[49,7690,7693],{"href":7691,"rel":7692},"https:\u002F\u002Fdocs.rs\u002Fmockall\u002Flatest\u002Fmockall\u002F",[347],"mockall",[12,7695,7696,7697,179],{},"L'utilisation de mocks m'a contraint à créer des structures, alors qu'initialement j'avais des méthodes. J'ai commencé\navec des méthodes statiques, mais leur gestion avec les mocks ne permet pas de réaliser des tests unitaires en\nparallèle. Pour exécuter les tests unitaires, j'ai dû lancer un test à la fois avec la commande\n",[184,7698,7699],{},"RUST_TEST_THREADS=1 cargo test",[12,7701,7702],{},"Cependant, j'ai réussi à modifier les tests unitaires pour qu'ils puissent s'exécuter en parallèle sans supprimer les\nméthodes statiques.",[128,7704,1621],{"id":1620},[12,7706,7707],{},"Maintenant que j'ai écrit ce petit programme, je vais pouvoir créer mon propre pool de stockage. De votre côté,\nn'hésitez pas à me faire des retours sur le programme. Je suis ouvert à toutes les critiques. De plus, si vous utilisez\nBackupPC (en version 4 uniquement et avec un pool de stockage en version 4), vous pouvez tester mon programme et\nl'utiliser.",[3358,7709,7710],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}",{"title":1646,"searchDepth":1647,"depth":1647,"links":7712},[7713,7714,7719,7728],{"id":3540,"depth":1647,"text":3541},{"id":3602,"depth":1647,"text":3603,"children":7715},[7716,7717,7718],{"id":3645,"depth":1652,"text":3646},{"id":3717,"depth":1652,"text":3718},{"id":3735,"depth":1652,"text":3736},{"id":3808,"depth":1647,"text":3809,"children":7720},[7721,7722,7723,7724,7725,7726,7727],{"id":3832,"depth":1652,"text":3833},{"id":6032,"depth":1652,"text":6033},{"id":6442,"depth":1652,"text":6443},{"id":6479,"depth":1652,"text":6480},{"id":7480,"depth":1652,"text":7481},{"id":7499,"depth":1652,"text":7500},{"id":7681,"depth":1652,"text":7682},{"id":1620,"depth":1647,"text":1621},"Une partie de cet article a été publiée sur LinuxFR.\nAprès avoir reçu quelques retours, j'ai décidé de publier une version modifiée et améliorée de cet article sur mon blog.",{"type":9,"value":7731},[7732,7737,7739,7741],[12,7733,3519,7734,3525],{},[49,7735,3524],{"href":3522,"rel":7736},[347],[12,7738,3528],{},[12,7740,3531],{},[12,7742,7743],{},[121,7744],{"src":3536,"alt":1646,"className":7745},[126],{"planet":1767},{"title":108,"description":7729},"pr_backuppc_pool","posts\u002FWoodstock\u002F2024-03-14_pr_backuppc_pool",[1683,1773,1774,3510,3511],"JVBo68qbMHY70AJ8I83XDPaOAeqZhhqQNzs8jDhZhvY",{"id":7753,"title":7754,"author":7,"body":7755,"category":2057,"categorySlug":2058,"date":97,"description":1646,"excerpt":15236,"extension":1764,"location":1765,"meta":15244,"navigation":1767,"path":93,"published":1767,"seo":15245,"slug":15246,"stem":15247,"tags":15248,"timeToRead":3192,"__hash__":15249},"posts\u002Fposts\u002FWoodstock\u002F2023-05-10_woodstock_rust.md","Woodstock Backup - Optimiser la consommation mémoire de Node.js avec Rust",{"type":9,"value":7756,"toc":15226},[7757,7761,7764,7767,7771,7774,9253,9256,9259,9327,9330,9333,9337,9344,9347,9350,9353,9451,9454,9514,9520,9524,9527,9537,11448,11451,11511,11514,11517,11520,11524,11527,11530,11533,11536,11543,11546,11549,11552,13121,13124,13469,13472,13510,13513,13519,13522,13530,13533,13541,14776,14782,15153,15156,15159,15198,15201,15204,15207,15210,15212,15215,15223],[128,7758,7760],{"id":7759},"introduction","Introduction",[12,7762,7763],{},"Node.js est un environnement d'exécution JavaScript côté serveur qui repose sur le moteur JavaScript V8 de Google. Il\nest utilisé pour développer des applications serveur en back-end d'une application web, des outils en ligne de commande\net des applications desktop. Cependant, la consommation de mémoire peut être un problème pour certaines applications\nNode.js, en particulier celles qui manipulent de grandes quantités de données ou des données volumineuses.",[12,7765,7766],{},"Dans cet article, nous allons voir comment optimiser la consommation de mémoire d'une application Node.js en le couplant\navec Rust. Rust est un langage de programmation système qui offre des performances similaires à celles du C++, tout en\noffrant une sécurité de mémoire à la compilation. Rust peut être utilisé pour écrire des bibliothèques C\u002FC++ natives\npour Node.js.",[128,7768,7770],{"id":7769},"problématique","Problématique",[12,7772,7773],{},"J'ai développé un logiciel de sauvegarde appelé Woodstock Backup, écrit en TypeScript. Lors du lancement des\nsauvegardes, il crée une représentation du système de fichier en mémoire et nécessite une grande quantité de mémoire.\nPour illustrer cela, nous avons reproduit notre cas avec le code suivant :",[2105,7775,7779],{"className":7776,"code":7777,"language":7778,"meta":1646,"style":1646},"language-js shiki shiki-themes one-dark-pro","const filesize = require(\"filesize.js\");\nconst fs = require(\"fs\");\n\u002F\u002F Utilisation des méthodes de sérialisation et de désérialisation du moteur V8\nconst { serialize, deserialize } = require(\"v8\");\n\n\u002F\u002F Méthode pour générer une chaîne de caractère contenant des caractères aléatoires\nfunction randomString(size) {\n  const buffer = Buffer.alloc(size);\n  for (let i = 0; i \u003C size; i++) {\n    buffer[i] = Math.floor(Math.random() * 256);\n  }\n  return buffer;\n}\n\n\u002F\u002F Méthode pour générer un nombre aléatoire de la taille d'un nombre de 53 bits\nfunction randomNumber() {\n  return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);\n}\n\n\u002F\u002F Création d'un objet de test en javascript contenant que des données aléatoires\nconst testObject = () => ({\n  path: randomString(100),\n  stats: {\n    ownerId: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    groupId: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    size: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    compressedSize: {\n      low: randomNumber(),\n      high: randomNumber(),\n      unsigned: true,\n    },\n    lastRead: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    lastModified: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    created: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    mode: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    dev: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    rdev: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    ino: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    nlink: { low: randomNumber(), high: randomNumber(), unsigned: true },\n  },\n  chunks: [randomString(32), randomString(32), randomString(32)],\n  sha256: randomString(32),\n});\n\n\u002F\u002F Lancement du GC pour s'assurer que la mémoire utilisé ne contient que les objets de test\nglobal.gc();\n\u002F\u002F On recupère la mémoire utilisé avant le test\nconst memoryBefore = process.memoryUsage().heapUsed;\n\u002F\u002F On recupère le temps avant le test (pour mesurer le temps de traitement)\nconst time = Date.now();\n\n\u002F\u002F Création des objets. J'ai du lancer plusieurs fois le script pour trouver une valeur qui ne causé pas de crash de \n\u002F\u002F Node.JS pour cause de manque de mémoire\nconst nbObjects = 1_300_000;\nconst testArray = new Array(nbObjects);\nfor (let i = 0; i \u003C nbObjects; i++) {\n  testArray[i] = testObject();\n}\n\n\u002F\u002F Combien de temps à pris la création des objets\nconsole.log(\"Creation time: \", Date.now() - time);\n\u002F\u002F Lancement du GC pour s'assurer que nous n'avons pas d'autres reliquats\nglobal.gc();\n\u002F\u002F Récupération de la mémoire après le test\nconst memoryAfter = process.memoryUsage().heapUsed;\n\nconsole.log(\"Memory consumption: \", filesize.default(memoryAfter - memoryBefore));\nconsole.log(\"Memory consumption by objects: \", filesize.default((memoryAfter - memoryBefore) \u002F nbObjects));\n\n\u002F\u002F Dans la suite on va écrire un fichier contenant le contenu de la mémoire. Cela a été fait initiallement pour \n\u002F\u002F s'assurer que le GC ne supprime pas mes objets car non utilisés.\nconst time2 = Date.now();\n\n\u002F\u002F Remove test file if exist\ntry {\n  fs.unlinkSync(\"test\");\n} catch (e) {}\n\nconst stream = fs.createWriteStream(\"test\");\n\n\u002F\u002F C'est moche, mais c'est pour tester\nstream.on(\"close\", () => {\n  console.log(\"Write to file time: \", Date.now() - time2);\n  \u002F\u002F Size of file test on disk\n  const stats = fs.statSync(\"test\");\n  console.log(\"Size of file on disk: \", filesize.default(stats.size));\n  console.log(\n    \"Size of object in the file\",\n    filesize.default(stats.size \u002F nbObjects)\n  );\n});\n\nfor (const obj of testArray) {\n  stream.write(serialize(obj));\n}\nstream.end();\n","js",[184,7780,7781,7801,7819,7824,7852,7856,7861,7876,7898,7934,7974,7978,7986,7990,7994,7999,8008,8036,8040,8044,8049,8067,8084,8092,8130,8161,8192,8199,8211,8222,8233,8238,8269,8300,8331,8362,8393,8424,8455,8486,8491,8525,8540,8545,8549,8554,8566,8571,8595,8600,8619,8623,8629,8635,8650,8672,8704,8722,8727,8732,8738,8770,8776,8787,8793,8815,8820,8855,8894,8899,8905,8911,8929,8934,8940,8948,8966,8981,8986,9009,9014,9020,9043,9074,9080,9103,9136,9147,9155,9180,9186,9191,9196,9214,9236,9241],{"__ignoreMap":1646},[2113,7782,7783,7786,7789,7791,7794,7796,7799],{"class":2115,"line":2116},[2113,7784,7785],{"class":2326},"const",[2113,7787,7788],{"class":2414}," filesize",[2113,7790,2153],{"class":2334},[2113,7792,7793],{"class":2133}," require",[2113,7795,2423],{"class":2119},[2113,7797,7798],{"class":2149},"\"filesize.js\"",[2113,7800,2553],{"class":2119},[2113,7802,7803,7805,7808,7810,7812,7814,7817],{"class":2115,"line":1647},[2113,7804,7785],{"class":2326},[2113,7806,7807],{"class":2414}," fs",[2113,7809,2153],{"class":2334},[2113,7811,7793],{"class":2133},[2113,7813,2423],{"class":2119},[2113,7815,7816],{"class":2149},"\"fs\"",[2113,7818,2553],{"class":2119},[2113,7820,7821],{"class":2115,"line":1652},[2113,7822,7823],{"class":2396},"\u002F\u002F Utilisation des méthodes de sérialisation et de désérialisation du moteur V8\n",[2113,7825,7826,7828,7830,7833,7835,7838,7841,7843,7845,7847,7850],{"class":2115,"line":2185},[2113,7827,7785],{"class":2326},[2113,7829,7616],{"class":2119},[2113,7831,7832],{"class":2414},"serialize",[2113,7834,932],{"class":2119},[2113,7836,7837],{"class":2414},"deserialize",[2113,7839,7840],{"class":2119}," } ",[2113,7842,2335],{"class":2334},[2113,7844,7793],{"class":2133},[2113,7846,2423],{"class":2119},[2113,7848,7849],{"class":2149},"\"v8\"",[2113,7851,2553],{"class":2119},[2113,7853,7854],{"class":2115,"line":2230},[2113,7855,2125],{"emptyLinePlaceholder":1767},[2113,7857,7858],{"class":2115,"line":2235},[2113,7859,7860],{"class":2396},"\u002F\u002F Méthode pour générer une chaîne de caractère contenant des caractères aléatoires\n",[2113,7862,7863,7866,7869,7871,7874],{"class":2115,"line":2241},[2113,7864,7865],{"class":2326},"function",[2113,7867,7868],{"class":2133}," randomString",[2113,7870,2423],{"class":2119},[2113,7872,7873],{"class":2429},"size",[2113,7875,2433],{"class":2119},[2113,7877,7878,7880,7882,7884,7887,7889,7892,7894,7896],{"class":2115,"line":2246},[2113,7879,2995],{"class":2326},[2113,7881,4003],{"class":2414},[2113,7883,2153],{"class":2334},[2113,7885,7886],{"class":2414}," Buffer",[2113,7888,179],{"class":2119},[2113,7890,7891],{"class":2133},"alloc",[2113,7893,2423],{"class":2119},[2113,7895,7873],{"class":2330},[2113,7897,2553],{"class":2119},[2113,7899,7900,7903,7905,7908,7911,7913,7915,7917,7920,7922,7925,7927,7929,7932],{"class":2115,"line":2464},[2113,7901,7902],{"class":2326},"  for",[2113,7904,2495],{"class":2119},[2113,7906,7907],{"class":2326},"let",[2113,7909,7910],{"class":2330}," i",[2113,7912,2153],{"class":2334},[2113,7914,3208],{"class":2166},[2113,7916,6160],{"class":2119},[2113,7918,7919],{"class":2330},"i",[2113,7921,5912],{"class":2334},[2113,7923,7924],{"class":2330}," size",[2113,7926,6160],{"class":2119},[2113,7928,7919],{"class":2330},[2113,7930,7931],{"class":2334},"++",[2113,7933,2433],{"class":2119},[2113,7935,7936,7939,7941,7943,7945,7947,7950,7952,7955,7957,7960,7962,7965,7967,7969,7972],{"class":2115,"line":2085},[2113,7937,7938],{"class":2330},"    buffer",[2113,7940,4124],{"class":2119},[2113,7942,7919],{"class":2330},[2113,7944,4129],{"class":2119},[2113,7946,2335],{"class":2334},[2113,7948,7949],{"class":2414}," Math",[2113,7951,179],{"class":2119},[2113,7953,7954],{"class":2133},"floor",[2113,7956,2423],{"class":2119},[2113,7958,7959],{"class":2414},"Math",[2113,7961,179],{"class":2119},[2113,7963,7964],{"class":2133},"random",[2113,7966,4543],{"class":2119},[2113,7968,2676],{"class":2334},[2113,7970,7971],{"class":2166}," 256",[2113,7973,2553],{"class":2119},[2113,7975,7976],{"class":2115,"line":2514},[2113,7977,2572],{"class":2119},[2113,7979,7980,7982,7984],{"class":2115,"line":2533},[2113,7981,2578],{"class":2326},[2113,7983,4003],{"class":2330},[2113,7985,2487],{"class":2119},[2113,7987,7988],{"class":2115,"line":2556},[2113,7989,2599],{"class":2119},[2113,7991,7992],{"class":2115,"line":2569},[2113,7993,2125],{"emptyLinePlaceholder":1767},[2113,7995,7996],{"class":2115,"line":2575},[2113,7997,7998],{"class":2396},"\u002F\u002F Méthode pour générer un nombre aléatoire de la taille d'un nombre de 53 bits\n",[2113,8000,8001,8003,8006],{"class":2115,"line":2596},[2113,8002,7865],{"class":2326},[2113,8004,8005],{"class":2133}," randomNumber",[2113,8007,3912],{"class":2119},[2113,8009,8010,8012,8014,8016,8018,8020,8022,8024,8026,8028,8030,8033],{"class":2115,"line":3192},[2113,8011,2578],{"class":2326},[2113,8013,7949],{"class":2414},[2113,8015,179],{"class":2119},[2113,8017,7954],{"class":2133},[2113,8019,2423],{"class":2119},[2113,8021,7959],{"class":2414},[2113,8023,179],{"class":2119},[2113,8025,7964],{"class":2133},[2113,8027,4543],{"class":2119},[2113,8029,2676],{"class":2334},[2113,8031,8032],{"class":2414}," Number",[2113,8034,8035],{"class":2119},".MAX_SAFE_INTEGER);\n",[2113,8037,8038],{"class":2115,"line":3213},[2113,8039,2599],{"class":2119},[2113,8041,8042],{"class":2115,"line":3236},[2113,8043,2125],{"emptyLinePlaceholder":1767},[2113,8045,8046],{"class":2115,"line":3248},[2113,8047,8048],{"class":2396},"\u002F\u002F Création d'un objet de test en javascript contenant que des données aléatoires\n",[2113,8050,8051,8053,8056,8058,8061,8064],{"class":2115,"line":4899},[2113,8052,7785],{"class":2326},[2113,8054,8055],{"class":2133}," testObject",[2113,8057,2153],{"class":2334},[2113,8059,8060],{"class":2119}," () ",[2113,8062,8063],{"class":2326},"=>",[2113,8065,8066],{"class":2119}," ({\n",[2113,8068,8069,8072,8074,8077,8079,8082],{"class":2115,"line":1777},[2113,8070,8071],{"class":2330},"  path",[2113,8073,4429],{"class":2119},[2113,8075,8076],{"class":2133},"randomString",[2113,8078,2423],{"class":2119},[2113,8080,8081],{"class":2166},"100",[2113,8083,7142],{"class":2119},[2113,8085,8086,8089],{"class":2115,"line":4931},[2113,8087,8088],{"class":2330},"  stats",[2113,8090,8091],{"class":2119},": {\n",[2113,8093,8094,8097,8100,8103,8105,8108,8111,8114,8116,8118,8120,8123,8125,8127],{"class":2115,"line":4961},[2113,8095,8096],{"class":2330},"    ownerId",[2113,8098,8099],{"class":2119},": { ",[2113,8101,8102],{"class":2330},"low",[2113,8104,4429],{"class":2119},[2113,8106,8107],{"class":2133},"randomNumber",[2113,8109,8110],{"class":2119},"(), ",[2113,8112,8113],{"class":2330},"high",[2113,8115,4429],{"class":2119},[2113,8117,8107],{"class":2133},[2113,8119,8110],{"class":2119},[2113,8121,8122],{"class":2330},"unsigned",[2113,8124,4429],{"class":2119},[2113,8126,4819],{"class":2166},[2113,8128,8129],{"class":2119}," },\n",[2113,8131,8132,8135,8137,8139,8141,8143,8145,8147,8149,8151,8153,8155,8157,8159],{"class":2115,"line":4976},[2113,8133,8134],{"class":2330},"    groupId",[2113,8136,8099],{"class":2119},[2113,8138,8102],{"class":2330},[2113,8140,4429],{"class":2119},[2113,8142,8107],{"class":2133},[2113,8144,8110],{"class":2119},[2113,8146,8113],{"class":2330},[2113,8148,4429],{"class":2119},[2113,8150,8107],{"class":2133},[2113,8152,8110],{"class":2119},[2113,8154,8122],{"class":2330},[2113,8156,4429],{"class":2119},[2113,8158,4819],{"class":2166},[2113,8160,8129],{"class":2119},[2113,8162,8163,8166,8168,8170,8172,8174,8176,8178,8180,8182,8184,8186,8188,8190],{"class":2115,"line":4993},[2113,8164,8165],{"class":2330},"    size",[2113,8167,8099],{"class":2119},[2113,8169,8102],{"class":2330},[2113,8171,4429],{"class":2119},[2113,8173,8107],{"class":2133},[2113,8175,8110],{"class":2119},[2113,8177,8113],{"class":2330},[2113,8179,4429],{"class":2119},[2113,8181,8107],{"class":2133},[2113,8183,8110],{"class":2119},[2113,8185,8122],{"class":2330},[2113,8187,4429],{"class":2119},[2113,8189,4819],{"class":2166},[2113,8191,8129],{"class":2119},[2113,8193,8194,8197],{"class":2115,"line":5013},[2113,8195,8196],{"class":2330},"    compressedSize",[2113,8198,8091],{"class":2119},[2113,8200,8201,8204,8206,8208],{"class":2115,"line":5018},[2113,8202,8203],{"class":2330},"      low",[2113,8205,4429],{"class":2119},[2113,8207,8107],{"class":2133},[2113,8209,8210],{"class":2119},"(),\n",[2113,8212,8213,8216,8218,8220],{"class":2115,"line":5042},[2113,8214,8215],{"class":2330},"      high",[2113,8217,4429],{"class":2119},[2113,8219,8107],{"class":2133},[2113,8221,8210],{"class":2119},[2113,8223,8224,8227,8229,8231],{"class":2115,"line":5057},[2113,8225,8226],{"class":2330},"      unsigned",[2113,8228,4429],{"class":2119},[2113,8230,4819],{"class":2166},[2113,8232,4706],{"class":2119},[2113,8234,8235],{"class":2115,"line":5062},[2113,8236,8237],{"class":2119},"    },\n",[2113,8239,8240,8243,8245,8247,8249,8251,8253,8255,8257,8259,8261,8263,8265,8267],{"class":2115,"line":5098},[2113,8241,8242],{"class":2330},"    lastRead",[2113,8244,8099],{"class":2119},[2113,8246,8102],{"class":2330},[2113,8248,4429],{"class":2119},[2113,8250,8107],{"class":2133},[2113,8252,8110],{"class":2119},[2113,8254,8113],{"class":2330},[2113,8256,4429],{"class":2119},[2113,8258,8107],{"class":2133},[2113,8260,8110],{"class":2119},[2113,8262,8122],{"class":2330},[2113,8264,4429],{"class":2119},[2113,8266,4819],{"class":2166},[2113,8268,8129],{"class":2119},[2113,8270,8271,8274,8276,8278,8280,8282,8284,8286,8288,8290,8292,8294,8296,8298],{"class":2115,"line":5117},[2113,8272,8273],{"class":2330},"    lastModified",[2113,8275,8099],{"class":2119},[2113,8277,8102],{"class":2330},[2113,8279,4429],{"class":2119},[2113,8281,8107],{"class":2133},[2113,8283,8110],{"class":2119},[2113,8285,8113],{"class":2330},[2113,8287,4429],{"class":2119},[2113,8289,8107],{"class":2133},[2113,8291,8110],{"class":2119},[2113,8293,8122],{"class":2330},[2113,8295,4429],{"class":2119},[2113,8297,4819],{"class":2166},[2113,8299,8129],{"class":2119},[2113,8301,8302,8305,8307,8309,8311,8313,8315,8317,8319,8321,8323,8325,8327,8329],{"class":2115,"line":5142},[2113,8303,8304],{"class":2330},"    created",[2113,8306,8099],{"class":2119},[2113,8308,8102],{"class":2330},[2113,8310,4429],{"class":2119},[2113,8312,8107],{"class":2133},[2113,8314,8110],{"class":2119},[2113,8316,8113],{"class":2330},[2113,8318,4429],{"class":2119},[2113,8320,8107],{"class":2133},[2113,8322,8110],{"class":2119},[2113,8324,8122],{"class":2330},[2113,8326,4429],{"class":2119},[2113,8328,4819],{"class":2166},[2113,8330,8129],{"class":2119},[2113,8332,8333,8336,8338,8340,8342,8344,8346,8348,8350,8352,8354,8356,8358,8360],{"class":2115,"line":5148},[2113,8334,8335],{"class":2330},"    mode",[2113,8337,8099],{"class":2119},[2113,8339,8102],{"class":2330},[2113,8341,4429],{"class":2119},[2113,8343,8107],{"class":2133},[2113,8345,8110],{"class":2119},[2113,8347,8113],{"class":2330},[2113,8349,4429],{"class":2119},[2113,8351,8107],{"class":2133},[2113,8353,8110],{"class":2119},[2113,8355,8122],{"class":2330},[2113,8357,4429],{"class":2119},[2113,8359,4819],{"class":2166},[2113,8361,8129],{"class":2119},[2113,8363,8364,8367,8369,8371,8373,8375,8377,8379,8381,8383,8385,8387,8389,8391],{"class":2115,"line":5163},[2113,8365,8366],{"class":2330},"    dev",[2113,8368,8099],{"class":2119},[2113,8370,8102],{"class":2330},[2113,8372,4429],{"class":2119},[2113,8374,8107],{"class":2133},[2113,8376,8110],{"class":2119},[2113,8378,8113],{"class":2330},[2113,8380,4429],{"class":2119},[2113,8382,8107],{"class":2133},[2113,8384,8110],{"class":2119},[2113,8386,8122],{"class":2330},[2113,8388,4429],{"class":2119},[2113,8390,4819],{"class":2166},[2113,8392,8129],{"class":2119},[2113,8394,8395,8398,8400,8402,8404,8406,8408,8410,8412,8414,8416,8418,8420,8422],{"class":2115,"line":5169},[2113,8396,8397],{"class":2330},"    rdev",[2113,8399,8099],{"class":2119},[2113,8401,8102],{"class":2330},[2113,8403,4429],{"class":2119},[2113,8405,8107],{"class":2133},[2113,8407,8110],{"class":2119},[2113,8409,8113],{"class":2330},[2113,8411,4429],{"class":2119},[2113,8413,8107],{"class":2133},[2113,8415,8110],{"class":2119},[2113,8417,8122],{"class":2330},[2113,8419,4429],{"class":2119},[2113,8421,4819],{"class":2166},[2113,8423,8129],{"class":2119},[2113,8425,8426,8429,8431,8433,8435,8437,8439,8441,8443,8445,8447,8449,8451,8453],{"class":2115,"line":5175},[2113,8427,8428],{"class":2330},"    ino",[2113,8430,8099],{"class":2119},[2113,8432,8102],{"class":2330},[2113,8434,4429],{"class":2119},[2113,8436,8107],{"class":2133},[2113,8438,8110],{"class":2119},[2113,8440,8113],{"class":2330},[2113,8442,4429],{"class":2119},[2113,8444,8107],{"class":2133},[2113,8446,8110],{"class":2119},[2113,8448,8122],{"class":2330},[2113,8450,4429],{"class":2119},[2113,8452,4819],{"class":2166},[2113,8454,8129],{"class":2119},[2113,8456,8457,8460,8462,8464,8466,8468,8470,8472,8474,8476,8478,8480,8482,8484],{"class":2115,"line":5180},[2113,8458,8459],{"class":2330},"    nlink",[2113,8461,8099],{"class":2119},[2113,8463,8102],{"class":2330},[2113,8465,4429],{"class":2119},[2113,8467,8107],{"class":2133},[2113,8469,8110],{"class":2119},[2113,8471,8113],{"class":2330},[2113,8473,4429],{"class":2119},[2113,8475,8107],{"class":2133},[2113,8477,8110],{"class":2119},[2113,8479,8122],{"class":2330},[2113,8481,4429],{"class":2119},[2113,8483,4819],{"class":2166},[2113,8485,8129],{"class":2119},[2113,8487,8488],{"class":2115,"line":5199},[2113,8489,8490],{"class":2119},"  },\n",[2113,8492,8493,8496,8498,8500,8502,8505,8508,8510,8512,8514,8516,8518,8520,8522],{"class":2115,"line":5204},[2113,8494,8495],{"class":2330},"  chunks",[2113,8497,6155],{"class":2119},[2113,8499,8076],{"class":2133},[2113,8501,2423],{"class":2119},[2113,8503,8504],{"class":2166},"32",[2113,8506,8507],{"class":2119},"), ",[2113,8509,8076],{"class":2133},[2113,8511,2423],{"class":2119},[2113,8513,8504],{"class":2166},[2113,8515,8507],{"class":2119},[2113,8517,8076],{"class":2133},[2113,8519,2423],{"class":2119},[2113,8521,8504],{"class":2166},[2113,8523,8524],{"class":2119},")],\n",[2113,8526,8527,8530,8532,8534,8536,8538],{"class":2115,"line":5209},[2113,8528,8529],{"class":2330},"  sha256",[2113,8531,4429],{"class":2119},[2113,8533,8076],{"class":2133},[2113,8535,2423],{"class":2119},[2113,8537,8504],{"class":2166},[2113,8539,7142],{"class":2119},[2113,8541,8542],{"class":2115,"line":5232},[2113,8543,8544],{"class":2119},"});\n",[2113,8546,8547],{"class":2115,"line":5237},[2113,8548,2125],{"emptyLinePlaceholder":1767},[2113,8550,8551],{"class":2115,"line":5242},[2113,8552,8553],{"class":2396},"\u002F\u002F Lancement du GC pour s'assurer que la mémoire utilisé ne contient que les objets de test\n",[2113,8555,8556,8559,8561,8564],{"class":2115,"line":5267},[2113,8557,8558],{"class":2414},"global",[2113,8560,179],{"class":2119},[2113,8562,8563],{"class":2133},"gc",[2113,8565,3944],{"class":2119},[2113,8567,8568],{"class":2115,"line":5282},[2113,8569,8570],{"class":2396},"\u002F\u002F On recupère la mémoire utilisé avant le test\n",[2113,8572,8573,8575,8578,8580,8583,8585,8588,8590,8593],{"class":2115,"line":5295},[2113,8574,7785],{"class":2326},[2113,8576,8577],{"class":2414}," memoryBefore",[2113,8579,2153],{"class":2334},[2113,8581,8582],{"class":2414}," process",[2113,8584,179],{"class":2119},[2113,8586,8587],{"class":2133},"memoryUsage",[2113,8589,5224],{"class":2119},[2113,8591,8592],{"class":2330},"heapUsed",[2113,8594,2487],{"class":2119},[2113,8596,8597],{"class":2115,"line":5310},[2113,8598,8599],{"class":2396},"\u002F\u002F On recupère le temps avant le test (pour mesurer le temps de traitement)\n",[2113,8601,8602,8604,8607,8609,8612,8614,8617],{"class":2115,"line":5315},[2113,8603,7785],{"class":2326},[2113,8605,8606],{"class":2414}," time",[2113,8608,2153],{"class":2334},[2113,8610,8611],{"class":2414}," Date",[2113,8613,179],{"class":2119},[2113,8615,8616],{"class":2133},"now",[2113,8618,3944],{"class":2119},[2113,8620,8621],{"class":2115,"line":5320},[2113,8622,2125],{"emptyLinePlaceholder":1767},[2113,8624,8626],{"class":2115,"line":8625},52,[2113,8627,8628],{"class":2396},"\u002F\u002F Création des objets. J'ai du lancer plusieurs fois le script pour trouver une valeur qui ne causé pas de crash de \n",[2113,8630,8632],{"class":2115,"line":8631},53,[2113,8633,8634],{"class":2396},"\u002F\u002F Node.JS pour cause de manque de mémoire\n",[2113,8636,8638,8640,8643,8645,8648],{"class":2115,"line":8637},54,[2113,8639,7785],{"class":2326},[2113,8641,8642],{"class":2414}," nbObjects",[2113,8644,2153],{"class":2334},[2113,8646,8647],{"class":2166}," 1_300_000",[2113,8649,2487],{"class":2119},[2113,8651,8653,8655,8658,8660,8662,8665,8667,8670],{"class":2115,"line":8652},55,[2113,8654,7785],{"class":2326},[2113,8656,8657],{"class":2414}," testArray",[2113,8659,2153],{"class":2334},[2113,8661,4778],{"class":2326},[2113,8663,8664],{"class":2133}," Array",[2113,8666,2423],{"class":2119},[2113,8668,8669],{"class":2330},"nbObjects",[2113,8671,2553],{"class":2119},[2113,8673,8675,8678,8680,8682,8684,8686,8688,8690,8692,8694,8696,8698,8700,8702],{"class":2115,"line":8674},56,[2113,8676,8677],{"class":2326},"for",[2113,8679,2495],{"class":2119},[2113,8681,7907],{"class":2326},[2113,8683,7910],{"class":2330},[2113,8685,2153],{"class":2334},[2113,8687,3208],{"class":2166},[2113,8689,6160],{"class":2119},[2113,8691,7919],{"class":2330},[2113,8693,5912],{"class":2334},[2113,8695,8642],{"class":2330},[2113,8697,6160],{"class":2119},[2113,8699,7919],{"class":2330},[2113,8701,7931],{"class":2334},[2113,8703,2433],{"class":2119},[2113,8705,8707,8710,8712,8714,8716,8718,8720],{"class":2115,"line":8706},57,[2113,8708,8709],{"class":2330},"  testArray",[2113,8711,4124],{"class":2119},[2113,8713,7919],{"class":2330},[2113,8715,4129],{"class":2119},[2113,8717,2335],{"class":2334},[2113,8719,8055],{"class":2133},[2113,8721,3944],{"class":2119},[2113,8723,8725],{"class":2115,"line":8724},58,[2113,8726,2599],{"class":2119},[2113,8728,8730],{"class":2115,"line":8729},59,[2113,8731,2125],{"emptyLinePlaceholder":1767},[2113,8733,8735],{"class":2115,"line":8734},60,[2113,8736,8737],{"class":2396},"\u002F\u002F Combien de temps à pris la création des objets\n",[2113,8739,8741,8744,8746,8749,8751,8754,8756,8758,8760,8762,8764,8766,8768],{"class":2115,"line":8740},61,[2113,8742,8743],{"class":2414},"console",[2113,8745,179],{"class":2119},[2113,8747,8748],{"class":2133},"log",[2113,8750,2423],{"class":2119},[2113,8752,8753],{"class":2149},"\"Creation time: \"",[2113,8755,932],{"class":2119},[2113,8757,36],{"class":2414},[2113,8759,179],{"class":2119},[2113,8761,8616],{"class":2133},[2113,8763,4543],{"class":2119},[2113,8765,3242],{"class":2334},[2113,8767,8606],{"class":2330},[2113,8769,2553],{"class":2119},[2113,8771,8773],{"class":2115,"line":8772},62,[2113,8774,8775],{"class":2396},"\u002F\u002F Lancement du GC pour s'assurer que nous n'avons pas d'autres reliquats\n",[2113,8777,8779,8781,8783,8785],{"class":2115,"line":8778},63,[2113,8780,8558],{"class":2414},[2113,8782,179],{"class":2119},[2113,8784,8563],{"class":2133},[2113,8786,3944],{"class":2119},[2113,8788,8790],{"class":2115,"line":8789},64,[2113,8791,8792],{"class":2396},"\u002F\u002F Récupération de la mémoire après le test\n",[2113,8794,8796,8798,8801,8803,8805,8807,8809,8811,8813],{"class":2115,"line":8795},65,[2113,8797,7785],{"class":2326},[2113,8799,8800],{"class":2414}," memoryAfter",[2113,8802,2153],{"class":2334},[2113,8804,8582],{"class":2414},[2113,8806,179],{"class":2119},[2113,8808,8587],{"class":2133},[2113,8810,5224],{"class":2119},[2113,8812,8592],{"class":2330},[2113,8814,2487],{"class":2119},[2113,8816,8818],{"class":2115,"line":8817},66,[2113,8819,2125],{"emptyLinePlaceholder":1767},[2113,8821,8823,8825,8827,8829,8831,8834,8836,8839,8841,8844,8846,8849,8851,8853],{"class":2115,"line":8822},67,[2113,8824,8743],{"class":2414},[2113,8826,179],{"class":2119},[2113,8828,8748],{"class":2133},[2113,8830,2423],{"class":2119},[2113,8832,8833],{"class":2149},"\"Memory consumption: \"",[2113,8835,932],{"class":2119},[2113,8837,8838],{"class":2414},"filesize",[2113,8840,179],{"class":2119},[2113,8842,8843],{"class":2133},"default",[2113,8845,2423],{"class":2119},[2113,8847,8848],{"class":2330},"memoryAfter",[2113,8850,2270],{"class":2334},[2113,8852,8577],{"class":2330},[2113,8854,5818],{"class":2119},[2113,8856,8858,8860,8862,8864,8866,8869,8871,8873,8875,8877,8880,8882,8884,8886,8888,8890,8892],{"class":2115,"line":8857},68,[2113,8859,8743],{"class":2414},[2113,8861,179],{"class":2119},[2113,8863,8748],{"class":2133},[2113,8865,2423],{"class":2119},[2113,8867,8868],{"class":2149},"\"Memory consumption by objects: \"",[2113,8870,932],{"class":2119},[2113,8872,8838],{"class":2414},[2113,8874,179],{"class":2119},[2113,8876,8843],{"class":2133},[2113,8878,8879],{"class":2119},"((",[2113,8881,8848],{"class":2330},[2113,8883,2270],{"class":2334},[2113,8885,8577],{"class":2330},[2113,8887,5709],{"class":2119},[2113,8889,2520],{"class":2334},[2113,8891,8642],{"class":2330},[2113,8893,5818],{"class":2119},[2113,8895,8897],{"class":2115,"line":8896},69,[2113,8898,2125],{"emptyLinePlaceholder":1767},[2113,8900,8902],{"class":2115,"line":8901},70,[2113,8903,8904],{"class":2396},"\u002F\u002F Dans la suite on va écrire un fichier contenant le contenu de la mémoire. Cela a été fait initiallement pour \n",[2113,8906,8908],{"class":2115,"line":8907},71,[2113,8909,8910],{"class":2396},"\u002F\u002F s'assurer que le GC ne supprime pas mes objets car non utilisés.\n",[2113,8912,8914,8916,8919,8921,8923,8925,8927],{"class":2115,"line":8913},72,[2113,8915,7785],{"class":2326},[2113,8917,8918],{"class":2414}," time2",[2113,8920,2153],{"class":2334},[2113,8922,8611],{"class":2414},[2113,8924,179],{"class":2119},[2113,8926,8616],{"class":2133},[2113,8928,3944],{"class":2119},[2113,8930,8932],{"class":2115,"line":8931},73,[2113,8933,2125],{"emptyLinePlaceholder":1767},[2113,8935,8937],{"class":2115,"line":8936},74,[2113,8938,8939],{"class":2396},"\u002F\u002F Remove test file if exist\n",[2113,8941,8943,8946],{"class":2115,"line":8942},75,[2113,8944,8945],{"class":2326},"try",[2113,8947,2647],{"class":2119},[2113,8949,8951,8954,8956,8959,8961,8964],{"class":2115,"line":8950},76,[2113,8952,8953],{"class":2414},"  fs",[2113,8955,179],{"class":2119},[2113,8957,8958],{"class":2133},"unlinkSync",[2113,8960,2423],{"class":2119},[2113,8962,8963],{"class":2149},"\"test\"",[2113,8965,2553],{"class":2119},[2113,8967,8969,8971,8974,8976,8978],{"class":2115,"line":8968},77,[2113,8970,4224],{"class":2119},[2113,8972,8973],{"class":2326},"catch",[2113,8975,2495],{"class":2119},[2113,8977,5601],{"class":2330},[2113,8979,8980],{"class":2119},") {}\n",[2113,8982,8984],{"class":2115,"line":8983},78,[2113,8985,2125],{"emptyLinePlaceholder":1767},[2113,8987,8989,8991,8994,8996,8998,9000,9003,9005,9007],{"class":2115,"line":8988},79,[2113,8990,7785],{"class":2326},[2113,8992,8993],{"class":2414}," stream",[2113,8995,2153],{"class":2334},[2113,8997,7807],{"class":2414},[2113,8999,179],{"class":2119},[2113,9001,9002],{"class":2133},"createWriteStream",[2113,9004,2423],{"class":2119},[2113,9006,8963],{"class":2149},[2113,9008,2553],{"class":2119},[2113,9010,9012],{"class":2115,"line":9011},80,[2113,9013,2125],{"emptyLinePlaceholder":1767},[2113,9015,9017],{"class":2115,"line":9016},81,[2113,9018,9019],{"class":2396},"\u002F\u002F C'est moche, mais c'est pour tester\n",[2113,9021,9023,9026,9028,9031,9033,9036,9039,9041],{"class":2115,"line":9022},82,[2113,9024,9025],{"class":2414},"stream",[2113,9027,179],{"class":2119},[2113,9029,9030],{"class":2133},"on",[2113,9032,2423],{"class":2119},[2113,9034,9035],{"class":2149},"\"close\"",[2113,9037,9038],{"class":2119},", () ",[2113,9040,8063],{"class":2326},[2113,9042,2647],{"class":2119},[2113,9044,9046,9049,9051,9053,9055,9058,9060,9062,9064,9066,9068,9070,9072],{"class":2115,"line":9045},83,[2113,9047,9048],{"class":2414},"  console",[2113,9050,179],{"class":2119},[2113,9052,8748],{"class":2133},[2113,9054,2423],{"class":2119},[2113,9056,9057],{"class":2149},"\"Write to file time: \"",[2113,9059,932],{"class":2119},[2113,9061,36],{"class":2414},[2113,9063,179],{"class":2119},[2113,9065,8616],{"class":2133},[2113,9067,4543],{"class":2119},[2113,9069,3242],{"class":2334},[2113,9071,8918],{"class":2330},[2113,9073,2553],{"class":2119},[2113,9075,9077],{"class":2115,"line":9076},84,[2113,9078,9079],{"class":2396},"  \u002F\u002F Size of file test on disk\n",[2113,9081,9083,9085,9088,9090,9092,9094,9097,9099,9101],{"class":2115,"line":9082},85,[2113,9084,2995],{"class":2326},[2113,9086,9087],{"class":2414}," stats",[2113,9089,2153],{"class":2334},[2113,9091,7807],{"class":2414},[2113,9093,179],{"class":2119},[2113,9095,9096],{"class":2133},"statSync",[2113,9098,2423],{"class":2119},[2113,9100,8963],{"class":2149},[2113,9102,2553],{"class":2119},[2113,9104,9106,9108,9110,9112,9114,9117,9119,9121,9123,9125,9127,9130,9132,9134],{"class":2115,"line":9105},86,[2113,9107,9048],{"class":2414},[2113,9109,179],{"class":2119},[2113,9111,8748],{"class":2133},[2113,9113,2423],{"class":2119},[2113,9115,9116],{"class":2149},"\"Size of file on disk: \"",[2113,9118,932],{"class":2119},[2113,9120,8838],{"class":2414},[2113,9122,179],{"class":2119},[2113,9124,8843],{"class":2133},[2113,9126,2423],{"class":2119},[2113,9128,9129],{"class":2414},"stats",[2113,9131,179],{"class":2119},[2113,9133,7873],{"class":2330},[2113,9135,5818],{"class":2119},[2113,9137,9139,9141,9143,9145],{"class":2115,"line":9138},87,[2113,9140,9048],{"class":2414},[2113,9142,179],{"class":2119},[2113,9144,8748],{"class":2133},[2113,9146,3146],{"class":2119},[2113,9148,9150,9153],{"class":2115,"line":9149},88,[2113,9151,9152],{"class":2149},"    \"Size of object in the file\"",[2113,9154,4706],{"class":2119},[2113,9156,9158,9161,9163,9165,9167,9169,9171,9173,9176,9178],{"class":2115,"line":9157},89,[2113,9159,9160],{"class":2414},"    filesize",[2113,9162,179],{"class":2119},[2113,9164,8843],{"class":2133},[2113,9166,2423],{"class":2119},[2113,9168,9129],{"class":2414},[2113,9170,179],{"class":2119},[2113,9172,7873],{"class":2330},[2113,9174,9175],{"class":2334}," \u002F",[2113,9177,8642],{"class":2330},[2113,9179,4660],{"class":2119},[2113,9181,9183],{"class":2115,"line":9182},90,[2113,9184,9185],{"class":2119},"  );\n",[2113,9187,9189],{"class":2115,"line":9188},91,[2113,9190,8544],{"class":2119},[2113,9192,9194],{"class":2115,"line":9193},92,[2113,9195,2125],{"emptyLinePlaceholder":1767},[2113,9197,9199,9201,9203,9205,9208,9210,9212],{"class":2115,"line":9198},93,[2113,9200,8677],{"class":2326},[2113,9202,2495],{"class":2119},[2113,9204,7785],{"class":2326},[2113,9206,9207],{"class":2414}," obj",[2113,9209,2281],{"class":2326},[2113,9211,8657],{"class":2330},[2113,9213,2433],{"class":2119},[2113,9215,9217,9220,9222,9225,9227,9229,9231,9234],{"class":2115,"line":9216},94,[2113,9218,9219],{"class":2414},"  stream",[2113,9221,179],{"class":2119},[2113,9223,9224],{"class":2133},"write",[2113,9226,2423],{"class":2119},[2113,9228,7832],{"class":2133},[2113,9230,2423],{"class":2119},[2113,9232,9233],{"class":2330},"obj",[2113,9235,5818],{"class":2119},[2113,9237,9239],{"class":2115,"line":9238},95,[2113,9240,2599],{"class":2119},[2113,9242,9244,9246,9248,9251],{"class":2115,"line":9243},96,[2113,9245,9025],{"class":2414},[2113,9247,179],{"class":2119},[2113,9249,9250],{"class":2133},"end",[2113,9252,3944],{"class":2119},[12,9254,9255],{},"En estimant rapidement la mémoire que la taille de l'objet aurait dû prendre, je l'estime à environ 432 octets (12\nnombres de 2*64 bits + 1 octet de boolean + 128 caractères pour les chunks et 100 caractères pour le nom).",[12,9257,9258],{},"Le code n'est pas le plus propre, mais le but est ici d'illustrer rapidement la problématique. Voici le résultat de ce\ntest :",[22,9260,9261,9269],{},[25,9262,9263],{},[28,9264,9265,9267],{},[31,9266],{},[31,9268],{},[41,9270,9271,9279,9287,9295,9303,9311,9319],{},[28,9272,9273,9276],{},[46,9274,9275],{},"Nombre d'objets créés",[46,9277,9278],{},"1 300 000",[28,9280,9281,9284],{},[46,9282,9283],{},"Temps de création",[46,9285,9286],{},"15 secondes",[28,9288,9289,9292],{},[46,9290,9291],{},"Consommation mémoire",[46,9293,9294],{},"2,8 Go",[28,9296,9297,9300],{},[46,9298,9299],{},"Consommation moyenne par objet",[46,9301,9302],{},"2,2 Ko",[28,9304,9305,9308],{},[46,9306,9307],{},"Temps d'écriture dans le fichier",[46,9309,9310],{},"91 secondes",[28,9312,9313,9316],{},[46,9314,9315],{},"Taille du fichier sur le disque",[46,9317,9318],{},"1,1 Go",[28,9320,9321,9324],{},[46,9322,9323],{},"Taille moyenne d'un objet dans le fichier",[46,9325,9326],{},"903 octets",[12,9328,9329],{},"Avec ce premier exemple, on peut déjà constater que la consommation mémoire est très importante. En effet, on est à\n2,2Ko par objet au lieu des 432 estimés (soit 5 fois plus). La taille des objets dans le fichier est déjà un peu plus\nraisonnable.",[12,9331,9332],{},"La seule explication au fait que la consommation mémoire soit aussi importante est, je pense, liée au fait que dans V8,\nles structures sont toutes des objets avec des méthodes par défaut et une structure minimaliste (les Buffers également).",[128,9334,9336],{"id":9335},"test-avec-un-bigint","Test avec un BigInt",[12,9338,9339,9340,9343],{},"La question que l'on peut se poser est : pourquoi utiliser la structure ",[184,9341,9342],{},"{ low: Number, high: Number, unsigned: Bool }"," ?",[12,9345,9346],{},"J'utilise cette structure car pour la persistance, j'utilise la librairie protobuf.js qui ne supporte pas les BigInt,\nmais qui utilise à la place la librairie long.js qui utilise cette structure.",[12,9348,9349],{},"J'ai donc effectué un test en remplaçant chaque objet complexe par le nouveau type BigInt de Node.js. Voici le code :",[12,9351,9352],{},"Pour générer le nombre aléatoire, je me base sur un entier de 2*53 bits.",[2105,9354,9356],{"className":7776,"code":9355,"language":7778,"meta":1646,"style":1646},"function randomNumber() {\n  return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);\n}\n\nfunction randomBigInt() {\n  return (BigInt(randomNumber()) \u003C\u003C 53n) + BigInt(randomNumber());\n}\n",[184,9357,9358,9366,9392,9396,9400,9409,9447],{"__ignoreMap":1646},[2113,9359,9360,9362,9364],{"class":2115,"line":2116},[2113,9361,7865],{"class":2326},[2113,9363,8005],{"class":2133},[2113,9365,3912],{"class":2119},[2113,9367,9368,9370,9372,9374,9376,9378,9380,9382,9384,9386,9388,9390],{"class":2115,"line":1647},[2113,9369,2578],{"class":2326},[2113,9371,7949],{"class":2414},[2113,9373,179],{"class":2119},[2113,9375,7954],{"class":2133},[2113,9377,2423],{"class":2119},[2113,9379,7959],{"class":2414},[2113,9381,179],{"class":2119},[2113,9383,7964],{"class":2133},[2113,9385,4543],{"class":2119},[2113,9387,2676],{"class":2334},[2113,9389,8032],{"class":2414},[2113,9391,8035],{"class":2119},[2113,9393,9394],{"class":2115,"line":1652},[2113,9395,2599],{"class":2119},[2113,9397,9398],{"class":2115,"line":2185},[2113,9399,2125],{"emptyLinePlaceholder":1767},[2113,9401,9402,9404,9407],{"class":2115,"line":2230},[2113,9403,7865],{"class":2326},[2113,9405,9406],{"class":2133}," randomBigInt",[2113,9408,3912],{"class":2119},[2113,9410,9411,9413,9415,9418,9420,9422,9425,9428,9431,9434,9436,9438,9441,9443,9445],{"class":2115,"line":2235},[2113,9412,2578],{"class":2326},[2113,9414,2495],{"class":2119},[2113,9416,9417],{"class":2133},"BigInt",[2113,9419,2423],{"class":2119},[2113,9421,8107],{"class":2133},[2113,9423,9424],{"class":2119},"()) ",[2113,9426,9427],{"class":2334},"\u003C\u003C",[2113,9429,9430],{"class":2166}," 53",[2113,9432,9433],{"class":2326},"n",[2113,9435,5709],{"class":2119},[2113,9437,3061],{"class":2334},[2113,9439,9440],{"class":2133}," BigInt",[2113,9442,2423],{"class":2119},[2113,9444,8107],{"class":2133},[2113,9446,2593],{"class":2119},[2113,9448,9449],{"class":2115,"line":2241},[2113,9450,2599],{"class":2119},[12,9452,9453],{},"Le résultat est le suivant:",[22,9455,9456,9464],{},[25,9457,9458],{},[28,9459,9460,9462],{},[31,9461],{},[31,9463],{},[41,9465,9466,9472,9479,9486,9493,9500,9507],{},[28,9467,9468,9470],{},[46,9469,9275],{},[46,9471,9278],{},[28,9473,9474,9476],{},[46,9475,9283],{},[46,9477,9478],{},"14 secondes",[28,9480,9481,9483],{},[46,9482,9291],{},[46,9484,9485],{},"2,1 Go",[28,9487,9488,9490],{},[46,9489,9299],{},[46,9491,9492],{},"1,7 Ko",[28,9494,9495,9497],{},[46,9496,9307],{},[46,9498,9499],{},"40 secondes",[28,9501,9502,9504],{},[46,9503,9315],{},[46,9505,9506],{},"747,8 Mo",[28,9508,9509,9511],{},[46,9510,9323],{},[46,9512,9513],{},"603 octets",[12,9515,9516,9517,9519],{},"L'utilisation de ",[184,9518,9417],{}," permet de réduire la consommation mémoire de 25% et la taille des objets dans le fichier de\n33%. La taille des objets sur le disque est acceptable. En revanche, la consommation mémoire de Node.js reste trop\nimportante.",[128,9521,9523],{"id":9522},"ecriture-du-même-code-en-rust","Ecriture du même code en Rust",[12,9525,9526],{},"Il est possible d'optimiser certaines parties de l'application grâce à la notion de node_modules natifs. Habituellement,\nces modules sont écrits en C++ en utilisant N-API. Il existe des bindings pour Rust, ce qui permet d'écrire une partie\nde l'application en Rust et de l'utiliser dans Node.JS.",[12,9528,9529,9530,9533,9534,179],{},"Pour comparer les performances de Rust et de Node.js, j'ai écrit le même code en Rust. Pour les nombres, je me suis basé\nsur des entiers de 64 bits et pour le nom de fichier, sur un ",[184,9531,9532],{},"Vec\u003Cu8>",". Je me suis basé sur une table de 32 caractères\npour le ",[184,9535,9536],{},"Sha256",[2105,9538,9540],{"className":3855,"code":9539,"language":1775,"meta":1646,"style":1646},"use humansize::{format_size, make_format, DECIMAL};\nuse procinfo::pid;\nuse rand::{rngs::ThreadRng, Rng};\nuse serde::{Deserialize, Serialize};\nuse std::io::BufWriter;\n\n#[derive(Serialize, Deserialize, Debug)]\nstruct Stats {\n    owner_id: u64,\n    group_id: u64,\n    size: u64,\n    compressed_size: u64,\n    last_read: u64,\n    last_modified: u64,\n    created: u64,\n    mode: u64,\n    dev: u64,\n    rdev: u64,\n    ino: u64,\n    nlink: u64,\n}\n\n#[derive(Serialize, Deserialize, Debug)]\nstruct Sha256 {\n    data: [u8; 32],\n}\n\n#[derive(Serialize, Deserialize, Debug)]\nstruct TestObject {\n    #[serde(with = \"serde_bytes\")]\n    path: Vec\u003Cu8>,\n    stats: Stats,\n\n    chunks: Vec\u003CSha256>,\n    sha256: Sha256,\n}\n\nfn generate_random_number(rng: &mut ThreadRng) -> u64 {\n    rng.gen()\n}\n\nfn generate_random_vect(rng: &mut ThreadRng, size: usize) -> Vec\u003Cu8> {\n    let mut vect: Vec\u003Cu8> = Vec::with_capacity(size);\n    for _ in 0..size {\n        vect.push(rng.gen());\n    }\n    vect\n}\n\nfn generate_random_sha256(rng: &mut ThreadRng) -> Sha256 {\n    let mut sha256 = Sha256 { data: [0; 32] };\n    for i in 0..32 {\n        sha256.data[i] = rng.gen();\n    }\n    sha256\n}\n\nfn main() {\n    let formatter = make_format(DECIMAL);\n    let mut rng = rand::thread_rng();\n    \u002F\u002F Get the memory consumption of the current process\n    let memory = pid::statm_self().unwrap();\n    \u002F\u002F Get the current time in ms\n    let now = std::time::SystemTime::now()\n        .duration_since(std::time::UNIX_EPOCH)\n        .unwrap()\n        .as_millis();\n\n    \u002F\u002F Create a vector with 1_300_000 elements of type TestObject\n    let nb_object = 1_300_000;\n    let mut test_objects: Vec\u003CTestObject> = Vec::with_capacity(nb_object);\n    for _ in 0..nb_object {\n        test_objects.push(TestObject {\n            path: generate_random_vect(&mut rng, 100),\n            stats: Stats {\n                owner_id: generate_random_number(&mut rng),\n                group_id: generate_random_number(&mut rng),\n                size: generate_random_number(&mut rng),\n                compressed_size: generate_random_number(&mut rng),\n                last_read: generate_random_number(&mut rng),\n                last_modified: generate_random_number(&mut rng),\n                created: generate_random_number(&mut rng),\n                mode: generate_random_number(&mut rng),\n                dev: generate_random_number(&mut rng),\n                rdev: generate_random_number(&mut rng),\n                ino: generate_random_number(&mut rng),\n                nlink: generate_random_number(&mut rng),\n            },\n            chunks: vec![\n                generate_random_sha256(&mut rng),\n                generate_random_sha256(&mut rng),\n                generate_random_sha256(&mut rng),\n            ],\n            sha256: generate_random_sha256(&mut rng),\n        });\n    }\n\n    let now_after_creation = std::time::SystemTime::now()\n        .duration_since(std::time::UNIX_EPOCH)\n        .unwrap()\n        .as_millis();\n    \u002F\u002F Calculate the creation time\n    println!(\"Creation time: {} ms\", now_after_creation - now);\n\n    \u002F\u002F Get the memory consumption of the current process\n    let memory_after_creation = pid::statm_self().unwrap();\n    \u002F\u002F Show memory consumption\n    println!(\n        \"Memory consumption after creation: {}\",\n        formatter((memory_after_creation.size - memory.size) * 4096)\n    );\n    \u002F\u002F Show average memory consumption per object\n    println!(\n        \"Average memory consumption per object: {}\",\n        formatter(((memory_after_creation.size - memory.size) * 4096) \u002F nb_object)\n    );\n\n    \u002F\u002F Remove the file test.bin if it exists\n    match std::fs::remove_file(\"test.bin\") {\n        Ok(_) => {}\n        Err(_) => {}\n    }\n\n    \u002F\u002F Serialize all the objects in a file\n    let file = std::fs::File::create(\"test.bin\").unwrap();\n    let mut buffile = BufWriter::new(file);\n    for test_object in test_objects.iter() {\n        bincode::serialize_into(&mut buffile, test_object).unwrap();\n    }\n\n    \u002F\u002F Show time to write\n    let now_after_serialization = std::time::SystemTime::now()\n        .duration_since(std::time::UNIX_EPOCH)\n        .unwrap()\n        .as_millis();\n\n    println!(\n        \"Write to file time: {} ms\",\n        now_after_serialization - now_after_creation\n    );\n\n    \u002F\u002F Get the file size and the average object size in file\n    let metadata = std::fs::metadata(\"test.bin\").unwrap();\n    println!(\n        \"Size of file on disk: {}\",\n        format_size(metadata.len(), DECIMAL)\n    );\n    println!(\n        \"Size of object in the file: {}\",\n        format_size(metadata.len() \u002F nb_object as u64, DECIMAL)\n    );\n}\n",[184,9541,9542,9557,9567,9592,9611,9628,9632,9651,9660,9671,9682,9692,9703,9714,9725,9735,9745,9755,9765,9775,9785,9789,9793,9809,9818,9833,9837,9841,9857,9866,9878,9893,9905,9909,9924,9935,9939,9943,9968,9980,9984,9988,10023,10057,10077,10097,10101,10106,10110,10114,10137,10166,10182,10205,10209,10214,10218,10222,10230,10248,10267,10272,10295,10300,10327,10350,10358,10367,10371,10376,10389,10424,10440,10455,10477,10488,10506,10523,10540,10557,10574,10591,10608,10625,10642,10659,10676,10693,10698,10711,10724,10736,10748,10753,10771,10776,10780,10785,10811,10832,10841,10850,10856,10878,10883,10888,10910,10916,10923,10931,10956,10962,10968,10975,10983,11008,11013,11018,11024,11047,11060,11072,11077,11082,11088,11122,11148,11167,11195,11200,11205,11211,11237,11258,11267,11276,11281,11288,11296,11307,11312,11317,11323,11354,11361,11369,11389,11394,11401,11409,11438,11443],{"__ignoreMap":1646},[2113,9543,9544,9546,9549,9552,9555],{"class":2115,"line":2116},[2113,9545,3863],{"class":2326},[2113,9547,9548],{"class":2414}," humansize",[2113,9550,9551],{"class":2119},"::{format_size, make_format, ",[2113,9553,9554],{"class":2414},"DECIMAL",[2113,9556,2787],{"class":2119},[2113,9558,9559,9561,9564],{"class":2115,"line":1647},[2113,9560,3863],{"class":2326},[2113,9562,9563],{"class":2414}," procinfo",[2113,9565,9566],{"class":2119},"::pid;\n",[2113,9568,9569,9571,9574,9577,9580,9582,9585,9587,9590],{"class":2115,"line":1652},[2113,9570,3863],{"class":2326},[2113,9572,9573],{"class":2414}," rand",[2113,9575,9576],{"class":2119},"::{",[2113,9578,9579],{"class":2414},"rngs",[2113,9581,2418],{"class":2119},[2113,9583,9584],{"class":2414},"ThreadRng",[2113,9586,932],{"class":2119},[2113,9588,9589],{"class":2414},"Rng",[2113,9591,2787],{"class":2119},[2113,9593,9594,9596,9599,9601,9604,9606,9609],{"class":2115,"line":2185},[2113,9595,3863],{"class":2326},[2113,9597,9598],{"class":2414}," serde",[2113,9600,9576],{"class":2119},[2113,9602,9603],{"class":2414},"Deserialize",[2113,9605,932],{"class":2119},[2113,9607,9608],{"class":2414},"Serialize",[2113,9610,2787],{"class":2119},[2113,9612,9613,9615,9617,9619,9621,9623,9626],{"class":2115,"line":2230},[2113,9614,3863],{"class":2326},[2113,9616,3885],{"class":2414},[2113,9618,2418],{"class":2119},[2113,9620,4486],{"class":2414},[2113,9622,2418],{"class":2119},[2113,9624,9625],{"class":2414},"BufWriter",[2113,9627,2487],{"class":2119},[2113,9629,9630],{"class":2115,"line":2235},[2113,9631,2125],{"emptyLinePlaceholder":1767},[2113,9633,9634,9637,9639,9641,9643,9645,9648],{"class":2115,"line":2241},[2113,9635,9636],{"class":2119},"#[derive(",[2113,9638,9608],{"class":2414},[2113,9640,932],{"class":2119},[2113,9642,9603],{"class":2414},[2113,9644,932],{"class":2119},[2113,9646,9647],{"class":2414},"Debug",[2113,9649,9650],{"class":2119},")]\n",[2113,9652,9653,9655,9658],{"class":2115,"line":2246},[2113,9654,2641],{"class":2326},[2113,9656,9657],{"class":2414}," Stats",[2113,9659,2647],{"class":2119},[2113,9661,9662,9665,9667,9669],{"class":2115,"line":2464},[2113,9663,9664],{"class":2330},"    owner_id",[2113,9666,4429],{"class":2119},[2113,9668,6101],{"class":2414},[2113,9670,4706],{"class":2119},[2113,9672,9673,9676,9678,9680],{"class":2115,"line":2085},[2113,9674,9675],{"class":2330},"    group_id",[2113,9677,4429],{"class":2119},[2113,9679,6101],{"class":2414},[2113,9681,4706],{"class":2119},[2113,9683,9684,9686,9688,9690],{"class":2115,"line":2514},[2113,9685,8165],{"class":2330},[2113,9687,4429],{"class":2119},[2113,9689,6101],{"class":2414},[2113,9691,4706],{"class":2119},[2113,9693,9694,9697,9699,9701],{"class":2115,"line":2533},[2113,9695,9696],{"class":2330},"    compressed_size",[2113,9698,4429],{"class":2119},[2113,9700,6101],{"class":2414},[2113,9702,4706],{"class":2119},[2113,9704,9705,9708,9710,9712],{"class":2115,"line":2556},[2113,9706,9707],{"class":2330},"    last_read",[2113,9709,4429],{"class":2119},[2113,9711,6101],{"class":2414},[2113,9713,4706],{"class":2119},[2113,9715,9716,9719,9721,9723],{"class":2115,"line":2569},[2113,9717,9718],{"class":2330},"    last_modified",[2113,9720,4429],{"class":2119},[2113,9722,6101],{"class":2414},[2113,9724,4706],{"class":2119},[2113,9726,9727,9729,9731,9733],{"class":2115,"line":2575},[2113,9728,8304],{"class":2330},[2113,9730,4429],{"class":2119},[2113,9732,6101],{"class":2414},[2113,9734,4706],{"class":2119},[2113,9736,9737,9739,9741,9743],{"class":2115,"line":2596},[2113,9738,8335],{"class":2330},[2113,9740,4429],{"class":2119},[2113,9742,6101],{"class":2414},[2113,9744,4706],{"class":2119},[2113,9746,9747,9749,9751,9753],{"class":2115,"line":3192},[2113,9748,8366],{"class":2330},[2113,9750,4429],{"class":2119},[2113,9752,6101],{"class":2414},[2113,9754,4706],{"class":2119},[2113,9756,9757,9759,9761,9763],{"class":2115,"line":3213},[2113,9758,8397],{"class":2330},[2113,9760,4429],{"class":2119},[2113,9762,6101],{"class":2414},[2113,9764,4706],{"class":2119},[2113,9766,9767,9769,9771,9773],{"class":2115,"line":3236},[2113,9768,8428],{"class":2330},[2113,9770,4429],{"class":2119},[2113,9772,6101],{"class":2414},[2113,9774,4706],{"class":2119},[2113,9776,9777,9779,9781,9783],{"class":2115,"line":3248},[2113,9778,8459],{"class":2330},[2113,9780,4429],{"class":2119},[2113,9782,6101],{"class":2414},[2113,9784,4706],{"class":2119},[2113,9786,9787],{"class":2115,"line":4899},[2113,9788,2599],{"class":2119},[2113,9790,9791],{"class":2115,"line":1777},[2113,9792,2125],{"emptyLinePlaceholder":1767},[2113,9794,9795,9797,9799,9801,9803,9805,9807],{"class":2115,"line":4931},[2113,9796,9636],{"class":2119},[2113,9798,9608],{"class":2414},[2113,9800,932],{"class":2119},[2113,9802,9603],{"class":2414},[2113,9804,932],{"class":2119},[2113,9806,9647],{"class":2414},[2113,9808,9650],{"class":2119},[2113,9810,9811,9813,9816],{"class":2115,"line":4961},[2113,9812,2641],{"class":2326},[2113,9814,9815],{"class":2414}," Sha256",[2113,9817,2647],{"class":2119},[2113,9819,9820,9823,9825,9827,9829,9831],{"class":2115,"line":4976},[2113,9821,9822],{"class":2330},"    data",[2113,9824,6155],{"class":2119},[2113,9826,4480],{"class":2414},[2113,9828,6160],{"class":2119},[2113,9830,8504],{"class":2166},[2113,9832,7173],{"class":2119},[2113,9834,9835],{"class":2115,"line":4993},[2113,9836,2599],{"class":2119},[2113,9838,9839],{"class":2115,"line":5013},[2113,9840,2125],{"emptyLinePlaceholder":1767},[2113,9842,9843,9845,9847,9849,9851,9853,9855],{"class":2115,"line":5018},[2113,9844,9636],{"class":2119},[2113,9846,9608],{"class":2414},[2113,9848,932],{"class":2119},[2113,9850,9603],{"class":2414},[2113,9852,932],{"class":2119},[2113,9854,9647],{"class":2414},[2113,9856,9650],{"class":2119},[2113,9858,9859,9861,9864],{"class":2115,"line":5042},[2113,9860,2641],{"class":2326},[2113,9862,9863],{"class":2414}," TestObject",[2113,9865,2647],{"class":2119},[2113,9867,9868,9871,9873,9876],{"class":2115,"line":5057},[2113,9869,9870],{"class":2119},"    #[serde(with ",[2113,9872,2335],{"class":2334},[2113,9874,9875],{"class":2149}," \"serde_bytes\"",[2113,9877,9650],{"class":2119},[2113,9879,9880,9882,9884,9886,9888,9890],{"class":2115,"line":5062},[2113,9881,7559],{"class":2330},[2113,9883,4429],{"class":2119},[2113,9885,4733],{"class":2414},[2113,9887,3109],{"class":2119},[2113,9889,4480],{"class":2414},[2113,9891,9892],{"class":2119},">,\n",[2113,9894,9895,9898,9900,9903],{"class":2115,"line":5098},[2113,9896,9897],{"class":2330},"    stats",[2113,9899,4429],{"class":2119},[2113,9901,9902],{"class":2414},"Stats",[2113,9904,4706],{"class":2119},[2113,9906,9907],{"class":2115,"line":5117},[2113,9908,2125],{"emptyLinePlaceholder":1767},[2113,9910,9911,9914,9916,9918,9920,9922],{"class":2115,"line":5142},[2113,9912,9913],{"class":2330},"    chunks",[2113,9915,4429],{"class":2119},[2113,9917,4733],{"class":2414},[2113,9919,3109],{"class":2119},[2113,9921,9536],{"class":2414},[2113,9923,9892],{"class":2119},[2113,9925,9926,9929,9931,9933],{"class":2115,"line":5148},[2113,9927,9928],{"class":2330},"    sha256",[2113,9930,4429],{"class":2119},[2113,9932,9536],{"class":2414},[2113,9934,4706],{"class":2119},[2113,9936,9937],{"class":2115,"line":5163},[2113,9938,2599],{"class":2119},[2113,9940,9941],{"class":2115,"line":5169},[2113,9942,2125],{"emptyLinePlaceholder":1767},[2113,9944,9945,9947,9950,9952,9955,9957,9959,9962,9964,9966],{"class":2115,"line":5175},[2113,9946,3906],{"class":2326},[2113,9948,9949],{"class":2133}," generate_random_number",[2113,9951,2423],{"class":2119},[2113,9953,9954],{"class":2330},"rng",[2113,9956,4472],{"class":2119},[2113,9958,4030],{"class":2326},[2113,9960,9961],{"class":2414}," ThreadRng",[2113,9963,4790],{"class":2119},[2113,9965,6101],{"class":2414},[2113,9967,2647],{"class":2119},[2113,9969,9970,9973,9975,9978],{"class":2115,"line":5180},[2113,9971,9972],{"class":2330},"    rng",[2113,9974,179],{"class":2119},[2113,9976,9977],{"class":2133},"gen",[2113,9979,6745],{"class":2119},[2113,9981,9982],{"class":2115,"line":5199},[2113,9983,2599],{"class":2119},[2113,9985,9986],{"class":2115,"line":5204},[2113,9987,2125],{"emptyLinePlaceholder":1767},[2113,9989,9990,9992,9995,9997,9999,10001,10003,10005,10007,10009,10011,10013,10015,10017,10019,10021],{"class":2115,"line":5209},[2113,9991,3906],{"class":2326},[2113,9993,9994],{"class":2133}," generate_random_vect",[2113,9996,2423],{"class":2119},[2113,9998,9954],{"class":2330},[2113,10000,4472],{"class":2119},[2113,10002,4030],{"class":2326},[2113,10004,9961],{"class":2414},[2113,10006,932],{"class":2119},[2113,10008,7873],{"class":2330},[2113,10010,4429],{"class":2119},[2113,10012,4496],{"class":2414},[2113,10014,4790],{"class":2119},[2113,10016,4733],{"class":2414},[2113,10018,3109],{"class":2119},[2113,10020,4480],{"class":2414},[2113,10022,4449],{"class":2119},[2113,10024,10025,10027,10029,10032,10034,10036,10038,10040,10042,10044,10046,10048,10051,10053,10055],{"class":2115,"line":5232},[2113,10026,3917],{"class":2326},[2113,10028,3975],{"class":2326},[2113,10030,10031],{"class":2330}," vect",[2113,10033,4429],{"class":2119},[2113,10035,4733],{"class":2414},[2113,10037,3109],{"class":2119},[2113,10039,4480],{"class":2414},[2113,10041,4435],{"class":2119},[2113,10043,2335],{"class":2334},[2113,10045,4008],{"class":2414},[2113,10047,2418],{"class":2119},[2113,10049,10050],{"class":2133},"with_capacity",[2113,10052,2423],{"class":2119},[2113,10054,7873],{"class":2330},[2113,10056,2553],{"class":2119},[2113,10058,10059,10062,10065,10068,10070,10073,10075],{"class":2115,"line":5237},[2113,10060,10061],{"class":2326},"    for",[2113,10063,10064],{"class":2330}," _",[2113,10066,10067],{"class":2326}," in",[2113,10069,3208],{"class":2166},[2113,10071,10072],{"class":2119},"..",[2113,10074,7873],{"class":2330},[2113,10076,2647],{"class":2119},[2113,10078,10079,10082,10084,10087,10089,10091,10093,10095],{"class":2115,"line":5242},[2113,10080,10081],{"class":2330},"        vect",[2113,10083,179],{"class":2119},[2113,10085,10086],{"class":2133},"push",[2113,10088,2423],{"class":2119},[2113,10090,9954],{"class":2330},[2113,10092,179],{"class":2119},[2113,10094,9977],{"class":2326},[2113,10096,2593],{"class":2119},[2113,10098,10099],{"class":2115,"line":5267},[2113,10100,4665],{"class":2119},[2113,10102,10103],{"class":2115,"line":5282},[2113,10104,10105],{"class":2330},"    vect\n",[2113,10107,10108],{"class":2115,"line":5295},[2113,10109,2599],{"class":2119},[2113,10111,10112],{"class":2115,"line":5310},[2113,10113,2125],{"emptyLinePlaceholder":1767},[2113,10115,10116,10118,10121,10123,10125,10127,10129,10131,10133,10135],{"class":2115,"line":5315},[2113,10117,3906],{"class":2326},[2113,10119,10120],{"class":2133}," generate_random_sha256",[2113,10122,2423],{"class":2119},[2113,10124,9954],{"class":2330},[2113,10126,4472],{"class":2119},[2113,10128,4030],{"class":2326},[2113,10130,9961],{"class":2414},[2113,10132,4790],{"class":2119},[2113,10134,9536],{"class":2414},[2113,10136,2647],{"class":2119},[2113,10138,10139,10141,10143,10146,10148,10150,10152,10155,10157,10159,10161,10163],{"class":2115,"line":5320},[2113,10140,3917],{"class":2326},[2113,10142,3975],{"class":2326},[2113,10144,10145],{"class":2330}," sha256",[2113,10147,2153],{"class":2334},[2113,10149,9815],{"class":2414},[2113,10151,7616],{"class":2119},[2113,10153,10154],{"class":2330},"data",[2113,10156,6155],{"class":2119},[2113,10158,3095],{"class":2166},[2113,10160,6160],{"class":2119},[2113,10162,8504],{"class":2166},[2113,10164,10165],{"class":2119},"] };\n",[2113,10167,10168,10170,10172,10174,10176,10178,10180],{"class":2115,"line":8625},[2113,10169,10061],{"class":2326},[2113,10171,7910],{"class":2330},[2113,10173,10067],{"class":2326},[2113,10175,3208],{"class":2166},[2113,10177,10072],{"class":2119},[2113,10179,8504],{"class":2166},[2113,10181,2647],{"class":2119},[2113,10183,10184,10187,10190,10192,10194,10196,10199,10201,10203],{"class":2115,"line":8631},[2113,10185,10186],{"class":2330},"        sha256",[2113,10188,10189],{"class":2119},".data[",[2113,10191,7919],{"class":2330},[2113,10193,4129],{"class":2119},[2113,10195,2335],{"class":2334},[2113,10197,10198],{"class":2330}," rng",[2113,10200,179],{"class":2119},[2113,10202,9977],{"class":2133},[2113,10204,3944],{"class":2119},[2113,10206,10207],{"class":2115,"line":8637},[2113,10208,4665],{"class":2119},[2113,10210,10211],{"class":2115,"line":8652},[2113,10212,10213],{"class":2330},"    sha256\n",[2113,10215,10216],{"class":2115,"line":8674},[2113,10217,2599],{"class":2119},[2113,10219,10220],{"class":2115,"line":8706},[2113,10221,2125],{"emptyLinePlaceholder":1767},[2113,10223,10224,10226,10228],{"class":2115,"line":8724},[2113,10225,3906],{"class":2326},[2113,10227,3909],{"class":2133},[2113,10229,3912],{"class":2119},[2113,10231,10232,10234,10237,10239,10242,10244,10246],{"class":2115,"line":8729},[2113,10233,3917],{"class":2326},[2113,10235,10236],{"class":2330}," formatter",[2113,10238,2153],{"class":2334},[2113,10240,10241],{"class":2133}," make_format",[2113,10243,2423],{"class":2119},[2113,10245,9554],{"class":2166},[2113,10247,2553],{"class":2119},[2113,10249,10250,10252,10254,10256,10258,10260,10262,10265],{"class":2115,"line":8734},[2113,10251,3917],{"class":2326},[2113,10253,3975],{"class":2326},[2113,10255,10198],{"class":2330},[2113,10257,2153],{"class":2334},[2113,10259,9573],{"class":2414},[2113,10261,2418],{"class":2119},[2113,10263,10264],{"class":2133},"thread_rng",[2113,10266,3944],{"class":2119},[2113,10268,10269],{"class":2115,"line":8740},[2113,10270,10271],{"class":2396},"    \u002F\u002F Get the memory consumption of the current process\n",[2113,10273,10274,10276,10279,10281,10284,10286,10289,10291,10293],{"class":2115,"line":8772},[2113,10275,3917],{"class":2326},[2113,10277,10278],{"class":2330}," memory",[2113,10280,2153],{"class":2334},[2113,10282,10283],{"class":2414}," pid",[2113,10285,2418],{"class":2119},[2113,10287,10288],{"class":2133},"statm_self",[2113,10290,5224],{"class":2119},[2113,10292,3941],{"class":2133},[2113,10294,3944],{"class":2119},[2113,10296,10297],{"class":2115,"line":8778},[2113,10298,10299],{"class":2396},"    \u002F\u002F Get the current time in ms\n",[2113,10301,10302,10304,10307,10309,10311,10313,10316,10318,10321,10323,10325],{"class":2115,"line":8789},[2113,10303,3917],{"class":2326},[2113,10305,10306],{"class":2330}," now",[2113,10308,2153],{"class":2334},[2113,10310,3885],{"class":2414},[2113,10312,2418],{"class":2119},[2113,10314,10315],{"class":2414},"time",[2113,10317,2418],{"class":2119},[2113,10319,10320],{"class":2414},"SystemTime",[2113,10322,2418],{"class":2119},[2113,10324,8616],{"class":2133},[2113,10326,6745],{"class":2119},[2113,10328,10329,10332,10335,10337,10339,10341,10343,10345,10348],{"class":2115,"line":8795},[2113,10330,10331],{"class":2119},"        .",[2113,10333,10334],{"class":2133},"duration_since",[2113,10336,2423],{"class":2119},[2113,10338,6833],{"class":2414},[2113,10340,2418],{"class":2119},[2113,10342,10315],{"class":2414},[2113,10344,2418],{"class":2119},[2113,10346,10347],{"class":2166},"UNIX_EPOCH",[2113,10349,4660],{"class":2119},[2113,10351,10352,10354,10356],{"class":2115,"line":8817},[2113,10353,10331],{"class":2119},[2113,10355,3941],{"class":2133},[2113,10357,6745],{"class":2119},[2113,10359,10360,10362,10365],{"class":2115,"line":8822},[2113,10361,10331],{"class":2119},[2113,10363,10364],{"class":2133},"as_millis",[2113,10366,3944],{"class":2119},[2113,10368,10369],{"class":2115,"line":8857},[2113,10370,2125],{"emptyLinePlaceholder":1767},[2113,10372,10373],{"class":2115,"line":8896},[2113,10374,10375],{"class":2396},"    \u002F\u002F Create a vector with 1_300_000 elements of type TestObject\n",[2113,10377,10378,10380,10383,10385,10387],{"class":2115,"line":8901},[2113,10379,3917],{"class":2326},[2113,10381,10382],{"class":2330}," nb_object",[2113,10384,2153],{"class":2334},[2113,10386,8647],{"class":2166},[2113,10388,2487],{"class":2119},[2113,10390,10391,10393,10395,10398,10400,10402,10404,10407,10409,10411,10413,10415,10417,10419,10422],{"class":2115,"line":8907},[2113,10392,3917],{"class":2326},[2113,10394,3975],{"class":2326},[2113,10396,10397],{"class":2330}," test_objects",[2113,10399,4429],{"class":2119},[2113,10401,4733],{"class":2414},[2113,10403,3109],{"class":2119},[2113,10405,10406],{"class":2414},"TestObject",[2113,10408,4435],{"class":2119},[2113,10410,2335],{"class":2334},[2113,10412,4008],{"class":2414},[2113,10414,2418],{"class":2119},[2113,10416,10050],{"class":2133},[2113,10418,2423],{"class":2119},[2113,10420,10421],{"class":2330},"nb_object",[2113,10423,2553],{"class":2119},[2113,10425,10426,10428,10430,10432,10434,10436,10438],{"class":2115,"line":8913},[2113,10427,10061],{"class":2326},[2113,10429,10064],{"class":2330},[2113,10431,10067],{"class":2326},[2113,10433,3208],{"class":2166},[2113,10435,10072],{"class":2119},[2113,10437,10421],{"class":2330},[2113,10439,2647],{"class":2119},[2113,10441,10442,10445,10447,10449,10451,10453],{"class":2115,"line":8931},[2113,10443,10444],{"class":2330},"        test_objects",[2113,10446,179],{"class":2119},[2113,10448,10086],{"class":2133},[2113,10450,2423],{"class":2119},[2113,10452,10406],{"class":2414},[2113,10454,2647],{"class":2119},[2113,10456,10457,10460,10462,10465,10467,10469,10471,10473,10475],{"class":2115,"line":8936},[2113,10458,10459],{"class":2330},"            path",[2113,10461,4429],{"class":2119},[2113,10463,10464],{"class":2133},"generate_random_vect",[2113,10466,4027],{"class":2119},[2113,10468,4030],{"class":2326},[2113,10470,10198],{"class":2330},[2113,10472,932],{"class":2119},[2113,10474,8081],{"class":2166},[2113,10476,7142],{"class":2119},[2113,10478,10479,10482,10484,10486],{"class":2115,"line":8942},[2113,10480,10481],{"class":2330},"            stats",[2113,10483,4429],{"class":2119},[2113,10485,9902],{"class":2414},[2113,10487,2647],{"class":2119},[2113,10489,10490,10493,10495,10498,10500,10502,10504],{"class":2115,"line":8950},[2113,10491,10492],{"class":2330},"                owner_id",[2113,10494,4429],{"class":2119},[2113,10496,10497],{"class":2133},"generate_random_number",[2113,10499,4027],{"class":2119},[2113,10501,4030],{"class":2326},[2113,10503,10198],{"class":2330},[2113,10505,7142],{"class":2119},[2113,10507,10508,10511,10513,10515,10517,10519,10521],{"class":2115,"line":8968},[2113,10509,10510],{"class":2330},"                group_id",[2113,10512,4429],{"class":2119},[2113,10514,10497],{"class":2133},[2113,10516,4027],{"class":2119},[2113,10518,4030],{"class":2326},[2113,10520,10198],{"class":2330},[2113,10522,7142],{"class":2119},[2113,10524,10525,10528,10530,10532,10534,10536,10538],{"class":2115,"line":8983},[2113,10526,10527],{"class":2330},"                size",[2113,10529,4429],{"class":2119},[2113,10531,10497],{"class":2133},[2113,10533,4027],{"class":2119},[2113,10535,4030],{"class":2326},[2113,10537,10198],{"class":2330},[2113,10539,7142],{"class":2119},[2113,10541,10542,10545,10547,10549,10551,10553,10555],{"class":2115,"line":8988},[2113,10543,10544],{"class":2330},"                compressed_size",[2113,10546,4429],{"class":2119},[2113,10548,10497],{"class":2133},[2113,10550,4027],{"class":2119},[2113,10552,4030],{"class":2326},[2113,10554,10198],{"class":2330},[2113,10556,7142],{"class":2119},[2113,10558,10559,10562,10564,10566,10568,10570,10572],{"class":2115,"line":9011},[2113,10560,10561],{"class":2330},"                last_read",[2113,10563,4429],{"class":2119},[2113,10565,10497],{"class":2133},[2113,10567,4027],{"class":2119},[2113,10569,4030],{"class":2326},[2113,10571,10198],{"class":2330},[2113,10573,7142],{"class":2119},[2113,10575,10576,10579,10581,10583,10585,10587,10589],{"class":2115,"line":9016},[2113,10577,10578],{"class":2330},"                last_modified",[2113,10580,4429],{"class":2119},[2113,10582,10497],{"class":2133},[2113,10584,4027],{"class":2119},[2113,10586,4030],{"class":2326},[2113,10588,10198],{"class":2330},[2113,10590,7142],{"class":2119},[2113,10592,10593,10596,10598,10600,10602,10604,10606],{"class":2115,"line":9022},[2113,10594,10595],{"class":2330},"                created",[2113,10597,4429],{"class":2119},[2113,10599,10497],{"class":2133},[2113,10601,4027],{"class":2119},[2113,10603,4030],{"class":2326},[2113,10605,10198],{"class":2330},[2113,10607,7142],{"class":2119},[2113,10609,10610,10613,10615,10617,10619,10621,10623],{"class":2115,"line":9045},[2113,10611,10612],{"class":2330},"                mode",[2113,10614,4429],{"class":2119},[2113,10616,10497],{"class":2133},[2113,10618,4027],{"class":2119},[2113,10620,4030],{"class":2326},[2113,10622,10198],{"class":2330},[2113,10624,7142],{"class":2119},[2113,10626,10627,10630,10632,10634,10636,10638,10640],{"class":2115,"line":9076},[2113,10628,10629],{"class":2330},"                dev",[2113,10631,4429],{"class":2119},[2113,10633,10497],{"class":2133},[2113,10635,4027],{"class":2119},[2113,10637,4030],{"class":2326},[2113,10639,10198],{"class":2330},[2113,10641,7142],{"class":2119},[2113,10643,10644,10647,10649,10651,10653,10655,10657],{"class":2115,"line":9082},[2113,10645,10646],{"class":2330},"                rdev",[2113,10648,4429],{"class":2119},[2113,10650,10497],{"class":2133},[2113,10652,4027],{"class":2119},[2113,10654,4030],{"class":2326},[2113,10656,10198],{"class":2330},[2113,10658,7142],{"class":2119},[2113,10660,10661,10664,10666,10668,10670,10672,10674],{"class":2115,"line":9105},[2113,10662,10663],{"class":2330},"                ino",[2113,10665,4429],{"class":2119},[2113,10667,10497],{"class":2133},[2113,10669,4027],{"class":2119},[2113,10671,4030],{"class":2326},[2113,10673,10198],{"class":2330},[2113,10675,7142],{"class":2119},[2113,10677,10678,10681,10683,10685,10687,10689,10691],{"class":2115,"line":9138},[2113,10679,10680],{"class":2330},"                nlink",[2113,10682,4429],{"class":2119},[2113,10684,10497],{"class":2133},[2113,10686,4027],{"class":2119},[2113,10688,4030],{"class":2326},[2113,10690,10198],{"class":2330},[2113,10692,7142],{"class":2119},[2113,10694,10695],{"class":2115,"line":9149},[2113,10696,10697],{"class":2119},"            },\n",[2113,10699,10700,10703,10705,10708],{"class":2115,"line":9157},[2113,10701,10702],{"class":2330},"            chunks",[2113,10704,4429],{"class":2119},[2113,10706,10707],{"class":2133},"vec!",[2113,10709,10710],{"class":2119},"[\n",[2113,10712,10713,10716,10718,10720,10722],{"class":2115,"line":9182},[2113,10714,10715],{"class":2133},"                generate_random_sha256",[2113,10717,4027],{"class":2119},[2113,10719,4030],{"class":2326},[2113,10721,10198],{"class":2330},[2113,10723,7142],{"class":2119},[2113,10725,10726,10728,10730,10732,10734],{"class":2115,"line":9188},[2113,10727,10715],{"class":2133},[2113,10729,4027],{"class":2119},[2113,10731,4030],{"class":2326},[2113,10733,10198],{"class":2330},[2113,10735,7142],{"class":2119},[2113,10737,10738,10740,10742,10744,10746],{"class":2115,"line":9193},[2113,10739,10715],{"class":2133},[2113,10741,4027],{"class":2119},[2113,10743,4030],{"class":2326},[2113,10745,10198],{"class":2330},[2113,10747,7142],{"class":2119},[2113,10749,10750],{"class":2115,"line":9198},[2113,10751,10752],{"class":2119},"            ],\n",[2113,10754,10755,10758,10760,10763,10765,10767,10769],{"class":2115,"line":9216},[2113,10756,10757],{"class":2330},"            sha256",[2113,10759,4429],{"class":2119},[2113,10761,10762],{"class":2133},"generate_random_sha256",[2113,10764,4027],{"class":2119},[2113,10766,4030],{"class":2326},[2113,10768,10198],{"class":2330},[2113,10770,7142],{"class":2119},[2113,10772,10773],{"class":2115,"line":9238},[2113,10774,10775],{"class":2119},"        });\n",[2113,10777,10778],{"class":2115,"line":9243},[2113,10779,4665],{"class":2119},[2113,10781,10783],{"class":2115,"line":10782},97,[2113,10784,2125],{"emptyLinePlaceholder":1767},[2113,10786,10788,10790,10793,10795,10797,10799,10801,10803,10805,10807,10809],{"class":2115,"line":10787},98,[2113,10789,3917],{"class":2326},[2113,10791,10792],{"class":2330}," now_after_creation",[2113,10794,2153],{"class":2334},[2113,10796,3885],{"class":2414},[2113,10798,2418],{"class":2119},[2113,10800,10315],{"class":2414},[2113,10802,2418],{"class":2119},[2113,10804,10320],{"class":2414},[2113,10806,2418],{"class":2119},[2113,10808,8616],{"class":2133},[2113,10810,6745],{"class":2119},[2113,10812,10814,10816,10818,10820,10822,10824,10826,10828,10830],{"class":2115,"line":10813},99,[2113,10815,10331],{"class":2119},[2113,10817,10334],{"class":2133},[2113,10819,2423],{"class":2119},[2113,10821,6833],{"class":2414},[2113,10823,2418],{"class":2119},[2113,10825,10315],{"class":2414},[2113,10827,2418],{"class":2119},[2113,10829,10347],{"class":2166},[2113,10831,4660],{"class":2119},[2113,10833,10835,10837,10839],{"class":2115,"line":10834},100,[2113,10836,10331],{"class":2119},[2113,10838,3941],{"class":2133},[2113,10840,6745],{"class":2119},[2113,10842,10844,10846,10848],{"class":2115,"line":10843},101,[2113,10845,10331],{"class":2119},[2113,10847,10364],{"class":2133},[2113,10849,3944],{"class":2119},[2113,10851,10853],{"class":2115,"line":10852},102,[2113,10854,10855],{"class":2396},"    \u002F\u002F Calculate the creation time\n",[2113,10857,10859,10861,10863,10866,10868,10871,10874,10876],{"class":2115,"line":10858},103,[2113,10860,4043],{"class":2133},[2113,10862,2423],{"class":2119},[2113,10864,10865],{"class":2149},"\"Creation time: {} ms\"",[2113,10867,932],{"class":2119},[2113,10869,10870],{"class":2330},"now_after_creation",[2113,10872,10873],{"class":2119}," - ",[2113,10875,8616],{"class":2330},[2113,10877,2553],{"class":2119},[2113,10879,10881],{"class":2115,"line":10880},104,[2113,10882,2125],{"emptyLinePlaceholder":1767},[2113,10884,10886],{"class":2115,"line":10885},105,[2113,10887,10271],{"class":2396},[2113,10889,10891,10893,10896,10898,10900,10902,10904,10906,10908],{"class":2115,"line":10890},106,[2113,10892,3917],{"class":2326},[2113,10894,10895],{"class":2330}," memory_after_creation",[2113,10897,2153],{"class":2334},[2113,10899,10283],{"class":2414},[2113,10901,2418],{"class":2119},[2113,10903,10288],{"class":2133},[2113,10905,5224],{"class":2119},[2113,10907,3941],{"class":2133},[2113,10909,3944],{"class":2119},[2113,10911,10913],{"class":2115,"line":10912},107,[2113,10914,10915],{"class":2396},"    \u002F\u002F Show memory consumption\n",[2113,10917,10919,10921],{"class":2115,"line":10918},108,[2113,10920,4043],{"class":2133},[2113,10922,3146],{"class":2119},[2113,10924,10926,10929],{"class":2115,"line":10925},109,[2113,10927,10928],{"class":2149},"        \"Memory consumption after creation: {}\"",[2113,10930,4706],{"class":2119},[2113,10932,10934,10937,10939,10942,10945,10948,10951,10954],{"class":2115,"line":10933},110,[2113,10935,10936],{"class":2133},"        formatter",[2113,10938,8879],{"class":2119},[2113,10940,10941],{"class":2330},"memory_after_creation",[2113,10943,10944],{"class":2119},".size - ",[2113,10946,10947],{"class":2330},"memory",[2113,10949,10950],{"class":2119},".size) * ",[2113,10952,10953],{"class":2166},"4096",[2113,10955,4660],{"class":2119},[2113,10957,10959],{"class":2115,"line":10958},111,[2113,10960,10961],{"class":2119},"    );\n",[2113,10963,10965],{"class":2115,"line":10964},112,[2113,10966,10967],{"class":2396},"    \u002F\u002F Show average memory consumption per object\n",[2113,10969,10971,10973],{"class":2115,"line":10970},113,[2113,10972,4043],{"class":2133},[2113,10974,3146],{"class":2119},[2113,10976,10978,10981],{"class":2115,"line":10977},114,[2113,10979,10980],{"class":2149},"        \"Average memory consumption per object: {}\"",[2113,10982,4706],{"class":2119},[2113,10984,10986,10988,10991,10993,10995,10997,10999,11001,11004,11006],{"class":2115,"line":10985},115,[2113,10987,10936],{"class":2133},[2113,10989,10990],{"class":2119},"(((",[2113,10992,10941],{"class":2330},[2113,10994,10944],{"class":2119},[2113,10996,10947],{"class":2330},[2113,10998,10950],{"class":2119},[2113,11000,10953],{"class":2166},[2113,11002,11003],{"class":2119},") \u002F ",[2113,11005,10421],{"class":2330},[2113,11007,4660],{"class":2119},[2113,11009,11011],{"class":2115,"line":11010},116,[2113,11012,10961],{"class":2119},[2113,11014,11016],{"class":2115,"line":11015},117,[2113,11017,2125],{"emptyLinePlaceholder":1767},[2113,11019,11021],{"class":2115,"line":11020},118,[2113,11022,11023],{"class":2396},"    \u002F\u002F Remove the file test.bin if it exists\n",[2113,11025,11027,11029,11031,11033,11035,11037,11040,11042,11045],{"class":2115,"line":11026},119,[2113,11028,6581],{"class":2326},[2113,11030,3885],{"class":2414},[2113,11032,2418],{"class":2119},[2113,11034,3890],{"class":2414},[2113,11036,2418],{"class":2119},[2113,11038,11039],{"class":2133},"remove_file",[2113,11041,2423],{"class":2119},[2113,11043,11044],{"class":2149},"\"test.bin\"",[2113,11046,2433],{"class":2119},[2113,11048,11050,11052,11054,11057],{"class":2115,"line":11049},120,[2113,11051,4653],{"class":2414},[2113,11053,2423],{"class":2119},[2113,11055,11056],{"class":2330},"_",[2113,11058,11059],{"class":2119},") => {}\n",[2113,11061,11063,11066,11068,11070],{"class":2115,"line":11062},121,[2113,11064,11065],{"class":2414},"        Err",[2113,11067,2423],{"class":2119},[2113,11069,11056],{"class":2330},[2113,11071,11059],{"class":2119},[2113,11073,11075],{"class":2115,"line":11074},122,[2113,11076,4665],{"class":2119},[2113,11078,11080],{"class":2115,"line":11079},123,[2113,11081,2125],{"emptyLinePlaceholder":1767},[2113,11083,11085],{"class":2115,"line":11084},124,[2113,11086,11087],{"class":2396},"    \u002F\u002F Serialize all the objects in a file\n",[2113,11089,11091,11093,11095,11097,11099,11101,11103,11105,11107,11109,11112,11114,11116,11118,11120],{"class":2115,"line":11090},125,[2113,11092,3917],{"class":2326},[2113,11094,7379],{"class":2330},[2113,11096,2153],{"class":2334},[2113,11098,3885],{"class":2414},[2113,11100,2418],{"class":2119},[2113,11102,3890],{"class":2414},[2113,11104,2418],{"class":2119},[2113,11106,3895],{"class":2414},[2113,11108,2418],{"class":2119},[2113,11110,11111],{"class":2133},"create",[2113,11113,2423],{"class":2119},[2113,11115,11044],{"class":2149},[2113,11117,3938],{"class":2119},[2113,11119,3941],{"class":2133},[2113,11121,3944],{"class":2119},[2113,11123,11125,11127,11129,11132,11134,11137,11139,11141,11143,11146],{"class":2115,"line":11124},126,[2113,11126,3917],{"class":2326},[2113,11128,3975],{"class":2326},[2113,11130,11131],{"class":2330}," buffile",[2113,11133,2153],{"class":2334},[2113,11135,11136],{"class":2414}," BufWriter",[2113,11138,2418],{"class":2119},[2113,11140,3961],{"class":2133},[2113,11142,2423],{"class":2119},[2113,11144,11145],{"class":2330},"file",[2113,11147,2553],{"class":2119},[2113,11149,11151,11153,11156,11158,11160,11162,11165],{"class":2115,"line":11150},127,[2113,11152,10061],{"class":2326},[2113,11154,11155],{"class":2330}," test_object",[2113,11157,10067],{"class":2326},[2113,11159,10397],{"class":2330},[2113,11161,179],{"class":2119},[2113,11163,11164],{"class":2133},"iter",[2113,11166,3912],{"class":2119},[2113,11168,11170,11173,11175,11178,11180,11182,11184,11186,11189,11191,11193],{"class":2115,"line":11169},128,[2113,11171,11172],{"class":2414},"        bincode",[2113,11174,2418],{"class":2119},[2113,11176,11177],{"class":2133},"serialize_into",[2113,11179,4027],{"class":2119},[2113,11181,4030],{"class":2326},[2113,11183,11131],{"class":2330},[2113,11185,932],{"class":2119},[2113,11187,11188],{"class":2330},"test_object",[2113,11190,3938],{"class":2119},[2113,11192,3941],{"class":2133},[2113,11194,3944],{"class":2119},[2113,11196,11198],{"class":2115,"line":11197},129,[2113,11199,4665],{"class":2119},[2113,11201,11203],{"class":2115,"line":11202},130,[2113,11204,2125],{"emptyLinePlaceholder":1767},[2113,11206,11208],{"class":2115,"line":11207},131,[2113,11209,11210],{"class":2396},"    \u002F\u002F Show time to write\n",[2113,11212,11214,11216,11219,11221,11223,11225,11227,11229,11231,11233,11235],{"class":2115,"line":11213},132,[2113,11215,3917],{"class":2326},[2113,11217,11218],{"class":2330}," now_after_serialization",[2113,11220,2153],{"class":2334},[2113,11222,3885],{"class":2414},[2113,11224,2418],{"class":2119},[2113,11226,10315],{"class":2414},[2113,11228,2418],{"class":2119},[2113,11230,10320],{"class":2414},[2113,11232,2418],{"class":2119},[2113,11234,8616],{"class":2133},[2113,11236,6745],{"class":2119},[2113,11238,11240,11242,11244,11246,11248,11250,11252,11254,11256],{"class":2115,"line":11239},133,[2113,11241,10331],{"class":2119},[2113,11243,10334],{"class":2133},[2113,11245,2423],{"class":2119},[2113,11247,6833],{"class":2414},[2113,11249,2418],{"class":2119},[2113,11251,10315],{"class":2414},[2113,11253,2418],{"class":2119},[2113,11255,10347],{"class":2166},[2113,11257,4660],{"class":2119},[2113,11259,11261,11263,11265],{"class":2115,"line":11260},134,[2113,11262,10331],{"class":2119},[2113,11264,3941],{"class":2133},[2113,11266,6745],{"class":2119},[2113,11268,11270,11272,11274],{"class":2115,"line":11269},135,[2113,11271,10331],{"class":2119},[2113,11273,10364],{"class":2133},[2113,11275,3944],{"class":2119},[2113,11277,11279],{"class":2115,"line":11278},136,[2113,11280,2125],{"emptyLinePlaceholder":1767},[2113,11282,11284,11286],{"class":2115,"line":11283},137,[2113,11285,4043],{"class":2133},[2113,11287,3146],{"class":2119},[2113,11289,11291,11294],{"class":2115,"line":11290},138,[2113,11292,11293],{"class":2149},"        \"Write to file time: {} ms\"",[2113,11295,4706],{"class":2119},[2113,11297,11299,11302,11304],{"class":2115,"line":11298},139,[2113,11300,11301],{"class":2330},"        now_after_serialization",[2113,11303,10873],{"class":2119},[2113,11305,11306],{"class":2330},"now_after_creation\n",[2113,11308,11310],{"class":2115,"line":11309},140,[2113,11311,10961],{"class":2119},[2113,11313,11315],{"class":2115,"line":11314},141,[2113,11316,2125],{"emptyLinePlaceholder":1767},[2113,11318,11320],{"class":2115,"line":11319},142,[2113,11321,11322],{"class":2396},"    \u002F\u002F Get the file size and the average object size in file\n",[2113,11324,11326,11328,11331,11333,11335,11337,11339,11341,11344,11346,11348,11350,11352],{"class":2115,"line":11325},143,[2113,11327,3917],{"class":2326},[2113,11329,11330],{"class":2330}," metadata",[2113,11332,2153],{"class":2334},[2113,11334,3885],{"class":2414},[2113,11336,2418],{"class":2119},[2113,11338,3890],{"class":2414},[2113,11340,2418],{"class":2119},[2113,11342,11343],{"class":2133},"metadata",[2113,11345,2423],{"class":2119},[2113,11347,11044],{"class":2149},[2113,11349,3938],{"class":2119},[2113,11351,3941],{"class":2133},[2113,11353,3944],{"class":2119},[2113,11355,11357,11359],{"class":2115,"line":11356},144,[2113,11358,4043],{"class":2133},[2113,11360,3146],{"class":2119},[2113,11362,11364,11367],{"class":2115,"line":11363},145,[2113,11365,11366],{"class":2149},"        \"Size of file on disk: {}\"",[2113,11368,4706],{"class":2119},[2113,11370,11372,11375,11377,11379,11381,11383,11385,11387],{"class":2115,"line":11371},146,[2113,11373,11374],{"class":2133},"        format_size",[2113,11376,2423],{"class":2119},[2113,11378,11343],{"class":2330},[2113,11380,179],{"class":2119},[2113,11382,4540],{"class":2133},[2113,11384,8110],{"class":2119},[2113,11386,9554],{"class":2166},[2113,11388,4660],{"class":2119},[2113,11390,11392],{"class":2115,"line":11391},147,[2113,11393,10961],{"class":2119},[2113,11395,11397,11399],{"class":2115,"line":11396},148,[2113,11398,4043],{"class":2133},[2113,11400,3146],{"class":2119},[2113,11402,11404,11407],{"class":2115,"line":11403},149,[2113,11405,11406],{"class":2149},"        \"Size of object in the file: {}\"",[2113,11408,4706],{"class":2119},[2113,11410,11412,11414,11416,11418,11420,11422,11425,11427,11430,11432,11434,11436],{"class":2115,"line":11411},150,[2113,11413,11374],{"class":2133},[2113,11415,2423],{"class":2119},[2113,11417,11343],{"class":2330},[2113,11419,179],{"class":2119},[2113,11421,4540],{"class":2133},[2113,11423,11424],{"class":2119},"() \u002F ",[2113,11426,10421],{"class":2330},[2113,11428,11429],{"class":2326}," as",[2113,11431,6245],{"class":2414},[2113,11433,932],{"class":2119},[2113,11435,9554],{"class":2166},[2113,11437,4660],{"class":2119},[2113,11439,11441],{"class":2115,"line":11440},151,[2113,11442,10961],{"class":2119},[2113,11444,11446],{"class":2115,"line":11445},152,[2113,11447,2599],{"class":2119},[12,11449,11450],{},"Ce qui donne le résultat suivant :",[22,11452,11453,11461],{},[25,11454,11455],{},[28,11456,11457,11459],{},[31,11458],{},[31,11460],{},[41,11462,11463,11469,11476,11483,11490,11497,11504],{},[28,11464,11465,11467],{},[46,11466,9275],{},[46,11468,9278],{},[28,11470,11471,11473],{},[46,11472,9283],{},[46,11474,11475],{},"2 secondes",[28,11477,11478,11480],{},[46,11479,9291],{},[46,11481,11482],{},"519,92 Mo",[28,11484,11485,11487],{},[46,11486,9299],{},[46,11488,11489],{},"399 octets",[28,11491,11492,11494],{},[46,11493,9307],{},[46,11495,11496],{},"1 seconde",[28,11498,11499,11501],{},[46,11500,9315],{},[46,11502,11503],{},"441,99 Mb",[28,11505,11506,11508],{},[46,11507,9323],{},[46,11509,11510],{},"339 octets",[12,11512,11513],{},"En comparant les résultats obtenus avec Node.js et Rust, on peut conclure que Rust est, sans contestation possible,\nbeaucoup plus performant que Node.js en termes de temps d'exécution et de consommation de mémoire pour la création de\n1,3 million d'objets. En effet, Rust a créé tous les objets en seulement 2 secondes et consommé 519,92 Mo de mémoire,\ntandis que Node.js a pris 15 secondes et consommé 2,8 Go de mémoire.",[12,11515,11516],{},"La consommation moyenne de mémoire par objet est du coup nettement plus faible avec Rust (399 octets) qu'avec\nNode.js (2,2 Ko).",[12,11518,11519],{},"En somme, cette première partie montre clairement que Rust est une alternative intéressante pour les applications\nnécessitant une manipulation intensive de données, en particulier lorsque la performance et la consommation de mémoire\nsont des critères importants.",[128,11521,11523],{"id":11522},"optimisation-de-notre-application","Optimisation de notre application",[12,11525,11526],{},"Pour optimiser notre application en TypeScript, nous allons développer un module natif en Node.js avec Rust. Pour cela,\nnous allons utiliser NAPI.RS.",[12,11528,11529],{},"N-API est une interface de programmation d'applications (API) qui permet aux modules natifs d'être facilement utilisés\ndans Node.js. Cela permet aux développeurs de créer des modules en C++ et de les utiliser dans des projets Node.js\nsans avoir à se soucier de la compatibilité entre les versions de Node.js.",[12,11531,11532],{},"N-API est une API stable et évolutive qui est maintenue par l'équipe Node.js. N-API fournit une interface de\nprogrammation d'applications indépendante du moteur JavaScript utilisé par Node.js. Cela signifie que les modules\ncompilés avec N-API fonctionnent de manière cohérente, quel que soit le moteur JavaScript utilisé par Node.js.",[12,11534,11535],{},"napi.rs est une bibliothèque Rust qui fournit une API pour écrire des modules Node.js en Rust. Elle simplifie la création\nde modules Node.js en fournissant des abstractions de niveau supérieur pour les fonctionnalités N-API. Avec napi.rs, les\ndéveloppeurs Rust peuvent facilement écrire des modules Node.js sans avoir à se soucier des détails techniques de N-API.",[12,11537,11538,11539,179],{},"Nous allons débuter le développement de la partie Rust de notre application. Je ne vais pas détailler la création d'un\nmodule Rust avec NAPI.RS. La documentation est assez bien faite pour cette partie : ",[49,11540,11541],{"href":11541,"rel":11542},"https:\u002F\u002Fnapi.rs\u002Fdocs\u002Fintroduction\u002Fsimple-package",[347],[12,11544,11545],{},"Le module que nous allons écrire doit conserver les objets qu'il crée en mémoire. En effet, si les objets créés côté Rust\nétaient ensuite transférés dans la partie Node.js, la consommation de mémoire serait la même que si nous avions créé\nles objets directement en Node.js.",[12,11547,11548],{},"L'exemple ci-dessous ne fait pas grand-chose, mais c'est un point à prendre en considération lorsque je vais développer\nle module pour mon application. Les objets transférés à la partie JavaScript ne doivent que transiter.",[12,11550,11551],{},"Voici la partie Rust :",[2105,11553,11555],{"className":3855,"code":11554,"language":1775,"meta":1646,"style":1646},"#![deny(clippy::all)]\nuse humansize::{format_size, DECIMAL};\nuse procinfo::pid;\nuse rand::{rngs::ThreadRng, Rng};\n\n#[macro_use]\nextern crate napi_derive;\n\nstruct Stats {\n  owner_id: u64,\n  group_id: u64,\n  size: u64,\n  compressed_size: u64,\n  last_read: u64,\n  last_modified: u64,\n  created: u64,\n  mode: u64,\n  dev: u64,\n  rdev: u64,\n  ino: u64,\n  nlink: u64,\n}\n\nstruct Sha256 {\n  data: [u8; 32],\n}\n\nstruct TestObject {\n  path: Vec\u003Cu8>,\n  stats: Stats,\n\n  chunks: Vec\u003CSha256>,\n  sha256: Sha256,\n}\n\nfn generate_random_number(rng: &mut ThreadRng) -> u64 {\n  rng.gen()\n}\n\nfn generate_random_vect(rng: &mut ThreadRng, size: usize) -> Vec\u003Cu8> {\n  let mut vect: Vec\u003Cu8> = Vec::with_capacity(size);\n  for _ in 0..size {\n    vect.push(rng.gen());\n  }\n  vect\n}\n\nfn generate_random_sha256(rng: &mut ThreadRng) -> Sha256 {\n  let mut sha256 = Sha256 { data: [0; 32] };\n  for i in 0..32 {\n    sha256.data[i] = rng.gen();\n  }\n  sha256\n}\n\npub struct TestObjectWrapper {\n  test_objects: Vec\u003CTestObject>,\n}\n\n#[napi(js_name = \"TestObjectWrapper\")]\npub struct JsTestObjectWrapper {\n  test_object_wrapper: TestObjectWrapper,\n}\n\n#[napi]\nimpl JsTestObjectWrapper {\n  #[napi(constructor)]\n  pub fn new() -> Self {\n    Self {\n      test_object_wrapper: TestObjectWrapper {\n        test_objects: Vec::new(),\n      },\n    }\n  }\n\n  #[napi]\n  pub fn fill(&mut self, nb_object: i32) {\n    let mut rng = rand::thread_rng();\n    \u002F\u002F Get the memory consumption of the current process\n    let memory = pid::statm_self().unwrap();\n    \u002F\u002F Get the current time in ms\n    let now = std::time::SystemTime::now()\n      .duration_since(std::time::UNIX_EPOCH)\n      .unwrap()\n      .as_millis();\n\n    self.test_object_wrapper.test_objects = Vec::with_capacity(nb_object.try_into().unwrap());\n\n    for _ in 0..nb_object {\n      self.test_object_wrapper.test_objects.push(TestObject {\n        path: generate_random_vect(&mut rng, 100),\n        stats: Stats {\n          owner_id: generate_random_number(&mut rng),\n          group_id: generate_random_number(&mut rng),\n          size: generate_random_number(&mut rng),\n          compressed_size: generate_random_number(&mut rng),\n          last_read: generate_random_number(&mut rng),\n          last_modified: generate_random_number(&mut rng),\n          created: generate_random_number(&mut rng),\n          mode: generate_random_number(&mut rng),\n          dev: generate_random_number(&mut rng),\n          rdev: generate_random_number(&mut rng),\n          ino: generate_random_number(&mut rng),\n          nlink: generate_random_number(&mut rng),\n        },\n        chunks: vec![\n          generate_random_sha256(&mut rng),\n          generate_random_sha256(&mut rng),\n          generate_random_sha256(&mut rng),\n        ],\n        sha256: generate_random_sha256(&mut rng),\n      });\n    }\n\n    let now_after_creation = std::time::SystemTime::now()\n      .duration_since(std::time::UNIX_EPOCH)\n      .unwrap()\n      .as_millis();\n    \u002F\u002F Calculate the creation time\n    println!(\"Creation time: {} ms\", now_after_creation - now);\n\n    \u002F\u002F Get the memory consumption of the current process\n    let memory_after_creation = pid::statm_self().unwrap();\n    \u002F\u002F Show memory consumption\n    println!(\n      \"Memory consumption after creation: {}\",\n      format_size((memory_after_creation.size - memory.size) * 4096, DECIMAL)\n    );\n    \u002F\u002F Show average memory consumption per object\n    println!(\n      \"Average memory consumption per object: {}\",\n      format_size(\n        ((memory_after_creation.size - memory.size) * 4096) \u002F nb_object as usize,\n        DECIMAL\n      )\n    );\n  }\n\n  #[napi(js_name = \"toString\")]\n  pub fn to_string(&self) -> String {\n    format!(\n      \"TestObjectWrapper {{ test_objects: {} }}\",\n      self.test_object_wrapper.test_objects.len()\n    )\n  }\n}\n",[184,11556,11557,11562,11575,11583,11603,11607,11612,11623,11627,11635,11646,11657,11668,11679,11690,11701,11712,11723,11734,11745,11756,11767,11771,11775,11783,11798,11802,11806,11814,11828,11838,11842,11856,11866,11870,11874,11896,11907,11911,11915,11949,11982,11998,12017,12021,12026,12030,12034,12056,12082,12098,12118,12122,12127,12131,12135,12146,12161,12165,12169,12181,12192,12204,12208,12212,12217,12225,12230,12246,12253,12264,12278,12283,12287,12291,12295,12300,12326,12344,12348,12368,12372,12396,12417,12425,12433,12437,12468,12472,12488,12504,12525,12536,12553,12570,12587,12604,12621,12638,12655,12672,12689,12706,12723,12740,12745,12756,12769,12781,12793,12798,12814,12819,12823,12827,12851,12871,12879,12887,12891,12909,12913,12917,12937,12941,12947,12954,12977,12981,12985,12991,12998,13004,13030,13035,13040,13044,13048,13052,13064,13084,13091,13098,13108,13113,13117],{"__ignoreMap":1646},[2113,11558,11559],{"class":2115,"line":2116},[2113,11560,11561],{"class":2119},"#![deny(clippy::all)]\n",[2113,11563,11564,11566,11568,11571,11573],{"class":2115,"line":1647},[2113,11565,3863],{"class":2326},[2113,11567,9548],{"class":2414},[2113,11569,11570],{"class":2119},"::{format_size, ",[2113,11572,9554],{"class":2414},[2113,11574,2787],{"class":2119},[2113,11576,11577,11579,11581],{"class":2115,"line":1652},[2113,11578,3863],{"class":2326},[2113,11580,9563],{"class":2414},[2113,11582,9566],{"class":2119},[2113,11584,11585,11587,11589,11591,11593,11595,11597,11599,11601],{"class":2115,"line":2185},[2113,11586,3863],{"class":2326},[2113,11588,9573],{"class":2414},[2113,11590,9576],{"class":2119},[2113,11592,9579],{"class":2414},[2113,11594,2418],{"class":2119},[2113,11596,9584],{"class":2414},[2113,11598,932],{"class":2119},[2113,11600,9589],{"class":2414},[2113,11602,2787],{"class":2119},[2113,11604,11605],{"class":2115,"line":2230},[2113,11606,2125],{"emptyLinePlaceholder":1767},[2113,11608,11609],{"class":2115,"line":2235},[2113,11610,11611],{"class":2119},"#[macro_use]\n",[2113,11613,11614,11617,11620],{"class":2115,"line":2241},[2113,11615,11616],{"class":2326},"extern",[2113,11618,11619],{"class":2326}," crate",[2113,11621,11622],{"class":2119}," napi_derive;\n",[2113,11624,11625],{"class":2115,"line":2246},[2113,11626,2125],{"emptyLinePlaceholder":1767},[2113,11628,11629,11631,11633],{"class":2115,"line":2464},[2113,11630,2641],{"class":2326},[2113,11632,9657],{"class":2414},[2113,11634,2647],{"class":2119},[2113,11636,11637,11640,11642,11644],{"class":2115,"line":2085},[2113,11638,11639],{"class":2330},"  owner_id",[2113,11641,4429],{"class":2119},[2113,11643,6101],{"class":2414},[2113,11645,4706],{"class":2119},[2113,11647,11648,11651,11653,11655],{"class":2115,"line":2514},[2113,11649,11650],{"class":2330},"  group_id",[2113,11652,4429],{"class":2119},[2113,11654,6101],{"class":2414},[2113,11656,4706],{"class":2119},[2113,11658,11659,11662,11664,11666],{"class":2115,"line":2533},[2113,11660,11661],{"class":2330},"  size",[2113,11663,4429],{"class":2119},[2113,11665,6101],{"class":2414},[2113,11667,4706],{"class":2119},[2113,11669,11670,11673,11675,11677],{"class":2115,"line":2556},[2113,11671,11672],{"class":2330},"  compressed_size",[2113,11674,4429],{"class":2119},[2113,11676,6101],{"class":2414},[2113,11678,4706],{"class":2119},[2113,11680,11681,11684,11686,11688],{"class":2115,"line":2569},[2113,11682,11683],{"class":2330},"  last_read",[2113,11685,4429],{"class":2119},[2113,11687,6101],{"class":2414},[2113,11689,4706],{"class":2119},[2113,11691,11692,11695,11697,11699],{"class":2115,"line":2575},[2113,11693,11694],{"class":2330},"  last_modified",[2113,11696,4429],{"class":2119},[2113,11698,6101],{"class":2414},[2113,11700,4706],{"class":2119},[2113,11702,11703,11706,11708,11710],{"class":2115,"line":2596},[2113,11704,11705],{"class":2330},"  created",[2113,11707,4429],{"class":2119},[2113,11709,6101],{"class":2414},[2113,11711,4706],{"class":2119},[2113,11713,11714,11717,11719,11721],{"class":2115,"line":3192},[2113,11715,11716],{"class":2330},"  mode",[2113,11718,4429],{"class":2119},[2113,11720,6101],{"class":2414},[2113,11722,4706],{"class":2119},[2113,11724,11725,11728,11730,11732],{"class":2115,"line":3213},[2113,11726,11727],{"class":2330},"  dev",[2113,11729,4429],{"class":2119},[2113,11731,6101],{"class":2414},[2113,11733,4706],{"class":2119},[2113,11735,11736,11739,11741,11743],{"class":2115,"line":3236},[2113,11737,11738],{"class":2330},"  rdev",[2113,11740,4429],{"class":2119},[2113,11742,6101],{"class":2414},[2113,11744,4706],{"class":2119},[2113,11746,11747,11750,11752,11754],{"class":2115,"line":3248},[2113,11748,11749],{"class":2330},"  ino",[2113,11751,4429],{"class":2119},[2113,11753,6101],{"class":2414},[2113,11755,4706],{"class":2119},[2113,11757,11758,11761,11763,11765],{"class":2115,"line":4899},[2113,11759,11760],{"class":2330},"  nlink",[2113,11762,4429],{"class":2119},[2113,11764,6101],{"class":2414},[2113,11766,4706],{"class":2119},[2113,11768,11769],{"class":2115,"line":1777},[2113,11770,2599],{"class":2119},[2113,11772,11773],{"class":2115,"line":4931},[2113,11774,2125],{"emptyLinePlaceholder":1767},[2113,11776,11777,11779,11781],{"class":2115,"line":4961},[2113,11778,2641],{"class":2326},[2113,11780,9815],{"class":2414},[2113,11782,2647],{"class":2119},[2113,11784,11785,11788,11790,11792,11794,11796],{"class":2115,"line":4976},[2113,11786,11787],{"class":2330},"  data",[2113,11789,6155],{"class":2119},[2113,11791,4480],{"class":2414},[2113,11793,6160],{"class":2119},[2113,11795,8504],{"class":2166},[2113,11797,7173],{"class":2119},[2113,11799,11800],{"class":2115,"line":4993},[2113,11801,2599],{"class":2119},[2113,11803,11804],{"class":2115,"line":5013},[2113,11805,2125],{"emptyLinePlaceholder":1767},[2113,11807,11808,11810,11812],{"class":2115,"line":5018},[2113,11809,2641],{"class":2326},[2113,11811,9863],{"class":2414},[2113,11813,2647],{"class":2119},[2113,11815,11816,11818,11820,11822,11824,11826],{"class":2115,"line":5042},[2113,11817,8071],{"class":2330},[2113,11819,4429],{"class":2119},[2113,11821,4733],{"class":2414},[2113,11823,3109],{"class":2119},[2113,11825,4480],{"class":2414},[2113,11827,9892],{"class":2119},[2113,11829,11830,11832,11834,11836],{"class":2115,"line":5057},[2113,11831,8088],{"class":2330},[2113,11833,4429],{"class":2119},[2113,11835,9902],{"class":2414},[2113,11837,4706],{"class":2119},[2113,11839,11840],{"class":2115,"line":5062},[2113,11841,2125],{"emptyLinePlaceholder":1767},[2113,11843,11844,11846,11848,11850,11852,11854],{"class":2115,"line":5098},[2113,11845,8495],{"class":2330},[2113,11847,4429],{"class":2119},[2113,11849,4733],{"class":2414},[2113,11851,3109],{"class":2119},[2113,11853,9536],{"class":2414},[2113,11855,9892],{"class":2119},[2113,11857,11858,11860,11862,11864],{"class":2115,"line":5117},[2113,11859,8529],{"class":2330},[2113,11861,4429],{"class":2119},[2113,11863,9536],{"class":2414},[2113,11865,4706],{"class":2119},[2113,11867,11868],{"class":2115,"line":5142},[2113,11869,2599],{"class":2119},[2113,11871,11872],{"class":2115,"line":5148},[2113,11873,2125],{"emptyLinePlaceholder":1767},[2113,11875,11876,11878,11880,11882,11884,11886,11888,11890,11892,11894],{"class":2115,"line":5163},[2113,11877,3906],{"class":2326},[2113,11879,9949],{"class":2133},[2113,11881,2423],{"class":2119},[2113,11883,9954],{"class":2330},[2113,11885,4472],{"class":2119},[2113,11887,4030],{"class":2326},[2113,11889,9961],{"class":2414},[2113,11891,4790],{"class":2119},[2113,11893,6101],{"class":2414},[2113,11895,2647],{"class":2119},[2113,11897,11898,11901,11903,11905],{"class":2115,"line":5169},[2113,11899,11900],{"class":2330},"  rng",[2113,11902,179],{"class":2119},[2113,11904,9977],{"class":2133},[2113,11906,6745],{"class":2119},[2113,11908,11909],{"class":2115,"line":5175},[2113,11910,2599],{"class":2119},[2113,11912,11913],{"class":2115,"line":5180},[2113,11914,2125],{"emptyLinePlaceholder":1767},[2113,11916,11917,11919,11921,11923,11925,11927,11929,11931,11933,11935,11937,11939,11941,11943,11945,11947],{"class":2115,"line":5199},[2113,11918,3906],{"class":2326},[2113,11920,9994],{"class":2133},[2113,11922,2423],{"class":2119},[2113,11924,9954],{"class":2330},[2113,11926,4472],{"class":2119},[2113,11928,4030],{"class":2326},[2113,11930,9961],{"class":2414},[2113,11932,932],{"class":2119},[2113,11934,7873],{"class":2330},[2113,11936,4429],{"class":2119},[2113,11938,4496],{"class":2414},[2113,11940,4790],{"class":2119},[2113,11942,4733],{"class":2414},[2113,11944,3109],{"class":2119},[2113,11946,4480],{"class":2414},[2113,11948,4449],{"class":2119},[2113,11950,11951,11954,11956,11958,11960,11962,11964,11966,11968,11970,11972,11974,11976,11978,11980],{"class":2115,"line":5204},[2113,11952,11953],{"class":2326},"  let",[2113,11955,3975],{"class":2326},[2113,11957,10031],{"class":2330},[2113,11959,4429],{"class":2119},[2113,11961,4733],{"class":2414},[2113,11963,3109],{"class":2119},[2113,11965,4480],{"class":2414},[2113,11967,4435],{"class":2119},[2113,11969,2335],{"class":2334},[2113,11971,4008],{"class":2414},[2113,11973,2418],{"class":2119},[2113,11975,10050],{"class":2133},[2113,11977,2423],{"class":2119},[2113,11979,7873],{"class":2330},[2113,11981,2553],{"class":2119},[2113,11983,11984,11986,11988,11990,11992,11994,11996],{"class":2115,"line":5209},[2113,11985,7902],{"class":2326},[2113,11987,10064],{"class":2330},[2113,11989,10067],{"class":2326},[2113,11991,3208],{"class":2166},[2113,11993,10072],{"class":2119},[2113,11995,7873],{"class":2330},[2113,11997,2647],{"class":2119},[2113,11999,12000,12003,12005,12007,12009,12011,12013,12015],{"class":2115,"line":5232},[2113,12001,12002],{"class":2330},"    vect",[2113,12004,179],{"class":2119},[2113,12006,10086],{"class":2133},[2113,12008,2423],{"class":2119},[2113,12010,9954],{"class":2330},[2113,12012,179],{"class":2119},[2113,12014,9977],{"class":2326},[2113,12016,2593],{"class":2119},[2113,12018,12019],{"class":2115,"line":5237},[2113,12020,2572],{"class":2119},[2113,12022,12023],{"class":2115,"line":5242},[2113,12024,12025],{"class":2330},"  vect\n",[2113,12027,12028],{"class":2115,"line":5267},[2113,12029,2599],{"class":2119},[2113,12031,12032],{"class":2115,"line":5282},[2113,12033,2125],{"emptyLinePlaceholder":1767},[2113,12035,12036,12038,12040,12042,12044,12046,12048,12050,12052,12054],{"class":2115,"line":5295},[2113,12037,3906],{"class":2326},[2113,12039,10120],{"class":2133},[2113,12041,2423],{"class":2119},[2113,12043,9954],{"class":2330},[2113,12045,4472],{"class":2119},[2113,12047,4030],{"class":2326},[2113,12049,9961],{"class":2414},[2113,12051,4790],{"class":2119},[2113,12053,9536],{"class":2414},[2113,12055,2647],{"class":2119},[2113,12057,12058,12060,12062,12064,12066,12068,12070,12072,12074,12076,12078,12080],{"class":2115,"line":5310},[2113,12059,11953],{"class":2326},[2113,12061,3975],{"class":2326},[2113,12063,10145],{"class":2330},[2113,12065,2153],{"class":2334},[2113,12067,9815],{"class":2414},[2113,12069,7616],{"class":2119},[2113,12071,10154],{"class":2330},[2113,12073,6155],{"class":2119},[2113,12075,3095],{"class":2166},[2113,12077,6160],{"class":2119},[2113,12079,8504],{"class":2166},[2113,12081,10165],{"class":2119},[2113,12083,12084,12086,12088,12090,12092,12094,12096],{"class":2115,"line":5315},[2113,12085,7902],{"class":2326},[2113,12087,7910],{"class":2330},[2113,12089,10067],{"class":2326},[2113,12091,3208],{"class":2166},[2113,12093,10072],{"class":2119},[2113,12095,8504],{"class":2166},[2113,12097,2647],{"class":2119},[2113,12099,12100,12102,12104,12106,12108,12110,12112,12114,12116],{"class":2115,"line":5320},[2113,12101,9928],{"class":2330},[2113,12103,10189],{"class":2119},[2113,12105,7919],{"class":2330},[2113,12107,4129],{"class":2119},[2113,12109,2335],{"class":2334},[2113,12111,10198],{"class":2330},[2113,12113,179],{"class":2119},[2113,12115,9977],{"class":2133},[2113,12117,3944],{"class":2119},[2113,12119,12120],{"class":2115,"line":8625},[2113,12121,2572],{"class":2119},[2113,12123,12124],{"class":2115,"line":8631},[2113,12125,12126],{"class":2330},"  sha256\n",[2113,12128,12129],{"class":2115,"line":8637},[2113,12130,2599],{"class":2119},[2113,12132,12133],{"class":2115,"line":8652},[2113,12134,2125],{"emptyLinePlaceholder":1767},[2113,12136,12137,12139,12141,12144],{"class":2115,"line":8674},[2113,12138,5374],{"class":2326},[2113,12140,5377],{"class":2326},[2113,12142,12143],{"class":2414}," TestObjectWrapper",[2113,12145,2647],{"class":2119},[2113,12147,12148,12151,12153,12155,12157,12159],{"class":2115,"line":8706},[2113,12149,12150],{"class":2330},"  test_objects",[2113,12152,4429],{"class":2119},[2113,12154,4733],{"class":2414},[2113,12156,3109],{"class":2119},[2113,12158,10406],{"class":2414},[2113,12160,9892],{"class":2119},[2113,12162,12163],{"class":2115,"line":8724},[2113,12164,2599],{"class":2119},[2113,12166,12167],{"class":2115,"line":8729},[2113,12168,2125],{"emptyLinePlaceholder":1767},[2113,12170,12171,12174,12176,12179],{"class":2115,"line":8734},[2113,12172,12173],{"class":2119},"#[napi(js_name ",[2113,12175,2335],{"class":2334},[2113,12177,12178],{"class":2149}," \"TestObjectWrapper\"",[2113,12180,9650],{"class":2119},[2113,12182,12183,12185,12187,12190],{"class":2115,"line":8740},[2113,12184,5374],{"class":2326},[2113,12186,5377],{"class":2326},[2113,12188,12189],{"class":2414}," JsTestObjectWrapper",[2113,12191,2647],{"class":2119},[2113,12193,12194,12197,12199,12202],{"class":2115,"line":8772},[2113,12195,12196],{"class":2330},"  test_object_wrapper",[2113,12198,4429],{"class":2119},[2113,12200,12201],{"class":2414},"TestObjectWrapper",[2113,12203,4706],{"class":2119},[2113,12205,12206],{"class":2115,"line":8778},[2113,12207,2599],{"class":2119},[2113,12209,12210],{"class":2115,"line":8789},[2113,12211,2125],{"emptyLinePlaceholder":1767},[2113,12213,12214],{"class":2115,"line":8795},[2113,12215,12216],{"class":2119},"#[napi]\n",[2113,12218,12219,12221,12223],{"class":2115,"line":8817},[2113,12220,4421],{"class":2326},[2113,12222,12189],{"class":2414},[2113,12224,2647],{"class":2119},[2113,12226,12227],{"class":2115,"line":8822},[2113,12228,12229],{"class":2119},"  #[napi(constructor)]\n",[2113,12231,12232,12235,12237,12239,12242,12244],{"class":2115,"line":8857},[2113,12233,12234],{"class":2326},"  pub",[2113,12236,6542],{"class":2326},[2113,12238,4778],{"class":2133},[2113,12240,12241],{"class":2119},"() -> ",[2113,12243,4793],{"class":2414},[2113,12245,2647],{"class":2119},[2113,12247,12248,12251],{"class":2115,"line":8896},[2113,12249,12250],{"class":2414},"    Self",[2113,12252,2647],{"class":2119},[2113,12254,12255,12258,12260,12262],{"class":2115,"line":8901},[2113,12256,12257],{"class":2330},"      test_object_wrapper",[2113,12259,4429],{"class":2119},[2113,12261,12201],{"class":2414},[2113,12263,2647],{"class":2119},[2113,12265,12266,12268,12270,12272,12274,12276],{"class":2115,"line":8907},[2113,12267,10444],{"class":2330},[2113,12269,4429],{"class":2119},[2113,12271,4733],{"class":2414},[2113,12273,2418],{"class":2119},[2113,12275,3961],{"class":2133},[2113,12277,8210],{"class":2119},[2113,12279,12280],{"class":2115,"line":8913},[2113,12281,12282],{"class":2119},"      },\n",[2113,12284,12285],{"class":2115,"line":8931},[2113,12286,4665],{"class":2119},[2113,12288,12289],{"class":2115,"line":8936},[2113,12290,2572],{"class":2119},[2113,12292,12293],{"class":2115,"line":8942},[2113,12294,2125],{"emptyLinePlaceholder":1767},[2113,12296,12297],{"class":2115,"line":8950},[2113,12298,12299],{"class":2119},"  #[napi]\n",[2113,12301,12302,12304,12306,12309,12311,12313,12315,12317,12319,12321,12324],{"class":2115,"line":8968},[2113,12303,12234],{"class":2326},[2113,12305,6542],{"class":2326},[2113,12307,12308],{"class":2133}," fill",[2113,12310,4027],{"class":2119},[2113,12312,4030],{"class":2326},[2113,12314,4464],{"class":2414},[2113,12316,932],{"class":2119},[2113,12318,10421],{"class":2330},[2113,12320,4429],{"class":2119},[2113,12322,12323],{"class":2414},"i32",[2113,12325,2433],{"class":2119},[2113,12327,12328,12330,12332,12334,12336,12338,12340,12342],{"class":2115,"line":8983},[2113,12329,3917],{"class":2326},[2113,12331,3975],{"class":2326},[2113,12333,10198],{"class":2330},[2113,12335,2153],{"class":2334},[2113,12337,9573],{"class":2414},[2113,12339,2418],{"class":2119},[2113,12341,10264],{"class":2133},[2113,12343,3944],{"class":2119},[2113,12345,12346],{"class":2115,"line":8988},[2113,12347,10271],{"class":2396},[2113,12349,12350,12352,12354,12356,12358,12360,12362,12364,12366],{"class":2115,"line":9011},[2113,12351,3917],{"class":2326},[2113,12353,10278],{"class":2330},[2113,12355,2153],{"class":2334},[2113,12357,10283],{"class":2414},[2113,12359,2418],{"class":2119},[2113,12361,10288],{"class":2133},[2113,12363,5224],{"class":2119},[2113,12365,3941],{"class":2133},[2113,12367,3944],{"class":2119},[2113,12369,12370],{"class":2115,"line":9016},[2113,12371,10299],{"class":2396},[2113,12373,12374,12376,12378,12380,12382,12384,12386,12388,12390,12392,12394],{"class":2115,"line":9022},[2113,12375,3917],{"class":2326},[2113,12377,10306],{"class":2330},[2113,12379,2153],{"class":2334},[2113,12381,3885],{"class":2414},[2113,12383,2418],{"class":2119},[2113,12385,10315],{"class":2414},[2113,12387,2418],{"class":2119},[2113,12389,10320],{"class":2414},[2113,12391,2418],{"class":2119},[2113,12393,8616],{"class":2133},[2113,12395,6745],{"class":2119},[2113,12397,12398,12401,12403,12405,12407,12409,12411,12413,12415],{"class":2115,"line":9045},[2113,12399,12400],{"class":2119},"      .",[2113,12402,10334],{"class":2133},[2113,12404,2423],{"class":2119},[2113,12406,6833],{"class":2414},[2113,12408,2418],{"class":2119},[2113,12410,10315],{"class":2414},[2113,12412,2418],{"class":2119},[2113,12414,10347],{"class":2166},[2113,12416,4660],{"class":2119},[2113,12418,12419,12421,12423],{"class":2115,"line":9076},[2113,12420,12400],{"class":2119},[2113,12422,3941],{"class":2133},[2113,12424,6745],{"class":2119},[2113,12426,12427,12429,12431],{"class":2115,"line":9082},[2113,12428,12400],{"class":2119},[2113,12430,10364],{"class":2133},[2113,12432,3944],{"class":2119},[2113,12434,12435],{"class":2115,"line":9105},[2113,12436,2125],{"emptyLinePlaceholder":1767},[2113,12438,12439,12442,12445,12447,12449,12451,12453,12455,12457,12459,12462,12464,12466],{"class":2115,"line":9138},[2113,12440,12441],{"class":2414},"    self",[2113,12443,12444],{"class":2119},".test_object_wrapper.test_objects ",[2113,12446,2335],{"class":2334},[2113,12448,4008],{"class":2414},[2113,12450,2418],{"class":2119},[2113,12452,10050],{"class":2133},[2113,12454,2423],{"class":2119},[2113,12456,10421],{"class":2330},[2113,12458,179],{"class":2119},[2113,12460,12461],{"class":2133},"try_into",[2113,12463,5224],{"class":2119},[2113,12465,3941],{"class":2133},[2113,12467,2593],{"class":2119},[2113,12469,12470],{"class":2115,"line":9149},[2113,12471,2125],{"emptyLinePlaceholder":1767},[2113,12473,12474,12476,12478,12480,12482,12484,12486],{"class":2115,"line":9157},[2113,12475,10061],{"class":2326},[2113,12477,10064],{"class":2330},[2113,12479,10067],{"class":2326},[2113,12481,3208],{"class":2166},[2113,12483,10072],{"class":2119},[2113,12485,10421],{"class":2330},[2113,12487,2647],{"class":2119},[2113,12489,12490,12493,12496,12498,12500,12502],{"class":2115,"line":9182},[2113,12491,12492],{"class":2414},"      self",[2113,12494,12495],{"class":2119},".test_object_wrapper.test_objects.",[2113,12497,10086],{"class":2133},[2113,12499,2423],{"class":2119},[2113,12501,10406],{"class":2414},[2113,12503,2647],{"class":2119},[2113,12505,12506,12509,12511,12513,12515,12517,12519,12521,12523],{"class":2115,"line":9188},[2113,12507,12508],{"class":2330},"        path",[2113,12510,4429],{"class":2119},[2113,12512,10464],{"class":2133},[2113,12514,4027],{"class":2119},[2113,12516,4030],{"class":2326},[2113,12518,10198],{"class":2330},[2113,12520,932],{"class":2119},[2113,12522,8081],{"class":2166},[2113,12524,7142],{"class":2119},[2113,12526,12527,12530,12532,12534],{"class":2115,"line":9193},[2113,12528,12529],{"class":2330},"        stats",[2113,12531,4429],{"class":2119},[2113,12533,9902],{"class":2414},[2113,12535,2647],{"class":2119},[2113,12537,12538,12541,12543,12545,12547,12549,12551],{"class":2115,"line":9198},[2113,12539,12540],{"class":2330},"          owner_id",[2113,12542,4429],{"class":2119},[2113,12544,10497],{"class":2133},[2113,12546,4027],{"class":2119},[2113,12548,4030],{"class":2326},[2113,12550,10198],{"class":2330},[2113,12552,7142],{"class":2119},[2113,12554,12555,12558,12560,12562,12564,12566,12568],{"class":2115,"line":9216},[2113,12556,12557],{"class":2330},"          group_id",[2113,12559,4429],{"class":2119},[2113,12561,10497],{"class":2133},[2113,12563,4027],{"class":2119},[2113,12565,4030],{"class":2326},[2113,12567,10198],{"class":2330},[2113,12569,7142],{"class":2119},[2113,12571,12572,12575,12577,12579,12581,12583,12585],{"class":2115,"line":9238},[2113,12573,12574],{"class":2330},"          size",[2113,12576,4429],{"class":2119},[2113,12578,10497],{"class":2133},[2113,12580,4027],{"class":2119},[2113,12582,4030],{"class":2326},[2113,12584,10198],{"class":2330},[2113,12586,7142],{"class":2119},[2113,12588,12589,12592,12594,12596,12598,12600,12602],{"class":2115,"line":9243},[2113,12590,12591],{"class":2330},"          compressed_size",[2113,12593,4429],{"class":2119},[2113,12595,10497],{"class":2133},[2113,12597,4027],{"class":2119},[2113,12599,4030],{"class":2326},[2113,12601,10198],{"class":2330},[2113,12603,7142],{"class":2119},[2113,12605,12606,12609,12611,12613,12615,12617,12619],{"class":2115,"line":10782},[2113,12607,12608],{"class":2330},"          last_read",[2113,12610,4429],{"class":2119},[2113,12612,10497],{"class":2133},[2113,12614,4027],{"class":2119},[2113,12616,4030],{"class":2326},[2113,12618,10198],{"class":2330},[2113,12620,7142],{"class":2119},[2113,12622,12623,12626,12628,12630,12632,12634,12636],{"class":2115,"line":10787},[2113,12624,12625],{"class":2330},"          last_modified",[2113,12627,4429],{"class":2119},[2113,12629,10497],{"class":2133},[2113,12631,4027],{"class":2119},[2113,12633,4030],{"class":2326},[2113,12635,10198],{"class":2330},[2113,12637,7142],{"class":2119},[2113,12639,12640,12643,12645,12647,12649,12651,12653],{"class":2115,"line":10813},[2113,12641,12642],{"class":2330},"          created",[2113,12644,4429],{"class":2119},[2113,12646,10497],{"class":2133},[2113,12648,4027],{"class":2119},[2113,12650,4030],{"class":2326},[2113,12652,10198],{"class":2330},[2113,12654,7142],{"class":2119},[2113,12656,12657,12660,12662,12664,12666,12668,12670],{"class":2115,"line":10834},[2113,12658,12659],{"class":2330},"          mode",[2113,12661,4429],{"class":2119},[2113,12663,10497],{"class":2133},[2113,12665,4027],{"class":2119},[2113,12667,4030],{"class":2326},[2113,12669,10198],{"class":2330},[2113,12671,7142],{"class":2119},[2113,12673,12674,12677,12679,12681,12683,12685,12687],{"class":2115,"line":10843},[2113,12675,12676],{"class":2330},"          dev",[2113,12678,4429],{"class":2119},[2113,12680,10497],{"class":2133},[2113,12682,4027],{"class":2119},[2113,12684,4030],{"class":2326},[2113,12686,10198],{"class":2330},[2113,12688,7142],{"class":2119},[2113,12690,12691,12694,12696,12698,12700,12702,12704],{"class":2115,"line":10852},[2113,12692,12693],{"class":2330},"          rdev",[2113,12695,4429],{"class":2119},[2113,12697,10497],{"class":2133},[2113,12699,4027],{"class":2119},[2113,12701,4030],{"class":2326},[2113,12703,10198],{"class":2330},[2113,12705,7142],{"class":2119},[2113,12707,12708,12711,12713,12715,12717,12719,12721],{"class":2115,"line":10858},[2113,12709,12710],{"class":2330},"          ino",[2113,12712,4429],{"class":2119},[2113,12714,10497],{"class":2133},[2113,12716,4027],{"class":2119},[2113,12718,4030],{"class":2326},[2113,12720,10198],{"class":2330},[2113,12722,7142],{"class":2119},[2113,12724,12725,12728,12730,12732,12734,12736,12738],{"class":2115,"line":10880},[2113,12726,12727],{"class":2330},"          nlink",[2113,12729,4429],{"class":2119},[2113,12731,10497],{"class":2133},[2113,12733,4027],{"class":2119},[2113,12735,4030],{"class":2326},[2113,12737,10198],{"class":2330},[2113,12739,7142],{"class":2119},[2113,12741,12742],{"class":2115,"line":10885},[2113,12743,12744],{"class":2119},"        },\n",[2113,12746,12747,12750,12752,12754],{"class":2115,"line":10890},[2113,12748,12749],{"class":2330},"        chunks",[2113,12751,4429],{"class":2119},[2113,12753,10707],{"class":2133},[2113,12755,10710],{"class":2119},[2113,12757,12758,12761,12763,12765,12767],{"class":2115,"line":10912},[2113,12759,12760],{"class":2133},"          generate_random_sha256",[2113,12762,4027],{"class":2119},[2113,12764,4030],{"class":2326},[2113,12766,10198],{"class":2330},[2113,12768,7142],{"class":2119},[2113,12770,12771,12773,12775,12777,12779],{"class":2115,"line":10918},[2113,12772,12760],{"class":2133},[2113,12774,4027],{"class":2119},[2113,12776,4030],{"class":2326},[2113,12778,10198],{"class":2330},[2113,12780,7142],{"class":2119},[2113,12782,12783,12785,12787,12789,12791],{"class":2115,"line":10925},[2113,12784,12760],{"class":2133},[2113,12786,4027],{"class":2119},[2113,12788,4030],{"class":2326},[2113,12790,10198],{"class":2330},[2113,12792,7142],{"class":2119},[2113,12794,12795],{"class":2115,"line":10933},[2113,12796,12797],{"class":2119},"        ],\n",[2113,12799,12800,12802,12804,12806,12808,12810,12812],{"class":2115,"line":10958},[2113,12801,10186],{"class":2330},[2113,12803,4429],{"class":2119},[2113,12805,10762],{"class":2133},[2113,12807,4027],{"class":2119},[2113,12809,4030],{"class":2326},[2113,12811,10198],{"class":2330},[2113,12813,7142],{"class":2119},[2113,12815,12816],{"class":2115,"line":10964},[2113,12817,12818],{"class":2119},"      });\n",[2113,12820,12821],{"class":2115,"line":10970},[2113,12822,4665],{"class":2119},[2113,12824,12825],{"class":2115,"line":10977},[2113,12826,2125],{"emptyLinePlaceholder":1767},[2113,12828,12829,12831,12833,12835,12837,12839,12841,12843,12845,12847,12849],{"class":2115,"line":10985},[2113,12830,3917],{"class":2326},[2113,12832,10792],{"class":2330},[2113,12834,2153],{"class":2334},[2113,12836,3885],{"class":2414},[2113,12838,2418],{"class":2119},[2113,12840,10315],{"class":2414},[2113,12842,2418],{"class":2119},[2113,12844,10320],{"class":2414},[2113,12846,2418],{"class":2119},[2113,12848,8616],{"class":2133},[2113,12850,6745],{"class":2119},[2113,12852,12853,12855,12857,12859,12861,12863,12865,12867,12869],{"class":2115,"line":11010},[2113,12854,12400],{"class":2119},[2113,12856,10334],{"class":2133},[2113,12858,2423],{"class":2119},[2113,12860,6833],{"class":2414},[2113,12862,2418],{"class":2119},[2113,12864,10315],{"class":2414},[2113,12866,2418],{"class":2119},[2113,12868,10347],{"class":2166},[2113,12870,4660],{"class":2119},[2113,12872,12873,12875,12877],{"class":2115,"line":11015},[2113,12874,12400],{"class":2119},[2113,12876,3941],{"class":2133},[2113,12878,6745],{"class":2119},[2113,12880,12881,12883,12885],{"class":2115,"line":11020},[2113,12882,12400],{"class":2119},[2113,12884,10364],{"class":2133},[2113,12886,3944],{"class":2119},[2113,12888,12889],{"class":2115,"line":11026},[2113,12890,10855],{"class":2396},[2113,12892,12893,12895,12897,12899,12901,12903,12905,12907],{"class":2115,"line":11049},[2113,12894,4043],{"class":2133},[2113,12896,2423],{"class":2119},[2113,12898,10865],{"class":2149},[2113,12900,932],{"class":2119},[2113,12902,10870],{"class":2330},[2113,12904,10873],{"class":2119},[2113,12906,8616],{"class":2330},[2113,12908,2553],{"class":2119},[2113,12910,12911],{"class":2115,"line":11062},[2113,12912,2125],{"emptyLinePlaceholder":1767},[2113,12914,12915],{"class":2115,"line":11074},[2113,12916,10271],{"class":2396},[2113,12918,12919,12921,12923,12925,12927,12929,12931,12933,12935],{"class":2115,"line":11079},[2113,12920,3917],{"class":2326},[2113,12922,10895],{"class":2330},[2113,12924,2153],{"class":2334},[2113,12926,10283],{"class":2414},[2113,12928,2418],{"class":2119},[2113,12930,10288],{"class":2133},[2113,12932,5224],{"class":2119},[2113,12934,3941],{"class":2133},[2113,12936,3944],{"class":2119},[2113,12938,12939],{"class":2115,"line":11084},[2113,12940,10915],{"class":2396},[2113,12942,12943,12945],{"class":2115,"line":11090},[2113,12944,4043],{"class":2133},[2113,12946,3146],{"class":2119},[2113,12948,12949,12952],{"class":2115,"line":11124},[2113,12950,12951],{"class":2149},"      \"Memory consumption after creation: {}\"",[2113,12953,4706],{"class":2119},[2113,12955,12956,12959,12961,12963,12965,12967,12969,12971,12973,12975],{"class":2115,"line":11150},[2113,12957,12958],{"class":2133},"      format_size",[2113,12960,8879],{"class":2119},[2113,12962,10941],{"class":2330},[2113,12964,10944],{"class":2119},[2113,12966,10947],{"class":2330},[2113,12968,10950],{"class":2119},[2113,12970,10953],{"class":2166},[2113,12972,932],{"class":2119},[2113,12974,9554],{"class":2166},[2113,12976,4660],{"class":2119},[2113,12978,12979],{"class":2115,"line":11169},[2113,12980,10961],{"class":2119},[2113,12982,12983],{"class":2115,"line":11197},[2113,12984,10967],{"class":2396},[2113,12986,12987,12989],{"class":2115,"line":11202},[2113,12988,4043],{"class":2133},[2113,12990,3146],{"class":2119},[2113,12992,12993,12996],{"class":2115,"line":11207},[2113,12994,12995],{"class":2149},"      \"Average memory consumption per object: {}\"",[2113,12997,4706],{"class":2119},[2113,12999,13000,13002],{"class":2115,"line":11213},[2113,13001,12958],{"class":2133},[2113,13003,3146],{"class":2119},[2113,13005,13006,13009,13011,13013,13015,13017,13019,13021,13023,13025,13028],{"class":2115,"line":11239},[2113,13007,13008],{"class":2119},"        ((",[2113,13010,10941],{"class":2330},[2113,13012,10944],{"class":2119},[2113,13014,10947],{"class":2330},[2113,13016,10950],{"class":2119},[2113,13018,10953],{"class":2166},[2113,13020,11003],{"class":2119},[2113,13022,10421],{"class":2330},[2113,13024,11429],{"class":2326},[2113,13026,13027],{"class":2414}," usize",[2113,13029,4706],{"class":2119},[2113,13031,13032],{"class":2115,"line":11260},[2113,13033,13034],{"class":2166},"        DECIMAL\n",[2113,13036,13037],{"class":2115,"line":11269},[2113,13038,13039],{"class":2119},"      )\n",[2113,13041,13042],{"class":2115,"line":11278},[2113,13043,10961],{"class":2119},[2113,13045,13046],{"class":2115,"line":11283},[2113,13047,2572],{"class":2119},[2113,13049,13050],{"class":2115,"line":11290},[2113,13051,2125],{"emptyLinePlaceholder":1767},[2113,13053,13054,13057,13059,13062],{"class":2115,"line":11298},[2113,13055,13056],{"class":2119},"  #[napi(js_name ",[2113,13058,2335],{"class":2334},[2113,13060,13061],{"class":2149}," \"toString\"",[2113,13063,9650],{"class":2119},[2113,13065,13066,13068,13070,13073,13075,13077,13079,13082],{"class":2115,"line":11309},[2113,13067,12234],{"class":2326},[2113,13069,6542],{"class":2326},[2113,13071,13072],{"class":2133}," to_string",[2113,13074,4027],{"class":2119},[2113,13076,5216],{"class":2414},[2113,13078,4790],{"class":2119},[2113,13080,13081],{"class":2414},"String",[2113,13083,2647],{"class":2119},[2113,13085,13086,13089],{"class":2115,"line":11314},[2113,13087,13088],{"class":2133},"    format!",[2113,13090,3146],{"class":2119},[2113,13092,13093,13096],{"class":2115,"line":11319},[2113,13094,13095],{"class":2149},"      \"TestObjectWrapper {{ test_objects: {} }}\"",[2113,13097,4706],{"class":2119},[2113,13099,13100,13102,13104,13106],{"class":2115,"line":11325},[2113,13101,12492],{"class":2414},[2113,13103,12495],{"class":2119},[2113,13105,4540],{"class":2133},[2113,13107,6745],{"class":2119},[2113,13109,13110],{"class":2115,"line":11356},[2113,13111,13112],{"class":2119},"    )\n",[2113,13114,13115],{"class":2115,"line":11363},[2113,13116,2572],{"class":2119},[2113,13118,13119],{"class":2115,"line":11371},[2113,13120,2599],{"class":2119},[12,13122,13123],{},"Pour la parte Node.JS :",[2105,13125,13127],{"className":7776,"code":13126,"language":7778,"meta":1646,"style":1646},"const test = require(\".\u002Findex.js\");\nconst filesize = require(\"filesize.js\");\nconst fs = require(\"fs\");\nconst { serialize, deserialize } = require(\"v8\");\n\u002F\u002F Start by consuming memory with big object\n\n\u002F\u002F Run GC\nglobal.gc();\n\u002F\u002F Get memory consumption before\nconst memoryBefore = process.memoryUsage().heapUsed;\n\nconst time = Date.now();\n\n\u002F\u002F Create objects\nconst nbObjects = 1_300_000;\nconst testArray = new test.TestObjectWrapper();\ntestArray.fill(nbObjects);\n\n\u002F\u002F Bench creation\nconsole.log(\"Creation time: \", Date.now() - time);\n\n\u002F\u002F Run GC\n\u002F\u002F Get memory consumption after\nconst memoryAfter = process.memoryUsage().heapUsed;\n\n\u002F\u002F Print memory consumption\nconsole.log(\n  \"Memory consumption in JS: \",\n  filesize.default(memoryAfter - memoryBefore)\n);\n\nconsole.log(testArray.toString());\n",[184,13128,13129,13147,13163,13179,13203,13208,13212,13217,13227,13232,13252,13256,13272,13276,13281,13293,13311,13327,13331,13336,13364,13368,13372,13377,13397,13401,13406,13416,13423,13442,13446,13450],{"__ignoreMap":1646},[2113,13130,13131,13133,13136,13138,13140,13142,13145],{"class":2115,"line":2116},[2113,13132,7785],{"class":2326},[2113,13134,13135],{"class":2414}," test",[2113,13137,2153],{"class":2334},[2113,13139,7793],{"class":2133},[2113,13141,2423],{"class":2119},[2113,13143,13144],{"class":2149},"\".\u002Findex.js\"",[2113,13146,2553],{"class":2119},[2113,13148,13149,13151,13153,13155,13157,13159,13161],{"class":2115,"line":1647},[2113,13150,7785],{"class":2326},[2113,13152,7788],{"class":2414},[2113,13154,2153],{"class":2334},[2113,13156,7793],{"class":2133},[2113,13158,2423],{"class":2119},[2113,13160,7798],{"class":2149},[2113,13162,2553],{"class":2119},[2113,13164,13165,13167,13169,13171,13173,13175,13177],{"class":2115,"line":1652},[2113,13166,7785],{"class":2326},[2113,13168,7807],{"class":2414},[2113,13170,2153],{"class":2334},[2113,13172,7793],{"class":2133},[2113,13174,2423],{"class":2119},[2113,13176,7816],{"class":2149},[2113,13178,2553],{"class":2119},[2113,13180,13181,13183,13185,13187,13189,13191,13193,13195,13197,13199,13201],{"class":2115,"line":2185},[2113,13182,7785],{"class":2326},[2113,13184,7616],{"class":2119},[2113,13186,7832],{"class":2414},[2113,13188,932],{"class":2119},[2113,13190,7837],{"class":2414},[2113,13192,7840],{"class":2119},[2113,13194,2335],{"class":2334},[2113,13196,7793],{"class":2133},[2113,13198,2423],{"class":2119},[2113,13200,7849],{"class":2149},[2113,13202,2553],{"class":2119},[2113,13204,13205],{"class":2115,"line":2230},[2113,13206,13207],{"class":2396},"\u002F\u002F Start by consuming memory with big object\n",[2113,13209,13210],{"class":2115,"line":2235},[2113,13211,2125],{"emptyLinePlaceholder":1767},[2113,13213,13214],{"class":2115,"line":2241},[2113,13215,13216],{"class":2396},"\u002F\u002F Run GC\n",[2113,13218,13219,13221,13223,13225],{"class":2115,"line":2246},[2113,13220,8558],{"class":2414},[2113,13222,179],{"class":2119},[2113,13224,8563],{"class":2133},[2113,13226,3944],{"class":2119},[2113,13228,13229],{"class":2115,"line":2464},[2113,13230,13231],{"class":2396},"\u002F\u002F Get memory consumption before\n",[2113,13233,13234,13236,13238,13240,13242,13244,13246,13248,13250],{"class":2115,"line":2085},[2113,13235,7785],{"class":2326},[2113,13237,8577],{"class":2414},[2113,13239,2153],{"class":2334},[2113,13241,8582],{"class":2414},[2113,13243,179],{"class":2119},[2113,13245,8587],{"class":2133},[2113,13247,5224],{"class":2119},[2113,13249,8592],{"class":2330},[2113,13251,2487],{"class":2119},[2113,13253,13254],{"class":2115,"line":2514},[2113,13255,2125],{"emptyLinePlaceholder":1767},[2113,13257,13258,13260,13262,13264,13266,13268,13270],{"class":2115,"line":2533},[2113,13259,7785],{"class":2326},[2113,13261,8606],{"class":2414},[2113,13263,2153],{"class":2334},[2113,13265,8611],{"class":2414},[2113,13267,179],{"class":2119},[2113,13269,8616],{"class":2133},[2113,13271,3944],{"class":2119},[2113,13273,13274],{"class":2115,"line":2556},[2113,13275,2125],{"emptyLinePlaceholder":1767},[2113,13277,13278],{"class":2115,"line":2569},[2113,13279,13280],{"class":2396},"\u002F\u002F Create objects\n",[2113,13282,13283,13285,13287,13289,13291],{"class":2115,"line":2575},[2113,13284,7785],{"class":2326},[2113,13286,8642],{"class":2414},[2113,13288,2153],{"class":2334},[2113,13290,8647],{"class":2166},[2113,13292,2487],{"class":2119},[2113,13294,13295,13297,13299,13301,13303,13305,13307,13309],{"class":2115,"line":2596},[2113,13296,7785],{"class":2326},[2113,13298,8657],{"class":2414},[2113,13300,2153],{"class":2334},[2113,13302,4778],{"class":2326},[2113,13304,13135],{"class":2414},[2113,13306,179],{"class":2119},[2113,13308,12201],{"class":2133},[2113,13310,3944],{"class":2119},[2113,13312,13313,13316,13318,13321,13323,13325],{"class":2115,"line":3192},[2113,13314,13315],{"class":2414},"testArray",[2113,13317,179],{"class":2119},[2113,13319,13320],{"class":2133},"fill",[2113,13322,2423],{"class":2119},[2113,13324,8669],{"class":2330},[2113,13326,2553],{"class":2119},[2113,13328,13329],{"class":2115,"line":3213},[2113,13330,2125],{"emptyLinePlaceholder":1767},[2113,13332,13333],{"class":2115,"line":3236},[2113,13334,13335],{"class":2396},"\u002F\u002F Bench creation\n",[2113,13337,13338,13340,13342,13344,13346,13348,13350,13352,13354,13356,13358,13360,13362],{"class":2115,"line":3248},[2113,13339,8743],{"class":2414},[2113,13341,179],{"class":2119},[2113,13343,8748],{"class":2133},[2113,13345,2423],{"class":2119},[2113,13347,8753],{"class":2149},[2113,13349,932],{"class":2119},[2113,13351,36],{"class":2414},[2113,13353,179],{"class":2119},[2113,13355,8616],{"class":2133},[2113,13357,4543],{"class":2119},[2113,13359,3242],{"class":2334},[2113,13361,8606],{"class":2330},[2113,13363,2553],{"class":2119},[2113,13365,13366],{"class":2115,"line":4899},[2113,13367,2125],{"emptyLinePlaceholder":1767},[2113,13369,13370],{"class":2115,"line":1777},[2113,13371,13216],{"class":2396},[2113,13373,13374],{"class":2115,"line":4931},[2113,13375,13376],{"class":2396},"\u002F\u002F Get memory consumption after\n",[2113,13378,13379,13381,13383,13385,13387,13389,13391,13393,13395],{"class":2115,"line":4961},[2113,13380,7785],{"class":2326},[2113,13382,8800],{"class":2414},[2113,13384,2153],{"class":2334},[2113,13386,8582],{"class":2414},[2113,13388,179],{"class":2119},[2113,13390,8587],{"class":2133},[2113,13392,5224],{"class":2119},[2113,13394,8592],{"class":2330},[2113,13396,2487],{"class":2119},[2113,13398,13399],{"class":2115,"line":4976},[2113,13400,2125],{"emptyLinePlaceholder":1767},[2113,13402,13403],{"class":2115,"line":4993},[2113,13404,13405],{"class":2396},"\u002F\u002F Print memory consumption\n",[2113,13407,13408,13410,13412,13414],{"class":2115,"line":5013},[2113,13409,8743],{"class":2414},[2113,13411,179],{"class":2119},[2113,13413,8748],{"class":2133},[2113,13415,3146],{"class":2119},[2113,13417,13418,13421],{"class":2115,"line":5018},[2113,13419,13420],{"class":2149},"  \"Memory consumption in JS: \"",[2113,13422,4706],{"class":2119},[2113,13424,13425,13428,13430,13432,13434,13436,13438,13440],{"class":2115,"line":5042},[2113,13426,13427],{"class":2414},"  filesize",[2113,13429,179],{"class":2119},[2113,13431,8843],{"class":2133},[2113,13433,2423],{"class":2119},[2113,13435,8848],{"class":2330},[2113,13437,2270],{"class":2334},[2113,13439,8577],{"class":2330},[2113,13441,4660],{"class":2119},[2113,13443,13444],{"class":2115,"line":5057},[2113,13445,2553],{"class":2119},[2113,13447,13448],{"class":2115,"line":5062},[2113,13449,2125],{"emptyLinePlaceholder":1767},[2113,13451,13452,13454,13456,13458,13460,13462,13464,13467],{"class":2115,"line":5098},[2113,13453,8743],{"class":2414},[2113,13455,179],{"class":2119},[2113,13457,8748],{"class":2133},[2113,13459,2423],{"class":2119},[2113,13461,13315],{"class":2414},[2113,13463,179],{"class":2119},[2113,13465,13466],{"class":2133},"toString",[2113,13468,2593],{"class":2119},[12,13470,13471],{},"Voici le résultat:",[22,13473,13474,13482],{},[25,13475,13476],{},[28,13477,13478,13480],{},[31,13479],{},[31,13481],{},[41,13483,13484,13490,13496,13503],{},[28,13485,13486,13488],{},[46,13487,9275],{},[46,13489,9278],{},[28,13491,13492,13494],{},[46,13493,9283],{},[46,13495,11496],{},[28,13497,13498,13500],{},[46,13499,9291],{},[46,13501,13502],{},"520,31 Mo",[28,13504,13505,13507],{},[46,13506,9299],{},[46,13508,13509],{},"400 octets",[12,13511,13512],{},"Parfait, le module écrit en Rust va nous permettre de réduire la consommation mémoire de notre application et améliorer\nses performances.",[128,13514,13516,13518],{"id":13515},"conclusion-wasm",[1892,13517,1621],{}," - Wasm",[12,13520,13521],{},"Une alternative possible à l'écriture d'un module natif en Rust est l'utilisation de WebAssembly. WebAssembly est un\nlangage de bas niveau qui permet d'écrire des modules qui seront exécutés dans un environnement sécurisé. Il est\npossible d'écrire des modules en Rust qui seront compilés en WebAssembly.",[12,13523,13524,13525,179],{},"Pour compiler notre module en WebAssembly, nous allons utiliser le compilateur ",[49,13526,13529],{"href":13527,"rel":13528},"https:\u002F\u002Frustwasm.github.io\u002Fwasm-pack\u002F",[347],"wasm-pack",[12,13531,13532],{},"La partie Rust du module n'est pas très différente de la version native. La seule différence est que nous devons\nutiliser le crate wasm-bindgen pour pouvoir utiliser notre module dans Node.JS.",[12,13534,13535,13536,13540],{},"Là encore, la documentation est très bien faite (",[49,13537,13538],{"href":13538,"rel":13539},"https:\u002F\u002Frustwasm.github.io\u002Fdocs\u002Fbook\u002F",[347],"). Je ne vais pas détailler ici\nla création de ce module.",[2105,13542,13544],{"className":3855,"code":13543,"language":1775,"meta":1646,"style":1646},"mod utils;\n\nuse rand::{rngs::ThreadRng, Rng};\n\nuse wasm_bindgen::prelude::*;\n\n\u002F\u002F When the `wee_alloc` feature is enabled, use `wee_alloc` as the global\n\u002F\u002F allocator.\n#[cfg(feature = \"wee_alloc\")]\n#[global_allocator]\nstatic ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;\n\n#[wasm_bindgen]\nstruct Stats {\n    owner_id: u64,\n    group_id: u64,\n    size: u64,\n    compressed_size: u64,\n    last_read: u64,\n    last_modified: u64,\n    created: u64,\n    mode: u64,\n    dev: u64,\n    rdev: u64,\n    ino: u64,\n    nlink: u64,\n}\n\n#[wasm_bindgen]\nstruct Sha256 {\n    data: [u8; 32],\n}\n\n#[wasm_bindgen]\nstruct TestObject {\n    path: Vec\u003Cu8>,\n    stats: Stats,\n\n    chunks: Vec\u003CSha256>,\n    sha256: Sha256,\n}\n\nfn generate_random_number(rng: &mut ThreadRng) -> u64 {\n    rng.gen()\n}\n\nfn generate_random_vect(rng: &mut ThreadRng, size: usize) -> Vec\u003Cu8> {\n    let mut vect: Vec\u003Cu8> = Vec::with_capacity(size);\n    for _ in 0..size {\n        vect.push(rng.gen());\n    }\n    vect\n}\n\nfn generate_random_sha256(rng: &mut ThreadRng) -> Sha256 {\n    let mut sha256 = Sha256 { data: [0; 32] };\n    for i in 0..32 {\n        sha256.data[i] = rng.gen();\n    }\n    sha256\n}\n\n#[wasm_bindgen]\npub struct TestObjectWrapper {\n    test_objects: Vec\u003CTestObject>,\n}\n\n#[wasm_bindgen]\nimpl TestObjectWrapper {\n    #[wasm_bindgen(constructor)]\n    pub fn new() -> Self {\n        Self {\n            test_objects: Vec::new(),\n        }\n    }\n\n    #[wasm_bindgen]\n    pub fn fill(&mut self, nb_object: i32) {\n        let mut rng = rand::thread_rng();\n        \u002F\u002F Get the current time in ms\n        self.test_objects = Vec::with_capacity(nb_object as usize);\n\n        for _ in 0..nb_object {\n            self.test_objects.push(TestObject {\n                path: generate_random_vect(&mut rng, 100),\n                stats: Stats {\n                    owner_id: generate_random_number(&mut rng),\n                    group_id: generate_random_number(&mut rng),\n                    size: generate_random_number(&mut rng),\n                    compressed_size: generate_random_number(&mut rng),\n                    last_read: generate_random_number(&mut rng),\n                    last_modified: generate_random_number(&mut rng),\n                    created: generate_random_number(&mut rng),\n                    mode: generate_random_number(&mut rng),\n                    dev: generate_random_number(&mut rng),\n                    rdev: generate_random_number(&mut rng),\n                    ino: generate_random_number(&mut rng),\n                    nlink: generate_random_number(&mut rng),\n                },\n                chunks: vec![\n                    generate_random_sha256(&mut rng),\n                    generate_random_sha256(&mut rng),\n                    generate_random_sha256(&mut rng),\n                ],\n                sha256: generate_random_sha256(&mut rng),\n            });\n        }\n    }\n\n    #[wasm_bindgen(js_name = toString)]\n    pub fn to_string(&self) -> String {\n        format!(\n            \"TestObjectWrapper {{ test_objects: {} }}\",\n            self.test_objects.len()\n        )\n    }\n}\n",[184,13545,13546,13554,13558,13578,13582,13597,13601,13606,13611,13623,13628,13661,13665,13670,13678,13688,13698,13708,13718,13728,13738,13748,13758,13768,13778,13788,13798,13802,13806,13810,13818,13832,13836,13840,13844,13852,13866,13876,13880,13894,13904,13908,13912,13934,13944,13948,13952,13986,14018,14034,14052,14056,14060,14064,14068,14090,14116,14132,14152,14156,14160,14164,14168,14172,14182,14197,14201,14205,14209,14217,14222,14237,14243,14258,14262,14266,14270,14275,14299,14317,14322,14347,14351,14368,14383,14404,14415,14432,14449,14466,14483,14500,14517,14534,14551,14568,14585,14602,14619,14624,14635,14648,14660,14672,14677,14694,14699,14703,14707,14711,14721,14739,14746,14753,14763,14768,14772],{"__ignoreMap":1646},[2113,13547,13548,13551],{"class":2115,"line":2116},[2113,13549,13550],{"class":2326},"mod",[2113,13552,13553],{"class":2119}," utils;\n",[2113,13555,13556],{"class":2115,"line":1647},[2113,13557,2125],{"emptyLinePlaceholder":1767},[2113,13559,13560,13562,13564,13566,13568,13570,13572,13574,13576],{"class":2115,"line":1652},[2113,13561,3863],{"class":2326},[2113,13563,9573],{"class":2414},[2113,13565,9576],{"class":2119},[2113,13567,9579],{"class":2414},[2113,13569,2418],{"class":2119},[2113,13571,9584],{"class":2414},[2113,13573,932],{"class":2119},[2113,13575,9589],{"class":2414},[2113,13577,2787],{"class":2119},[2113,13579,13580],{"class":2115,"line":2185},[2113,13581,2125],{"emptyLinePlaceholder":1767},[2113,13583,13584,13586,13589,13591,13594],{"class":2115,"line":2230},[2113,13585,3863],{"class":2326},[2113,13587,13588],{"class":2414}," wasm_bindgen",[2113,13590,2418],{"class":2119},[2113,13592,13593],{"class":2414},"prelude",[2113,13595,13596],{"class":2119},"::*;\n",[2113,13598,13599],{"class":2115,"line":2235},[2113,13600,2125],{"emptyLinePlaceholder":1767},[2113,13602,13603],{"class":2115,"line":2241},[2113,13604,13605],{"class":2396},"\u002F\u002F When the `wee_alloc` feature is enabled, use `wee_alloc` as the global\n",[2113,13607,13608],{"class":2115,"line":2246},[2113,13609,13610],{"class":2396},"\u002F\u002F allocator.\n",[2113,13612,13613,13616,13618,13621],{"class":2115,"line":2464},[2113,13614,13615],{"class":2119},"#[cfg(feature ",[2113,13617,2335],{"class":2334},[2113,13619,13620],{"class":2149}," \"wee_alloc\"",[2113,13622,9650],{"class":2119},[2113,13624,13625],{"class":2115,"line":2085},[2113,13626,13627],{"class":2119},"#[global_allocator]\n",[2113,13629,13630,13632,13635,13637,13640,13642,13645,13647,13650,13652,13654,13656,13659],{"class":2115,"line":2514},[2113,13631,7668],{"class":2326},[2113,13633,13634],{"class":2166}," ALLOC",[2113,13636,4429],{"class":2119},[2113,13638,13639],{"class":2414},"wee_alloc",[2113,13641,2418],{"class":2119},[2113,13643,13644],{"class":2414},"WeeAlloc",[2113,13646,2153],{"class":2334},[2113,13648,13649],{"class":2414}," wee_alloc",[2113,13651,2418],{"class":2119},[2113,13653,13644],{"class":2414},[2113,13655,2418],{"class":2119},[2113,13657,13658],{"class":2166},"INIT",[2113,13660,2487],{"class":2119},[2113,13662,13663],{"class":2115,"line":2533},[2113,13664,2125],{"emptyLinePlaceholder":1767},[2113,13666,13667],{"class":2115,"line":2556},[2113,13668,13669],{"class":2119},"#[wasm_bindgen]\n",[2113,13671,13672,13674,13676],{"class":2115,"line":2569},[2113,13673,2641],{"class":2326},[2113,13675,9657],{"class":2414},[2113,13677,2647],{"class":2119},[2113,13679,13680,13682,13684,13686],{"class":2115,"line":2575},[2113,13681,9664],{"class":2330},[2113,13683,4429],{"class":2119},[2113,13685,6101],{"class":2414},[2113,13687,4706],{"class":2119},[2113,13689,13690,13692,13694,13696],{"class":2115,"line":2596},[2113,13691,9675],{"class":2330},[2113,13693,4429],{"class":2119},[2113,13695,6101],{"class":2414},[2113,13697,4706],{"class":2119},[2113,13699,13700,13702,13704,13706],{"class":2115,"line":3192},[2113,13701,8165],{"class":2330},[2113,13703,4429],{"class":2119},[2113,13705,6101],{"class":2414},[2113,13707,4706],{"class":2119},[2113,13709,13710,13712,13714,13716],{"class":2115,"line":3213},[2113,13711,9696],{"class":2330},[2113,13713,4429],{"class":2119},[2113,13715,6101],{"class":2414},[2113,13717,4706],{"class":2119},[2113,13719,13720,13722,13724,13726],{"class":2115,"line":3236},[2113,13721,9707],{"class":2330},[2113,13723,4429],{"class":2119},[2113,13725,6101],{"class":2414},[2113,13727,4706],{"class":2119},[2113,13729,13730,13732,13734,13736],{"class":2115,"line":3248},[2113,13731,9718],{"class":2330},[2113,13733,4429],{"class":2119},[2113,13735,6101],{"class":2414},[2113,13737,4706],{"class":2119},[2113,13739,13740,13742,13744,13746],{"class":2115,"line":4899},[2113,13741,8304],{"class":2330},[2113,13743,4429],{"class":2119},[2113,13745,6101],{"class":2414},[2113,13747,4706],{"class":2119},[2113,13749,13750,13752,13754,13756],{"class":2115,"line":1777},[2113,13751,8335],{"class":2330},[2113,13753,4429],{"class":2119},[2113,13755,6101],{"class":2414},[2113,13757,4706],{"class":2119},[2113,13759,13760,13762,13764,13766],{"class":2115,"line":4931},[2113,13761,8366],{"class":2330},[2113,13763,4429],{"class":2119},[2113,13765,6101],{"class":2414},[2113,13767,4706],{"class":2119},[2113,13769,13770,13772,13774,13776],{"class":2115,"line":4961},[2113,13771,8397],{"class":2330},[2113,13773,4429],{"class":2119},[2113,13775,6101],{"class":2414},[2113,13777,4706],{"class":2119},[2113,13779,13780,13782,13784,13786],{"class":2115,"line":4976},[2113,13781,8428],{"class":2330},[2113,13783,4429],{"class":2119},[2113,13785,6101],{"class":2414},[2113,13787,4706],{"class":2119},[2113,13789,13790,13792,13794,13796],{"class":2115,"line":4993},[2113,13791,8459],{"class":2330},[2113,13793,4429],{"class":2119},[2113,13795,6101],{"class":2414},[2113,13797,4706],{"class":2119},[2113,13799,13800],{"class":2115,"line":5013},[2113,13801,2599],{"class":2119},[2113,13803,13804],{"class":2115,"line":5018},[2113,13805,2125],{"emptyLinePlaceholder":1767},[2113,13807,13808],{"class":2115,"line":5042},[2113,13809,13669],{"class":2119},[2113,13811,13812,13814,13816],{"class":2115,"line":5057},[2113,13813,2641],{"class":2326},[2113,13815,9815],{"class":2414},[2113,13817,2647],{"class":2119},[2113,13819,13820,13822,13824,13826,13828,13830],{"class":2115,"line":5062},[2113,13821,9822],{"class":2330},[2113,13823,6155],{"class":2119},[2113,13825,4480],{"class":2414},[2113,13827,6160],{"class":2119},[2113,13829,8504],{"class":2166},[2113,13831,7173],{"class":2119},[2113,13833,13834],{"class":2115,"line":5098},[2113,13835,2599],{"class":2119},[2113,13837,13838],{"class":2115,"line":5117},[2113,13839,2125],{"emptyLinePlaceholder":1767},[2113,13841,13842],{"class":2115,"line":5142},[2113,13843,13669],{"class":2119},[2113,13845,13846,13848,13850],{"class":2115,"line":5148},[2113,13847,2641],{"class":2326},[2113,13849,9863],{"class":2414},[2113,13851,2647],{"class":2119},[2113,13853,13854,13856,13858,13860,13862,13864],{"class":2115,"line":5163},[2113,13855,7559],{"class":2330},[2113,13857,4429],{"class":2119},[2113,13859,4733],{"class":2414},[2113,13861,3109],{"class":2119},[2113,13863,4480],{"class":2414},[2113,13865,9892],{"class":2119},[2113,13867,13868,13870,13872,13874],{"class":2115,"line":5169},[2113,13869,9897],{"class":2330},[2113,13871,4429],{"class":2119},[2113,13873,9902],{"class":2414},[2113,13875,4706],{"class":2119},[2113,13877,13878],{"class":2115,"line":5175},[2113,13879,2125],{"emptyLinePlaceholder":1767},[2113,13881,13882,13884,13886,13888,13890,13892],{"class":2115,"line":5180},[2113,13883,9913],{"class":2330},[2113,13885,4429],{"class":2119},[2113,13887,4733],{"class":2414},[2113,13889,3109],{"class":2119},[2113,13891,9536],{"class":2414},[2113,13893,9892],{"class":2119},[2113,13895,13896,13898,13900,13902],{"class":2115,"line":5199},[2113,13897,9928],{"class":2330},[2113,13899,4429],{"class":2119},[2113,13901,9536],{"class":2414},[2113,13903,4706],{"class":2119},[2113,13905,13906],{"class":2115,"line":5204},[2113,13907,2599],{"class":2119},[2113,13909,13910],{"class":2115,"line":5209},[2113,13911,2125],{"emptyLinePlaceholder":1767},[2113,13913,13914,13916,13918,13920,13922,13924,13926,13928,13930,13932],{"class":2115,"line":5232},[2113,13915,3906],{"class":2326},[2113,13917,9949],{"class":2133},[2113,13919,2423],{"class":2119},[2113,13921,9954],{"class":2330},[2113,13923,4472],{"class":2119},[2113,13925,4030],{"class":2326},[2113,13927,9961],{"class":2414},[2113,13929,4790],{"class":2119},[2113,13931,6101],{"class":2414},[2113,13933,2647],{"class":2119},[2113,13935,13936,13938,13940,13942],{"class":2115,"line":5237},[2113,13937,9972],{"class":2330},[2113,13939,179],{"class":2119},[2113,13941,9977],{"class":2133},[2113,13943,6745],{"class":2119},[2113,13945,13946],{"class":2115,"line":5242},[2113,13947,2599],{"class":2119},[2113,13949,13950],{"class":2115,"line":5267},[2113,13951,2125],{"emptyLinePlaceholder":1767},[2113,13953,13954,13956,13958,13960,13962,13964,13966,13968,13970,13972,13974,13976,13978,13980,13982,13984],{"class":2115,"line":5282},[2113,13955,3906],{"class":2326},[2113,13957,9994],{"class":2133},[2113,13959,2423],{"class":2119},[2113,13961,9954],{"class":2330},[2113,13963,4472],{"class":2119},[2113,13965,4030],{"class":2326},[2113,13967,9961],{"class":2414},[2113,13969,932],{"class":2119},[2113,13971,7873],{"class":2330},[2113,13973,4429],{"class":2119},[2113,13975,4496],{"class":2414},[2113,13977,4790],{"class":2119},[2113,13979,4733],{"class":2414},[2113,13981,3109],{"class":2119},[2113,13983,4480],{"class":2414},[2113,13985,4449],{"class":2119},[2113,13987,13988,13990,13992,13994,13996,13998,14000,14002,14004,14006,14008,14010,14012,14014,14016],{"class":2115,"line":5295},[2113,13989,3917],{"class":2326},[2113,13991,3975],{"class":2326},[2113,13993,10031],{"class":2330},[2113,13995,4429],{"class":2119},[2113,13997,4733],{"class":2414},[2113,13999,3109],{"class":2119},[2113,14001,4480],{"class":2414},[2113,14003,4435],{"class":2119},[2113,14005,2335],{"class":2334},[2113,14007,4008],{"class":2414},[2113,14009,2418],{"class":2119},[2113,14011,10050],{"class":2133},[2113,14013,2423],{"class":2119},[2113,14015,7873],{"class":2330},[2113,14017,2553],{"class":2119},[2113,14019,14020,14022,14024,14026,14028,14030,14032],{"class":2115,"line":5310},[2113,14021,10061],{"class":2326},[2113,14023,10064],{"class":2330},[2113,14025,10067],{"class":2326},[2113,14027,3208],{"class":2166},[2113,14029,10072],{"class":2119},[2113,14031,7873],{"class":2330},[2113,14033,2647],{"class":2119},[2113,14035,14036,14038,14040,14042,14044,14046,14048,14050],{"class":2115,"line":5315},[2113,14037,10081],{"class":2330},[2113,14039,179],{"class":2119},[2113,14041,10086],{"class":2133},[2113,14043,2423],{"class":2119},[2113,14045,9954],{"class":2330},[2113,14047,179],{"class":2119},[2113,14049,9977],{"class":2326},[2113,14051,2593],{"class":2119},[2113,14053,14054],{"class":2115,"line":5320},[2113,14055,4665],{"class":2119},[2113,14057,14058],{"class":2115,"line":8625},[2113,14059,10105],{"class":2330},[2113,14061,14062],{"class":2115,"line":8631},[2113,14063,2599],{"class":2119},[2113,14065,14066],{"class":2115,"line":8637},[2113,14067,2125],{"emptyLinePlaceholder":1767},[2113,14069,14070,14072,14074,14076,14078,14080,14082,14084,14086,14088],{"class":2115,"line":8652},[2113,14071,3906],{"class":2326},[2113,14073,10120],{"class":2133},[2113,14075,2423],{"class":2119},[2113,14077,9954],{"class":2330},[2113,14079,4472],{"class":2119},[2113,14081,4030],{"class":2326},[2113,14083,9961],{"class":2414},[2113,14085,4790],{"class":2119},[2113,14087,9536],{"class":2414},[2113,14089,2647],{"class":2119},[2113,14091,14092,14094,14096,14098,14100,14102,14104,14106,14108,14110,14112,14114],{"class":2115,"line":8674},[2113,14093,3917],{"class":2326},[2113,14095,3975],{"class":2326},[2113,14097,10145],{"class":2330},[2113,14099,2153],{"class":2334},[2113,14101,9815],{"class":2414},[2113,14103,7616],{"class":2119},[2113,14105,10154],{"class":2330},[2113,14107,6155],{"class":2119},[2113,14109,3095],{"class":2166},[2113,14111,6160],{"class":2119},[2113,14113,8504],{"class":2166},[2113,14115,10165],{"class":2119},[2113,14117,14118,14120,14122,14124,14126,14128,14130],{"class":2115,"line":8706},[2113,14119,10061],{"class":2326},[2113,14121,7910],{"class":2330},[2113,14123,10067],{"class":2326},[2113,14125,3208],{"class":2166},[2113,14127,10072],{"class":2119},[2113,14129,8504],{"class":2166},[2113,14131,2647],{"class":2119},[2113,14133,14134,14136,14138,14140,14142,14144,14146,14148,14150],{"class":2115,"line":8724},[2113,14135,10186],{"class":2330},[2113,14137,10189],{"class":2119},[2113,14139,7919],{"class":2330},[2113,14141,4129],{"class":2119},[2113,14143,2335],{"class":2334},[2113,14145,10198],{"class":2330},[2113,14147,179],{"class":2119},[2113,14149,9977],{"class":2133},[2113,14151,3944],{"class":2119},[2113,14153,14154],{"class":2115,"line":8729},[2113,14155,4665],{"class":2119},[2113,14157,14158],{"class":2115,"line":8734},[2113,14159,10213],{"class":2330},[2113,14161,14162],{"class":2115,"line":8740},[2113,14163,2599],{"class":2119},[2113,14165,14166],{"class":2115,"line":8772},[2113,14167,2125],{"emptyLinePlaceholder":1767},[2113,14169,14170],{"class":2115,"line":8778},[2113,14171,13669],{"class":2119},[2113,14173,14174,14176,14178,14180],{"class":2115,"line":8789},[2113,14175,5374],{"class":2326},[2113,14177,5377],{"class":2326},[2113,14179,12143],{"class":2414},[2113,14181,2647],{"class":2119},[2113,14183,14184,14187,14189,14191,14193,14195],{"class":2115,"line":8795},[2113,14185,14186],{"class":2330},"    test_objects",[2113,14188,4429],{"class":2119},[2113,14190,4733],{"class":2414},[2113,14192,3109],{"class":2119},[2113,14194,10406],{"class":2414},[2113,14196,9892],{"class":2119},[2113,14198,14199],{"class":2115,"line":8817},[2113,14200,2599],{"class":2119},[2113,14202,14203],{"class":2115,"line":8822},[2113,14204,2125],{"emptyLinePlaceholder":1767},[2113,14206,14207],{"class":2115,"line":8857},[2113,14208,13669],{"class":2119},[2113,14210,14211,14213,14215],{"class":2115,"line":8896},[2113,14212,4421],{"class":2326},[2113,14214,12143],{"class":2414},[2113,14216,2647],{"class":2119},[2113,14218,14219],{"class":2115,"line":8901},[2113,14220,14221],{"class":2119},"    #[wasm_bindgen(constructor)]\n",[2113,14223,14224,14227,14229,14231,14233,14235],{"class":2115,"line":8907},[2113,14225,14226],{"class":2326},"    pub",[2113,14228,6542],{"class":2326},[2113,14230,4778],{"class":2133},[2113,14232,12241],{"class":2119},[2113,14234,4793],{"class":2414},[2113,14236,2647],{"class":2119},[2113,14238,14239,14241],{"class":2115,"line":8913},[2113,14240,4800],{"class":2414},[2113,14242,2647],{"class":2119},[2113,14244,14245,14248,14250,14252,14254,14256],{"class":2115,"line":8931},[2113,14246,14247],{"class":2330},"            test_objects",[2113,14249,4429],{"class":2119},[2113,14251,4733],{"class":2414},[2113,14253,2418],{"class":2119},[2113,14255,3961],{"class":2133},[2113,14257,8210],{"class":2119},[2113,14259,14260],{"class":2115,"line":8936},[2113,14261,4578],{"class":2119},[2113,14263,14264],{"class":2115,"line":8942},[2113,14265,4665],{"class":2119},[2113,14267,14268],{"class":2115,"line":8950},[2113,14269,2125],{"emptyLinePlaceholder":1767},[2113,14271,14272],{"class":2115,"line":8968},[2113,14273,14274],{"class":2119},"    #[wasm_bindgen]\n",[2113,14276,14277,14279,14281,14283,14285,14287,14289,14291,14293,14295,14297],{"class":2115,"line":8983},[2113,14278,14226],{"class":2326},[2113,14280,6542],{"class":2326},[2113,14282,12308],{"class":2133},[2113,14284,4027],{"class":2119},[2113,14286,4030],{"class":2326},[2113,14288,4464],{"class":2414},[2113,14290,932],{"class":2119},[2113,14292,10421],{"class":2330},[2113,14294,4429],{"class":2119},[2113,14296,12323],{"class":2414},[2113,14298,2433],{"class":2119},[2113,14300,14301,14303,14305,14307,14309,14311,14313,14315],{"class":2115,"line":8988},[2113,14302,4583],{"class":2326},[2113,14304,3975],{"class":2326},[2113,14306,10198],{"class":2330},[2113,14308,2153],{"class":2334},[2113,14310,9573],{"class":2414},[2113,14312,2418],{"class":2119},[2113,14314,10264],{"class":2133},[2113,14316,3944],{"class":2119},[2113,14318,14319],{"class":2115,"line":9011},[2113,14320,14321],{"class":2396},"        \u002F\u002F Get the current time in ms\n",[2113,14323,14324,14326,14329,14331,14333,14335,14337,14339,14341,14343,14345],{"class":2115,"line":9016},[2113,14325,4637],{"class":2414},[2113,14327,14328],{"class":2119},".test_objects ",[2113,14330,2335],{"class":2334},[2113,14332,4008],{"class":2414},[2113,14334,2418],{"class":2119},[2113,14336,10050],{"class":2133},[2113,14338,2423],{"class":2119},[2113,14340,10421],{"class":2330},[2113,14342,11429],{"class":2326},[2113,14344,13027],{"class":2414},[2113,14346,2553],{"class":2119},[2113,14348,14349],{"class":2115,"line":9022},[2113,14350,2125],{"emptyLinePlaceholder":1767},[2113,14352,14353,14356,14358,14360,14362,14364,14366],{"class":2115,"line":9045},[2113,14354,14355],{"class":2326},"        for",[2113,14357,10064],{"class":2330},[2113,14359,10067],{"class":2326},[2113,14361,3208],{"class":2166},[2113,14363,10072],{"class":2119},[2113,14365,10421],{"class":2330},[2113,14367,2647],{"class":2119},[2113,14369,14370,14372,14375,14377,14379,14381],{"class":2115,"line":9076},[2113,14371,5183],{"class":2414},[2113,14373,14374],{"class":2119},".test_objects.",[2113,14376,10086],{"class":2133},[2113,14378,2423],{"class":2119},[2113,14380,10406],{"class":2414},[2113,14382,2647],{"class":2119},[2113,14384,14385,14388,14390,14392,14394,14396,14398,14400,14402],{"class":2115,"line":9082},[2113,14386,14387],{"class":2330},"                path",[2113,14389,4429],{"class":2119},[2113,14391,10464],{"class":2133},[2113,14393,4027],{"class":2119},[2113,14395,4030],{"class":2326},[2113,14397,10198],{"class":2330},[2113,14399,932],{"class":2119},[2113,14401,8081],{"class":2166},[2113,14403,7142],{"class":2119},[2113,14405,14406,14409,14411,14413],{"class":2115,"line":9105},[2113,14407,14408],{"class":2330},"                stats",[2113,14410,4429],{"class":2119},[2113,14412,9902],{"class":2414},[2113,14414,2647],{"class":2119},[2113,14416,14417,14420,14422,14424,14426,14428,14430],{"class":2115,"line":9138},[2113,14418,14419],{"class":2330},"                    owner_id",[2113,14421,4429],{"class":2119},[2113,14423,10497],{"class":2133},[2113,14425,4027],{"class":2119},[2113,14427,4030],{"class":2326},[2113,14429,10198],{"class":2330},[2113,14431,7142],{"class":2119},[2113,14433,14434,14437,14439,14441,14443,14445,14447],{"class":2115,"line":9149},[2113,14435,14436],{"class":2330},"                    group_id",[2113,14438,4429],{"class":2119},[2113,14440,10497],{"class":2133},[2113,14442,4027],{"class":2119},[2113,14444,4030],{"class":2326},[2113,14446,10198],{"class":2330},[2113,14448,7142],{"class":2119},[2113,14450,14451,14454,14456,14458,14460,14462,14464],{"class":2115,"line":9157},[2113,14452,14453],{"class":2330},"                    size",[2113,14455,4429],{"class":2119},[2113,14457,10497],{"class":2133},[2113,14459,4027],{"class":2119},[2113,14461,4030],{"class":2326},[2113,14463,10198],{"class":2330},[2113,14465,7142],{"class":2119},[2113,14467,14468,14471,14473,14475,14477,14479,14481],{"class":2115,"line":9182},[2113,14469,14470],{"class":2330},"                    compressed_size",[2113,14472,4429],{"class":2119},[2113,14474,10497],{"class":2133},[2113,14476,4027],{"class":2119},[2113,14478,4030],{"class":2326},[2113,14480,10198],{"class":2330},[2113,14482,7142],{"class":2119},[2113,14484,14485,14488,14490,14492,14494,14496,14498],{"class":2115,"line":9188},[2113,14486,14487],{"class":2330},"                    last_read",[2113,14489,4429],{"class":2119},[2113,14491,10497],{"class":2133},[2113,14493,4027],{"class":2119},[2113,14495,4030],{"class":2326},[2113,14497,10198],{"class":2330},[2113,14499,7142],{"class":2119},[2113,14501,14502,14505,14507,14509,14511,14513,14515],{"class":2115,"line":9193},[2113,14503,14504],{"class":2330},"                    last_modified",[2113,14506,4429],{"class":2119},[2113,14508,10497],{"class":2133},[2113,14510,4027],{"class":2119},[2113,14512,4030],{"class":2326},[2113,14514,10198],{"class":2330},[2113,14516,7142],{"class":2119},[2113,14518,14519,14522,14524,14526,14528,14530,14532],{"class":2115,"line":9198},[2113,14520,14521],{"class":2330},"                    created",[2113,14523,4429],{"class":2119},[2113,14525,10497],{"class":2133},[2113,14527,4027],{"class":2119},[2113,14529,4030],{"class":2326},[2113,14531,10198],{"class":2330},[2113,14533,7142],{"class":2119},[2113,14535,14536,14539,14541,14543,14545,14547,14549],{"class":2115,"line":9216},[2113,14537,14538],{"class":2330},"                    mode",[2113,14540,4429],{"class":2119},[2113,14542,10497],{"class":2133},[2113,14544,4027],{"class":2119},[2113,14546,4030],{"class":2326},[2113,14548,10198],{"class":2330},[2113,14550,7142],{"class":2119},[2113,14552,14553,14556,14558,14560,14562,14564,14566],{"class":2115,"line":9238},[2113,14554,14555],{"class":2330},"                    dev",[2113,14557,4429],{"class":2119},[2113,14559,10497],{"class":2133},[2113,14561,4027],{"class":2119},[2113,14563,4030],{"class":2326},[2113,14565,10198],{"class":2330},[2113,14567,7142],{"class":2119},[2113,14569,14570,14573,14575,14577,14579,14581,14583],{"class":2115,"line":9243},[2113,14571,14572],{"class":2330},"                    rdev",[2113,14574,4429],{"class":2119},[2113,14576,10497],{"class":2133},[2113,14578,4027],{"class":2119},[2113,14580,4030],{"class":2326},[2113,14582,10198],{"class":2330},[2113,14584,7142],{"class":2119},[2113,14586,14587,14590,14592,14594,14596,14598,14600],{"class":2115,"line":10782},[2113,14588,14589],{"class":2330},"                    ino",[2113,14591,4429],{"class":2119},[2113,14593,10497],{"class":2133},[2113,14595,4027],{"class":2119},[2113,14597,4030],{"class":2326},[2113,14599,10198],{"class":2330},[2113,14601,7142],{"class":2119},[2113,14603,14604,14607,14609,14611,14613,14615,14617],{"class":2115,"line":10787},[2113,14605,14606],{"class":2330},"                    nlink",[2113,14608,4429],{"class":2119},[2113,14610,10497],{"class":2133},[2113,14612,4027],{"class":2119},[2113,14614,4030],{"class":2326},[2113,14616,10198],{"class":2330},[2113,14618,7142],{"class":2119},[2113,14620,14621],{"class":2115,"line":10813},[2113,14622,14623],{"class":2119},"                },\n",[2113,14625,14626,14629,14631,14633],{"class":2115,"line":10834},[2113,14627,14628],{"class":2330},"                chunks",[2113,14630,4429],{"class":2119},[2113,14632,10707],{"class":2133},[2113,14634,10710],{"class":2119},[2113,14636,14637,14640,14642,14644,14646],{"class":2115,"line":10843},[2113,14638,14639],{"class":2133},"                    generate_random_sha256",[2113,14641,4027],{"class":2119},[2113,14643,4030],{"class":2326},[2113,14645,10198],{"class":2330},[2113,14647,7142],{"class":2119},[2113,14649,14650,14652,14654,14656,14658],{"class":2115,"line":10852},[2113,14651,14639],{"class":2133},[2113,14653,4027],{"class":2119},[2113,14655,4030],{"class":2326},[2113,14657,10198],{"class":2330},[2113,14659,7142],{"class":2119},[2113,14661,14662,14664,14666,14668,14670],{"class":2115,"line":10858},[2113,14663,14639],{"class":2133},[2113,14665,4027],{"class":2119},[2113,14667,4030],{"class":2326},[2113,14669,10198],{"class":2330},[2113,14671,7142],{"class":2119},[2113,14673,14674],{"class":2115,"line":10880},[2113,14675,14676],{"class":2119},"                ],\n",[2113,14678,14679,14682,14684,14686,14688,14690,14692],{"class":2115,"line":10885},[2113,14680,14681],{"class":2330},"                sha256",[2113,14683,4429],{"class":2119},[2113,14685,10762],{"class":2133},[2113,14687,4027],{"class":2119},[2113,14689,4030],{"class":2326},[2113,14691,10198],{"class":2330},[2113,14693,7142],{"class":2119},[2113,14695,14696],{"class":2115,"line":10890},[2113,14697,14698],{"class":2119},"            });\n",[2113,14700,14701],{"class":2115,"line":10912},[2113,14702,4578],{"class":2119},[2113,14704,14705],{"class":2115,"line":10918},[2113,14706,4665],{"class":2119},[2113,14708,14709],{"class":2115,"line":10925},[2113,14710,2125],{"emptyLinePlaceholder":1767},[2113,14712,14713,14716,14718],{"class":2115,"line":10933},[2113,14714,14715],{"class":2119},"    #[wasm_bindgen(js_name ",[2113,14717,2335],{"class":2334},[2113,14719,14720],{"class":2119}," toString)]\n",[2113,14722,14723,14725,14727,14729,14731,14733,14735,14737],{"class":2115,"line":10958},[2113,14724,14226],{"class":2326},[2113,14726,6542],{"class":2326},[2113,14728,13072],{"class":2133},[2113,14730,4027],{"class":2119},[2113,14732,5216],{"class":2414},[2113,14734,4790],{"class":2119},[2113,14736,13081],{"class":2414},[2113,14738,2647],{"class":2119},[2113,14740,14741,14744],{"class":2115,"line":10964},[2113,14742,14743],{"class":2133},"        format!",[2113,14745,3146],{"class":2119},[2113,14747,14748,14751],{"class":2115,"line":10970},[2113,14749,14750],{"class":2149},"            \"TestObjectWrapper {{ test_objects: {} }}\"",[2113,14752,4706],{"class":2119},[2113,14754,14755,14757,14759,14761],{"class":2115,"line":10977},[2113,14756,5183],{"class":2414},[2113,14758,14374],{"class":2119},[2113,14760,4540],{"class":2133},[2113,14762,6745],{"class":2119},[2113,14764,14765],{"class":2115,"line":10985},[2113,14766,14767],{"class":2119},"        )\n",[2113,14769,14770],{"class":2115,"line":11010},[2113,14771,4665],{"class":2119},[2113,14773,14774],{"class":2115,"line":11015},[2113,14775,2599],{"class":2119},[12,14777,14778,14779,179],{},"Pour utiliser notre module dans Node.JS, nous devons importer le module ",[184,14780,14781],{},"wasm-bindgen",[2105,14783,14785],{"className":7776,"code":14784,"language":7778,"meta":1646,"style":1646},"const { webcrypto } = require('node:crypto')\nglobal.crypto = webcrypto\n\nconst wasm = require('..\u002Fpkg\u002Ftest_rust_was.js');\nconst filesize = require(\"filesize.js\");\n\nglobal.gc();\nconst memoryBefore = process.memoryUsage().rss;\n\nconst time = Date.now();\n\nconst nbObjects = 1300000;\nconst testArray = new wasm.TestObjectWrapper();\ntestArray.fill(nbObjects);\n\nconsole.log(\"Creation time: \", Date.now() - time);\n\nglobal.gc();\nconst memoryAfter = process.memoryUsage().rss;\n\nconsole.log(\"Memory consumption in JS: \", filesize.default(memoryAfter - memoryBefore));\nconsole.log(\"Memory consumption calculated by head: \", filesize.default(432));\nconsole.log(\"Memory consumption by objects: \", filesize.default((memoryAfter - memoryBefore) \u002F nbObjects));\n  \nconsole.log(testArray.toString());\n",[184,14786,14787,14809,14823,14827,14845,14861,14865,14875,14896,14900,14916,14920,14933,14951,14965,14969,14997,15001,15011,15031,15035,15066,15094,15130,15135],{"__ignoreMap":1646},[2113,14788,14789,14791,14793,14796,14798,14800,14802,14804,14807],{"class":2115,"line":2116},[2113,14790,7785],{"class":2326},[2113,14792,7616],{"class":2119},[2113,14794,14795],{"class":2414},"webcrypto",[2113,14797,7840],{"class":2119},[2113,14799,2335],{"class":2334},[2113,14801,7793],{"class":2133},[2113,14803,2423],{"class":2119},[2113,14805,14806],{"class":2149},"'node:crypto'",[2113,14808,4660],{"class":2119},[2113,14810,14811,14813,14815,14818,14820],{"class":2115,"line":1647},[2113,14812,8558],{"class":2414},[2113,14814,179],{"class":2119},[2113,14816,14817],{"class":2330},"crypto",[2113,14819,2153],{"class":2334},[2113,14821,14822],{"class":2330}," webcrypto\n",[2113,14824,14825],{"class":2115,"line":1652},[2113,14826,2125],{"emptyLinePlaceholder":1767},[2113,14828,14829,14831,14834,14836,14838,14840,14843],{"class":2115,"line":2185},[2113,14830,7785],{"class":2326},[2113,14832,14833],{"class":2414}," wasm",[2113,14835,2153],{"class":2334},[2113,14837,7793],{"class":2133},[2113,14839,2423],{"class":2119},[2113,14841,14842],{"class":2149},"'..\u002Fpkg\u002Ftest_rust_was.js'",[2113,14844,2553],{"class":2119},[2113,14846,14847,14849,14851,14853,14855,14857,14859],{"class":2115,"line":2230},[2113,14848,7785],{"class":2326},[2113,14850,7788],{"class":2414},[2113,14852,2153],{"class":2334},[2113,14854,7793],{"class":2133},[2113,14856,2423],{"class":2119},[2113,14858,7798],{"class":2149},[2113,14860,2553],{"class":2119},[2113,14862,14863],{"class":2115,"line":2235},[2113,14864,2125],{"emptyLinePlaceholder":1767},[2113,14866,14867,14869,14871,14873],{"class":2115,"line":2241},[2113,14868,8558],{"class":2414},[2113,14870,179],{"class":2119},[2113,14872,8563],{"class":2133},[2113,14874,3944],{"class":2119},[2113,14876,14877,14879,14881,14883,14885,14887,14889,14891,14894],{"class":2115,"line":2246},[2113,14878,7785],{"class":2326},[2113,14880,8577],{"class":2414},[2113,14882,2153],{"class":2334},[2113,14884,8582],{"class":2414},[2113,14886,179],{"class":2119},[2113,14888,8587],{"class":2133},[2113,14890,5224],{"class":2119},[2113,14892,14893],{"class":2330},"rss",[2113,14895,2487],{"class":2119},[2113,14897,14898],{"class":2115,"line":2464},[2113,14899,2125],{"emptyLinePlaceholder":1767},[2113,14901,14902,14904,14906,14908,14910,14912,14914],{"class":2115,"line":2085},[2113,14903,7785],{"class":2326},[2113,14905,8606],{"class":2414},[2113,14907,2153],{"class":2334},[2113,14909,8611],{"class":2414},[2113,14911,179],{"class":2119},[2113,14913,8616],{"class":2133},[2113,14915,3944],{"class":2119},[2113,14917,14918],{"class":2115,"line":2514},[2113,14919,2125],{"emptyLinePlaceholder":1767},[2113,14921,14922,14924,14926,14928,14931],{"class":2115,"line":2533},[2113,14923,7785],{"class":2326},[2113,14925,8642],{"class":2414},[2113,14927,2153],{"class":2334},[2113,14929,14930],{"class":2166}," 1300000",[2113,14932,2487],{"class":2119},[2113,14934,14935,14937,14939,14941,14943,14945,14947,14949],{"class":2115,"line":2556},[2113,14936,7785],{"class":2326},[2113,14938,8657],{"class":2414},[2113,14940,2153],{"class":2334},[2113,14942,4778],{"class":2326},[2113,14944,14833],{"class":2414},[2113,14946,179],{"class":2119},[2113,14948,12201],{"class":2133},[2113,14950,3944],{"class":2119},[2113,14952,14953,14955,14957,14959,14961,14963],{"class":2115,"line":2569},[2113,14954,13315],{"class":2414},[2113,14956,179],{"class":2119},[2113,14958,13320],{"class":2133},[2113,14960,2423],{"class":2119},[2113,14962,8669],{"class":2330},[2113,14964,2553],{"class":2119},[2113,14966,14967],{"class":2115,"line":2575},[2113,14968,2125],{"emptyLinePlaceholder":1767},[2113,14970,14971,14973,14975,14977,14979,14981,14983,14985,14987,14989,14991,14993,14995],{"class":2115,"line":2596},[2113,14972,8743],{"class":2414},[2113,14974,179],{"class":2119},[2113,14976,8748],{"class":2133},[2113,14978,2423],{"class":2119},[2113,14980,8753],{"class":2149},[2113,14982,932],{"class":2119},[2113,14984,36],{"class":2414},[2113,14986,179],{"class":2119},[2113,14988,8616],{"class":2133},[2113,14990,4543],{"class":2119},[2113,14992,3242],{"class":2334},[2113,14994,8606],{"class":2330},[2113,14996,2553],{"class":2119},[2113,14998,14999],{"class":2115,"line":3192},[2113,15000,2125],{"emptyLinePlaceholder":1767},[2113,15002,15003,15005,15007,15009],{"class":2115,"line":3213},[2113,15004,8558],{"class":2414},[2113,15006,179],{"class":2119},[2113,15008,8563],{"class":2133},[2113,15010,3944],{"class":2119},[2113,15012,15013,15015,15017,15019,15021,15023,15025,15027,15029],{"class":2115,"line":3236},[2113,15014,7785],{"class":2326},[2113,15016,8800],{"class":2414},[2113,15018,2153],{"class":2334},[2113,15020,8582],{"class":2414},[2113,15022,179],{"class":2119},[2113,15024,8587],{"class":2133},[2113,15026,5224],{"class":2119},[2113,15028,14893],{"class":2330},[2113,15030,2487],{"class":2119},[2113,15032,15033],{"class":2115,"line":3248},[2113,15034,2125],{"emptyLinePlaceholder":1767},[2113,15036,15037,15039,15041,15043,15045,15048,15050,15052,15054,15056,15058,15060,15062,15064],{"class":2115,"line":4899},[2113,15038,8743],{"class":2414},[2113,15040,179],{"class":2119},[2113,15042,8748],{"class":2133},[2113,15044,2423],{"class":2119},[2113,15046,15047],{"class":2149},"\"Memory consumption in JS: \"",[2113,15049,932],{"class":2119},[2113,15051,8838],{"class":2414},[2113,15053,179],{"class":2119},[2113,15055,8843],{"class":2133},[2113,15057,2423],{"class":2119},[2113,15059,8848],{"class":2330},[2113,15061,2270],{"class":2334},[2113,15063,8577],{"class":2330},[2113,15065,5818],{"class":2119},[2113,15067,15068,15070,15072,15074,15076,15079,15081,15083,15085,15087,15089,15092],{"class":2115,"line":1777},[2113,15069,8743],{"class":2414},[2113,15071,179],{"class":2119},[2113,15073,8748],{"class":2133},[2113,15075,2423],{"class":2119},[2113,15077,15078],{"class":2149},"\"Memory consumption calculated by head: \"",[2113,15080,932],{"class":2119},[2113,15082,8838],{"class":2414},[2113,15084,179],{"class":2119},[2113,15086,8843],{"class":2133},[2113,15088,2423],{"class":2119},[2113,15090,15091],{"class":2166},"432",[2113,15093,5818],{"class":2119},[2113,15095,15096,15098,15100,15102,15104,15106,15108,15110,15112,15114,15116,15118,15120,15122,15124,15126,15128],{"class":2115,"line":4931},[2113,15097,8743],{"class":2414},[2113,15099,179],{"class":2119},[2113,15101,8748],{"class":2133},[2113,15103,2423],{"class":2119},[2113,15105,8868],{"class":2149},[2113,15107,932],{"class":2119},[2113,15109,8838],{"class":2414},[2113,15111,179],{"class":2119},[2113,15113,8843],{"class":2133},[2113,15115,8879],{"class":2119},[2113,15117,8848],{"class":2330},[2113,15119,2270],{"class":2334},[2113,15121,8577],{"class":2330},[2113,15123,5709],{"class":2119},[2113,15125,2520],{"class":2334},[2113,15127,8642],{"class":2330},[2113,15129,5818],{"class":2119},[2113,15131,15132],{"class":2115,"line":4961},[2113,15133,15134],{"class":2119},"  \n",[2113,15136,15137,15139,15141,15143,15145,15147,15149,15151],{"class":2115,"line":4976},[2113,15138,8743],{"class":2414},[2113,15140,179],{"class":2119},[2113,15142,8748],{"class":2133},[2113,15144,2423],{"class":2119},[2113,15146,13315],{"class":2414},[2113,15148,179],{"class":2119},[2113,15150,13466],{"class":2133},[2113,15152,2593],{"class":2119},[12,15154,15155],{},"Nous n'oublions pas de compiler notre module en mode release et avec la target nodejs.",[12,15157,15158],{},"Le résultat est alors le suivant :",[22,15160,15161,15169],{},[25,15162,15163],{},[28,15164,15165,15167],{},[31,15166],{},[31,15168],{},[41,15170,15171,15177,15184,15191],{},[28,15172,15173,15175],{},[46,15174,9275],{},[46,15176,9278],{},[28,15178,15179,15181],{},[46,15180,9283],{},[46,15182,15183],{},"42 secondes",[28,15185,15186,15188],{},[46,15187,9291],{},[46,15189,15190],{},"450,6 Mo",[28,15192,15193,15195],{},[46,15194,9299],{},[46,15196,15197],{},"363.5 octets",[12,15199,15200],{},"Wasm permet d'améliorer la consommation mémoire. Sur mon exemple, le temps de création n'est pas très impressionnant\nmais est, je crois, lié à la génération des nombres aléatoires.",[12,15202,15203],{},"L'utilisation de WASM pourrait être une bonne alternative.",[12,15205,15206],{},"Quand utiliser WASM et quand faire un module node ? L'avantage de faire du Wasm, c'est que le module peut tourner\négalement côté navigateur. L'autre avantage est que le module n'a pas besoin d'être recompilé en cas de changement de\nplateforme (Linux, Windows, ... ; x86, ARM, ...).",[12,15208,15209],{},"D'un autre côté, un module node natif a l'avantage d'être plus simple (moins de bindings) et sera sûrement plus rapide\nà l'exécution.",[128,15211,1621],{"id":1620},[12,15213,15214],{},"Pour améliorer les performances de notre application, nous allons donc écrire un module natif en Rust avec N-API.",[12,15216,15217,15218,179],{},"Vous pouvez retrouver le code source de cet article sur mon dépôt ",[49,15219,15222],{"href":15220,"rel":15221},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002Fblog_compare_js_rust_wasm",[347],"Gitea",[3358,15224,15225],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":1646,"searchDepth":1647,"depth":1647,"links":15227},[15228,15229,15230,15231,15232,15233,15235],{"id":7759,"depth":1647,"text":7760},{"id":7769,"depth":1647,"text":7770},{"id":9335,"depth":1647,"text":9336},{"id":9522,"depth":1647,"text":9523},{"id":11522,"depth":1647,"text":11523},{"id":13515,"depth":1647,"text":15234},"Conclusion - Wasm",{"id":1620,"depth":1647,"text":1621},{"type":9,"value":15237},[15238,15240,15242],[128,15239,7760],{"id":7759},[12,15241,7763],{},[12,15243,7766],{},{"planet":1767},{"title":7754,"description":1646},"woodstock_rust","posts\u002FWoodstock\u002F2023-05-10_woodstock_rust",[1683,1773,1774,3510,3511],"RXnKX2dJmrksM5sZOeN70MXCyeSAMu1H3Jj5D3fie28",{"id":15251,"title":15252,"author":7,"body":15253,"category":2057,"categorySlug":2058,"date":17355,"description":1646,"excerpt":17356,"extension":1764,"location":1765,"meta":17375,"navigation":1767,"path":79,"published":1767,"seo":17376,"slug":17377,"stem":17378,"tags":17379,"timeToRead":4931,"__hash__":17380},"posts\u002Fposts\u002FWoodstock\u002F2021-04-18_woodstock_protocol_language_sauvegarde.md","Woodstock Backup - Protocol et Language de sauvegarde",{"type":9,"value":15254,"toc":17341},[15255,15262,15273,15280,15283,15286,15289,15292,15295,15298,15302,15305,15319,15322,15330,15333,15336,15352,15356,15359,15373,15379,15382,15385,15402,15405,15416,15422,15425,15428,15432,15438,15441,15465,15468,15471,15474,15477,15480,15484,15487,15492,15495,15506,15509,15513,15516,15519,15538,15541,15549,15552,15558,15582,15585,15589,15592,15612,15615,15618,15621,15624,15628,15631,15634,15637,15640,15644,15663,15689,15695,15709,15722,15737,15747,15965,15972,16967,16975,16984,16987,16998,17004,17008,17020,17029,17032,17049,17052,17055,17135,17138,17141,17144,17202,17214,17217,17247,17250,17253,17258,17262,17285,17289,17292,17295,17298,17301,17318,17321,17324,17327,17338],[12,15256,15257],{},[121,15258],{"alt":15252,"src":15259,"className":15260,"width":15261},"\u002FWoodstock\u002Fsplash_proto.png",[126],"400px,",[1886,15263,15264,15267,15270],{},[12,15265,15266],{},"Note de 2023 : Ce billet a été écrit en avril 2021, il y a deux ans, mais n'a jamais été publié. Le temps passe vite.",[12,15268,15269],{},"Depuis lors, j'ai travaillé sur d'autres projets, mais aussi sur ce logiciel de sauvegarde. En progressant dans le\ndéveloppement du projet, j'ai pu optimiser les performances et me faire une opinion sur le choix que j'ai finalement\nfait, que je partagerai à la fin de l'article.",[12,15271,15272],{},"Je mettrai à jour mes conclusions en fonction de mes avancées sur le sujet.",[12,15274,15275,15276,15279],{},"Dans notre ",[49,15277,15278],{"href":65},"précédent article",", nous avons vu comment dédupliquer les fichiers dans un pool sans\nutiliser btrfs, un système de fichier permettant la déduplication. Pour pouvoir copier les fichiers dans notre pool,\nle logiciel doit savoir comment écrire les fichiers de manière appropriée.",[12,15281,15282],{},"Si nous voulons continuer à utiliser rsync, nous pourrions envisager de créer un système de fichiers FUSE (Filesystem in\nUser Space) pour faire le pont entre rsync et notre pool de stockage. Cela nous permettrait de continuer à utiliser\nrsync pour les sauvegardes et les restaurations. Cependant, cela nécessiterait la mise en œuvre d'un système de fichiers\ncomplet, y compris la lecture, l'écriture, la déduplication, etc., pour un usage très spécifique de rsync. En fin de\ncompte, le système de fichiers ne serait utilisé que pour la sauvegarde (ajout de nouveaux éléments) et la restauration\n(lecture d'une sauvegarde). Il n'y aurait pas d'écriture aléatoire dans le système de fichiers, ni dans un fichier même.",[12,15284,15285],{},"Après avoir rapidement examiné cette possibilité, je l'ai écartée car elle me semble être une solution trop lourde pour\nmes besoins.",[12,15287,15288],{},"Par conséquent, nous devrons nous passer de rsync. Je n'ai pas trouvé de bibliothèque permettant d'implémenter\nfacilement une synchronisation sur le protocole rsync. Nous devrons donc écrire notre propre protocole de\nsynchronisation de fichiers.",[12,15290,15291],{},"Lorsque nous développerons ce protocole, nous devrons nous assurer de la sécurité. En effet, rsync est capable de\nsynchroniser des fichiers via un tunnel ssh. Notre protocole devra empêcher les attaquants d'écouter ce qui est\nsynchronisé et d'accéder aux fichiers sans autorisation.",[12,15293,15294],{},"Dans la suite de cet article, nous étudierons comment écrire notre protocole de synchronisation et nous comparerons les\nperformances de sauvegarde entre le langage JavaScript (actuellement utilisé par le serveur de sauvegarde) et le\nlangage C++.",[12,15296,15297],{},"Comme toujours, n'hésitez pas à me faire part de vos commentaires et de vos avis (système de commentaires en bas de la\npage).",[128,15299,15301],{"id":15300},"création-de-la-communication-périphériqueserveur","Création de la communication périphérique\u002Fserveur",[12,15303,15304],{},"Nous allons aborder la question de la communication entre le client et le serveur. Afin d'éviter toute confusion entre\nle client et le serveur dans le sens réseau et le client et le serveur dans le sens applicatif, nous allons désigner :",[506,15306,15307,15313],{},[370,15308,15309,15312],{},[184,15310,15311],{},"Périphérique"," pour le client à sauvegarder",[370,15314,15315,15318],{},[184,15316,15317],{},"Serveur de sauvegarde"," pour le serveur de sauvegarde.",[12,15320,15321],{},"En ce qui concerne le protocole de communication, je pense utiliser une HTTP2 plutôt que d'ouvrir un socket TCP et\ntravailler directement dessus. L'utilisation d'un protocole de haut niveau me permettra de m'appuyer sur des\nbibliothèques existantes et déjà largement utilisées. De plus, je n'aurai pas à gérer :",[506,15323,15324,15327],{},[370,15325,15326],{},"la compression",[370,15328,15329],{},"la prise en charge de TLS",[12,15331,15332],{},"En raison de la nature d'HTTP2, l'initiateur de la connexion est important : il faut déterminer qui est le serveur HTTP\net qui est le client HTTP.",[12,15334,15335],{},"Dans la suite de cet article, nous allons essayer de répondre aux deux questions suivantes :",[506,15337,15338,15341],{},[370,15339,15340],{},"Comment effectuer la communication entre le serveur de sauvegarde et le périphérique à sauvegarder ?",[370,15342,15343,15344],{},"Qui doit initier la connexion ?\n",[506,15345,15346,15349],{},[370,15347,15348],{},"Est-ce que le serveur de sauvegarde doit contacter le périphérique ?",[370,15350,15351],{},"Ou est-ce que le périphérique doit contacter le serveur de sauvegarde ?",[133,15353,15355],{"id":15354},"le-serveur-de-sauvegarde-initie-la-connection","Le serveur de sauvegarde initie la connection",[12,15357,15358],{},"Nous allons parler de deux éléments clés pour la sauvegarde: le manifeste et les chunks.",[506,15360,15361,15367],{},[370,15362,15363,15366],{},[184,15364,15365],{},"Manifeste",": la liste des fichiers à sauvegarder avec le nom du fichier, les attributs, les acl, ... un hash du\nfichier et un hash des différents morceaux qui le constituent.",[370,15368,15369,15372],{},[184,15370,15371],{},"Chunk",": un morceau de fichier (un fichier peut être découpé en plusieurs morceaux de taille donnée).",[239,15374,15375],{},[121,15376],{"alt":15377,"src":15378},"Le serveur initie la connection","https:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fpng\u002FZP1DJWCn38NtEOKvm5oWYweIOe740i49hEILM2JErFcvqvnZBeO6LAcqAcBZZTydVtw7sjXQpyazj8WCojnWmiwzmmQCfZszhel97BTvwjZHiqeJJbAvIL4AeCHKkGzyi0NyXRwmUcHekwNODndSSCMuLCfCS-b6FlAfWuxYey2g-nsaQThJp-KTFUaeGW6LCgiSKLibxbItTRTBmpFnMFBCnbAB4W_upIx0L62urBp_szkw-3wlYvrhgUHZryz_Ydvd7JGu5t2lZ0Cqz9o-0000",[12,15380,15381],{},"Lorsqu'une sauvegarde doit démarrer, le serveur contacte le périphérique et initie la sauvegarde. Le périphérique\nenvoie ensuite les différents fichiers au serveur qui peut alors demander au périphérique les morceaux de fichiers\nqui lui manquent.",[12,15383,15384],{},"Voici des exemples de logiciels de sauvegarde où le périphérique initie la connexion:",[506,15386,15387,15394],{},[370,15388,15389,15393],{},[49,15390,1013],{"href":15391,"rel":15392},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002F",[347]," - basé sur un rsync modifié pour gérer la partie pool",[370,15395,15396,15401],{},[49,15397,15400],{"href":15398,"rel":15399},"https:\u002F\u002Fwww.urbackup.org\u002F",[347],"UrBackup"," - basé sur son propre client de sauvegarde à installer sur le périphérique",[12,15403,15404],{},"Lorsque le serveur initie la connexion sur le périphérique:",[506,15406,15407,15410,15413],{},[370,15408,15409],{},"Le serveur décide quand faire la sauvegarde (en fonction de quand la dernière sauvegarde a été construite, des heures\nde travail, ...).",[370,15411,15412],{},"Le serveur a besoin de pouvoir accéder aux machines, mais les périphériques n'ont pas besoin d'avoir un accès direct\nau serveur de sauvegarde. Cela permet de ne pas compromettre le serveur de sauvegarde en cas de compromission d'un\npériphérique.",[370,15414,15415],{},"Le serveur doit détecter la présence du périphérique (si ce dernier n'est pas toujours sur le réseau) et donc faire\nun ping régulier, par exemple.",[12,15417,15418,15419,15421],{},"Il faudra prévoir un ",[184,15420,319],{}," pour vérifier la présence des machines sur le réseau. Le serveur doit rester éveillé\n(et donc être actif) et ne peut pas juste attendre l'arrivée d'un périphérique.",[12,15423,15424],{},"Il doit y avoir un serveur (le serveur ne peut pas être juste un espace de stockage, comme un S3). Le serveur décide à\npartir du nouveau manifeste du client quels sont les fichiers qui ont changé et demande au client de lui envoyer ceux\nqui lui manquent.",[12,15426,15427],{},"Le serveur décide d'organiser son pool de sauvegarde comme il le souhaite, ainsi que le nombre de sauvegardes en\nparallèle. Il peut bloquer les sauvegardes si le pool est plein.",[133,15429,15431],{"id":15430},"le-périphérique-initie-la-connection","Le périphérique initie la connection",[239,15433,15434],{},[121,15435],{"alt":15436,"src":15437},"Le périphérique initie la connexion","https:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fpng\u002FZP3DJGD138NlKuKfW0PoG94G1sv8q80ryvArD3yRZxrAIMmTsnWQ52c00ENYW-tt_3tlkx6QbnpkR4815JQeS0WlsweoJwEU77J_GA_G1RgZvoecdAM3CbDdNt1aJGY1eyd2XijgoAtDD3TNYXCFbuF4IQ3z5_VldHqzjjfDFPgqIicfS9K3klq3zhQjULlZn7f4GRKXIz0gqAlyfjDbPcNfhH8lY2Fcfy_shlWQ-6-KfxeqeOHEfMa4-vdIDVwWEJbNwxTPNDWxiLCVOzU0ca98-FdoFMhoaZy0",[12,15439,15440],{},"Voici quelques exemples de logiciels de sauvegarde qui peuvent être déclenchés depuis le périphérique:",[506,15442,15443,15451,15457],{},[370,15444,15445,15450],{},[49,15446,15449],{"href":15447,"rel":15448},"https:\u002F\u002Fborgbackup.readthedocs.io\u002Fen\u002Fstable\u002Findex.html",[347],"Borg"," (avec un lieu de stockage)",[370,15452,15453,15450],{},[49,15454,1007],{"href":15455,"rel":15456},"https:\u002F\u002Frestic.net\u002F",[347],[370,15458,15459,15464],{},[49,15460,15463],{"href":15461,"rel":15462},"https:\u002F\u002Fburp.grke.org\u002F",[347],"Burp"," (avec un serveur qui centralise les sauvegardes)",[12,15466,15467],{},"Le périphérique sait quand il est allumé et connecté au réseau. S'il y a une erreur de sauvegarde (par exemple, si le\nserveur de stockage n'est pas disponible), il peut réessayer plus tard. Il n'est donc pas nécessaire de planifier des\nsauvegardes sur le serveur, mais il est nécessaire d'avoir un plan de sauvegarde pour chaque périphérique.",[12,15469,15470],{},"Le périphérique doit être capable de se connecter au serveur, donc il est important d'ouvrir le flux de données du\npériphérique vers le serveur (par exemple, si le périphérique est un ordinateur hors réseau interne). Cela peut être\nrésolu en créant un VPN.",[12,15472,15473],{},"Lorsque la sauvegarde est initialisée, le serveur peut renvoyer des informations au périphérique pour indiquer qu'il\nrefuse la sauvegarde (pool plein, ...). Le périphérique devra alors réessayer plus tard.",[12,15475,15476],{},"Si un périphérique est compromis, toutes les sauvegardes de ce périphérique sont en danger (il existe des solutions\npour bloquer les sauvegardes en lecture\u002Fécriture). Si le pool de sauvegarde  est mutualisé entre plusieurs machines,\ncela peut compromettre l'ensemble du pool.",[12,15478,15479],{},"Le serveur reçoit un manifeste et une liste de chunks. Si nous souhaitons mutualiser le pool de sauvegarde, nous ne\npouvons pas laisser le client accéder à l'intégralité du pool. Nous devons donc disposer d'un serveur qui gère les\nchunks et vérifie que les fichiers reçus correspondent bien aux sauvegardes effectuées. Il est important de s'assurer\nque les clients n'envoient pas de chunks qui ne sont pas relatifs à une sauvegarde ou qui ont un sha incorrect pour\néviter que les fichiers soient mal rangés. Il faut aussi bloquer la modification des anciennes sauvegardes, et aussi\nconditionner la lecture de ces derniers.",[133,15481,15483],{"id":15482},"choix","Choix",[12,15485,15486],{},"Personnellement, j'ai une préférence pour l'initialisation de la sauvegarde par le serveur. Dans mon projet actuel,\nj'ai déjà prévu le code pour gérer la file d'attente, lancer rsync, ainsi que le planificateur. Il serait facile de\nremplacer ce code par celui de mon nouveau serveur.",[1886,15488,15489],{},[12,15490,15491],{},"Note de 2023: En avançant dans la réécriture, les dépendances ont été mises à jour et une grande partie du code a\nété modifié. Par conséquent, remplacer l'appel à rsync par un appel au nouveau système de sauvegarde n'a pas été\naussi simple que prévu.",[12,15493,15494],{},"Il y a plusieurs raisons pour lesquelles je penche vers le contrôle du déclenchement par le serveur :",[506,15496,15497,15500,15503],{},[370,15498,15499],{},"Le serveur sera le propriétaire du pool et des sauvegardes.",[370,15501,15502],{},"Le serveur décide du cycle de vie des sauvegardes (nombre de sauvegarde, durée de vie, ...)",[370,15504,15505],{},"Le serveur peut fusionner les sauvegardes de plusieurs périphériques dans le pool, donc il doit contrôler tout ce qui\ny entre.",[12,15507,15508],{},"Je tiens à souligner que la version finale sera terminée, tous les choix ici présent peuvent avoir été remis en\nquestion. En informatique, faire et défaire, c'est avancer.",[128,15510,15512],{"id":15511},"cinematique-dappel","Cinematique d'appel",[12,15514,15515],{},"Dans cet article, nous allons examiner la cinématique d'appel entre les requêtes et comment elle peut être utilisée\npour améliorer la sauvegarde de contenu en fonction de nos besoins.",[12,15517,15518],{},"Pour sauvegarder le contenu d'un périphérique, celui-ci doit suivre les étapes suivantes :",[506,15520,15521,15535],{},[370,15522,15523,15524],{},"Parcourir l'ensemble de ses fichiers et pour chaque fichier :\n",[506,15525,15526,15529,15532],{},[370,15527,15528],{},"Récupérer les attributs du fichier, tels que le nom, la date de création, de modification, les droits, etc.",[370,15530,15531],{},"Calculer un hash pour déterminer si le fichier est modifié et quelle partie du fichier a été modifiée.",[370,15533,15534],{},"Envoyer cette liste de fichiers (manifest) au serveur de sauvegarde.",[370,15536,15537],{},"Envoyer le contenu nouveau ou modifié des fichiers, chunk par chunk, vers le serveur.",[12,15539,15540],{},"Le processus de sauvegarde peut être optimisé de la manière suivante :",[506,15542,15543,15546],{},[370,15544,15545],{},"Le périphérique peut recevoir la dernière sauvegarde qu'il a effectuée et la comparer avec ce qu'il a de son côté\npour n'envoyer que le différentiel.",[370,15547,15548],{},"Il n'est pas nécessaire de calculer le hash du fichier entier s'il n'a jamais été sauvegardé. Ce hash peut être\ncalculé lors de la récupération des données, ce qui évite de lire deux fois le fichier.",[12,15550,15551],{},"Voici la séquence utilisée pour la sauvegade:",[239,15553,15554],{},[121,15555],{"alt":15556,"src":15557},"Sequence de sauvegarde","https:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fpng\u002FVT5DJiCm40NWlK_nsC6YGVnBNLH5Y1qB12SOsLCo4DkfxScPo3boCTIW_b6HJPJ5wEczpyjSA1NrZJahDNk6fy99o9XtJXqdp1Pu7VeaRRtvhfNdkAhmgANcK6Gbbeh4C75zNU4vT57W53Q6ma7X60t1SGeoV2T69ktuWv9ZdEUIcFp5L86R2Y-I2wFXZ9NOMZXbq6VKClJvqaSdAzdyPMtR97xeio5RfAF2VyACQM9iqKPDi3MjzI3H79zYDblWjzGSjxjLdE4fo8fpoI15tbnesW_Xu8nn_6-1_Svj-5s5f-XRIYGv1b37TkV5HrmdxKyu41LRa0dI-mflGs-r7VeqlkWupDOQllwtc_1vEFGPO_OW4nQZrPA3Kz_y0000",[367,15559,15560,15563,15570,15576],{},[370,15561,15562],{},"Le serveur doit commencer par s'authentifier auprès du client pour garantir que la communication se fait en toute\nsécurité. L'authentification peut se faire via un échange de clés publique\u002Fprivée, avec la connaissance d'un mot de\npasse commun associé au device. L'objectif est que le client puisse s'assurer qu'il communique avec le bon serveur.",[370,15564,15565,15566,15569],{},"Si une sauvegarde a déjà eu lieu par le passé, le serveur peut envoyer au client le contenu de la dernière\nsauvegarde. Cette liste comprend les fichiers avec leurs attributs et leur hash. Cette étape permet au client de\nsavoir quels fichiers ont été ajoutés, supprimés ou modifiés depuis la dernière sauvegarde.",[15567,15568],"br",{},"Par rapport à rsync, le serveur n'a pas besoin de recalculer cette liste.",[370,15571,15572,15573,15575],{},"Le client parcourt les différents dossiers demandés et génère une nouvelle liste. Il peut se contenter de n'envoyer\nque les fichiers qui ont changé depuis la dernière fois.",[15567,15574],{},"Le serveur stocke cela pour préparer la nouvelle sauvegarde. Il est possible d'utiliser un système de journal pour\nécrire les modifications retournées par le client. Ces modifications ne sont persistées dans le fichier final qu'une\nfois la sauvegarde terminée.",[370,15577,15578,15579,15581],{},"Le serveur utilise la liste reçue pour demander au client de lui renvoyer les fichiers modifiés. Si le client a\ncalculé le hash des chunks (morceaux de fichier), le serveur peut ne demander que les morceaux qui ont été modifiés\net pas l'intégralité du fichier.",[15567,15580],{},"Il est important de trouver une taille idéale pour les chunks. Si la taille est trop petite, cela risque de\nmultiplier la quantité de fichiers dans le pool sans aucun bénéfice. Si elle est trop grande, cela peut bloquer le\ntransfert intégral du fichier sur le serveur.",[12,15583,15584],{},"À l'avenir, il pourrait être envisageable d'avoir différents types de chunks pour déterminer ce qui doit être\nrenvoyé dans les transferts réseaux et ceux pour le stockage du pool. Cependant, cela complexifie le stockage.",[128,15586,15588],{"id":15587},"choix-du-protocol","Choix du protocol",[12,15590,15591],{},"Pour envoyer des informations en flux continu du périphérique au serveur et vice versa (un stream pour le manifeste,\nun autre pour les fichiers, un autre pour les logs), le choix de protocole est crucial. Voici quelques options que nous\nallons considérer pour notre shortlist :",[506,15593,15594,15600,15606],{},[370,15595,15596,15599],{},[184,15597,15598],{},"REST",": Protocole simple et facilement utilisable pour transférer des fichiers via upload ou download. Pour le\nstreaming, nous pouvons également envisager l'utilisation de Websocket.",[370,15601,15602,15605],{},[184,15603,15604],{},"gRPC",": Protocole permettant d'utiliser HTTP\u002F2 pour faire des requêtes avec du streaming bidirectionnel.",[370,15607,15608,15611],{},[184,15609,15610],{},"Protocole maison à base de socket",": Maîtrise complète du flux (comme rsync par exemple).",[12,15613,15614],{},"Comme vu précédement, les protocoles basés sur HTTP, comme REST et gRPC, offrent des avantages tels que l'utilisation\nd'un protocole de haut niveau permettant de bénéficier de TLS, de la compression, de la mutualisation de la connexion,\netc.",[12,15616,15617],{},"Le protocole maison, même s'il permettrait de bonnes performances, nécessite une plus grande complexité de\ndéveloppement, mais aussi un plus grand risque de bugs ou de failles de sécurité. Il est donc écarté de notre list\npour le moment, mais nous pourrions l'envisager dans une future refonte si les performances sont vraiment mauvaises.",[12,15619,15620],{},"Pour déterminer le protocole le plus performant pour nos besoins, nous allons comparer les protocoles REST s\u002F HTTP\u002F2 et\ngRPC.",[12,15622,15623],{},"Il est important de noter qu'il est crucial de choisir le langage ou le protocole le plus adapté à notre besoin et ne\npas partir sur des \"a priori\". Souvent, on choisit un langage ou un protocole parce qu'on le connaît bien ou parce\nqu'on l'estime meilleur dans certaines situations. Mais chaque besoin étant différent, l'utilisation dans un contexte\nne signifie pas qu'un langage ou un protocole est le meilleur dans tous les contextes.",[128,15625,15627],{"id":15626},"quel-langage-choisir","Quel langage choisir",[12,15629,15630],{},"Le client de sauvegarde doit être compatible avec Windows, Linux et MacOS.",[12,15632,15633],{},"Ma première approche pour développer un logiciel performant consiste à utiliser le language C++ pour le périphérique\net la partie synchronisation et gestion du pool pour serveur, tout en gardant Node.JS pour l'orchestration.",[12,15635,15636],{},"Comme une partie du serveur est développée en Node.JS, je vais également tester les performances dans ce langage. Si\nj'obtiens des performances similaires, le développement en Node.JS pourrait être plus simple (un seul language pour\ntout et moins de complexité à gérer pour le multithreading).",[12,15638,15639],{},"Si je décide de développer en C++, j'utiliserai Qt pour l'interface graphique, car je le connais bien.",[128,15641,15643],{"id":15642},"lécriture","L'écriture",[12,15645,15646,15648,15649,15652,15653,15656,15657,15659,15660,15662],{},[184,15647,15604],{}," est un framework moderne pour la création de services distants. Il est basé sur ",[184,15650,15651],{},"HTTP\u002F2",", qui offre des\naméliorations de performances significatives par rapport à ",[184,15654,15655],{},"HTTP\u002F1.1",". Pour le protocole ",[184,15658,15598],{},", il existe des\nlibrairies permettant de créer des serveurs et clients ",[184,15661,15651],{}," en C++, mais à mon grand désarroi c'est plus difficile\nque prévu.",[506,15664,15665,15673,15681],{},[370,15666,15667,15672],{},[49,15668,15671],{"href":15669,"rel":15670},"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcpprestsdk",[347],"cpprestsdk",": Support HTTPS mais pas HTTP\u002F2 (en 2023, il n'évoluera plus),",[370,15674,15675,15680],{},[49,15676,15679],{"href":15677,"rel":15678},"https:\u002F\u002Fnghttp2.org\u002F",[347],"nghttp2",": Librairie très bas niveau (trop bas niveau),",[370,15682,15683,15688],{},[49,15684,15687],{"href":15685,"rel":15686},"https:\u002F\u002Fnghttp2.org\u002Fdocumentation\u002Flibnghttp2_asio.html",[347],"libnghttp2_asio",": Librairie plus haut niveau, possède une\ndépendance sur boost. J'ai également l'impression que cette librairie ne permet pas de faire tout ce qu'on veut en\nHTTP\u002F2 (par exemple le streaming d'un fichier n'est pas quelque chose de facile à écrire, contrairement à l'allocation\ncomplète en mémoire de la réponse à envoyer).",[12,15690,15691,15692,15694],{},"Pour le protocole ",[184,15693,15604],{},", nous avons:",[506,15696,15697],{},[370,15698,15699,4429,15704,15706,15707,179],{},[49,15700,15703],{"href":15701,"rel":15702},"https:\u002F\u002Fgrpc.io\u002Fdocs\u002Flanguages\u002Fcpp\u002Fquickstart\u002F",[347],"grpccpp",[184,15705,15604],{}," est de basé basé sur ",[184,15708,15651],{},[12,15710,15711,15712,15715,15716,15719,15720,179],{},"Coté ",[184,15713,15714],{},"Node.JS",", j'utilise ",[184,15717,15718],{},"Nest.JS"," pour la partie serveur, je vais donc m'appuyer sur les librairies suivante pour le\nprotocol ",[184,15721,15604],{},[506,15723,15724,15730],{},[370,15725,15726],{},[49,15727,1776],{"href":15728,"rel":15729},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fgrpc",[347],[370,15731,15732],{},[49,15733,15736],{"href":15734,"rel":15735},"https:\u002F\u002Fdocs.nestjs.com\u002Fmicroservices\u002Fgrpc",[347],"@grpc\u002Fproto-loader",[12,15738,15739,15740,15742,15743,15746],{},"L'utilisation d'",[184,15741,15651],{}," avec Axios (utilisé par Nest.JS) peut se faire en utilisant ",[184,15744,15745],{},"http2-wrapper",":",[2105,15748,15752],{"className":15749,"code":15750,"language":15751,"meta":1646,"style":1646},"language-ts shiki shiki-themes one-dark-pro","import * as http2 from \"http2-wrapper\";\nimport { AxiosRequestConfig } from \"axios\";\n\nconst data = ProtoGetChunkRequest.encode(request).finish();\nreturn this.httpService\n  .request\u003CReadable>({\n    method: \"post\",\n    url: `https:\u002F\u002F${this.hostToBackup}:3000\u002Fget-chunk`,\n    data,\n    transport: http2,\n    responseType: \"stream\",\n  } as AxiosRequestConfig)\n  .pipe(map((response) => response.data));\n","ts",[184,15753,15754,15774,15793,15797,15826,15838,15853,15865,15894,15900,15912,15924,15936],{"__ignoreMap":1646},[2113,15755,15756,15759,15761,15763,15766,15769,15772],{"class":2115,"line":2116},[2113,15757,15758],{"class":2326},"import",[2113,15760,2671],{"class":2166},[2113,15762,11429],{"class":2326},[2113,15764,15765],{"class":2330}," http2",[2113,15767,15768],{"class":2326}," from",[2113,15770,15771],{"class":2149}," \"http2-wrapper\"",[2113,15773,2487],{"class":2119},[2113,15775,15776,15778,15780,15783,15785,15788,15791],{"class":2115,"line":1647},[2113,15777,15758],{"class":2326},[2113,15779,7616],{"class":2119},[2113,15781,15782],{"class":2330},"AxiosRequestConfig",[2113,15784,7840],{"class":2119},[2113,15786,15787],{"class":2326},"from",[2113,15789,15790],{"class":2149}," \"axios\"",[2113,15792,2487],{"class":2119},[2113,15794,15795],{"class":2115,"line":1652},[2113,15796,2125],{"emptyLinePlaceholder":1767},[2113,15798,15799,15801,15804,15806,15809,15811,15814,15816,15819,15821,15824],{"class":2115,"line":2185},[2113,15800,7785],{"class":2326},[2113,15802,15803],{"class":2414}," data",[2113,15805,2153],{"class":2334},[2113,15807,15808],{"class":2414}," ProtoGetChunkRequest",[2113,15810,179],{"class":2119},[2113,15812,15813],{"class":2133},"encode",[2113,15815,2423],{"class":2119},[2113,15817,15818],{"class":2330},"request",[2113,15820,3938],{"class":2119},[2113,15822,15823],{"class":2133},"finish",[2113,15825,3944],{"class":2119},[2113,15827,15828,15830,15833,15835],{"class":2115,"line":2230},[2113,15829,3205],{"class":2326},[2113,15831,15832],{"class":2414}," this",[2113,15834,179],{"class":2119},[2113,15836,15837],{"class":2330},"httpService\n",[2113,15839,15840,15843,15845,15847,15850],{"class":2115,"line":2235},[2113,15841,15842],{"class":2119},"  .",[2113,15844,15818],{"class":2133},[2113,15846,3109],{"class":2119},[2113,15848,15849],{"class":2414},"Readable",[2113,15851,15852],{"class":2119},">({\n",[2113,15854,15855,15858,15860,15863],{"class":2115,"line":2241},[2113,15856,15857],{"class":2330},"    method",[2113,15859,4429],{"class":2119},[2113,15861,15862],{"class":2149},"\"post\"",[2113,15864,4706],{"class":2119},[2113,15866,15867,15870,15872,15875,15878,15881,15883,15886,15889,15892],{"class":2115,"line":2246},[2113,15868,15869],{"class":2330},"    url",[2113,15871,4429],{"class":2119},[2113,15873,15874],{"class":2149},"`https:\u002F\u002F",[2113,15876,15877],{"class":2326},"${",[2113,15879,15880],{"class":2414},"this",[2113,15882,179],{"class":2119},[2113,15884,15885],{"class":2330},"hostToBackup",[2113,15887,15888],{"class":2326},"}",[2113,15890,15891],{"class":2149},":3000\u002Fget-chunk`",[2113,15893,4706],{"class":2119},[2113,15895,15896,15898],{"class":2115,"line":2464},[2113,15897,9822],{"class":2330},[2113,15899,4706],{"class":2119},[2113,15901,15902,15905,15907,15910],{"class":2115,"line":2085},[2113,15903,15904],{"class":2330},"    transport",[2113,15906,4429],{"class":2119},[2113,15908,15909],{"class":2330},"http2",[2113,15911,4706],{"class":2119},[2113,15913,15914,15917,15919,15922],{"class":2115,"line":2514},[2113,15915,15916],{"class":2330},"    responseType",[2113,15918,4429],{"class":2119},[2113,15920,15921],{"class":2149},"\"stream\"",[2113,15923,4706],{"class":2119},[2113,15925,15926,15929,15931,15934],{"class":2115,"line":2533},[2113,15927,15928],{"class":2119},"  } ",[2113,15930,6242],{"class":2326},[2113,15932,15933],{"class":2414}," AxiosRequestConfig",[2113,15935,4660],{"class":2119},[2113,15937,15938,15940,15943,15945,15947,15949,15952,15954,15956,15959,15961,15963],{"class":2115,"line":2556},[2113,15939,15842],{"class":2119},[2113,15941,15942],{"class":2133},"pipe",[2113,15944,2423],{"class":2119},[2113,15946,6644],{"class":2133},[2113,15948,8879],{"class":2119},[2113,15950,15951],{"class":2429},"response",[2113,15953,5709],{"class":2119},[2113,15955,8063],{"class":2326},[2113,15957,15958],{"class":2414}," response",[2113,15960,179],{"class":2119},[2113,15962,10154],{"class":2330},[2113,15964,5818],{"class":2119},[12,15966,15967,15968,15971],{},"Nous allons écrire un fichier de description ",[184,15969,15970],{},"protobuf"," (qui pourra être le même entre la version C++ et Node.JS). Le\nfichier va décrire chaque élément du fichier manifest, ainsi que les appels RPC entre le périphérique et le serveur.",[2105,15973,15976],{"className":15974,"code":15975,"language":15970,"meta":1646,"style":1646},"language-protobuf shiki shiki-themes one-dark-pro","syntax = \"proto3\";\n\npackage woodstock;\n\nenum StatusCode {\n  Ok = 0;\n  Failed = 1;\n}\n\nmessage FileManifest {\n  message FileManifestStat {\n    int32 ownerId = 1;\n    int32 groupId = 2;\n    int64 size = 3;\n    int64 lastRead = 4;\n    int64 lastModified = 5;\n    int64 created = 6;\n    int32 mode = 7;\n  }\n\n  bytes path = 1;\n  FileManifestStat stats = 2;\n  repeated bytes chunks = 3;\n  bytes sha256 = 4;\n}\n\nmessage FileManifestJournalEntry {\n  enum EntryType {\n    ADD = 0;\n    MODIFY = 1;\n    REMOVE = 2;\n  }\n\n  oneof entry {\n    FileManifest manifest = 1;\n    bytes path = 2;\n  }\n  EntryType type = 3;\n}\n\nmessage BackupConfiguration {\n    message Share {\n        string name = 1;\n        repeated string includes = 2;\n        repeated string excludes = 3;\n        string pathPrefix = 4;\n    }\n\n    message Task {\n        string command = 1;\n        repeated Share shares = 2;\n        repeated string includes = 3;\n        repeated string excludes = 4;\n    }\n\n    message Operations {\n        repeated Task tasks = 1;\n        repeated Task finalizedTasks = 2;\n    }\n\n    Operations operations = 1;\n}\n\nmessage FileChunk {\n  bytes data = 1;\n}\n\nmessage PrepareBackupRequest {\n  BackupConfiguration configuration = 1;\n  uint32 lastBackupNumber = 2;\n  uint32 newBackupNumber = 3;\n}\n\nmessage PrepareBackupReply {\n  StatusCode code = 1;\n  bool needRefreshCache = 2;\n}\n\nmessage RefreshCacheReply {\n  StatusCode code = 1;\n}\n\nmessage LaunchBackupRequest {\n  uint32 backupNumber = 1;\n}\n\nmessage GetChunkRequest {\n  bytes filename = 1;\n  uint64 position = 2;\n  uint64 size = 3;\n  bytes sha256 = 4;\n}\n\nservice WoodstockpériphériqueService {\n  rpc PrepareBackup(PrepareBackupRequest) returns (PrepareBackupReply) {}\n\n  rpc RefreshCache(stream FileManifest) returns (RefreshCacheReply) {}\n\n  rpc LaunchBackup(LaunchBackupRequest) returns (stream FileManifestJournalEntry) {}\n\n  rpc GetChunk(GetChunkRequest) returns (stream FileChunk) {}\n}\n",[184,15977,15978,15990,15994,16004,16008,16018,16029,16040,16044,16048,16058,16068,16082,16095,16109,16122,16136,16150,16163,16167,16171,16184,16197,16214,16226,16230,16234,16243,16253,16264,16275,16286,16290,16294,16304,16318,16331,16335,16349,16353,16357,16366,16376,16390,16407,16422,16435,16439,16443,16452,16465,16479,16493,16507,16511,16515,16524,16539,16554,16558,16562,16576,16580,16584,16593,16605,16609,16613,16622,16636,16650,16663,16667,16671,16680,16694,16708,16712,16716,16725,16737,16741,16745,16754,16767,16771,16775,16784,16797,16811,16823,16835,16839,16843,16854,16879,16883,16907,16911,16935,16939,16963],{"__ignoreMap":1646},[2113,15979,15980,15983,15985,15988],{"class":2115,"line":2116},[2113,15981,15982],{"class":2326},"syntax",[2113,15984,2153],{"class":2334},[2113,15986,15987],{"class":2149}," \"proto3\"",[2113,15989,2487],{"class":2119},[2113,15991,15992],{"class":2115,"line":1647},[2113,15993,2125],{"emptyLinePlaceholder":1767},[2113,15995,15996,15999,16002],{"class":2115,"line":1652},[2113,15997,15998],{"class":2326},"package",[2113,16000,16001],{"class":2149}," woodstock",[2113,16003,2487],{"class":2119},[2113,16005,16006],{"class":2115,"line":2185},[2113,16007,2125],{"emptyLinePlaceholder":1767},[2113,16009,16010,16013,16016],{"class":2115,"line":2230},[2113,16011,16012],{"class":2326},"enum",[2113,16014,16015],{"class":2414}," StatusCode",[2113,16017,2647],{"class":2119},[2113,16019,16020,16023,16025,16027],{"class":2115,"line":2235},[2113,16021,16022],{"class":2330},"  Ok",[2113,16024,2153],{"class":2334},[2113,16026,3208],{"class":2166},[2113,16028,2487],{"class":2119},[2113,16030,16031,16034,16036,16038],{"class":2115,"line":2241},[2113,16032,16033],{"class":2330},"  Failed",[2113,16035,2153],{"class":2334},[2113,16037,2850],{"class":2166},[2113,16039,2487],{"class":2119},[2113,16041,16042],{"class":2115,"line":2246},[2113,16043,2599],{"class":2119},[2113,16045,16046],{"class":2115,"line":2464},[2113,16047,2125],{"emptyLinePlaceholder":1767},[2113,16049,16050,16053,16056],{"class":2115,"line":2085},[2113,16051,16052],{"class":2326},"message",[2113,16054,16055],{"class":2414}," FileManifest",[2113,16057,2647],{"class":2119},[2113,16059,16060,16063,16066],{"class":2115,"line":2514},[2113,16061,16062],{"class":2326},"  message",[2113,16064,16065],{"class":2414}," FileManifestStat",[2113,16067,2647],{"class":2119},[2113,16069,16070,16073,16076,16078,16080],{"class":2115,"line":2533},[2113,16071,16072],{"class":2326},"    int32",[2113,16074,16075],{"class":2330}," ownerId",[2113,16077,2153],{"class":2334},[2113,16079,2850],{"class":2166},[2113,16081,2487],{"class":2119},[2113,16083,16084,16086,16089,16091,16093],{"class":2115,"line":2556},[2113,16085,16072],{"class":2326},[2113,16087,16088],{"class":2330}," groupId",[2113,16090,2153],{"class":2334},[2113,16092,2484],{"class":2166},[2113,16094,2487],{"class":2119},[2113,16096,16097,16100,16102,16104,16107],{"class":2115,"line":2569},[2113,16098,16099],{"class":2326},"    int64",[2113,16101,7924],{"class":2330},[2113,16103,2153],{"class":2334},[2113,16105,16106],{"class":2166}," 3",[2113,16108,2487],{"class":2119},[2113,16110,16111,16113,16116,16118,16120],{"class":2115,"line":2575},[2113,16112,16099],{"class":2326},[2113,16114,16115],{"class":2330}," lastRead",[2113,16117,2153],{"class":2334},[2113,16119,2894],{"class":2166},[2113,16121,2487],{"class":2119},[2113,16123,16124,16126,16129,16131,16134],{"class":2115,"line":2596},[2113,16125,16099],{"class":2326},[2113,16127,16128],{"class":2330}," lastModified",[2113,16130,2153],{"class":2334},[2113,16132,16133],{"class":2166}," 5",[2113,16135,2487],{"class":2119},[2113,16137,16138,16140,16143,16145,16148],{"class":2115,"line":3192},[2113,16139,16099],{"class":2326},[2113,16141,16142],{"class":2330}," created",[2113,16144,2153],{"class":2334},[2113,16146,16147],{"class":2166}," 6",[2113,16149,2487],{"class":2119},[2113,16151,16152,16154,16157,16159,16161],{"class":2115,"line":3213},[2113,16153,16072],{"class":2326},[2113,16155,16156],{"class":2330}," mode",[2113,16158,2153],{"class":2334},[2113,16160,6408],{"class":2166},[2113,16162,2487],{"class":2119},[2113,16164,16165],{"class":2115,"line":3236},[2113,16166,2572],{"class":2119},[2113,16168,16169],{"class":2115,"line":3248},[2113,16170,2125],{"emptyLinePlaceholder":1767},[2113,16172,16173,16176,16178,16180,16182],{"class":2115,"line":4899},[2113,16174,16175],{"class":2326},"  bytes",[2113,16177,6584],{"class":2330},[2113,16179,2153],{"class":2334},[2113,16181,2850],{"class":2166},[2113,16183,2487],{"class":2119},[2113,16185,16186,16189,16191,16193,16195],{"class":2115,"line":1777},[2113,16187,16188],{"class":2326},"  FileManifestStat",[2113,16190,9087],{"class":2330},[2113,16192,2153],{"class":2334},[2113,16194,2484],{"class":2166},[2113,16196,2487],{"class":2119},[2113,16198,16199,16202,16205,16208,16210,16212],{"class":2115,"line":4931},[2113,16200,16201],{"class":2326},"  repeated",[2113,16203,16204],{"class":2326}," bytes",[2113,16206,16207],{"class":2330}," chunks",[2113,16209,2153],{"class":2334},[2113,16211,16106],{"class":2166},[2113,16213,2487],{"class":2119},[2113,16215,16216,16218,16220,16222,16224],{"class":2115,"line":4961},[2113,16217,16175],{"class":2326},[2113,16219,10145],{"class":2330},[2113,16221,2153],{"class":2334},[2113,16223,2894],{"class":2166},[2113,16225,2487],{"class":2119},[2113,16227,16228],{"class":2115,"line":4976},[2113,16229,2599],{"class":2119},[2113,16231,16232],{"class":2115,"line":4993},[2113,16233,2125],{"emptyLinePlaceholder":1767},[2113,16235,16236,16238,16241],{"class":2115,"line":5013},[2113,16237,16052],{"class":2326},[2113,16239,16240],{"class":2414}," FileManifestJournalEntry",[2113,16242,2647],{"class":2119},[2113,16244,16245,16248,16251],{"class":2115,"line":5018},[2113,16246,16247],{"class":2326},"  enum",[2113,16249,16250],{"class":2414}," EntryType",[2113,16252,2647],{"class":2119},[2113,16254,16255,16258,16260,16262],{"class":2115,"line":5042},[2113,16256,16257],{"class":2330},"    ADD",[2113,16259,2153],{"class":2334},[2113,16261,3208],{"class":2166},[2113,16263,2487],{"class":2119},[2113,16265,16266,16269,16271,16273],{"class":2115,"line":5057},[2113,16267,16268],{"class":2330},"    MODIFY",[2113,16270,2153],{"class":2334},[2113,16272,2850],{"class":2166},[2113,16274,2487],{"class":2119},[2113,16276,16277,16280,16282,16284],{"class":2115,"line":5062},[2113,16278,16279],{"class":2330},"    REMOVE",[2113,16281,2153],{"class":2334},[2113,16283,2484],{"class":2166},[2113,16285,2487],{"class":2119},[2113,16287,16288],{"class":2115,"line":5098},[2113,16289,2572],{"class":2119},[2113,16291,16292],{"class":2115,"line":5117},[2113,16293,2125],{"emptyLinePlaceholder":1767},[2113,16295,16296,16299,16302],{"class":2115,"line":5142},[2113,16297,16298],{"class":2326},"  oneof",[2113,16300,16301],{"class":2330}," entry",[2113,16303,2647],{"class":2119},[2113,16305,16306,16309,16312,16314,16316],{"class":2115,"line":5148},[2113,16307,16308],{"class":2326},"    FileManifest",[2113,16310,16311],{"class":2330}," manifest",[2113,16313,2153],{"class":2334},[2113,16315,2850],{"class":2166},[2113,16317,2487],{"class":2119},[2113,16319,16320,16323,16325,16327,16329],{"class":2115,"line":5163},[2113,16321,16322],{"class":2326},"    bytes",[2113,16324,6584],{"class":2330},[2113,16326,2153],{"class":2334},[2113,16328,2484],{"class":2166},[2113,16330,2487],{"class":2119},[2113,16332,16333],{"class":2115,"line":5169},[2113,16334,2572],{"class":2119},[2113,16336,16337,16340,16343,16345,16347],{"class":2115,"line":5175},[2113,16338,16339],{"class":2326},"  EntryType",[2113,16341,16342],{"class":2330}," type",[2113,16344,2153],{"class":2334},[2113,16346,16106],{"class":2166},[2113,16348,2487],{"class":2119},[2113,16350,16351],{"class":2115,"line":5180},[2113,16352,2599],{"class":2119},[2113,16354,16355],{"class":2115,"line":5199},[2113,16356,2125],{"emptyLinePlaceholder":1767},[2113,16358,16359,16361,16364],{"class":2115,"line":5204},[2113,16360,16052],{"class":2326},[2113,16362,16363],{"class":2414}," BackupConfiguration",[2113,16365,2647],{"class":2119},[2113,16367,16368,16371,16374],{"class":2115,"line":5209},[2113,16369,16370],{"class":2326},"    message",[2113,16372,16373],{"class":2414}," Share",[2113,16375,2647],{"class":2119},[2113,16377,16378,16381,16384,16386,16388],{"class":2115,"line":5232},[2113,16379,16380],{"class":2326},"        string",[2113,16382,16383],{"class":2330}," name",[2113,16385,2153],{"class":2334},[2113,16387,2850],{"class":2166},[2113,16389,2487],{"class":2119},[2113,16391,16392,16395,16398,16401,16403,16405],{"class":2115,"line":5237},[2113,16393,16394],{"class":2326},"        repeated",[2113,16396,16397],{"class":2326}," string",[2113,16399,16400],{"class":2330}," includes",[2113,16402,2153],{"class":2334},[2113,16404,2484],{"class":2166},[2113,16406,2487],{"class":2119},[2113,16408,16409,16411,16413,16416,16418,16420],{"class":2115,"line":5242},[2113,16410,16394],{"class":2326},[2113,16412,16397],{"class":2326},[2113,16414,16415],{"class":2330}," excludes",[2113,16417,2153],{"class":2334},[2113,16419,16106],{"class":2166},[2113,16421,2487],{"class":2119},[2113,16423,16424,16426,16429,16431,16433],{"class":2115,"line":5267},[2113,16425,16380],{"class":2326},[2113,16427,16428],{"class":2330}," pathPrefix",[2113,16430,2153],{"class":2334},[2113,16432,2894],{"class":2166},[2113,16434,2487],{"class":2119},[2113,16436,16437],{"class":2115,"line":5282},[2113,16438,4665],{"class":2119},[2113,16440,16441],{"class":2115,"line":5295},[2113,16442,2125],{"emptyLinePlaceholder":1767},[2113,16444,16445,16447,16450],{"class":2115,"line":5310},[2113,16446,16370],{"class":2326},[2113,16448,16449],{"class":2414}," Task",[2113,16451,2647],{"class":2119},[2113,16453,16454,16456,16459,16461,16463],{"class":2115,"line":5315},[2113,16455,16380],{"class":2326},[2113,16457,16458],{"class":2330}," command",[2113,16460,2153],{"class":2334},[2113,16462,2850],{"class":2166},[2113,16464,2487],{"class":2119},[2113,16466,16467,16469,16471,16473,16475,16477],{"class":2115,"line":5320},[2113,16468,16394],{"class":2326},[2113,16470,16373],{"class":2326},[2113,16472,7077],{"class":2330},[2113,16474,2153],{"class":2334},[2113,16476,2484],{"class":2166},[2113,16478,2487],{"class":2119},[2113,16480,16481,16483,16485,16487,16489,16491],{"class":2115,"line":8625},[2113,16482,16394],{"class":2326},[2113,16484,16397],{"class":2326},[2113,16486,16400],{"class":2330},[2113,16488,2153],{"class":2334},[2113,16490,16106],{"class":2166},[2113,16492,2487],{"class":2119},[2113,16494,16495,16497,16499,16501,16503,16505],{"class":2115,"line":8631},[2113,16496,16394],{"class":2326},[2113,16498,16397],{"class":2326},[2113,16500,16415],{"class":2330},[2113,16502,2153],{"class":2334},[2113,16504,2894],{"class":2166},[2113,16506,2487],{"class":2119},[2113,16508,16509],{"class":2115,"line":8637},[2113,16510,4665],{"class":2119},[2113,16512,16513],{"class":2115,"line":8652},[2113,16514,2125],{"emptyLinePlaceholder":1767},[2113,16516,16517,16519,16522],{"class":2115,"line":8674},[2113,16518,16370],{"class":2326},[2113,16520,16521],{"class":2414}," Operations",[2113,16523,2647],{"class":2119},[2113,16525,16526,16528,16530,16533,16535,16537],{"class":2115,"line":8706},[2113,16527,16394],{"class":2326},[2113,16529,16449],{"class":2326},[2113,16531,16532],{"class":2330}," tasks",[2113,16534,2153],{"class":2334},[2113,16536,2850],{"class":2166},[2113,16538,2487],{"class":2119},[2113,16540,16541,16543,16545,16548,16550,16552],{"class":2115,"line":8724},[2113,16542,16394],{"class":2326},[2113,16544,16449],{"class":2326},[2113,16546,16547],{"class":2330}," finalizedTasks",[2113,16549,2153],{"class":2334},[2113,16551,2484],{"class":2166},[2113,16553,2487],{"class":2119},[2113,16555,16556],{"class":2115,"line":8729},[2113,16557,4665],{"class":2119},[2113,16559,16560],{"class":2115,"line":8734},[2113,16561,2125],{"emptyLinePlaceholder":1767},[2113,16563,16564,16567,16570,16572,16574],{"class":2115,"line":8740},[2113,16565,16566],{"class":2326},"    Operations",[2113,16568,16569],{"class":2330}," operations",[2113,16571,2153],{"class":2334},[2113,16573,2850],{"class":2166},[2113,16575,2487],{"class":2119},[2113,16577,16578],{"class":2115,"line":8772},[2113,16579,2599],{"class":2119},[2113,16581,16582],{"class":2115,"line":8778},[2113,16583,2125],{"emptyLinePlaceholder":1767},[2113,16585,16586,16588,16591],{"class":2115,"line":8789},[2113,16587,16052],{"class":2326},[2113,16589,16590],{"class":2414}," FileChunk",[2113,16592,2647],{"class":2119},[2113,16594,16595,16597,16599,16601,16603],{"class":2115,"line":8795},[2113,16596,16175],{"class":2326},[2113,16598,15803],{"class":2330},[2113,16600,2153],{"class":2334},[2113,16602,2850],{"class":2166},[2113,16604,2487],{"class":2119},[2113,16606,16607],{"class":2115,"line":8817},[2113,16608,2599],{"class":2119},[2113,16610,16611],{"class":2115,"line":8822},[2113,16612,2125],{"emptyLinePlaceholder":1767},[2113,16614,16615,16617,16620],{"class":2115,"line":8857},[2113,16616,16052],{"class":2326},[2113,16618,16619],{"class":2414}," PrepareBackupRequest",[2113,16621,2647],{"class":2119},[2113,16623,16624,16627,16630,16632,16634],{"class":2115,"line":8896},[2113,16625,16626],{"class":2326},"  BackupConfiguration",[2113,16628,16629],{"class":2330}," configuration",[2113,16631,2153],{"class":2334},[2113,16633,2850],{"class":2166},[2113,16635,2487],{"class":2119},[2113,16637,16638,16641,16644,16646,16648],{"class":2115,"line":8901},[2113,16639,16640],{"class":2326},"  uint32",[2113,16642,16643],{"class":2330}," lastBackupNumber",[2113,16645,2153],{"class":2334},[2113,16647,2484],{"class":2166},[2113,16649,2487],{"class":2119},[2113,16651,16652,16654,16657,16659,16661],{"class":2115,"line":8907},[2113,16653,16640],{"class":2326},[2113,16655,16656],{"class":2330}," newBackupNumber",[2113,16658,2153],{"class":2334},[2113,16660,16106],{"class":2166},[2113,16662,2487],{"class":2119},[2113,16664,16665],{"class":2115,"line":8913},[2113,16666,2599],{"class":2119},[2113,16668,16669],{"class":2115,"line":8931},[2113,16670,2125],{"emptyLinePlaceholder":1767},[2113,16672,16673,16675,16678],{"class":2115,"line":8936},[2113,16674,16052],{"class":2326},[2113,16676,16677],{"class":2414}," PrepareBackupReply",[2113,16679,2647],{"class":2119},[2113,16681,16682,16685,16688,16690,16692],{"class":2115,"line":8942},[2113,16683,16684],{"class":2326},"  StatusCode",[2113,16686,16687],{"class":2330}," code",[2113,16689,2153],{"class":2334},[2113,16691,2850],{"class":2166},[2113,16693,2487],{"class":2119},[2113,16695,16696,16699,16702,16704,16706],{"class":2115,"line":8950},[2113,16697,16698],{"class":2326},"  bool",[2113,16700,16701],{"class":2330}," needRefreshCache",[2113,16703,2153],{"class":2334},[2113,16705,2484],{"class":2166},[2113,16707,2487],{"class":2119},[2113,16709,16710],{"class":2115,"line":8968},[2113,16711,2599],{"class":2119},[2113,16713,16714],{"class":2115,"line":8983},[2113,16715,2125],{"emptyLinePlaceholder":1767},[2113,16717,16718,16720,16723],{"class":2115,"line":8988},[2113,16719,16052],{"class":2326},[2113,16721,16722],{"class":2414}," RefreshCacheReply",[2113,16724,2647],{"class":2119},[2113,16726,16727,16729,16731,16733,16735],{"class":2115,"line":9011},[2113,16728,16684],{"class":2326},[2113,16730,16687],{"class":2330},[2113,16732,2153],{"class":2334},[2113,16734,2850],{"class":2166},[2113,16736,2487],{"class":2119},[2113,16738,16739],{"class":2115,"line":9016},[2113,16740,2599],{"class":2119},[2113,16742,16743],{"class":2115,"line":9022},[2113,16744,2125],{"emptyLinePlaceholder":1767},[2113,16746,16747,16749,16752],{"class":2115,"line":9045},[2113,16748,16052],{"class":2326},[2113,16750,16751],{"class":2414}," LaunchBackupRequest",[2113,16753,2647],{"class":2119},[2113,16755,16756,16758,16761,16763,16765],{"class":2115,"line":9076},[2113,16757,16640],{"class":2326},[2113,16759,16760],{"class":2330}," backupNumber",[2113,16762,2153],{"class":2334},[2113,16764,2850],{"class":2166},[2113,16766,2487],{"class":2119},[2113,16768,16769],{"class":2115,"line":9082},[2113,16770,2599],{"class":2119},[2113,16772,16773],{"class":2115,"line":9105},[2113,16774,2125],{"emptyLinePlaceholder":1767},[2113,16776,16777,16779,16782],{"class":2115,"line":9138},[2113,16778,16052],{"class":2326},[2113,16780,16781],{"class":2414}," GetChunkRequest",[2113,16783,2647],{"class":2119},[2113,16785,16786,16788,16791,16793,16795],{"class":2115,"line":9149},[2113,16787,16175],{"class":2326},[2113,16789,16790],{"class":2330}," filename",[2113,16792,2153],{"class":2334},[2113,16794,2850],{"class":2166},[2113,16796,2487],{"class":2119},[2113,16798,16799,16802,16805,16807,16809],{"class":2115,"line":9157},[2113,16800,16801],{"class":2326},"  uint64",[2113,16803,16804],{"class":2330}," position",[2113,16806,2153],{"class":2334},[2113,16808,2484],{"class":2166},[2113,16810,2487],{"class":2119},[2113,16812,16813,16815,16817,16819,16821],{"class":2115,"line":9182},[2113,16814,16801],{"class":2326},[2113,16816,7924],{"class":2330},[2113,16818,2153],{"class":2334},[2113,16820,16106],{"class":2166},[2113,16822,2487],{"class":2119},[2113,16824,16825,16827,16829,16831,16833],{"class":2115,"line":9188},[2113,16826,16175],{"class":2326},[2113,16828,10145],{"class":2330},[2113,16830,2153],{"class":2334},[2113,16832,2894],{"class":2166},[2113,16834,2487],{"class":2119},[2113,16836,16837],{"class":2115,"line":9193},[2113,16838,2599],{"class":2119},[2113,16840,16841],{"class":2115,"line":9198},[2113,16842,2125],{"emptyLinePlaceholder":1767},[2113,16844,16845,16848,16851],{"class":2115,"line":9216},[2113,16846,16847],{"class":2326},"service",[2113,16849,16850],{"class":2414}," Woodstockp",[2113,16852,16853],{"class":2119},"ériphériqueService {\n",[2113,16855,16856,16859,16862,16864,16867,16869,16872,16874,16877],{"class":2115,"line":9238},[2113,16857,16858],{"class":2326},"  rpc",[2113,16860,16861],{"class":2133}," PrepareBackup",[2113,16863,2423],{"class":2119},[2113,16865,16866],{"class":2414},"PrepareBackupRequest",[2113,16868,5709],{"class":2119},[2113,16870,16871],{"class":2326},"returns",[2113,16873,2495],{"class":2119},[2113,16875,16876],{"class":2414},"PrepareBackupReply",[2113,16878,8980],{"class":2119},[2113,16880,16881],{"class":2115,"line":9243},[2113,16882,2125],{"emptyLinePlaceholder":1767},[2113,16884,16885,16887,16890,16892,16894,16896,16898,16900,16902,16905],{"class":2115,"line":10782},[2113,16886,16858],{"class":2326},[2113,16888,16889],{"class":2133}," RefreshCache",[2113,16891,2423],{"class":2119},[2113,16893,9025],{"class":2326},[2113,16895,16055],{"class":2414},[2113,16897,5709],{"class":2119},[2113,16899,16871],{"class":2326},[2113,16901,2495],{"class":2119},[2113,16903,16904],{"class":2414},"RefreshCacheReply",[2113,16906,8980],{"class":2119},[2113,16908,16909],{"class":2115,"line":10787},[2113,16910,2125],{"emptyLinePlaceholder":1767},[2113,16912,16913,16915,16918,16920,16923,16925,16927,16929,16931,16933],{"class":2115,"line":10813},[2113,16914,16858],{"class":2326},[2113,16916,16917],{"class":2133}," LaunchBackup",[2113,16919,2423],{"class":2119},[2113,16921,16922],{"class":2414},"LaunchBackupRequest",[2113,16924,5709],{"class":2119},[2113,16926,16871],{"class":2326},[2113,16928,2495],{"class":2119},[2113,16930,9025],{"class":2326},[2113,16932,16240],{"class":2414},[2113,16934,8980],{"class":2119},[2113,16936,16937],{"class":2115,"line":10834},[2113,16938,2125],{"emptyLinePlaceholder":1767},[2113,16940,16941,16943,16946,16948,16951,16953,16955,16957,16959,16961],{"class":2115,"line":10843},[2113,16942,16858],{"class":2326},[2113,16944,16945],{"class":2133}," GetChunk",[2113,16947,2423],{"class":2119},[2113,16949,16950],{"class":2414},"GetChunkRequest",[2113,16952,5709],{"class":2119},[2113,16954,16871],{"class":2326},[2113,16956,2495],{"class":2119},[2113,16958,9025],{"class":2326},[2113,16960,16590],{"class":2414},[2113,16962,8980],{"class":2119},[2113,16964,16965],{"class":2115,"line":10852},[2113,16966,2599],{"class":2119},[12,16968,16969,16970,16974],{},"Vient ensuite l'écriture de la partie périphérique et de la partie serveur. Je me base sur le\n",[49,16971,16973],{"href":15701,"rel":16972},[347],"Quick Start"," pour la partie C++.",[12,16976,16977,16978,16983],{},"L'exemple se veut simple, et surtout utilise l'API synchrone. gRPC propose également une\n",[49,16979,16982],{"href":16980,"rel":16981},"https:\u002F\u002Fgrpc.io\u002Fdocs\u002Flanguages\u002Fcpp\u002Fasync\u002F",[347],"API asynchrone"," mais qui est plus complexe à mettre en place.",[12,16985,16986],{},"L'API asynchrone devrait permettre d'améliorer les performances dans le cadre d'application multi-threadé. Pour notre\ncas de test nous allons commencer par utiliser l'API synchrone.",[12,16988,16989,16990,16994,16995,179],{},"Dans le cas de l'API synchrone, le serveur reste multi-threadé (et peut donc aussi recevoir plusieurs requêtes en même\ntemps). Vous trouverez les sources de la partie périphérique et de la partie serveur dans le ",[49,16991,16993],{"href":16992},"\u002FWoodstock\u002Fwoodstock-backup-feature_storage-db.zip","ZIP suivant"," dans le dossier ",[184,16996,16997],{},"périphérique-sync",[12,16999,17000,17001,17003],{},"Le développement de la partie ",[184,17002,15651],{}," vient ensuite. La librairie n'est pas des plus faciles d'utilisation, je me\nconcentre sur le développement à des fins de tests de perfs et non de sécurité (ou de non bug).",[128,17005,17007],{"id":17006},"benchmark","Benchmark",[12,17009,17010,17011,17013,17014,17019],{},"Un bench d'envoi de fichier via ",[184,17012,15604],{}," a déjà été fait par d'autres personnes. L'article se trouve ici :\n",[49,17015,17018],{"href":17016,"rel":17017},"https:\u002F\u002Fops.tips\u002Fblog\u002Fsending-files-via-grpc\u002F",[347],"Sending files via gRPC",". Je vous laisse le lire.\nCet article a été fait en Go. Je suppose que le résultat devrait être très proche d'un résultat en C++.",[12,17021,17022,17023,17025,17026,17028],{},"Le résultat de cet article est que du pur ",[184,17024,15651],{}," en Go est tout de même plus performant que ",[184,17027,15604],{}," (probablement du\nà la sérialisation et à la déserialisation des messages qui prend un peu plus de temps).",[12,17030,17031],{},"Pour réaliser les tests, nous avons utilisé le scénario suivant :",[506,17033,17034,17037,17040,17043,17046],{},[370,17035,17036],{},"Sauvegarde d'environ 46 Go de données à partir d'un périphérique équipé d'un SSD ;",[370,17038,17039],{},"Le PC de destination est équipé d'un disque dur connecté en USB 3 ;",[370,17041,17042],{},"Les machines sont reliées par un tuyau de 1 Gb\u002Fs ;",[370,17044,17045],{},"Les chunks sont stockés au format du pool défini dans l'article\n[\u002Fpost\u002Fwoodstock_brtfs](Utilisation de Btrfs et son remplacement) ceci afin de representer au mieux notre cas\nd'utilisation.",[370,17047,17048],{},"Le hash est un SHA3_256, qui est plus performant qu'un SHA2_256.",[12,17050,17051],{},"Les tests ont été réalisés dans des conditions relativement réelles (calcul des hash, copie des fichiers dans un pool\nqui fait de la déduplication), ce qui prend en compte le transfert des fichiers mais aussi les calculs effectués autour.",[12,17053,17054],{},"Une fois le développement effectué, pour compiler la version compatible gRPC, il suffit de lancer la commande suivante :",[2105,17056,17058],{"className":2317,"code":17057,"language":2319,"meta":1646,"style":1646},"cd périphérique-sync\nmkdir build\ncd build\ncmake -DWITH_PROTOCOL_HTTP2=OFF -DWITH_PROTOCOL_GRPC=ON -DCMAKE_BUILD_TYPE=Release ..\u002F\nmake -j16\n\n# Lancement du périphérique\n.\u002Fsrc\u002Fpériphérique_daemon\u002Fpériphérique\n\n# Lancement du serveur\n.\u002Fsrc\u002Fserver\u002Fserver\n",[184,17059,17060,17068,17076,17082,17099,17107,17111,17116,17121,17125,17130],{"__ignoreMap":1646},[2113,17061,17062,17065],{"class":2115,"line":2116},[2113,17063,17064],{"class":2334},"cd",[2113,17066,17067],{"class":2149}," périphérique-sync\n",[2113,17069,17070,17073],{"class":2115,"line":1647},[2113,17071,17072],{"class":2133},"mkdir",[2113,17074,17075],{"class":2149}," build\n",[2113,17077,17078,17080],{"class":2115,"line":1652},[2113,17079,17064],{"class":2334},[2113,17081,17075],{"class":2149},[2113,17083,17084,17087,17090,17093,17096],{"class":2115,"line":2185},[2113,17085,17086],{"class":2133},"cmake",[2113,17088,17089],{"class":2166}," -DWITH_PROTOCOL_HTTP2=OFF",[2113,17091,17092],{"class":2166}," -DWITH_PROTOCOL_GRPC=ON",[2113,17094,17095],{"class":2166}," -DCMAKE_BUILD_TYPE=Release",[2113,17097,17098],{"class":2149}," ..\u002F\n",[2113,17100,17101,17104],{"class":2115,"line":2230},[2113,17102,17103],{"class":2133},"make",[2113,17105,17106],{"class":2166}," -j16\n",[2113,17108,17109],{"class":2115,"line":2235},[2113,17110,2125],{"emptyLinePlaceholder":1767},[2113,17112,17113],{"class":2115,"line":2241},[2113,17114,17115],{"class":2396},"# Lancement du périphérique\n",[2113,17117,17118],{"class":2115,"line":2246},[2113,17119,17120],{"class":2133},".\u002Fsrc\u002Fpériphérique_daemon\u002Fpériphérique\n",[2113,17122,17123],{"class":2115,"line":2464},[2113,17124,2125],{"emptyLinePlaceholder":1767},[2113,17126,17127],{"class":2115,"line":2085},[2113,17128,17129],{"class":2396},"# Lancement du serveur\n",[2113,17131,17132],{"class":2115,"line":2514},[2113,17133,17134],{"class":2133},".\u002Fsrc\u002Fserver\u002Fserver\n",[12,17136,17137],{},"Pour la partie HTTP\u002F2, il faudra inverser les valeurs ON et OFF. Il est important de noter que la configuration du\nserveur est actuellement en dur et liée à la sauvegarde de l'ordinateur. Par conséquent, si vous souhaiter lancer\nle bench chez vous, cette configuration doit être modifiée pour adapter le serveur à votre propre configuration.",[12,17139,17140],{},"Une fois les tests effectués, j'ai réécris uniquement la partie serveur en NodeJS. La réécriture en Node.JS a été plus\nrapide que le développement en C++, car je savais exactement où nous allions et que le Javascript est plus simple à\nécrire (quand on utilise des frameworks comme RxJs, ...).",[12,17142,17143],{},"Voici le résultat du benchmark:",[22,17145,17146,17161],{},[25,17147,17148],{},[28,17149,17150,17153,17157,17159],{},[31,17151],{"align":17152},"left",[31,17154,17156],{"align":17155},"right","rSync",[31,17158,15604],{"align":17155},[31,17160,15651],{"align":17155},[41,17162,17163,17177,17190],{},[28,17164,17165,17168,17171,17174],{},[46,17166,17167],{"align":17152},"Server C++ \u002F Périphérique C++",[46,17169,17170],{"align":17155},"47m",[46,17172,17173],{"align":17155},"3h01",[46,17175,17176],{"align":17155},"2h25",[28,17178,17179,17182,17184,17187],{},[46,17180,17181],{"align":17152},"Server Node.JS \u002F Périphérique C++ (concatMap)",[46,17183],{"align":17155},[46,17185,17186],{"align":17155},"4h57",[46,17188,17189],{"align":17155},"1h50",[28,17191,17192,17195,17197,17200],{},[46,17193,17194],{"align":17152},"Server Node.JS \u002F Périphérique C++ (mergeMap)",[46,17196],{"align":17155},[46,17198,17199],{"align":17155},"60m",[46,17201],{"align":17155},[12,17203,17204,17205,17207,17208,17210,17211,17213],{},"Je n'ai pas testé tous les cas de figure, j'ai principalement fait varier la partie serveur. Et je me suis concentré sur\n",[184,17206,15604],{}," car je voulais comprendre pourquoi ",[184,17209,15651],{}," était beaucoup plus rapide que ",[184,17212,15604],{}," en Node.JS.",[12,17215,17216],{},"Ce que le l'on peut remarquer:",[506,17218,17219,17233,17241,17244],{},[370,17220,17221,17222],{},"En C++ pure:\n",[506,17223,17224,17227,17230],{},[370,17225,17226],{},"rsync est le plus rapide",[370,17228,17229],{},"HTTP\u002F2 est légèrement plus rapide que gRPC",[370,17231,17232],{},"La lenteur est problablement due au fait qu'on soit monothreadé",[370,17234,17235,17236],{},"Sur une écriture monothreadé coté serveur:\n",[506,17237,17238],{},[370,17239,17240],{},"C++ est plus rapide que Node.JS pour gRPC mais pas pour HTTP\u002F2",[370,17242,17243],{},"La parallélisation avec NodeJS est beaucoup plus simple qu'en C++ : il est important de noter que l'amélioration\nvisible en NodeJS vient du fait qu'à partir du moment où l'on ne fait que des I\u002FO en asynchrone, le thread JavaScript\neffectue peu de traitement, ce qui permet d'avoir des traitements asynchrones multi-threadés.",[370,17245,17246],{},"Le transfert est nettement plus lent que rSync (même en HTTP\u002F2)",[12,17248,17249],{},"La grande question est donc : dans quel langage doit-on écrire le périphérique et le serveur au vu de ces résultats ?\nL'écriture en C++ sera plus performante avec l'utilisation de threads. Cependant, le code en C++ sera plus complexe\n(surtout avec le multi-threading) et plus long à écrire, avec des risques de fuites mémoires et de segmentation fault.",[12,17251,17252],{},"L'écriture en JavaScript sera moins performante, mais la parallélisation sera plus facile, et le risque de fuite sera\nmoins élevé. J'ai par contre peur que la consommation mémoire des objets JavaScript soit plus importante que celle des\nobjets en C++.",[1886,17254,17255],{},[12,17256,17257],{},"Note de 2023: Le constat de la consommation mémoire des objets en Node.JS par rapport à C++ est vérifier. Je ferai un\narticle dédié sur le sujet.",[128,17259,17261],{"id":17260},"amélioration-possible-du-bench","Amélioration possible du bench",[506,17263,17264,17274,17282],{},[370,17265,17266,17267,17270,17271,17273],{},"Coté C++: J'ai testé ",[184,17268,17269],{},"RxCpp"," pour ajouter plus d'asynchronisme et traiter les objets en mode flux. Malheureusement,\ncontrairement à Node.JS, la librairie ",[184,17272,17269],{}," est mono-threadé. Du coup cela ne change rien. L'utilisation de RxCpp\nfait aussi des noeuds au cerveau à cause de l'utilisation des templates.",[370,17275,17276,17277,17281],{},"Coté C++: utiliser l'",[49,17278,17280],{"href":16980,"rel":17279},[347],"API Asynchrone"," de gRPC pourrait améliorer les choses.",[370,17283,17284],{},"Coté Node.JS: Il est possible d'améliorer les performances avec des bindings sur des librairies C++.",[128,17286,17288],{"id":17287},"pourquoi-nodejs-pourquoi-pas-nodejs","Pourquoi NodeJS \u002F Pourquoi pas NodeJS",[12,17290,17291],{},"On m'a toujours dit, et je considère que c'est une bonne pratique, d'avancer vite pour avoir une première version et\nensuite d'optimiser ce qui doit l'être (et uniquement ce qui doit l'être).",[12,17293,17294],{},"Clairement écrire en Javascript sera plus rapide que l'écriture en C++. Je pense donc commencer par un MVP en utilisant\nNodeJS. Et ensuite, si le besoin s'en fait sentir, je viendrai réécrire des parties en C++ soit via des bindings, soit\nvia des binaires dédiés.",[12,17296,17297],{},"Je vois déjà les anti NodeJS qui vont me dire que faire un logiciel de sauvegarde avec NodeJS c'est pas terrible. Que\nNodeJS est un language pourris :) ou que c'est un language pour faire des sites Internet, ou que cela utilise un\ninterpreteur.",[12,17299,17300],{},"Mais quand on pense que:",[506,17302,17303,17311],{},[370,17304,17305,17307,17308,17310],{},[184,17306,1013],{}," est fait en ",[184,17309,1063],{}," (avec une partie faite en C depuis la version 4)",[370,17312,17313,17307,17315],{},[184,17314,15449],{},[184,17316,17317],{},"Python",[12,17319,17320],{},"Bref des logiciels de sauvegardes faits avec un language intreprété, il y en a.",[12,17322,17323],{},"Node.JS est basé sur le moteur V8 de Google dont les performances augmentent à chaque version. Donc la réponse pourrait\nsimplement être: pourquoi pas !\nLe plus important c'est de faire un logiciel fiable et qui fonctionne. Donc que l'on utiliser NodeJS, PHP ou autre pour\nfaire le développement, au final : OSEF.",[12,17325,17326],{},"Je terminerai donc sur cette note finale. Je vais finaliser quelques tests et me lancer dans le développement.",[1886,17328,17329,17332,17335],{},[12,17330,17331],{},"Note de 2023 : Finalement, j'ai donc fait le développement en NodeJS. Les performances ont été grandement améliorées\ndepuis le bench. Par contre la consomation en mémoire des objets NodeJS éclate les scores les plus pessimistes\nque j'avais :).",[12,17333,17334],{},"Une fois la première version sortie, je réécrirai certaines parties dans des modules natifs pour améliorer les\nperformances. Voir le client complètement.",[12,17336,17337],{},"Par contre au lieu de choisir C++, je pense partir sur Rust que j'ai commencé à utiliser très récemment. Je ferai un\narticle sur le bench que j'ai pu faire sur la consommation mémoire de nodejs vs rust sur le sujet.",[3358,17339,17340],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}",{"title":1646,"searchDepth":1647,"depth":1647,"links":17342},[17343,17348,17349,17350,17351,17352,17353,17354],{"id":15300,"depth":1647,"text":15301,"children":17344},[17345,17346,17347],{"id":15354,"depth":1652,"text":15355},{"id":15430,"depth":1652,"text":15431},{"id":15482,"depth":1652,"text":15483},{"id":15511,"depth":1647,"text":15512},{"id":15587,"depth":1647,"text":15588},{"id":15626,"depth":1647,"text":15627},{"id":15642,"depth":1647,"text":15643},{"id":17006,"depth":1647,"text":17007},{"id":17260,"depth":1647,"text":17261},{"id":17287,"depth":1647,"text":17288},"2023-04-07",{"type":9,"value":17357},[17358,17363,17371],[12,17359,17360],{},[121,17361],{"alt":15252,"src":15259,"className":17362,"width":15261},[126],[1886,17364,17365,17367,17369],{},[12,17366,15266],{},[12,17368,15269],{},[12,17370,15272],{},[12,17372,15275,17373,15279],{},[49,17374,15278],{"href":65},{},{"title":15252,"description":1646},"woodstock_protocol_language_sauvegarde","posts\u002FWoodstock\u002F2021-04-18_woodstock_protocol_language_sauvegarde",[1683,1773,1774,3510,3511],"Ysf2Tbm729RouGvBsMuyaZAuMY3uaQkdpCWyqg6fk7U",{"id":17382,"title":17383,"author":7,"body":17384,"category":2057,"categorySlug":2058,"date":23273,"description":17388,"excerpt":23274,"extension":1764,"location":1765,"meta":23299,"navigation":1767,"path":23300,"published":1767,"seo":23301,"slug":23302,"stem":23303,"tags":23304,"timeToRead":3236,"__hash__":23309},"posts\u002Fposts\u002FProgrammation\u002F2021-09-27_achat_velo.md","Du souhait d'achat d'un vélo ...",{"type":9,"value":17385,"toc":23257},[17386,17389,17396,17408,17411,17418,17421,17424,17428,17431,17442,17445,17452,17455,17463,17466,17477,17482,17489,17497,17505,17581,17584,17916,17919,17922,17929,17932,17959,17962,17965,17984,17990,18001,18004,18008,18015,18018,18025,18032,18035,18046,18049,18144,18147,18151,18154,18200,18203,18496,18499,18503,18506,18509,18518,18520,19149,19152,19158,19635,19638,19645,19649,19657,19665,20089,20092,20108,20111,20115,20118,20121,20124,20127,20130,20133,20136,20140,20143,20146,20149,20152,20155,20526,20529,20947,20950,20953,20971,21833,21836,21843,21847,21850,21861,22157,22160,22163,22690,22693,22697,22700,22703,22710,22767,22774,22781,23119,23122,23179,23183,23186,23189,23196,23200,23203,23206,23209,23212,23215,23218,23254],[12,17387,17388],{},"Que penseriez-vous si je vous racontais un peu mes vacances ? Attendez ... attendez ... ne partez pas ... l'histoire est intéressante,\net surtout nous allons parler informatique.",[12,17390,17391,17392,17395],{},"Début Août j'ai décidé de m'acheter un nouveau vélo (un VTC à assistance électrique). Le choix du vélo importe peu, mais du fait d'une pénurie\nde matière première et d'une forte demande en vélo depuis le début de la crise ",[1892,17393,17394],{},"de mes"," sanitaire, tous les vélos sont en rupture de stock.",[12,17397,17398,17399],{},"J'ai fait le choix personnel de me rendre dans une enseigne connue dont le nom est aussi une discipline de l'athlétisme pour acheter ce VTC.",[17400,17401,17402],"sup",{},[49,17403,3686],{"href":17404,"ariaDescribedBy":17405,"dataFootnoteRef":1646,"id":17407},"#user-content-fn-1",[17406],"footnote-label","user-content-fnref-1",[12,17409,17410],{},"Et là c'est le drame.",[12,17412,17413,17414,17417],{},"Si vous regardez les différents vélos de la marque (et en fonction de la taille du cadre qui vous correspond) vous tombez sur le message: ",[362,17415,17416],{},"En\nrupture de stock",". (Bon. À aujourd'hui, nous avons un peu plus de stock sur le site)",[12,17419,17420],{},"Arf. Moi qui quand j'ai décidé quelque chose, je deviens impatient ...",[12,17422,17423],{},"Alors il est vrai que j'aurais pu aller voir sur un autre site, aller dans une autre boutique pour choisir un autre modèle, mais ce n'est pas\nce que j'ai fait.",[128,17425,17427],{"id":17426},"lattente","L'attente",[12,17429,17430],{},"J'ai donc commencé par attendre, en allant régulièrement sur le site ou en rafraîchissant l'onglet d'un navigateur. C'est long, la page est\nrelativement lourde pour un site WEB: plus de 2.2M de fichier à transférer pour un total de 175 requêtes et ce malgrès la présence d'un adblock.",[12,17432,17433,17434,17437,17438,17441],{},"De plus je dois sélectionner manuellement la taille dans une select box pour voir : ",[362,17435,17436],{},"Rupture de stock sur cette taille",". Là je clique sur\nle bouton ",[362,17439,17440],{},"Vérifier le stock en magasin",", ... je rentre mon code postal, ... et je regarde sur l'ensemble des magasins de ma région...",[12,17443,17444],{},"Bref, je prépare mes valises, et je pars me mettre au vert. Je fais un peu de marche avec les enfants (C'est les vacances quoi). Et je ne\npeux donc pas m'amuser à rafraîchir en continu (même si je le fais depuis mon téléphone portable).",[12,17446,17447,17448,17451],{},"Bien sûr j'ai cliqué sur le bouton ",[362,17449,17450],{},"M'avertir lorsque le produit est à nouveau disponible"," mais je me demande si ce bouton fonctionne, je n'ai\nà aujourd'hui toujours pas recu de mail 😄.",[12,17453,17454],{},"Je décide donc de passer à l'étape suivante.",[128,17456,17458,17459,17462],{"id":17457},"la-1er-version","La 1",[17400,17460,17461],{},"er"," version",[12,17464,17465],{},"Je me pose donc et me demande comment je peux mettre mes compétences à contribution pour m'aider moi-même à m'acheter pour moi mon propre vélo. Je décide donc de mon propre chef de me développer une application sur mon compte AWS que je viens de me créer. Ce programme devra\nme permettre de faire plusieurs choses:",[367,17467,17468,17471,17474],{},[370,17469,17470],{},"En un coup d'oeil visualiser le stock pour le vélo sur Internet et dans les différents magasins",[370,17472,17473],{},"M'avertir si le stock change",[370,17475,17476],{},"Et le plus important, ne pas me prendre la tête et développer l'application dans le moins de temps possible",[12,17478,17458,17479,17481],{},[17400,17480,17461],{}," version que je développe a pour but de visualiser le stock.",[12,17483,17484],{},[121,17485],{"alt":17486,"className":17487,"src":17488},"Selecteur JS",[126],"\u002FProgrammation\u002Fachat_velo\u002Ffirst_version.png",[12,17490,17491,17492,17496],{},"Le but de l'application est de récupérer l'état des stocks du site Internet. Si je peux le faire avec un navigateur internet sur le site,\nalors un programme peut le faire aussi. Je prends les outils dont j'ai l'habitude pour faire le travail: Node.JS et le framework\n",[49,17493,15718],{"href":17494,"rel":17495},"https:\u002F\u002Fnestjs.com\u002F",[347]," - framework que j'apprécie pour sa simplicité d'utilisation.",[12,17498,17499,17500,15746],{},"Pour parser la page, j'utilise ",[49,17501,17504],{"href":17502,"rel":17503},"https:\u002F\u002Fgithub.com\u002Fjsdom\u002Fjsdom",[347],"JSDom",[2105,17506,17508],{"className":15749,"code":17507,"language":15751,"meta":1646,"style":1646},"const dom = await JSDOM.fromURL(VTC_540E_URL, options);\n\nconsole.log(dom.window.document.querySelector(\"#product-size-selection\"));\n",[184,17509,17510,17542,17546],{"__ignoreMap":1646},[2113,17511,17512,17514,17517,17519,17522,17525,17527,17530,17532,17535,17537,17540],{"class":2115,"line":2116},[2113,17513,7785],{"class":2326},[2113,17515,17516],{"class":2414}," dom",[2113,17518,2153],{"class":2334},[2113,17520,17521],{"class":2326}," await",[2113,17523,17524],{"class":2414}," JSDOM",[2113,17526,179],{"class":2119},[2113,17528,17529],{"class":2133},"fromURL",[2113,17531,2423],{"class":2119},[2113,17533,17534],{"class":2414},"VTC_540E_URL",[2113,17536,932],{"class":2119},[2113,17538,17539],{"class":2330},"options",[2113,17541,2553],{"class":2119},[2113,17543,17544],{"class":2115,"line":1647},[2113,17545,2125],{"emptyLinePlaceholder":1767},[2113,17547,17548,17550,17552,17554,17556,17559,17561,17564,17566,17569,17571,17574,17576,17579],{"class":2115,"line":1652},[2113,17549,8743],{"class":2414},[2113,17551,179],{"class":2119},[2113,17553,8748],{"class":2133},[2113,17555,2423],{"class":2119},[2113,17557,17558],{"class":2414},"dom",[2113,17560,179],{"class":2119},[2113,17562,17563],{"class":2414},"window",[2113,17565,179],{"class":2119},[2113,17567,17568],{"class":2414},"document",[2113,17570,179],{"class":2119},[2113,17572,17573],{"class":2133},"querySelector",[2113,17575,2423],{"class":2119},[2113,17577,17578],{"class":2149},"\"#product-size-selection\"",[2113,17580,5818],{"class":2119},[12,17582,17583],{},"Première erreur quand je lance le programme:",[2105,17585,17589],{"className":17586,"code":17587,"language":17588,"meta":1646,"style":1646},"language-sh shiki shiki-themes one-dark-pro","(node:15246) UnhandledPromiseRejectionWarning: Error: Parse Error: Header overflow\n    at TLSSocket.socketOnData (_http_client.js:476:22)\n    at TLSSocket.emit (events.js:311:20)\n    at addChunk (_stream_readable.js:294:12)\n    at readableAddChunk (_stream_readable.js:275:11)\n    at TLSSocket.Readable.push (_stream_readable.js:209:10)\n    at TLSWrap.onStreamRead (internal\u002Fstream_base_commons.js:186:23)\n(node:15246) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https:\u002F\u002Fnodejs.org\u002Fapi\u002Fcli.html#cli_unhandled_rejections_mode). (rejection id: 1)\n(node:15246) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.\n","sh",[184,17590,17591,17617,17628,17638,17648,17658,17668,17678,17845],{"__ignoreMap":1646},[2113,17592,17593,17595,17598,17600,17603,17606,17609,17611,17614],{"class":2115,"line":2116},[2113,17594,2423],{"class":2119},[2113,17596,17597],{"class":2133},"node:15246",[2113,17599,5709],{"class":2119},[2113,17601,17602],{"class":2133},"UnhandledPromiseRejectionWarning:",[2113,17604,17605],{"class":2149}," Error:",[2113,17607,17608],{"class":2149}," Parse",[2113,17610,17605],{"class":2149},[2113,17612,17613],{"class":2149}," Header",[2113,17615,17616],{"class":2149}," overflow\n",[2113,17618,17619,17622,17625],{"class":2115,"line":1647},[2113,17620,17621],{"class":2133},"    at",[2113,17623,17624],{"class":2149}," TLSSocket.socketOnData",[2113,17626,17627],{"class":2119}," (_http_client.js:476:22)\n",[2113,17629,17630,17632,17635],{"class":2115,"line":1652},[2113,17631,17621],{"class":2133},[2113,17633,17634],{"class":2149}," TLSSocket.emit",[2113,17636,17637],{"class":2119}," (events.js:311:20)\n",[2113,17639,17640,17642,17645],{"class":2115,"line":2185},[2113,17641,17621],{"class":2133},[2113,17643,17644],{"class":2149}," addChunk",[2113,17646,17647],{"class":2119}," (_stream_readable.js:294:12)\n",[2113,17649,17650,17652,17655],{"class":2115,"line":2230},[2113,17651,17621],{"class":2133},[2113,17653,17654],{"class":2149}," readableAddChunk",[2113,17656,17657],{"class":2119}," (_stream_readable.js:275:11)\n",[2113,17659,17660,17662,17665],{"class":2115,"line":2235},[2113,17661,17621],{"class":2133},[2113,17663,17664],{"class":2149}," TLSSocket.Readable.push",[2113,17666,17667],{"class":2119}," (_stream_readable.js:209:10)\n",[2113,17669,17670,17672,17675],{"class":2115,"line":2241},[2113,17671,17621],{"class":2133},[2113,17673,17674],{"class":2149}," TLSWrap.onStreamRead",[2113,17676,17677],{"class":2119}," (internal\u002Fstream_base_commons.js:186:23)\n",[2113,17679,17680,17682,17684,17686,17688,17691,17694,17697,17700,17703,17706,17709,17712,17715,17718,17720,17723,17726,17729,17732,17735,17738,17741,17744,17746,17749,17751,17753,17756,17759,17761,17764,17767,17770,17773,17775,17778,17781,17784,17787,17789,17792,17795,17797,17800,17803,17805,17808,17811,17814,17817,17819,17822,17824,17827,17830,17833,17835,17838,17841,17843],{"class":2115,"line":2246},[2113,17681,2423],{"class":2119},[2113,17683,17597],{"class":2133},[2113,17685,5709],{"class":2119},[2113,17687,17602],{"class":2133},[2113,17689,17690],{"class":2149}," Unhandled",[2113,17692,17693],{"class":2149}," promise",[2113,17695,17696],{"class":2149}," rejection.",[2113,17698,17699],{"class":2149}," This",[2113,17701,17702],{"class":2149}," error",[2113,17704,17705],{"class":2149}," originated",[2113,17707,17708],{"class":2149}," either",[2113,17710,17711],{"class":2149}," by",[2113,17713,17714],{"class":2149}," throwing",[2113,17716,17717],{"class":2149}," inside",[2113,17719,2281],{"class":2149},[2113,17721,17722],{"class":2149}," an",[2113,17724,17725],{"class":2149}," async",[2113,17727,17728],{"class":2149}," function",[2113,17730,17731],{"class":2149}," without",[2113,17733,17734],{"class":2149}," a",[2113,17736,17737],{"class":2149}," catch",[2113,17739,17740],{"class":2149}," block,",[2113,17742,17743],{"class":2149}," or",[2113,17745,17711],{"class":2149},[2113,17747,17748],{"class":2149}," rejecting",[2113,17750,17734],{"class":2149},[2113,17752,17693],{"class":2149},[2113,17754,17755],{"class":2149}," which",[2113,17757,17758],{"class":2149}," was",[2113,17760,2179],{"class":2149},[2113,17762,17763],{"class":2149}," handled",[2113,17765,17766],{"class":2149}," with",[2113,17768,17769],{"class":2149}," .catch",[2113,17771,17772],{"class":2119},"()",[2113,17774,179],{"class":2149},[2113,17776,17777],{"class":2149}," To",[2113,17779,17780],{"class":2149}," terminate",[2113,17782,17783],{"class":2149}," the",[2113,17785,17786],{"class":2149}," node",[2113,17788,8582],{"class":2149},[2113,17790,17791],{"class":2149}," on",[2113,17793,17794],{"class":2149}," unhandled",[2113,17796,17693],{"class":2149},[2113,17798,17799],{"class":2149}," rejection,",[2113,17801,17802],{"class":2149}," use",[2113,17804,17783],{"class":2149},[2113,17806,17807],{"class":2149}," CLI",[2113,17809,17810],{"class":2149}," flag",[2113,17812,17813],{"class":2149}," `",[2113,17815,17816],{"class":2330},"--unhandled-rejections",[2113,17818,2335],{"class":2334},[2113,17820,17821],{"class":2149},"strict`",[2113,17823,2495],{"class":2119},[2113,17825,17826],{"class":2133},"see",[2113,17828,17829],{"class":2149}," https:\u002F\u002Fnodejs.org\u002Fapi\u002Fcli.html#cli_unhandled_rejections_mode",[2113,17831,17832],{"class":2119},")",[2113,17834,179],{"class":2334},[2113,17836,17837],{"class":2119}," (rejection ",[2113,17839,17840],{"class":2149},"id:",[2113,17842,2850],{"class":2166},[2113,17844,4660],{"class":2119},[2113,17846,17847,17849,17851,17854,17857,17859,17861,17864,17867,17870,17873,17875,17878,17880,17882,17885,17887,17889,17891,17894,17896,17898,17901,17903,17905,17907,17910,17913],{"class":2115,"line":2464},[2113,17848,2423],{"class":2119},[2113,17850,17597],{"class":2133},[2113,17852,17853],{"class":2119},") [DEP0018] ",[2113,17855,17856],{"class":2133},"DeprecationWarning:",[2113,17858,17690],{"class":2149},[2113,17860,17693],{"class":2149},[2113,17862,17863],{"class":2149}," rejections",[2113,17865,17866],{"class":2149}," are",[2113,17868,17869],{"class":2149}," deprecated.",[2113,17871,17872],{"class":2149}," In",[2113,17874,17783],{"class":2149},[2113,17876,17877],{"class":2149}," future,",[2113,17879,17693],{"class":2149},[2113,17881,17863],{"class":2149},[2113,17883,17884],{"class":2149}," that",[2113,17886,17866],{"class":2149},[2113,17888,2179],{"class":2149},[2113,17890,17763],{"class":2149},[2113,17892,17893],{"class":2149}," will",[2113,17895,17780],{"class":2149},[2113,17897,17783],{"class":2149},[2113,17899,17900],{"class":2149}," Node.js",[2113,17902,8582],{"class":2149},[2113,17904,17766],{"class":2149},[2113,17906,17734],{"class":2149},[2113,17908,17909],{"class":2149}," non-zero",[2113,17911,17912],{"class":2149}," exit",[2113,17914,17915],{"class":2149}," code.\n",[12,17917,17918],{},"Pas de chance, les headers http de la grande enseigne contiennent tellement de blabla que nodejs limite et bloque. C'est l'effet CSP (Content-Security-Policy). Les CSP sont\nutilisés pour bloquer les requêtes faites par le navigateur sur une page si elle ne sont pas autorisées.",[12,17920,17921],{},"Par exemple, si un utisateur arrive à profiter d'une faille et arrive dans un avis client à mettre une iframe, une image, ... vers un site dont il possède le contrôle. La CSP\nva dire au navigateur que ce site n'est pas autorisé. Et le hack de l'utilisateur ne fonctionnera pas totalement.",[12,17923,17924,17925,17928],{},"Mais quand un site commence à avoir beaucoup de ",[362,17926,17927],{},"\"partenaire\""," (google, vimeo, cloudfront, facebook, gstatic.com, et tous les trucs bloqués par\nadblock ...), la liste prend de la place.",[12,17930,17931],{},"Je relance node avec l'option adéquate:",[2105,17933,17935],{"className":17586,"code":17934,"language":17588,"meta":1646,"style":1646},"node --max-http-header-size=80000 app.js\n\nHTMLSelectElement {}\n",[184,17936,17937,17947,17951],{"__ignoreMap":1646},[2113,17938,17939,17941,17944],{"class":2115,"line":2116},[2113,17940,2303],{"class":2133},[2113,17942,17943],{"class":2166}," --max-http-header-size=80000",[2113,17945,17946],{"class":2149}," app.js\n",[2113,17948,17949],{"class":2115,"line":1647},[2113,17950,2125],{"emptyLinePlaceholder":1767},[2113,17952,17953,17956],{"class":2115,"line":1652},[2113,17954,17955],{"class":2133},"HTMLSelectElement",[2113,17957,17958],{"class":2149}," {}\n",[12,17960,17961],{},"Cela commence bien.",[12,17963,17964],{},"Maintenant nous allons devoir ouvrir le sélecteur de taille, choisir la taille L (qui me correspond) et cliquer dessus. Je pourrai alors récupérer l'info de stock.",[12,17966,17967,17968,17973,17980,17981,179],{},"Malheureusement beaucoup de choses sur le site sont en JavaScript, ",[49,17969,17972],{"href":17970,"rel":17971},"https:\u002F\u002Fgithub.com\u002Fjsdom\u002Fjsdom\u002Fissues\u002F2162",[347],"JSDom ne permet pas d'executer le JS de la page",[17400,17974,17975],{},[49,17976,2547],{"href":17977,"ariaDescribedBy":17978,"dataFootnoteRef":1646,"id":17979},"#user-content-fn-2",[17406],"user-content-fnref-2"," et donc je suis bloqué. Déjà, pour un truc que je voulais faire rapidement, mauvais choix de solution ;). Je commence à me dire que je vais devoir sortir la grosse\nartillerie : ",[362,17982,17983],{},"Puppeteer",[12,17985,17986],{},[121,17987],{"alt":17486,"className":17988,"src":17989},[126],"\u002FProgrammation\u002Fachat_velo\u002Fselecteur_js.png",[12,17991,17992,17996,17997,18000],{},[49,17993,17983],{"href":17994,"rel":17995},"https:\u002F\u002Fgithub.com\u002Fpuppeteer\u002Fpuppeteer",[347]," est une librairie javascript qui permet de contrôler le navigateur chrome en mode headless (sans fenêtre). Cela me\npermettrait donc de parser la page en ",[1892,17998,17999],{},"simulant"," avec un navigateur chrome.",[12,18002,18003],{},"Mais attendez, s'il y a beaucoup de javascript, il y a peut-être des requêtes XHR ?",[128,18005,18007],{"id":18006},"xhr","XHR",[12,18009,18010,18011,18014],{},"XHR, c'est pour ",[184,18012,18013],{},"XMLHttpRequest",", ce sont des requêtes executées par le Javascript pour récupérer quelques informations.",[12,18016,18017],{},"Avec Chrome, on peut récupérer la liste des requêtes qui sont effectuées lors de l'affichage de la page mais également lors des différentes interactions.",[12,18019,18020],{},[121,18021],{"alt":18022,"className":18023,"src":18024},"Vue network de Chrome",[126],"\u002FProgrammation\u002Fachat_velo\u002Fchrome_network.png",[12,18026,18027,18028,18031],{},"On active alors le filtre ",[362,18029,18030],{},"Fetch\u002FXHR"," qu nous permet de voir les requêtes qui ne sont pas du CSS, ni du HTML, ni du JS, mais uniquement des requêtes qui sont lancées\npar le code Javascript.",[12,18033,18034],{},"Bien sûr il faut éliminer les requêtes vers d'autres sites (comme abtasty, ...). On peut alors retrouver la requête qui permet de retourner le stock du site Internet:",[2105,18036,18040],{"className":18037,"code":18038,"language":18039,"meta":1646,"style":1646},"language-http shiki shiki-themes one-dark-pro","GET \u002Ffr\u002Fajax\u002Fnfs\u002Fstocks\u002Fonline?skuIds=4216661,4216663,4216662,4216664\n","http",[184,18041,18042],{"__ignoreMap":1646},[2113,18043,18044],{"class":2115,"line":2116},[2113,18045,18038],{},[12,18047,18048],{},"Et avec la réponse:",[2105,18050,18054],{"className":18051,"code":18052,"language":18053,"meta":1646,"style":1646},"language-json shiki shiki-themes one-dark-pro","{\n  \"4216661\": {\n    \"stockOnline\": 0\n  },\n  \"4216662\": {\n    \"stockOnline\": 0\n  },\n  \"4216663\": {\n    \"stockOnline\": 0\n  },\n  \"4216664\": {\n    \"stockOnline\": 18\n  }\n}\n","json",[184,18055,18056,18061,18068,18078,18082,18089,18097,18101,18108,18116,18120,18127,18136,18140],{"__ignoreMap":1646},[2113,18057,18058],{"class":2115,"line":2116},[2113,18059,18060],{"class":2119},"{\n",[2113,18062,18063,18066],{"class":2115,"line":1647},[2113,18064,18065],{"class":2330},"  \"4216661\"",[2113,18067,8091],{"class":2119},[2113,18069,18070,18073,18075],{"class":2115,"line":1652},[2113,18071,18072],{"class":2330},"    \"stockOnline\"",[2113,18074,4429],{"class":2119},[2113,18076,18077],{"class":2166},"0\n",[2113,18079,18080],{"class":2115,"line":2185},[2113,18081,8490],{"class":2119},[2113,18083,18084,18087],{"class":2115,"line":2230},[2113,18085,18086],{"class":2330},"  \"4216662\"",[2113,18088,8091],{"class":2119},[2113,18090,18091,18093,18095],{"class":2115,"line":2235},[2113,18092,18072],{"class":2330},[2113,18094,4429],{"class":2119},[2113,18096,18077],{"class":2166},[2113,18098,18099],{"class":2115,"line":2241},[2113,18100,8490],{"class":2119},[2113,18102,18103,18106],{"class":2115,"line":2246},[2113,18104,18105],{"class":2330},"  \"4216663\"",[2113,18107,8091],{"class":2119},[2113,18109,18110,18112,18114],{"class":2115,"line":2464},[2113,18111,18072],{"class":2330},[2113,18113,4429],{"class":2119},[2113,18115,18077],{"class":2166},[2113,18117,18118],{"class":2115,"line":2085},[2113,18119,8490],{"class":2119},[2113,18121,18122,18125],{"class":2115,"line":2514},[2113,18123,18124],{"class":2330},"  \"4216664\"",[2113,18126,8091],{"class":2119},[2113,18128,18129,18131,18133],{"class":2115,"line":2533},[2113,18130,18072],{"class":2330},[2113,18132,4429],{"class":2119},[2113,18134,18135],{"class":2166},"18\n",[2113,18137,18138],{"class":2115,"line":2556},[2113,18139,2572],{"class":2119},[2113,18141,18142],{"class":2115,"line":2569},[2113,18143,2599],{"class":2119},[12,18145,18146],{},"La requête nous retourne les différents stocks pour les différentes tailles de cadre (S, M, L, XL). C'est la taille L qui m'intéresse, mais comment savoir quel\ncode produit correspond à quelle taille ?",[128,18148,18150],{"id":18149},"code-source-de-la-page","Code source de la page",[12,18152,18153],{},"On peut afficher le code source de la page (clic droit sur la page HTML : Afficher la source). Dans le source HTML on retrouve le bout de code suivant :",[2105,18155,18159],{"className":18156,"code":18157,"language":18158,"meta":1646,"style":1646},"language-html shiki shiki-themes one-dark-pro","\u003Cscript id=\"__dkt\" type=\"application\u002Fjson\">\n  ...\n\u003C\u002Fscript>\n","html",[184,18160,18161,18186,18191],{"__ignoreMap":1646},[2113,18162,18163,18165,18168,18171,18173,18176,18178,18180,18183],{"class":2115,"line":2116},[2113,18164,3109],{"class":2119},[2113,18166,18167],{"class":2330},"script",[2113,18169,18170],{"class":2166}," id",[2113,18172,2335],{"class":2119},[2113,18174,18175],{"class":2149},"\"__dkt\"",[2113,18177,16342],{"class":2166},[2113,18179,2335],{"class":2119},[2113,18181,18182],{"class":2149},"\"application\u002Fjson\"",[2113,18184,18185],{"class":2119},">\n",[2113,18187,18188],{"class":2115,"line":1647},[2113,18189,18190],{"class":2119},"  ...\n",[2113,18192,18193,18196,18198],{"class":2115,"line":1652},[2113,18194,18195],{"class":2119},"\u003C\u002F",[2113,18197,18167],{"class":2330},[2113,18199,18185],{"class":2119},[12,18201,18202],{},"Le JSON contient le contexte qui sera utilisé par le code Javascript pour la génération de la page et le côté interactif. C'est l'équivalent d'un appel XHR intégré au démarrage\nde la page. On peut y retrouver ce que l'on cherche :",[2105,18204,18206],{"className":18051,"code":18205,"language":18053,"meta":1646,"style":1646},"{\n  \"skus\": [\n    {\n      \"skuId\": \"4216661\",\n      \"size\": \"S\",\n      \"grossWeight\": \"20.9\",\n      \"price\": 2199,\n      \"isNotAvailable\": true,\n      \"availableInStores\": []\n    },\n    {\n      \"skuId\": \"4216663\",\n      \"size\": \"M\",\n      \"grossWeight\": \"21.0\",\n      \"price\": 2199,\n      \"isNotAvailable\": true,\n      \"availableInStores\": []\n    },\n    {\n      \"skuId\": \"4216662\",\n      \"size\": \"L\",\n      \"grossWeight\": \"21.1\",\n      \"price\": 2199,\n      \"isNotAvailable\": true,\n      \"availableInStores\": []\n    },\n    {\n      \"skuId\": \"4216664\",\n      \"size\": \"XL\",\n      \"grossWeight\": \"21.2\",\n      \"price\": 2199,\n      \"availableInStores\": []\n    }\n  ]\n}\n",[184,18207,18208,18212,18220,18225,18237,18249,18261,18273,18284,18292,18296,18300,18311,18322,18333,18343,18353,18359,18363,18367,18378,18389,18400,18410,18420,18426,18430,18434,18445,18456,18467,18477,18483,18487,18492],{"__ignoreMap":1646},[2113,18209,18210],{"class":2115,"line":2116},[2113,18211,18060],{"class":2119},[2113,18213,18214,18217],{"class":2115,"line":1647},[2113,18215,18216],{"class":2330},"  \"skus\"",[2113,18218,18219],{"class":2119},": [\n",[2113,18221,18222],{"class":2115,"line":1652},[2113,18223,18224],{"class":2119},"    {\n",[2113,18226,18227,18230,18232,18235],{"class":2115,"line":2185},[2113,18228,18229],{"class":2330},"      \"skuId\"",[2113,18231,4429],{"class":2119},[2113,18233,18234],{"class":2149},"\"4216661\"",[2113,18236,4706],{"class":2119},[2113,18238,18239,18242,18244,18247],{"class":2115,"line":2230},[2113,18240,18241],{"class":2330},"      \"size\"",[2113,18243,4429],{"class":2119},[2113,18245,18246],{"class":2149},"\"S\"",[2113,18248,4706],{"class":2119},[2113,18250,18251,18254,18256,18259],{"class":2115,"line":2235},[2113,18252,18253],{"class":2330},"      \"grossWeight\"",[2113,18255,4429],{"class":2119},[2113,18257,18258],{"class":2149},"\"20.9\"",[2113,18260,4706],{"class":2119},[2113,18262,18263,18266,18268,18271],{"class":2115,"line":2241},[2113,18264,18265],{"class":2330},"      \"price\"",[2113,18267,4429],{"class":2119},[2113,18269,18270],{"class":2166},"2199",[2113,18272,4706],{"class":2119},[2113,18274,18275,18278,18280,18282],{"class":2115,"line":2246},[2113,18276,18277],{"class":2330},"      \"isNotAvailable\"",[2113,18279,4429],{"class":2119},[2113,18281,4819],{"class":2166},[2113,18283,4706],{"class":2119},[2113,18285,18286,18289],{"class":2115,"line":2464},[2113,18287,18288],{"class":2330},"      \"availableInStores\"",[2113,18290,18291],{"class":2119},": []\n",[2113,18293,18294],{"class":2115,"line":2085},[2113,18295,8237],{"class":2119},[2113,18297,18298],{"class":2115,"line":2514},[2113,18299,18224],{"class":2119},[2113,18301,18302,18304,18306,18309],{"class":2115,"line":2533},[2113,18303,18229],{"class":2330},[2113,18305,4429],{"class":2119},[2113,18307,18308],{"class":2149},"\"4216663\"",[2113,18310,4706],{"class":2119},[2113,18312,18313,18315,18317,18320],{"class":2115,"line":2556},[2113,18314,18241],{"class":2330},[2113,18316,4429],{"class":2119},[2113,18318,18319],{"class":2149},"\"M\"",[2113,18321,4706],{"class":2119},[2113,18323,18324,18326,18328,18331],{"class":2115,"line":2569},[2113,18325,18253],{"class":2330},[2113,18327,4429],{"class":2119},[2113,18329,18330],{"class":2149},"\"21.0\"",[2113,18332,4706],{"class":2119},[2113,18334,18335,18337,18339,18341],{"class":2115,"line":2575},[2113,18336,18265],{"class":2330},[2113,18338,4429],{"class":2119},[2113,18340,18270],{"class":2166},[2113,18342,4706],{"class":2119},[2113,18344,18345,18347,18349,18351],{"class":2115,"line":2596},[2113,18346,18277],{"class":2330},[2113,18348,4429],{"class":2119},[2113,18350,4819],{"class":2166},[2113,18352,4706],{"class":2119},[2113,18354,18355,18357],{"class":2115,"line":3192},[2113,18356,18288],{"class":2330},[2113,18358,18291],{"class":2119},[2113,18360,18361],{"class":2115,"line":3213},[2113,18362,8237],{"class":2119},[2113,18364,18365],{"class":2115,"line":3236},[2113,18366,18224],{"class":2119},[2113,18368,18369,18371,18373,18376],{"class":2115,"line":3248},[2113,18370,18229],{"class":2330},[2113,18372,4429],{"class":2119},[2113,18374,18375],{"class":2149},"\"4216662\"",[2113,18377,4706],{"class":2119},[2113,18379,18380,18382,18384,18387],{"class":2115,"line":4899},[2113,18381,18241],{"class":2330},[2113,18383,4429],{"class":2119},[2113,18385,18386],{"class":2149},"\"L\"",[2113,18388,4706],{"class":2119},[2113,18390,18391,18393,18395,18398],{"class":2115,"line":1777},[2113,18392,18253],{"class":2330},[2113,18394,4429],{"class":2119},[2113,18396,18397],{"class":2149},"\"21.1\"",[2113,18399,4706],{"class":2119},[2113,18401,18402,18404,18406,18408],{"class":2115,"line":4931},[2113,18403,18265],{"class":2330},[2113,18405,4429],{"class":2119},[2113,18407,18270],{"class":2166},[2113,18409,4706],{"class":2119},[2113,18411,18412,18414,18416,18418],{"class":2115,"line":4961},[2113,18413,18277],{"class":2330},[2113,18415,4429],{"class":2119},[2113,18417,4819],{"class":2166},[2113,18419,4706],{"class":2119},[2113,18421,18422,18424],{"class":2115,"line":4976},[2113,18423,18288],{"class":2330},[2113,18425,18291],{"class":2119},[2113,18427,18428],{"class":2115,"line":4993},[2113,18429,8237],{"class":2119},[2113,18431,18432],{"class":2115,"line":5013},[2113,18433,18224],{"class":2119},[2113,18435,18436,18438,18440,18443],{"class":2115,"line":5018},[2113,18437,18229],{"class":2330},[2113,18439,4429],{"class":2119},[2113,18441,18442],{"class":2149},"\"4216664\"",[2113,18444,4706],{"class":2119},[2113,18446,18447,18449,18451,18454],{"class":2115,"line":5042},[2113,18448,18241],{"class":2330},[2113,18450,4429],{"class":2119},[2113,18452,18453],{"class":2149},"\"XL\"",[2113,18455,4706],{"class":2119},[2113,18457,18458,18460,18462,18465],{"class":2115,"line":5057},[2113,18459,18253],{"class":2330},[2113,18461,4429],{"class":2119},[2113,18463,18464],{"class":2149},"\"21.2\"",[2113,18466,4706],{"class":2119},[2113,18468,18469,18471,18473,18475],{"class":2115,"line":5062},[2113,18470,18265],{"class":2330},[2113,18472,4429],{"class":2119},[2113,18474,18270],{"class":2166},[2113,18476,4706],{"class":2119},[2113,18478,18479,18481],{"class":2115,"line":5098},[2113,18480,18288],{"class":2330},[2113,18482,18291],{"class":2119},[2113,18484,18485],{"class":2115,"line":5117},[2113,18486,4665],{"class":2119},[2113,18488,18489],{"class":2115,"line":5142},[2113,18490,18491],{"class":2119},"  ]\n",[2113,18493,18494],{"class":2115,"line":5148},[2113,18495,2599],{"class":2119},[12,18497,18498],{},"La taille qui m'intéresse est donc le SKU 4216662.",[128,18500,18502],{"id":18501},"stock-des-magasins","Stock des magasins",[12,18504,18505],{},"Vient ensuite la partie sur le stock des magasins. Je clique sur le bouton permettant de vérifier le stock des magasins, rentre mon code postal, et je me\nretrouve avec la liste des magasins.",[12,18507,18508],{},"Je regarde les requêtes et trouve celle qui m'intéresse avec l'ensemble des magasins du nord de la france:",[2105,18510,18512],{"className":18037,"code":18511,"language":18039,"meta":1646,"style":1646},"GET \u002Ffr\u002Fajax\u002Frest\u002Fmodel\u002Fcom\u002Fdecathlon\u002Fcube\u002Fcommerce\u002Finventory\u002FInventoryActor\u002FgetStoreAvailability?storeIds=0070000200002%2C0070093300933%2C0070043700437%2C0070051800518%2C0070011800118%2C0070219902199%2C0070050400504%2C0070253902539%2C0070001500015%2C0070064800648&skuId=4216662&modelId=8614842&displayStoreDetails=false\n",[184,18513,18514],{"__ignoreMap":1646},[2113,18515,18516],{"class":2115,"line":2116},[2113,18517,18511],{},[12,18519,18048],{},[2105,18521,18523],{"className":18051,"code":18522,"language":18053,"meta":1646,"style":1646},"{\n  \"responseTO\": {\n    \"data\": [\n      {\n        \"aboveThreshold\": false,\n        \"address\": null,\n        \"availabilityInfo\": \"noStock\",\n        \"clickNcollect1h\": false,\n        \"favoriteStore\": false,\n        \"latitude\": 0,\n        \"longitude\": 0,\n        \"optionId\": null,\n        \"originId\": null,\n        \"phoneNumber\": null,\n        \"priceId\": null,\n        \"quantity\": 0,\n        \"securedStockLevel\": 0,\n        \"skuId\": \"4216662\",\n        \"storeId\": \"0070000200002\",\n        \"storeName\": \"Neuville en Ferrain - Roncq\",\n        \"storeSchedule\": null,\n        \"storeUrl\": null\n      },\n      {\n        \"aboveThreshold\": false,\n        \"address\": null,\n        \"availabilityInfo\": \"noStock\",\n        \"clickNcollect1h\": false,\n        \"favoriteStore\": false,\n        \"latitude\": 0,\n        \"longitude\": 0,\n        \"optionId\": null,\n        \"originId\": null,\n        \"phoneNumber\": null,\n        \"priceId\": null,\n        \"quantity\": 0,\n        \"securedStockLevel\": 1,\n        \"skuId\": \"4216662\",\n        \"storeId\": \"0070051800518\",\n        \"storeName\": \"Marcq-en-Baroeul DOMYOS\",\n        \"storeSchedule\": null,\n        \"storeUrl\": null\n      },\n      {\n        \"aboveThreshold\": false,\n        \"address\": null,\n        \"availabilityInfo\": \"noStock\",\n        \"clickNcollect1h\": false,\n        \"favoriteStore\": false,\n        \"latitude\": 0,\n        \"longitude\": 0,\n        \"optionId\": null,\n        \"originId\": null,\n        \"phoneNumber\": null,\n        \"priceId\": null,\n        \"quantity\": 0,\n        \"securedStockLevel\": 1,\n        \"skuId\": \"4216662\",\n        \"storeId\": \"0070219902199\",\n        \"storeName\": \"Villeneuve d'Ascq - DX\",\n        \"storeSchedule\": null,\n        \"storeUrl\": null\n      },\n      ...\n    ],\n  }\n}\n",[184,18524,18525,18529,18536,18543,18548,18560,18572,18584,18595,18606,18617,18628,18639,18650,18661,18672,18683,18694,18705,18717,18729,18740,18750,18754,18758,18768,18778,18788,18798,18808,18818,18828,18838,18848,18858,18868,18878,18888,18898,18909,18920,18930,18938,18942,18946,18956,18966,18976,18986,18996,19006,19016,19026,19036,19046,19056,19066,19076,19086,19097,19108,19118,19126,19130,19136,19141,19145],{"__ignoreMap":1646},[2113,18526,18527],{"class":2115,"line":2116},[2113,18528,18060],{"class":2119},[2113,18530,18531,18534],{"class":2115,"line":1647},[2113,18532,18533],{"class":2330},"  \"responseTO\"",[2113,18535,8091],{"class":2119},[2113,18537,18538,18541],{"class":2115,"line":1652},[2113,18539,18540],{"class":2330},"    \"data\"",[2113,18542,18219],{"class":2119},[2113,18544,18545],{"class":2115,"line":2185},[2113,18546,18547],{"class":2119},"      {\n",[2113,18549,18550,18553,18555,18558],{"class":2115,"line":2230},[2113,18551,18552],{"class":2330},"        \"aboveThreshold\"",[2113,18554,4429],{"class":2119},[2113,18556,18557],{"class":2166},"false",[2113,18559,4706],{"class":2119},[2113,18561,18562,18565,18567,18570],{"class":2115,"line":2235},[2113,18563,18564],{"class":2330},"        \"address\"",[2113,18566,4429],{"class":2119},[2113,18568,18569],{"class":2166},"null",[2113,18571,4706],{"class":2119},[2113,18573,18574,18577,18579,18582],{"class":2115,"line":2241},[2113,18575,18576],{"class":2330},"        \"availabilityInfo\"",[2113,18578,4429],{"class":2119},[2113,18580,18581],{"class":2149},"\"noStock\"",[2113,18583,4706],{"class":2119},[2113,18585,18586,18589,18591,18593],{"class":2115,"line":2246},[2113,18587,18588],{"class":2330},"        \"clickNcollect1h\"",[2113,18590,4429],{"class":2119},[2113,18592,18557],{"class":2166},[2113,18594,4706],{"class":2119},[2113,18596,18597,18600,18602,18604],{"class":2115,"line":2464},[2113,18598,18599],{"class":2330},"        \"favoriteStore\"",[2113,18601,4429],{"class":2119},[2113,18603,18557],{"class":2166},[2113,18605,4706],{"class":2119},[2113,18607,18608,18611,18613,18615],{"class":2115,"line":2085},[2113,18609,18610],{"class":2330},"        \"latitude\"",[2113,18612,4429],{"class":2119},[2113,18614,3095],{"class":2166},[2113,18616,4706],{"class":2119},[2113,18618,18619,18622,18624,18626],{"class":2115,"line":2514},[2113,18620,18621],{"class":2330},"        \"longitude\"",[2113,18623,4429],{"class":2119},[2113,18625,3095],{"class":2166},[2113,18627,4706],{"class":2119},[2113,18629,18630,18633,18635,18637],{"class":2115,"line":2533},[2113,18631,18632],{"class":2330},"        \"optionId\"",[2113,18634,4429],{"class":2119},[2113,18636,18569],{"class":2166},[2113,18638,4706],{"class":2119},[2113,18640,18641,18644,18646,18648],{"class":2115,"line":2556},[2113,18642,18643],{"class":2330},"        \"originId\"",[2113,18645,4429],{"class":2119},[2113,18647,18569],{"class":2166},[2113,18649,4706],{"class":2119},[2113,18651,18652,18655,18657,18659],{"class":2115,"line":2569},[2113,18653,18654],{"class":2330},"        \"phoneNumber\"",[2113,18656,4429],{"class":2119},[2113,18658,18569],{"class":2166},[2113,18660,4706],{"class":2119},[2113,18662,18663,18666,18668,18670],{"class":2115,"line":2575},[2113,18664,18665],{"class":2330},"        \"priceId\"",[2113,18667,4429],{"class":2119},[2113,18669,18569],{"class":2166},[2113,18671,4706],{"class":2119},[2113,18673,18674,18677,18679,18681],{"class":2115,"line":2596},[2113,18675,18676],{"class":2330},"        \"quantity\"",[2113,18678,4429],{"class":2119},[2113,18680,3095],{"class":2166},[2113,18682,4706],{"class":2119},[2113,18684,18685,18688,18690,18692],{"class":2115,"line":3192},[2113,18686,18687],{"class":2330},"        \"securedStockLevel\"",[2113,18689,4429],{"class":2119},[2113,18691,3095],{"class":2166},[2113,18693,4706],{"class":2119},[2113,18695,18696,18699,18701,18703],{"class":2115,"line":3213},[2113,18697,18698],{"class":2330},"        \"skuId\"",[2113,18700,4429],{"class":2119},[2113,18702,18375],{"class":2149},[2113,18704,4706],{"class":2119},[2113,18706,18707,18710,18712,18715],{"class":2115,"line":3236},[2113,18708,18709],{"class":2330},"        \"storeId\"",[2113,18711,4429],{"class":2119},[2113,18713,18714],{"class":2149},"\"0070000200002\"",[2113,18716,4706],{"class":2119},[2113,18718,18719,18722,18724,18727],{"class":2115,"line":3248},[2113,18720,18721],{"class":2330},"        \"storeName\"",[2113,18723,4429],{"class":2119},[2113,18725,18726],{"class":2149},"\"Neuville en Ferrain - Roncq\"",[2113,18728,4706],{"class":2119},[2113,18730,18731,18734,18736,18738],{"class":2115,"line":4899},[2113,18732,18733],{"class":2330},"        \"storeSchedule\"",[2113,18735,4429],{"class":2119},[2113,18737,18569],{"class":2166},[2113,18739,4706],{"class":2119},[2113,18741,18742,18745,18747],{"class":2115,"line":1777},[2113,18743,18744],{"class":2330},"        \"storeUrl\"",[2113,18746,4429],{"class":2119},[2113,18748,18749],{"class":2166},"null\n",[2113,18751,18752],{"class":2115,"line":4931},[2113,18753,12282],{"class":2119},[2113,18755,18756],{"class":2115,"line":4961},[2113,18757,18547],{"class":2119},[2113,18759,18760,18762,18764,18766],{"class":2115,"line":4976},[2113,18761,18552],{"class":2330},[2113,18763,4429],{"class":2119},[2113,18765,18557],{"class":2166},[2113,18767,4706],{"class":2119},[2113,18769,18770,18772,18774,18776],{"class":2115,"line":4993},[2113,18771,18564],{"class":2330},[2113,18773,4429],{"class":2119},[2113,18775,18569],{"class":2166},[2113,18777,4706],{"class":2119},[2113,18779,18780,18782,18784,18786],{"class":2115,"line":5013},[2113,18781,18576],{"class":2330},[2113,18783,4429],{"class":2119},[2113,18785,18581],{"class":2149},[2113,18787,4706],{"class":2119},[2113,18789,18790,18792,18794,18796],{"class":2115,"line":5018},[2113,18791,18588],{"class":2330},[2113,18793,4429],{"class":2119},[2113,18795,18557],{"class":2166},[2113,18797,4706],{"class":2119},[2113,18799,18800,18802,18804,18806],{"class":2115,"line":5042},[2113,18801,18599],{"class":2330},[2113,18803,4429],{"class":2119},[2113,18805,18557],{"class":2166},[2113,18807,4706],{"class":2119},[2113,18809,18810,18812,18814,18816],{"class":2115,"line":5057},[2113,18811,18610],{"class":2330},[2113,18813,4429],{"class":2119},[2113,18815,3095],{"class":2166},[2113,18817,4706],{"class":2119},[2113,18819,18820,18822,18824,18826],{"class":2115,"line":5062},[2113,18821,18621],{"class":2330},[2113,18823,4429],{"class":2119},[2113,18825,3095],{"class":2166},[2113,18827,4706],{"class":2119},[2113,18829,18830,18832,18834,18836],{"class":2115,"line":5098},[2113,18831,18632],{"class":2330},[2113,18833,4429],{"class":2119},[2113,18835,18569],{"class":2166},[2113,18837,4706],{"class":2119},[2113,18839,18840,18842,18844,18846],{"class":2115,"line":5117},[2113,18841,18643],{"class":2330},[2113,18843,4429],{"class":2119},[2113,18845,18569],{"class":2166},[2113,18847,4706],{"class":2119},[2113,18849,18850,18852,18854,18856],{"class":2115,"line":5142},[2113,18851,18654],{"class":2330},[2113,18853,4429],{"class":2119},[2113,18855,18569],{"class":2166},[2113,18857,4706],{"class":2119},[2113,18859,18860,18862,18864,18866],{"class":2115,"line":5148},[2113,18861,18665],{"class":2330},[2113,18863,4429],{"class":2119},[2113,18865,18569],{"class":2166},[2113,18867,4706],{"class":2119},[2113,18869,18870,18872,18874,18876],{"class":2115,"line":5163},[2113,18871,18676],{"class":2330},[2113,18873,4429],{"class":2119},[2113,18875,3095],{"class":2166},[2113,18877,4706],{"class":2119},[2113,18879,18880,18882,18884,18886],{"class":2115,"line":5169},[2113,18881,18687],{"class":2330},[2113,18883,4429],{"class":2119},[2113,18885,3686],{"class":2166},[2113,18887,4706],{"class":2119},[2113,18889,18890,18892,18894,18896],{"class":2115,"line":5175},[2113,18891,18698],{"class":2330},[2113,18893,4429],{"class":2119},[2113,18895,18375],{"class":2149},[2113,18897,4706],{"class":2119},[2113,18899,18900,18902,18904,18907],{"class":2115,"line":5180},[2113,18901,18709],{"class":2330},[2113,18903,4429],{"class":2119},[2113,18905,18906],{"class":2149},"\"0070051800518\"",[2113,18908,4706],{"class":2119},[2113,18910,18911,18913,18915,18918],{"class":2115,"line":5199},[2113,18912,18721],{"class":2330},[2113,18914,4429],{"class":2119},[2113,18916,18917],{"class":2149},"\"Marcq-en-Baroeul DOMYOS\"",[2113,18919,4706],{"class":2119},[2113,18921,18922,18924,18926,18928],{"class":2115,"line":5204},[2113,18923,18733],{"class":2330},[2113,18925,4429],{"class":2119},[2113,18927,18569],{"class":2166},[2113,18929,4706],{"class":2119},[2113,18931,18932,18934,18936],{"class":2115,"line":5209},[2113,18933,18744],{"class":2330},[2113,18935,4429],{"class":2119},[2113,18937,18749],{"class":2166},[2113,18939,18940],{"class":2115,"line":5232},[2113,18941,12282],{"class":2119},[2113,18943,18944],{"class":2115,"line":5237},[2113,18945,18547],{"class":2119},[2113,18947,18948,18950,18952,18954],{"class":2115,"line":5242},[2113,18949,18552],{"class":2330},[2113,18951,4429],{"class":2119},[2113,18953,18557],{"class":2166},[2113,18955,4706],{"class":2119},[2113,18957,18958,18960,18962,18964],{"class":2115,"line":5267},[2113,18959,18564],{"class":2330},[2113,18961,4429],{"class":2119},[2113,18963,18569],{"class":2166},[2113,18965,4706],{"class":2119},[2113,18967,18968,18970,18972,18974],{"class":2115,"line":5282},[2113,18969,18576],{"class":2330},[2113,18971,4429],{"class":2119},[2113,18973,18581],{"class":2149},[2113,18975,4706],{"class":2119},[2113,18977,18978,18980,18982,18984],{"class":2115,"line":5295},[2113,18979,18588],{"class":2330},[2113,18981,4429],{"class":2119},[2113,18983,18557],{"class":2166},[2113,18985,4706],{"class":2119},[2113,18987,18988,18990,18992,18994],{"class":2115,"line":5310},[2113,18989,18599],{"class":2330},[2113,18991,4429],{"class":2119},[2113,18993,18557],{"class":2166},[2113,18995,4706],{"class":2119},[2113,18997,18998,19000,19002,19004],{"class":2115,"line":5315},[2113,18999,18610],{"class":2330},[2113,19001,4429],{"class":2119},[2113,19003,3095],{"class":2166},[2113,19005,4706],{"class":2119},[2113,19007,19008,19010,19012,19014],{"class":2115,"line":5320},[2113,19009,18621],{"class":2330},[2113,19011,4429],{"class":2119},[2113,19013,3095],{"class":2166},[2113,19015,4706],{"class":2119},[2113,19017,19018,19020,19022,19024],{"class":2115,"line":8625},[2113,19019,18632],{"class":2330},[2113,19021,4429],{"class":2119},[2113,19023,18569],{"class":2166},[2113,19025,4706],{"class":2119},[2113,19027,19028,19030,19032,19034],{"class":2115,"line":8631},[2113,19029,18643],{"class":2330},[2113,19031,4429],{"class":2119},[2113,19033,18569],{"class":2166},[2113,19035,4706],{"class":2119},[2113,19037,19038,19040,19042,19044],{"class":2115,"line":8637},[2113,19039,18654],{"class":2330},[2113,19041,4429],{"class":2119},[2113,19043,18569],{"class":2166},[2113,19045,4706],{"class":2119},[2113,19047,19048,19050,19052,19054],{"class":2115,"line":8652},[2113,19049,18665],{"class":2330},[2113,19051,4429],{"class":2119},[2113,19053,18569],{"class":2166},[2113,19055,4706],{"class":2119},[2113,19057,19058,19060,19062,19064],{"class":2115,"line":8674},[2113,19059,18676],{"class":2330},[2113,19061,4429],{"class":2119},[2113,19063,3095],{"class":2166},[2113,19065,4706],{"class":2119},[2113,19067,19068,19070,19072,19074],{"class":2115,"line":8706},[2113,19069,18687],{"class":2330},[2113,19071,4429],{"class":2119},[2113,19073,3686],{"class":2166},[2113,19075,4706],{"class":2119},[2113,19077,19078,19080,19082,19084],{"class":2115,"line":8724},[2113,19079,18698],{"class":2330},[2113,19081,4429],{"class":2119},[2113,19083,18375],{"class":2149},[2113,19085,4706],{"class":2119},[2113,19087,19088,19090,19092,19095],{"class":2115,"line":8729},[2113,19089,18709],{"class":2330},[2113,19091,4429],{"class":2119},[2113,19093,19094],{"class":2149},"\"0070219902199\"",[2113,19096,4706],{"class":2119},[2113,19098,19099,19101,19103,19106],{"class":2115,"line":8734},[2113,19100,18721],{"class":2330},[2113,19102,4429],{"class":2119},[2113,19104,19105],{"class":2149},"\"Villeneuve d'Ascq - DX\"",[2113,19107,4706],{"class":2119},[2113,19109,19110,19112,19114,19116],{"class":2115,"line":8740},[2113,19111,18733],{"class":2330},[2113,19113,4429],{"class":2119},[2113,19115,18569],{"class":2166},[2113,19117,4706],{"class":2119},[2113,19119,19120,19122,19124],{"class":2115,"line":8772},[2113,19121,18744],{"class":2330},[2113,19123,4429],{"class":2119},[2113,19125,18749],{"class":2166},[2113,19127,19128],{"class":2115,"line":8778},[2113,19129,12282],{"class":2119},[2113,19131,19132],{"class":2115,"line":8789},[2113,19133,19135],{"class":19134},"sLaUg","      ...\n",[2113,19137,19138],{"class":2115,"line":8795},[2113,19139,19140],{"class":2119},"    ],\n",[2113,19142,19143],{"class":2115,"line":8817},[2113,19144,2572],{"class":2119},[2113,19146,19147],{"class":2115,"line":8822},[2113,19148,2599],{"class":2119},[12,19150,19151],{},"Parfait, j'ai l'ensemble des requêtes et le résultat permettant de recupérer les informations dont j'ai besoin. Il ne me reste plus qu'à coder. Je crée donc\nun premier service dont le but est pour un code donné de récupérer le stock internet et les stocks en magasin.",[12,19153,19154,19155,19157],{},"Il est probable que j'aurais pu mieux écrire mon code (et d'ailleurs mieux utiliser RxJS), mais je vous rappelle, ceci est un 1",[17400,19156,17461],{}," jet. Le but étant de déployer\ncela le plus rapidement possible.",[2105,19159,19161],{"className":15749,"code":19160,"language":15751,"meta":1646,"style":1646},"@Injectable()\nexport class AppService {\n  constructor(private httpService: HttpService) {}\n\n  async getVtcElectectricL(code: string): Promise\u003CStockInformation[]> {\n    const availability = [];\n    const sku = await firstValueFrom(\n      this.httpService.get\u003CStocksOnline>(STOCKS_ONLINE([code])),\n    );\n    const onlineStocks = sku.data[code]?.stockOnline;\n\n    availability.push({\n      type: 'Internet',\n      stocks: onlineStocks,\n    });\n\n    const stores = await firstValueFrom(\n      this.httpService.get\u003CStocksStores>(STOCKS_STORE(code)),\n    );\n\n    availability.push(\n      ...stores.data.responseTO.data\n        .map((store) => ({\n          type: store.storeName,\n          stocks: store.quantity,\n        }))\n        .sort((a, b) => b.stocks - a.stocks),\n    );\n\n    return availability.map((store) => ({\n      ...store,\n      availability: store.stocks > 0 ? 'available' : 'not_available',\n    }));\n  }\n}\n",[184,19162,19163,19173,19185,19205,19209,19240,19253,19269,19303,19307,19334,19338,19350,19362,19374,19379,19383,19398,19427,19431,19435,19445,19467,19484,19500,19516,19521,19557,19561,19565,19585,19593,19622,19627,19631],{"__ignoreMap":1646},[2113,19164,19165,19168,19171],{"class":2115,"line":2116},[2113,19166,19167],{"class":2119},"@",[2113,19169,19170],{"class":2133},"Injectable",[2113,19172,6745],{"class":2119},[2113,19174,19175,19177,19180,19183],{"class":2115,"line":1647},[2113,19176,2327],{"class":2326},[2113,19178,19179],{"class":2326}," class",[2113,19181,19182],{"class":2414}," AppService",[2113,19184,2647],{"class":2119},[2113,19186,19187,19190,19192,19195,19198,19200,19203],{"class":2115,"line":1652},[2113,19188,19189],{"class":2326},"  constructor",[2113,19191,2423],{"class":2119},[2113,19193,19194],{"class":2326},"private",[2113,19196,19197],{"class":2429}," httpService",[2113,19199,4429],{"class":2119},[2113,19201,19202],{"class":2414},"HttpService",[2113,19204,8980],{"class":2119},[2113,19206,19207],{"class":2115,"line":2185},[2113,19208,2125],{"emptyLinePlaceholder":1767},[2113,19210,19211,19214,19217,19219,19221,19223,19226,19229,19232,19234,19237],{"class":2115,"line":2230},[2113,19212,19213],{"class":2326},"  async",[2113,19215,19216],{"class":2133}," getVtcElectectricL",[2113,19218,2423],{"class":2119},[2113,19220,184],{"class":2429},[2113,19222,4429],{"class":2119},[2113,19224,19225],{"class":2414},"string",[2113,19227,19228],{"class":2119},"): ",[2113,19230,19231],{"class":2414},"Promise",[2113,19233,3109],{"class":2119},[2113,19235,19236],{"class":2414},"StockInformation",[2113,19238,19239],{"class":2119},"[]> {\n",[2113,19241,19242,19245,19248,19250],{"class":2115,"line":2235},[2113,19243,19244],{"class":2326},"    const",[2113,19246,19247],{"class":2414}," availability",[2113,19249,2153],{"class":2334},[2113,19251,19252],{"class":2119}," [];\n",[2113,19254,19255,19257,19260,19262,19264,19267],{"class":2115,"line":2241},[2113,19256,19244],{"class":2326},[2113,19258,19259],{"class":2414}," sku",[2113,19261,2153],{"class":2334},[2113,19263,17521],{"class":2326},[2113,19265,19266],{"class":2133}," firstValueFrom",[2113,19268,3146],{"class":2119},[2113,19270,19271,19274,19276,19279,19281,19284,19286,19289,19292,19295,19298,19300],{"class":2115,"line":2246},[2113,19272,19273],{"class":2414},"      this",[2113,19275,179],{"class":2119},[2113,19277,19278],{"class":2414},"httpService",[2113,19280,179],{"class":2119},[2113,19282,19283],{"class":2133},"get",[2113,19285,3109],{"class":2119},[2113,19287,19288],{"class":2414},"StocksOnline",[2113,19290,19291],{"class":2119},">(",[2113,19293,19294],{"class":2133},"STOCKS_ONLINE",[2113,19296,19297],{"class":2119},"([",[2113,19299,184],{"class":2330},[2113,19301,19302],{"class":2119},"])),\n",[2113,19304,19305],{"class":2115,"line":2464},[2113,19306,10961],{"class":2119},[2113,19308,19309,19311,19314,19316,19318,19320,19322,19324,19326,19329,19332],{"class":2115,"line":2085},[2113,19310,19244],{"class":2326},[2113,19312,19313],{"class":2414}," onlineStocks",[2113,19315,2153],{"class":2334},[2113,19317,19259],{"class":2414},[2113,19319,179],{"class":2119},[2113,19321,10154],{"class":2330},[2113,19323,4124],{"class":2119},[2113,19325,184],{"class":2330},[2113,19327,19328],{"class":2119},"]?.",[2113,19330,19331],{"class":2330},"stockOnline",[2113,19333,2487],{"class":2119},[2113,19335,19336],{"class":2115,"line":2514},[2113,19337,2125],{"emptyLinePlaceholder":1767},[2113,19339,19340,19343,19345,19347],{"class":2115,"line":2533},[2113,19341,19342],{"class":2414},"    availability",[2113,19344,179],{"class":2119},[2113,19346,10086],{"class":2133},[2113,19348,19349],{"class":2119},"({\n",[2113,19351,19352,19355,19357,19360],{"class":2115,"line":2556},[2113,19353,19354],{"class":2330},"      type",[2113,19356,4429],{"class":2119},[2113,19358,19359],{"class":2149},"'Internet'",[2113,19361,4706],{"class":2119},[2113,19363,19364,19367,19369,19372],{"class":2115,"line":2569},[2113,19365,19366],{"class":2330},"      stocks",[2113,19368,4429],{"class":2119},[2113,19370,19371],{"class":2330},"onlineStocks",[2113,19373,4706],{"class":2119},[2113,19375,19376],{"class":2115,"line":2575},[2113,19377,19378],{"class":2119},"    });\n",[2113,19380,19381],{"class":2115,"line":2596},[2113,19382,2125],{"emptyLinePlaceholder":1767},[2113,19384,19385,19387,19390,19392,19394,19396],{"class":2115,"line":3192},[2113,19386,19244],{"class":2326},[2113,19388,19389],{"class":2414}," stores",[2113,19391,2153],{"class":2334},[2113,19393,17521],{"class":2326},[2113,19395,19266],{"class":2133},[2113,19397,3146],{"class":2119},[2113,19399,19400,19402,19404,19406,19408,19410,19412,19415,19417,19420,19422,19424],{"class":2115,"line":3213},[2113,19401,19273],{"class":2414},[2113,19403,179],{"class":2119},[2113,19405,19278],{"class":2414},[2113,19407,179],{"class":2119},[2113,19409,19283],{"class":2133},[2113,19411,3109],{"class":2119},[2113,19413,19414],{"class":2414},"StocksStores",[2113,19416,19291],{"class":2119},[2113,19418,19419],{"class":2133},"STOCKS_STORE",[2113,19421,2423],{"class":2119},[2113,19423,184],{"class":2330},[2113,19425,19426],{"class":2119},")),\n",[2113,19428,19429],{"class":2115,"line":3236},[2113,19430,10961],{"class":2119},[2113,19432,19433],{"class":2115,"line":3248},[2113,19434,2125],{"emptyLinePlaceholder":1767},[2113,19436,19437,19439,19441,19443],{"class":2115,"line":4899},[2113,19438,19342],{"class":2414},[2113,19440,179],{"class":2119},[2113,19442,10086],{"class":2133},[2113,19444,3146],{"class":2119},[2113,19446,19447,19450,19453,19455,19457,19459,19462,19464],{"class":2115,"line":1777},[2113,19448,19449],{"class":2119},"      ...",[2113,19451,19452],{"class":2414},"stores",[2113,19454,179],{"class":2119},[2113,19456,10154],{"class":2414},[2113,19458,179],{"class":2119},[2113,19460,19461],{"class":2414},"responseTO",[2113,19463,179],{"class":2119},[2113,19465,19466],{"class":2330},"data\n",[2113,19468,19469,19471,19473,19475,19478,19480,19482],{"class":2115,"line":4931},[2113,19470,10331],{"class":2119},[2113,19472,6644],{"class":2133},[2113,19474,8879],{"class":2119},[2113,19476,19477],{"class":2429},"store",[2113,19479,5709],{"class":2119},[2113,19481,8063],{"class":2326},[2113,19483,8066],{"class":2119},[2113,19485,19486,19489,19491,19493,19495,19498],{"class":2115,"line":4961},[2113,19487,19488],{"class":2330},"          type",[2113,19490,4429],{"class":2119},[2113,19492,19477],{"class":2414},[2113,19494,179],{"class":2119},[2113,19496,19497],{"class":2330},"storeName",[2113,19499,4706],{"class":2119},[2113,19501,19502,19505,19507,19509,19511,19514],{"class":2115,"line":4976},[2113,19503,19504],{"class":2330},"          stocks",[2113,19506,4429],{"class":2119},[2113,19508,19477],{"class":2414},[2113,19510,179],{"class":2119},[2113,19512,19513],{"class":2330},"quantity",[2113,19515,4706],{"class":2119},[2113,19517,19518],{"class":2115,"line":4993},[2113,19519,19520],{"class":2119},"        }))\n",[2113,19522,19523,19525,19528,19530,19532,19534,19536,19538,19540,19542,19544,19547,19549,19551,19553,19555],{"class":2115,"line":5013},[2113,19524,10331],{"class":2119},[2113,19526,19527],{"class":2133},"sort",[2113,19529,8879],{"class":2119},[2113,19531,49],{"class":2429},[2113,19533,932],{"class":2119},[2113,19535,3992],{"class":2429},[2113,19537,5709],{"class":2119},[2113,19539,8063],{"class":2326},[2113,19541,3951],{"class":2414},[2113,19543,179],{"class":2119},[2113,19545,19546],{"class":2330},"stocks",[2113,19548,2270],{"class":2334},[2113,19550,17734],{"class":2414},[2113,19552,179],{"class":2119},[2113,19554,19546],{"class":2330},[2113,19556,7142],{"class":2119},[2113,19558,19559],{"class":2115,"line":5018},[2113,19560,10961],{"class":2119},[2113,19562,19563],{"class":2115,"line":5042},[2113,19564,2125],{"emptyLinePlaceholder":1767},[2113,19566,19567,19569,19571,19573,19575,19577,19579,19581,19583],{"class":2115,"line":5057},[2113,19568,3137],{"class":2326},[2113,19570,19247],{"class":2414},[2113,19572,179],{"class":2119},[2113,19574,6644],{"class":2133},[2113,19576,8879],{"class":2119},[2113,19578,19477],{"class":2429},[2113,19580,5709],{"class":2119},[2113,19582,8063],{"class":2326},[2113,19584,8066],{"class":2119},[2113,19586,19587,19589,19591],{"class":2115,"line":5062},[2113,19588,19449],{"class":2119},[2113,19590,19477],{"class":2330},[2113,19592,4706],{"class":2119},[2113,19594,19595,19598,19600,19602,19604,19606,19608,19610,19612,19615,19617,19620],{"class":2115,"line":5098},[2113,19596,19597],{"class":2330},"      availability",[2113,19599,4429],{"class":2119},[2113,19601,19477],{"class":2414},[2113,19603,179],{"class":2119},[2113,19605,19546],{"class":2330},[2113,19607,5275],{"class":2334},[2113,19609,3208],{"class":2166},[2113,19611,9343],{"class":2326},[2113,19613,19614],{"class":2149}," 'available'",[2113,19616,2384],{"class":2326},[2113,19618,19619],{"class":2149}," 'not_available'",[2113,19621,4706],{"class":2119},[2113,19623,19624],{"class":2115,"line":5117},[2113,19625,19626],{"class":2119},"    }));\n",[2113,19628,19629],{"class":2115,"line":5142},[2113,19630,2572],{"class":2119},[2113,19632,19633],{"class":2115,"line":5148},[2113,19634,2599],{"class":2119},[12,19636,19637],{},"Une petite page en Mustashe pour afficher en HTML le contenu de la page et c'est parti !",[12,19639,19640],{},[121,19641],{"alt":19642,"className":19643,"src":19644},"La page WEB",[126],"\u002FProgrammation\u002Fachat_velo\u002Fpage_web.png",[128,19646,19648],{"id":19647},"le-déploiement","Le déploiement",[12,19650,19651,19652,179],{},"Afin d'améliorer mes compétences professionnelles je décide de déployer cette application sur AWS, et afin que cette application me coûte le moins cher possible, je\nvais déployer cette application dans une Lambda à l'aide du framework ",[49,19653,19656],{"href":19654,"rel":19655},"https:\u002F\u002Fwww.serverless.com\u002F",[347],"Serverless",[12,19658,19659,19660,179],{},"Viens alors l'adaptation du l'application Nest.JS pour utiliser le framework Serverless. Je laisse la documentation de Nest.JS expliquer comment faire:\n",[49,19661,19664],{"href":19662,"rel":19663},"https:\u002F\u002Fdocs.nestjs.com\u002Ffaq\u002Fserverless",[347],"Nest.JS - Serverless",[2105,19666,19670],{"className":19667,"code":19668,"language":19669,"meta":1646,"style":1646},"language-yaml shiki shiki-themes one-dark-pro","service: triathlon\nframeworkVersion: \"2\"\n\nplugins:\n  - serverless-plugin-log-retention\n  - serverless-offline\n  - serverless-domain-manager\n\ncustom:\n  region: eu-central-1\n  logRetentionInDays: 7\n  serverless-offline:\n    noPrependStageInUrl: true\n    allowCache: false\n  customDomain:\n    domainName: \"decat-${self:provider.stage}.aws.shadoware.org\"\n    basePath: \"\"\n    certificateName: \"aws.shadoware.org\"\n    createRoute53Record: true\n    endpointType: \"regional\"\n    securityPolicy: tls_1_2\n    apiType: rest\n    autoDomain: false\n\nprovider:\n  name: aws\n  region: ${self:custom.region}\n  runtime: nodejs12.x\n  lambdaHashingVersion: \"20201221\"\n  stage: ${opt:stage, 'dev'}\n  environment:\n    NODE_OPTIONS: \"--max-http-header-size=80000\"\n\npackage:\n  patterns:\n    - \"!.\u002F**\"\n    - dist\u002F**\n    - views\u002F**\n\nfunctions:\n  api:\n    handler: dist\u002Fmain.handler\n    environment:\n      region: ${self:custom.region}\n    events:\n      - http:\n          path: \u002F\n          method: ANY\n      - http:\n          path: \u002F{proxy+}\n          method: ANY\n","yaml",[184,19671,19672,19681,19691,19695,19703,19711,19718,19725,19729,19736,19746,19756,19763,19773,19783,19790,19800,19810,19820,19829,19839,19849,19859,19868,19872,19879,19889,19898,19908,19918,19928,19935,19945,19949,19955,19962,19970,19977,19984,19988,19995,20002,20012,20019,20028,20035,20044,20054,20064,20072,20081],{"__ignoreMap":1646},[2113,19673,19674,19676,19678],{"class":2115,"line":2116},[2113,19675,16847],{"class":2330},[2113,19677,4429],{"class":2119},[2113,19679,19680],{"class":2149},"triathlon\n",[2113,19682,19683,19686,19688],{"class":2115,"line":1647},[2113,19684,19685],{"class":2330},"frameworkVersion",[2113,19687,4429],{"class":2119},[2113,19689,19690],{"class":2149},"\"2\"\n",[2113,19692,19693],{"class":2115,"line":1652},[2113,19694,2125],{"emptyLinePlaceholder":1767},[2113,19696,19697,19700],{"class":2115,"line":2185},[2113,19698,19699],{"class":2330},"plugins",[2113,19701,19702],{"class":2119},":\n",[2113,19704,19705,19708],{"class":2115,"line":2230},[2113,19706,19707],{"class":2119},"  - ",[2113,19709,19710],{"class":2149},"serverless-plugin-log-retention\n",[2113,19712,19713,19715],{"class":2115,"line":2235},[2113,19714,19707],{"class":2119},[2113,19716,19717],{"class":2149},"serverless-offline\n",[2113,19719,19720,19722],{"class":2115,"line":2241},[2113,19721,19707],{"class":2119},[2113,19723,19724],{"class":2149},"serverless-domain-manager\n",[2113,19726,19727],{"class":2115,"line":2246},[2113,19728,2125],{"emptyLinePlaceholder":1767},[2113,19730,19731,19734],{"class":2115,"line":2464},[2113,19732,19733],{"class":2330},"custom",[2113,19735,19702],{"class":2119},[2113,19737,19738,19741,19743],{"class":2115,"line":2085},[2113,19739,19740],{"class":2330},"  region",[2113,19742,4429],{"class":2119},[2113,19744,19745],{"class":2149},"eu-central-1\n",[2113,19747,19748,19751,19753],{"class":2115,"line":2514},[2113,19749,19750],{"class":2330},"  logRetentionInDays",[2113,19752,4429],{"class":2119},[2113,19754,19755],{"class":2166},"7\n",[2113,19757,19758,19761],{"class":2115,"line":2533},[2113,19759,19760],{"class":2330},"  serverless-offline",[2113,19762,19702],{"class":2119},[2113,19764,19765,19768,19770],{"class":2115,"line":2556},[2113,19766,19767],{"class":2330},"    noPrependStageInUrl",[2113,19769,4429],{"class":2119},[2113,19771,19772],{"class":2166},"true\n",[2113,19774,19775,19778,19780],{"class":2115,"line":2569},[2113,19776,19777],{"class":2330},"    allowCache",[2113,19779,4429],{"class":2119},[2113,19781,19782],{"class":2166},"false\n",[2113,19784,19785,19788],{"class":2115,"line":2575},[2113,19786,19787],{"class":2330},"  customDomain",[2113,19789,19702],{"class":2119},[2113,19791,19792,19795,19797],{"class":2115,"line":2596},[2113,19793,19794],{"class":2330},"    domainName",[2113,19796,4429],{"class":2119},[2113,19798,19799],{"class":2149},"\"decat-${self:provider.stage}.aws.shadoware.org\"\n",[2113,19801,19802,19805,19807],{"class":2115,"line":3192},[2113,19803,19804],{"class":2330},"    basePath",[2113,19806,4429],{"class":2119},[2113,19808,19809],{"class":2149},"\"\"\n",[2113,19811,19812,19815,19817],{"class":2115,"line":3213},[2113,19813,19814],{"class":2330},"    certificateName",[2113,19816,4429],{"class":2119},[2113,19818,19819],{"class":2149},"\"aws.shadoware.org\"\n",[2113,19821,19822,19825,19827],{"class":2115,"line":3236},[2113,19823,19824],{"class":2330},"    createRoute53Record",[2113,19826,4429],{"class":2119},[2113,19828,19772],{"class":2166},[2113,19830,19831,19834,19836],{"class":2115,"line":3248},[2113,19832,19833],{"class":2330},"    endpointType",[2113,19835,4429],{"class":2119},[2113,19837,19838],{"class":2149},"\"regional\"\n",[2113,19840,19841,19844,19846],{"class":2115,"line":4899},[2113,19842,19843],{"class":2330},"    securityPolicy",[2113,19845,4429],{"class":2119},[2113,19847,19848],{"class":2149},"tls_1_2\n",[2113,19850,19851,19854,19856],{"class":2115,"line":1777},[2113,19852,19853],{"class":2330},"    apiType",[2113,19855,4429],{"class":2119},[2113,19857,19858],{"class":2149},"rest\n",[2113,19860,19861,19864,19866],{"class":2115,"line":4931},[2113,19862,19863],{"class":2330},"    autoDomain",[2113,19865,4429],{"class":2119},[2113,19867,19782],{"class":2166},[2113,19869,19870],{"class":2115,"line":4961},[2113,19871,2125],{"emptyLinePlaceholder":1767},[2113,19873,19874,19877],{"class":2115,"line":4976},[2113,19875,19876],{"class":2330},"provider",[2113,19878,19702],{"class":2119},[2113,19880,19881,19884,19886],{"class":2115,"line":4993},[2113,19882,19883],{"class":2330},"  name",[2113,19885,4429],{"class":2119},[2113,19887,19888],{"class":2149},"aws\n",[2113,19890,19891,19893,19895],{"class":2115,"line":5013},[2113,19892,19740],{"class":2330},[2113,19894,4429],{"class":2119},[2113,19896,19897],{"class":2149},"${self:custom.region}\n",[2113,19899,19900,19903,19905],{"class":2115,"line":5018},[2113,19901,19902],{"class":2330},"  runtime",[2113,19904,4429],{"class":2119},[2113,19906,19907],{"class":2149},"nodejs12.x\n",[2113,19909,19910,19913,19915],{"class":2115,"line":5042},[2113,19911,19912],{"class":2330},"  lambdaHashingVersion",[2113,19914,4429],{"class":2119},[2113,19916,19917],{"class":2149},"\"20201221\"\n",[2113,19919,19920,19923,19925],{"class":2115,"line":5057},[2113,19921,19922],{"class":2330},"  stage",[2113,19924,4429],{"class":2119},[2113,19926,19927],{"class":2149},"${opt:stage, 'dev'}\n",[2113,19929,19930,19933],{"class":2115,"line":5062},[2113,19931,19932],{"class":2330},"  environment",[2113,19934,19702],{"class":2119},[2113,19936,19937,19940,19942],{"class":2115,"line":5098},[2113,19938,19939],{"class":2330},"    NODE_OPTIONS",[2113,19941,4429],{"class":2119},[2113,19943,19944],{"class":2149},"\"--max-http-header-size=80000\"\n",[2113,19946,19947],{"class":2115,"line":5117},[2113,19948,2125],{"emptyLinePlaceholder":1767},[2113,19950,19951,19953],{"class":2115,"line":5142},[2113,19952,15998],{"class":2330},[2113,19954,19702],{"class":2119},[2113,19956,19957,19960],{"class":2115,"line":5148},[2113,19958,19959],{"class":2330},"  patterns",[2113,19961,19702],{"class":2119},[2113,19963,19964,19967],{"class":2115,"line":5163},[2113,19965,19966],{"class":2119},"    - ",[2113,19968,19969],{"class":2149},"\"!.\u002F**\"\n",[2113,19971,19972,19974],{"class":2115,"line":5169},[2113,19973,19966],{"class":2119},[2113,19975,19976],{"class":2149},"dist\u002F**\n",[2113,19978,19979,19981],{"class":2115,"line":5175},[2113,19980,19966],{"class":2119},[2113,19982,19983],{"class":2149},"views\u002F**\n",[2113,19985,19986],{"class":2115,"line":5180},[2113,19987,2125],{"emptyLinePlaceholder":1767},[2113,19989,19990,19993],{"class":2115,"line":5199},[2113,19991,19992],{"class":2330},"functions",[2113,19994,19702],{"class":2119},[2113,19996,19997,20000],{"class":2115,"line":5204},[2113,19998,19999],{"class":2330},"  api",[2113,20001,19702],{"class":2119},[2113,20003,20004,20007,20009],{"class":2115,"line":5209},[2113,20005,20006],{"class":2330},"    handler",[2113,20008,4429],{"class":2119},[2113,20010,20011],{"class":2149},"dist\u002Fmain.handler\n",[2113,20013,20014,20017],{"class":2115,"line":5232},[2113,20015,20016],{"class":2330},"    environment",[2113,20018,19702],{"class":2119},[2113,20020,20021,20024,20026],{"class":2115,"line":5237},[2113,20022,20023],{"class":2330},"      region",[2113,20025,4429],{"class":2119},[2113,20027,19897],{"class":2149},[2113,20029,20030,20033],{"class":2115,"line":5242},[2113,20031,20032],{"class":2330},"    events",[2113,20034,19702],{"class":2119},[2113,20036,20037,20040,20042],{"class":2115,"line":5267},[2113,20038,20039],{"class":2119},"      - ",[2113,20041,18039],{"class":2330},[2113,20043,19702],{"class":2119},[2113,20045,20046,20049,20051],{"class":2115,"line":5282},[2113,20047,20048],{"class":2330},"          path",[2113,20050,4429],{"class":2119},[2113,20052,20053],{"class":2149},"\u002F\n",[2113,20055,20056,20059,20061],{"class":2115,"line":5295},[2113,20057,20058],{"class":2330},"          method",[2113,20060,4429],{"class":2119},[2113,20062,20063],{"class":2149},"ANY\n",[2113,20065,20066,20068,20070],{"class":2115,"line":5310},[2113,20067,20039],{"class":2119},[2113,20069,18039],{"class":2330},[2113,20071,19702],{"class":2119},[2113,20073,20074,20076,20078],{"class":2115,"line":5315},[2113,20075,20048],{"class":2330},[2113,20077,4429],{"class":2119},[2113,20079,20080],{"class":2149},"\u002F{proxy+}\n",[2113,20082,20083,20085,20087],{"class":2115,"line":5320},[2113,20084,20058],{"class":2330},[2113,20086,4429],{"class":2119},[2113,20088,20063],{"class":2149},[12,20090,20091],{},"Il ne reste plus qu'à lancer le déploiement:",[2105,20093,20095],{"className":17586,"code":20094,"language":17588,"meta":1646,"style":1646},"npx serverless deploy\n",[184,20096,20097],{"__ignoreMap":1646},[2113,20098,20099,20102,20105],{"class":2115,"line":2116},[2113,20100,20101],{"class":2133},"npx",[2113,20103,20104],{"class":2149}," serverless",[2113,20106,20107],{"class":2149}," deploy\n",[12,20109,20110],{},"Nous avons alors notre belle page de déployée.",[128,20112,20114],{"id":20113},"la-suite-de-laventure","La suite de l'aventure",[12,20116,20117],{},"Me voilà avec un petit programme qui me permet de facilement visualiser les stocks du vélo que je souhaite. J'en profite également pour ajouter le même\nproduit en taille XL (où il y a du stock) et un VTT.",[12,20119,20120],{},"Le problème c'est que je dois continuer à rafraîchir la page, même si c'est plus rapide.",[12,20122,20123],{},"Pour l'anecdocte, après une matinée de glandouille (c'est les vacances), je décide de rafraîchir la page vers midi. Je vois \"Stock: 1\". Heureux, je me précipite pour\nacheter le seul exemplaire. Mais lors de la commande je tombe sur le fameux message: \"Internal Server Error\" dans un bandeau rouge en haut de l'écran.",[12,20125,20126],{},"J'appelle le service client qui m'informe que cela provient du fait qu'il n'y a déjà plus de stock mais que le stock du site n'est mis à jours que tout les 24h.\nJe m'en doutais mais le message d'erreur laisse à désirer un peu.",[12,20128,20129],{},"Malgrès l'apparition soudaine d'un stock, je n'ai recu aucun mail pour me prévenir (relatif à la fonctionnalité \"M'avertir lorsque le produit est à nouveau disponible\"). Il\nme faut un moyen d'être averti dans les premiers dès qu'il y a du stock.",[12,20131,20132],{},"La situation s'est reproduite sans que je sois averti par mail.",[12,20134,20135],{},"Bref, il me faut une solution !",[128,20137,20139],{"id":20138},"la-notification","La notification",[12,20141,20142],{},"Je profite donc des services AWS pour développer une nouvelle fonctionnalité. Cette dernière est simple: je vais faire une requête à mon\nservice toutes les 30 minutes, pour vérifier le stock, et si ce dernier bouge, j'envoie un SMS.",[12,20144,20145],{},"Le coût actuel d'envoi d'un SMS vers la France sur AWS est de 0.06933$. Donc si je recois 1 SMS par heure en journée, je vais aller vers 1$ par jour.\nC'est à prévoir pour mon budget. Maintenant je veux recevoir le minimum de SMS. Je n'ai pas envie de recevoir de SMS quand le stock ne change pas. Il faut donc\nque je puisse comparer le stock entre l'état précédent et l'etat actuel.",[12,20147,20148],{},"Quand on utilise les lambda (fonction) d'AWS, il n'y a pas de serveur de démarré. Donc je ne peux pas stocker les valeurs précédentes temporairement en\nmémoire pour contrôler les changements (ce qui ne serait pas une bonne pratique, mais un truc rapide à faire pour un dev maison).",[12,20150,20151],{},"Je stocke donc le dernier accès au site dans une base de données de type Document de chez AWS: DynamoDB.",[12,20153,20154],{},"Le service d'envoi de SMS est simple (et prend un numéro de téléphone et un message):",[2105,20156,20158],{"className":15749,"code":20157,"language":15751,"meta":1646,"style":1646},"  \u002F**\n   * Sends a SMS message to the specificed phone number.\n   * Returns the message ID from SNS\n   * @param   {String} phoneNumber\n   * @param   {String} message\n   * @returns {Promise|String}\n   *\u002F\n  async sendMessage(phoneNumber, message) {\n    await this.SNS.setSMSAttributes({\n      attributes: { DefaultSMSType: 'Promotional' },\n    }).promise();\n\n    const smsData = {\n      Message: message,\n      PhoneNumber: phoneNumber,\n      MessageAttributes: {\n        'AWS.SNS.SMS.SenderID': {\n          DataType: 'String',\n          StringValue: 'DecatStock',\n        },\n      },\n    };\n\n    this.logger.log(\n      `Send message to ${phoneNumber} (${message.length}):\\n${message}`,\n    );\n    const response = await this.SNS.publish(smsData).promise();\n\n    this.logger.log(`Response of SMS is ${JSON.stringify(response)}`);\n    return response.MessageId;\n  }\n",[184,20159,20160,20165,20170,20175,20191,20202,20212,20217,20235,20254,20271,20281,20285,20296,20307,20318,20325,20332,20344,20356,20360,20364,20369,20373,20389,20430,20434,20466,20470,20509,20522],{"__ignoreMap":1646},[2113,20161,20162],{"class":2115,"line":2116},[2113,20163,20164],{"class":2396},"  \u002F**\n",[2113,20166,20167],{"class":2115,"line":1647},[2113,20168,20169],{"class":2396},"   * Sends a SMS message to the specificed phone number.\n",[2113,20171,20172],{"class":2115,"line":1652},[2113,20173,20174],{"class":2396},"   * Returns the message ID from SNS\n",[2113,20176,20177,20180,20184,20188],{"class":2115,"line":2185},[2113,20178,20179],{"class":2396},"   * ",[2113,20181,20183],{"class":20182},"shdRp","@param",[2113,20185,20187],{"class":20186},"sKU4T","   {String}",[2113,20189,20190],{"class":2429}," phoneNumber\n",[2113,20192,20193,20195,20197,20199],{"class":2115,"line":2230},[2113,20194,20179],{"class":2396},[2113,20196,20183],{"class":20182},[2113,20198,20187],{"class":20186},[2113,20200,20201],{"class":2429}," message\n",[2113,20203,20204,20206,20209],{"class":2115,"line":2235},[2113,20205,20179],{"class":2396},[2113,20207,20208],{"class":20182},"@returns",[2113,20210,20211],{"class":20186}," {Promise|String}\n",[2113,20213,20214],{"class":2115,"line":2241},[2113,20215,20216],{"class":2396},"   *\u002F\n",[2113,20218,20219,20221,20224,20226,20229,20231,20233],{"class":2115,"line":2246},[2113,20220,19213],{"class":2330},[2113,20222,20223],{"class":2133}," sendMessage",[2113,20225,2423],{"class":2119},[2113,20227,20228],{"class":2330},"phoneNumber",[2113,20230,932],{"class":2119},[2113,20232,16052],{"class":2330},[2113,20234,2433],{"class":2119},[2113,20236,20237,20240,20242,20244,20247,20249,20252],{"class":2115,"line":2464},[2113,20238,20239],{"class":2326},"    await",[2113,20241,15832],{"class":2414},[2113,20243,179],{"class":2119},[2113,20245,20246],{"class":2414},"SNS",[2113,20248,179],{"class":2119},[2113,20250,20251],{"class":2133},"setSMSAttributes",[2113,20253,19349],{"class":2119},[2113,20255,20256,20259,20261,20264,20266,20269],{"class":2115,"line":2085},[2113,20257,20258],{"class":2330},"      attributes",[2113,20260,8099],{"class":2119},[2113,20262,20263],{"class":2330},"DefaultSMSType",[2113,20265,4429],{"class":2119},[2113,20267,20268],{"class":2149},"'Promotional'",[2113,20270,8129],{"class":2119},[2113,20272,20273,20276,20279],{"class":2115,"line":2514},[2113,20274,20275],{"class":2119},"    }).",[2113,20277,20278],{"class":2133},"promise",[2113,20280,3944],{"class":2119},[2113,20282,20283],{"class":2115,"line":2533},[2113,20284,2125],{"emptyLinePlaceholder":1767},[2113,20286,20287,20289,20292,20294],{"class":2115,"line":2556},[2113,20288,19244],{"class":2326},[2113,20290,20291],{"class":2414}," smsData",[2113,20293,2153],{"class":2334},[2113,20295,2647],{"class":2119},[2113,20297,20298,20301,20303,20305],{"class":2115,"line":2569},[2113,20299,20300],{"class":2330},"      Message",[2113,20302,4429],{"class":2119},[2113,20304,16052],{"class":2330},[2113,20306,4706],{"class":2119},[2113,20308,20309,20312,20314,20316],{"class":2115,"line":2575},[2113,20310,20311],{"class":2330},"      PhoneNumber",[2113,20313,4429],{"class":2119},[2113,20315,20228],{"class":2330},[2113,20317,4706],{"class":2119},[2113,20319,20320,20323],{"class":2115,"line":2596},[2113,20321,20322],{"class":2330},"      MessageAttributes",[2113,20324,8091],{"class":2119},[2113,20326,20327,20330],{"class":2115,"line":3192},[2113,20328,20329],{"class":2149},"        'AWS.SNS.SMS.SenderID'",[2113,20331,8091],{"class":2119},[2113,20333,20334,20337,20339,20342],{"class":2115,"line":3213},[2113,20335,20336],{"class":2330},"          DataType",[2113,20338,4429],{"class":2119},[2113,20340,20341],{"class":2149},"'String'",[2113,20343,4706],{"class":2119},[2113,20345,20346,20349,20351,20354],{"class":2115,"line":3236},[2113,20347,20348],{"class":2330},"          StringValue",[2113,20350,4429],{"class":2119},[2113,20352,20353],{"class":2149},"'DecatStock'",[2113,20355,4706],{"class":2119},[2113,20357,20358],{"class":2115,"line":3248},[2113,20359,12744],{"class":2119},[2113,20361,20362],{"class":2115,"line":4899},[2113,20363,12282],{"class":2119},[2113,20365,20366],{"class":2115,"line":1777},[2113,20367,20368],{"class":2119},"    };\n",[2113,20370,20371],{"class":2115,"line":4931},[2113,20372,2125],{"emptyLinePlaceholder":1767},[2113,20374,20375,20378,20380,20383,20385,20387],{"class":2115,"line":4961},[2113,20376,20377],{"class":2414},"    this",[2113,20379,179],{"class":2119},[2113,20381,20382],{"class":2414},"logger",[2113,20384,179],{"class":2119},[2113,20386,8748],{"class":2133},[2113,20388,3146],{"class":2119},[2113,20390,20391,20394,20396,20398,20400,20402,20404,20406,20408,20411,20413,20416,20419,20421,20423,20425,20428],{"class":2115,"line":4976},[2113,20392,20393],{"class":2149},"      `Send message to ",[2113,20395,15877],{"class":2326},[2113,20397,20228],{"class":2330},[2113,20399,15888],{"class":2326},[2113,20401,2495],{"class":2149},[2113,20403,15877],{"class":2326},[2113,20405,16052],{"class":2414},[2113,20407,179],{"class":2119},[2113,20409,20410],{"class":2330},"length",[2113,20412,15888],{"class":2326},[2113,20414,20415],{"class":2149},"):",[2113,20417,20418],{"class":2334},"\\n",[2113,20420,15877],{"class":2326},[2113,20422,16052],{"class":2330},[2113,20424,15888],{"class":2326},[2113,20426,20427],{"class":2149},"`",[2113,20429,4706],{"class":2119},[2113,20431,20432],{"class":2115,"line":4993},[2113,20433,10961],{"class":2119},[2113,20435,20436,20438,20440,20442,20444,20446,20448,20450,20452,20455,20457,20460,20462,20464],{"class":2115,"line":5013},[2113,20437,19244],{"class":2326},[2113,20439,15958],{"class":2414},[2113,20441,2153],{"class":2334},[2113,20443,17521],{"class":2326},[2113,20445,15832],{"class":2414},[2113,20447,179],{"class":2119},[2113,20449,20246],{"class":2414},[2113,20451,179],{"class":2119},[2113,20453,20454],{"class":2133},"publish",[2113,20456,2423],{"class":2119},[2113,20458,20459],{"class":2330},"smsData",[2113,20461,3938],{"class":2119},[2113,20463,20278],{"class":2133},[2113,20465,3944],{"class":2119},[2113,20467,20468],{"class":2115,"line":5018},[2113,20469,2125],{"emptyLinePlaceholder":1767},[2113,20471,20472,20474,20476,20478,20480,20482,20484,20487,20489,20492,20494,20497,20499,20501,20503,20505,20507],{"class":2115,"line":5042},[2113,20473,20377],{"class":2414},[2113,20475,179],{"class":2119},[2113,20477,20382],{"class":2414},[2113,20479,179],{"class":2119},[2113,20481,8748],{"class":2133},[2113,20483,2423],{"class":2119},[2113,20485,20486],{"class":2149},"`Response of SMS is ",[2113,20488,15877],{"class":2326},[2113,20490,20491],{"class":2414},"JSON",[2113,20493,179],{"class":2119},[2113,20495,20496],{"class":2133},"stringify",[2113,20498,2423],{"class":2119},[2113,20500,15951],{"class":2330},[2113,20502,17832],{"class":2119},[2113,20504,15888],{"class":2326},[2113,20506,20427],{"class":2149},[2113,20508,2553],{"class":2119},[2113,20510,20511,20513,20515,20517,20520],{"class":2115,"line":5057},[2113,20512,3137],{"class":2326},[2113,20514,15958],{"class":2414},[2113,20516,179],{"class":2119},[2113,20518,20519],{"class":2330},"MessageId",[2113,20521,2487],{"class":2119},[2113,20523,20524],{"class":2115,"line":5062},[2113,20525,2572],{"class":2119},[12,20527,20528],{},"Le service permettant de lire et d'écrire dans la base DynamoDB est lui aussi très simple et ne possède que deux méthodes:",[2105,20530,20532],{"className":15749,"code":20531,"language":15751,"meta":1646,"style":1646},"class {\n  async findOneById(sku: string): Promise\u003CDecathlonStatus> {\n    try {\n      const result = await dynamoDB\n        .get({\n          TableName: process.env.DECATHLON_TABLE_NAME,\n          Key: { id: sku },\n        })\n        .promise();\n      return result.Item as DecathlonStatus;\n    } catch (error) {\n      this.logger.warn(`Can't read the table ${sku}: ${error.message}`);\n      return null;\n    }\n  }\n\n  async update(\n    sku: string,\n    items: StockInformations\n  ): Promise\u003CDecathlonStatus> {\n    const status = new DecathlonStatus();\n    status.id = sku;\n    status.informations = items;\n\n    try {\n      await dynamoDB\n        .put({\n          TableName: process.env.DECATHLON_TABLE_NAME,\n          Item: status,\n        })\n        .promise();\n      return status;\n    } catch (error) {\n      throw new InternalServerErrorException(error);\n    }\n  }\n}\n",[184,20533,20534,20541,20568,20575,20589,20597,20619,20635,20640,20648,20667,20681,20721,20730,20734,20738,20742,20751,20762,20772,20785,20800,20815,20831,20835,20841,20848,20857,20875,20887,20891,20899,20907,20919,20935,20939,20943],{"__ignoreMap":1646},[2113,20535,20536,20539],{"class":2115,"line":2116},[2113,20537,20538],{"class":2326},"class",[2113,20540,2647],{"class":2119},[2113,20542,20543,20545,20548,20550,20553,20555,20557,20559,20561,20563,20566],{"class":2115,"line":1647},[2113,20544,19213],{"class":2326},[2113,20546,20547],{"class":2133}," findOneById",[2113,20549,2423],{"class":2119},[2113,20551,20552],{"class":2429},"sku",[2113,20554,4429],{"class":2119},[2113,20556,19225],{"class":2414},[2113,20558,19228],{"class":2119},[2113,20560,19231],{"class":2414},[2113,20562,3109],{"class":2119},[2113,20564,20565],{"class":2414},"DecathlonStatus",[2113,20567,4449],{"class":2119},[2113,20569,20570,20573],{"class":2115,"line":1652},[2113,20571,20572],{"class":2326},"    try",[2113,20574,2647],{"class":2119},[2113,20576,20577,20580,20582,20584,20586],{"class":2115,"line":2185},[2113,20578,20579],{"class":2326},"      const",[2113,20581,6112],{"class":2414},[2113,20583,2153],{"class":2334},[2113,20585,17521],{"class":2326},[2113,20587,20588],{"class":2330}," dynamoDB\n",[2113,20590,20591,20593,20595],{"class":2115,"line":2230},[2113,20592,10331],{"class":2119},[2113,20594,19283],{"class":2133},[2113,20596,19349],{"class":2119},[2113,20598,20599,20602,20604,20607,20609,20612,20614,20617],{"class":2115,"line":2235},[2113,20600,20601],{"class":2330},"          TableName",[2113,20603,4429],{"class":2119},[2113,20605,20606],{"class":2414},"process",[2113,20608,179],{"class":2119},[2113,20610,20611],{"class":2414},"env",[2113,20613,179],{"class":2119},[2113,20615,20616],{"class":2330},"DECATHLON_TABLE_NAME",[2113,20618,4706],{"class":2119},[2113,20620,20621,20624,20626,20629,20631,20633],{"class":2115,"line":2241},[2113,20622,20623],{"class":2330},"          Key",[2113,20625,8099],{"class":2119},[2113,20627,20628],{"class":2330},"id",[2113,20630,4429],{"class":2119},[2113,20632,20552],{"class":2330},[2113,20634,8129],{"class":2119},[2113,20636,20637],{"class":2115,"line":2246},[2113,20638,20639],{"class":2119},"        })\n",[2113,20641,20642,20644,20646],{"class":2115,"line":2464},[2113,20643,10331],{"class":2119},[2113,20645,20278],{"class":2133},[2113,20647,3944],{"class":2119},[2113,20649,20650,20653,20655,20657,20660,20662,20665],{"class":2115,"line":2085},[2113,20651,20652],{"class":2326},"      return",[2113,20654,6112],{"class":2414},[2113,20656,179],{"class":2119},[2113,20658,20659],{"class":2330},"Item",[2113,20661,11429],{"class":2326},[2113,20663,20664],{"class":2414}," DecathlonStatus",[2113,20666,2487],{"class":2119},[2113,20668,20669,20672,20674,20676,20679],{"class":2115,"line":2514},[2113,20670,20671],{"class":2119},"    } ",[2113,20673,8973],{"class":2326},[2113,20675,2495],{"class":2119},[2113,20677,20678],{"class":2330},"error",[2113,20680,2433],{"class":2119},[2113,20682,20683,20685,20687,20689,20691,20694,20696,20699,20701,20703,20705,20707,20709,20711,20713,20715,20717,20719],{"class":2115,"line":2533},[2113,20684,19273],{"class":2414},[2113,20686,179],{"class":2119},[2113,20688,20382],{"class":2414},[2113,20690,179],{"class":2119},[2113,20692,20693],{"class":2133},"warn",[2113,20695,2423],{"class":2119},[2113,20697,20698],{"class":2149},"`Can't read the table ",[2113,20700,15877],{"class":2326},[2113,20702,20552],{"class":2330},[2113,20704,15888],{"class":2326},[2113,20706,4429],{"class":2149},[2113,20708,15877],{"class":2326},[2113,20710,20678],{"class":2414},[2113,20712,179],{"class":2119},[2113,20714,16052],{"class":2330},[2113,20716,15888],{"class":2326},[2113,20718,20427],{"class":2149},[2113,20720,2553],{"class":2119},[2113,20722,20723,20725,20728],{"class":2115,"line":2556},[2113,20724,20652],{"class":2326},[2113,20726,20727],{"class":2166}," null",[2113,20729,2487],{"class":2119},[2113,20731,20732],{"class":2115,"line":2569},[2113,20733,4665],{"class":2119},[2113,20735,20736],{"class":2115,"line":2575},[2113,20737,2572],{"class":2119},[2113,20739,20740],{"class":2115,"line":2596},[2113,20741,2125],{"emptyLinePlaceholder":1767},[2113,20743,20744,20746,20749],{"class":2115,"line":3192},[2113,20745,19213],{"class":2326},[2113,20747,20748],{"class":2133}," update",[2113,20750,3146],{"class":2119},[2113,20752,20753,20756,20758,20760],{"class":2115,"line":3213},[2113,20754,20755],{"class":2429},"    sku",[2113,20757,4429],{"class":2119},[2113,20759,19225],{"class":2414},[2113,20761,4706],{"class":2119},[2113,20763,20764,20767,20769],{"class":2115,"line":3236},[2113,20765,20766],{"class":2429},"    items",[2113,20768,4429],{"class":2119},[2113,20770,20771],{"class":2414},"StockInformations\n",[2113,20773,20774,20777,20779,20781,20783],{"class":2115,"line":3248},[2113,20775,20776],{"class":2119},"  ): ",[2113,20778,19231],{"class":2414},[2113,20780,3109],{"class":2119},[2113,20782,20565],{"class":2414},[2113,20784,4449],{"class":2119},[2113,20786,20787,20789,20792,20794,20796,20798],{"class":2115,"line":4899},[2113,20788,19244],{"class":2326},[2113,20790,20791],{"class":2414}," status",[2113,20793,2153],{"class":2334},[2113,20795,4778],{"class":2326},[2113,20797,20664],{"class":2133},[2113,20799,3944],{"class":2119},[2113,20801,20802,20805,20807,20809,20811,20813],{"class":2115,"line":1777},[2113,20803,20804],{"class":2414},"    status",[2113,20806,179],{"class":2119},[2113,20808,20628],{"class":2330},[2113,20810,2153],{"class":2334},[2113,20812,19259],{"class":2330},[2113,20814,2487],{"class":2119},[2113,20816,20817,20819,20821,20824,20826,20829],{"class":2115,"line":4931},[2113,20818,20804],{"class":2414},[2113,20820,179],{"class":2119},[2113,20822,20823],{"class":2330},"informations",[2113,20825,2153],{"class":2334},[2113,20827,20828],{"class":2330}," items",[2113,20830,2487],{"class":2119},[2113,20832,20833],{"class":2115,"line":4961},[2113,20834,2125],{"emptyLinePlaceholder":1767},[2113,20836,20837,20839],{"class":2115,"line":4976},[2113,20838,20572],{"class":2326},[2113,20840,2647],{"class":2119},[2113,20842,20843,20846],{"class":2115,"line":4993},[2113,20844,20845],{"class":2326},"      await",[2113,20847,20588],{"class":2330},[2113,20849,20850,20852,20855],{"class":2115,"line":5013},[2113,20851,10331],{"class":2119},[2113,20853,20854],{"class":2133},"put",[2113,20856,19349],{"class":2119},[2113,20858,20859,20861,20863,20865,20867,20869,20871,20873],{"class":2115,"line":5018},[2113,20860,20601],{"class":2330},[2113,20862,4429],{"class":2119},[2113,20864,20606],{"class":2414},[2113,20866,179],{"class":2119},[2113,20868,20611],{"class":2414},[2113,20870,179],{"class":2119},[2113,20872,20616],{"class":2330},[2113,20874,4706],{"class":2119},[2113,20876,20877,20880,20882,20885],{"class":2115,"line":5042},[2113,20878,20879],{"class":2330},"          Item",[2113,20881,4429],{"class":2119},[2113,20883,20884],{"class":2330},"status",[2113,20886,4706],{"class":2119},[2113,20888,20889],{"class":2115,"line":5057},[2113,20890,20639],{"class":2119},[2113,20892,20893,20895,20897],{"class":2115,"line":5062},[2113,20894,10331],{"class":2119},[2113,20896,20278],{"class":2133},[2113,20898,3944],{"class":2119},[2113,20900,20901,20903,20905],{"class":2115,"line":5098},[2113,20902,20652],{"class":2326},[2113,20904,20791],{"class":2330},[2113,20906,2487],{"class":2119},[2113,20908,20909,20911,20913,20915,20917],{"class":2115,"line":5117},[2113,20910,20671],{"class":2119},[2113,20912,8973],{"class":2326},[2113,20914,2495],{"class":2119},[2113,20916,20678],{"class":2330},[2113,20918,2433],{"class":2119},[2113,20920,20921,20924,20926,20929,20931,20933],{"class":2115,"line":5142},[2113,20922,20923],{"class":2326},"      throw",[2113,20925,4778],{"class":2326},[2113,20927,20928],{"class":2133}," InternalServerErrorException",[2113,20930,2423],{"class":2119},[2113,20932,20678],{"class":2330},[2113,20934,2553],{"class":2119},[2113,20936,20937],{"class":2115,"line":5148},[2113,20938,4665],{"class":2119},[2113,20940,20941],{"class":2115,"line":5163},[2113,20942,2572],{"class":2119},[2113,20944,20945],{"class":2115,"line":5169},[2113,20946,2599],{"class":2119},[12,20948,20949],{},"Le but est bien de lire le status et d'écrire le status pour un produit.",[12,20951,20952],{},"Le service qui sera appellé de façon régulière est relativement simple. Il va:",[367,20954,20955,20961,20968],{},[370,20956,20957,20958,20960],{},"appeler le service d'API ",[184,20959,15598],{}," pour vérifier le stock en live,",[370,20962,20963,20964,20967],{},"appeler la service ",[184,20965,20966],{},"DynamoDB"," pour récupérer le dernier stock en base de données,",[370,20969,20970],{},"et faire la différences entre les deux, et s'il y en a une: envoyer un SMS.",[2105,20972,20974],{"className":15749,"code":20973,"language":15751,"meta":1646,"style":1646},"export class AvailibilityCheckerService {\n  private logger = new Logger(AvailibilityCheckerService.name);\n\n  constructor(\n    private service: AppService,\n    private decathlonService: DecathlonsService,\n    private smsService: SMSService,\n  ) {}\n\n  async checkAvailibility() {\n    const message = (\n      await Promise.all(\n        PRODUCTS.map(\n          async (product) =>\n            await this.checkAvailabilityOfSku(product.code, product.name),\n        ),\n      )\n    )\n      .filter((message) => !!message)\n      .join('\\n\\n');\n\n    if (message.length) {\n      await this.smsService.sendMessage(process.env.PHONE_NUMBER, message);\n    }\n  }\n\n  async checkAvailabilityOfSku(code: string, name: string) {\n    const stocks = await this.service.getVtcElectectricL(code);\n    const oldStocks = await this.decathlonService.findOneById(code);\n    const diffStocks = this.diffStocks(oldStocks?.informations, stocks);\n    this.logger.debug(`The are ${diffStocks.length} stocks that change`);\n\n    let message;\n    if (diffStocks.length) {\n      message = diffStocks\n        .map((value) => `  ${value.type} - ${value.stocks}`)\n        .join('\\n');\n    }\n\n    await this.decathlonService.update(code, stocks);\n    return message && `${name}:\\n${message}`;\n  }\n\n  diffStocks(oldInfos: StockInformations, newInfos: StockInformations) {\n    oldInfos || (oldInfos = []);\n    newInfos || (newInfos = []);\n\n    const oldMap = oldInfos.reduce((acc, i) => {\n      acc[i.type] = i;\n      return acc;\n    }, {} as Record\u003Cstring, StockInformation>);\n\n    return newInfos.filter((info) => {\n      const oldStocks = oldMap[info.type]?.stocks || 0;\n      return oldStocks !== (info?.stocks || 0);\n    });\n  }\n}\n",[184,20975,20976,20987,21014,21018,21024,21039,21053,21067,21072,21076,21085,21097,21111,21122,21137,21167,21172,21176,21180,21202,21220,21224,21238,21273,21277,21281,21285,21310,21338,21367,21399,21432,21436,21444,21458,21468,21513,21529,21533,21537,21562,21592,21596,21600,21626,21642,21657,21661,21693,21714,21723,21744,21748,21770,21798,21821,21825,21829],{"__ignoreMap":1646},[2113,20977,20978,20980,20982,20985],{"class":2115,"line":2116},[2113,20979,2327],{"class":2326},[2113,20981,19179],{"class":2326},[2113,20983,20984],{"class":2414}," AvailibilityCheckerService",[2113,20986,2647],{"class":2119},[2113,20988,20989,20992,20995,20997,20999,21002,21004,21007,21009,21012],{"class":2115,"line":1647},[2113,20990,20991],{"class":2326},"  private",[2113,20993,20994],{"class":2330}," logger",[2113,20996,2153],{"class":2334},[2113,20998,4778],{"class":2326},[2113,21000,21001],{"class":2133}," Logger",[2113,21003,2423],{"class":2119},[2113,21005,21006],{"class":2414},"AvailibilityCheckerService",[2113,21008,179],{"class":2119},[2113,21010,21011],{"class":2330},"name",[2113,21013,2553],{"class":2119},[2113,21015,21016],{"class":2115,"line":1652},[2113,21017,2125],{"emptyLinePlaceholder":1767},[2113,21019,21020,21022],{"class":2115,"line":2185},[2113,21021,19189],{"class":2326},[2113,21023,3146],{"class":2119},[2113,21025,21026,21029,21032,21034,21037],{"class":2115,"line":2230},[2113,21027,21028],{"class":2326},"    private",[2113,21030,21031],{"class":2429}," service",[2113,21033,4429],{"class":2119},[2113,21035,21036],{"class":2414},"AppService",[2113,21038,4706],{"class":2119},[2113,21040,21041,21043,21046,21048,21051],{"class":2115,"line":2235},[2113,21042,21028],{"class":2326},[2113,21044,21045],{"class":2429}," decathlonService",[2113,21047,4429],{"class":2119},[2113,21049,21050],{"class":2414},"DecathlonsService",[2113,21052,4706],{"class":2119},[2113,21054,21055,21057,21060,21062,21065],{"class":2115,"line":2241},[2113,21056,21028],{"class":2326},[2113,21058,21059],{"class":2429}," smsService",[2113,21061,4429],{"class":2119},[2113,21063,21064],{"class":2414},"SMSService",[2113,21066,4706],{"class":2119},[2113,21068,21069],{"class":2115,"line":2246},[2113,21070,21071],{"class":2119},"  ) {}\n",[2113,21073,21074],{"class":2115,"line":2464},[2113,21075,2125],{"emptyLinePlaceholder":1767},[2113,21077,21078,21080,21083],{"class":2115,"line":2085},[2113,21079,19213],{"class":2326},[2113,21081,21082],{"class":2133}," checkAvailibility",[2113,21084,3912],{"class":2119},[2113,21086,21087,21089,21092,21094],{"class":2115,"line":2514},[2113,21088,19244],{"class":2326},[2113,21090,21091],{"class":2133}," message",[2113,21093,2153],{"class":2334},[2113,21095,21096],{"class":2119}," (\n",[2113,21098,21099,21101,21104,21106,21109],{"class":2115,"line":2533},[2113,21100,20845],{"class":2326},[2113,21102,21103],{"class":2414}," Promise",[2113,21105,179],{"class":2119},[2113,21107,21108],{"class":2133},"all",[2113,21110,3146],{"class":2119},[2113,21112,21113,21116,21118,21120],{"class":2115,"line":2556},[2113,21114,21115],{"class":2414},"        PRODUCTS",[2113,21117,179],{"class":2119},[2113,21119,6644],{"class":2133},[2113,21121,3146],{"class":2119},[2113,21123,21124,21127,21129,21132,21134],{"class":2115,"line":2569},[2113,21125,21126],{"class":2326},"          async",[2113,21128,2495],{"class":2119},[2113,21130,21131],{"class":2429},"product",[2113,21133,5709],{"class":2119},[2113,21135,21136],{"class":2326},"=>\n",[2113,21138,21139,21142,21144,21146,21149,21151,21153,21155,21157,21159,21161,21163,21165],{"class":2115,"line":2575},[2113,21140,21141],{"class":2326},"            await",[2113,21143,15832],{"class":2414},[2113,21145,179],{"class":2119},[2113,21147,21148],{"class":2133},"checkAvailabilityOfSku",[2113,21150,2423],{"class":2119},[2113,21152,21131],{"class":2414},[2113,21154,179],{"class":2119},[2113,21156,184],{"class":2330},[2113,21158,932],{"class":2119},[2113,21160,21131],{"class":2414},[2113,21162,179],{"class":2119},[2113,21164,21011],{"class":2330},[2113,21166,7142],{"class":2119},[2113,21168,21169],{"class":2115,"line":2596},[2113,21170,21171],{"class":2119},"        ),\n",[2113,21173,21174],{"class":2115,"line":3192},[2113,21175,13039],{"class":2119},[2113,21177,21178],{"class":2115,"line":3213},[2113,21179,13112],{"class":2119},[2113,21181,21182,21184,21187,21189,21191,21193,21195,21198,21200],{"class":2115,"line":3236},[2113,21183,12400],{"class":2119},[2113,21185,21186],{"class":2133},"filter",[2113,21188,8879],{"class":2119},[2113,21190,16052],{"class":2429},[2113,21192,5709],{"class":2119},[2113,21194,8063],{"class":2326},[2113,21196,21197],{"class":2334}," !!",[2113,21199,16052],{"class":2330},[2113,21201,4660],{"class":2119},[2113,21203,21204,21206,21208,21210,21213,21216,21218],{"class":2115,"line":3248},[2113,21205,12400],{"class":2119},[2113,21207,7230],{"class":2133},[2113,21209,2423],{"class":2119},[2113,21211,21212],{"class":2149},"'",[2113,21214,21215],{"class":2334},"\\n\\n",[2113,21217,21212],{"class":2149},[2113,21219,2553],{"class":2119},[2113,21221,21222],{"class":2115,"line":4899},[2113,21223,2125],{"emptyLinePlaceholder":1767},[2113,21225,21226,21228,21230,21232,21234,21236],{"class":2115,"line":1777},[2113,21227,7394],{"class":2326},[2113,21229,2495],{"class":2119},[2113,21231,16052],{"class":2414},[2113,21233,179],{"class":2119},[2113,21235,20410],{"class":2330},[2113,21237,2433],{"class":2119},[2113,21239,21240,21242,21244,21246,21249,21251,21254,21256,21258,21260,21262,21264,21267,21269,21271],{"class":2115,"line":4931},[2113,21241,20845],{"class":2326},[2113,21243,15832],{"class":2414},[2113,21245,179],{"class":2119},[2113,21247,21248],{"class":2414},"smsService",[2113,21250,179],{"class":2119},[2113,21252,21253],{"class":2133},"sendMessage",[2113,21255,2423],{"class":2119},[2113,21257,20606],{"class":2414},[2113,21259,179],{"class":2119},[2113,21261,20611],{"class":2414},[2113,21263,179],{"class":2119},[2113,21265,21266],{"class":2330},"PHONE_NUMBER",[2113,21268,932],{"class":2119},[2113,21270,16052],{"class":2330},[2113,21272,2553],{"class":2119},[2113,21274,21275],{"class":2115,"line":4961},[2113,21276,4665],{"class":2119},[2113,21278,21279],{"class":2115,"line":4976},[2113,21280,2572],{"class":2119},[2113,21282,21283],{"class":2115,"line":4993},[2113,21284,2125],{"emptyLinePlaceholder":1767},[2113,21286,21287,21289,21292,21294,21296,21298,21300,21302,21304,21306,21308],{"class":2115,"line":5013},[2113,21288,19213],{"class":2326},[2113,21290,21291],{"class":2133}," checkAvailabilityOfSku",[2113,21293,2423],{"class":2119},[2113,21295,184],{"class":2429},[2113,21297,4429],{"class":2119},[2113,21299,19225],{"class":2414},[2113,21301,932],{"class":2119},[2113,21303,21011],{"class":2429},[2113,21305,4429],{"class":2119},[2113,21307,19225],{"class":2414},[2113,21309,2433],{"class":2119},[2113,21311,21312,21314,21317,21319,21321,21323,21325,21327,21329,21332,21334,21336],{"class":2115,"line":5018},[2113,21313,19244],{"class":2326},[2113,21315,21316],{"class":2414}," stocks",[2113,21318,2153],{"class":2334},[2113,21320,17521],{"class":2326},[2113,21322,15832],{"class":2414},[2113,21324,179],{"class":2119},[2113,21326,16847],{"class":2414},[2113,21328,179],{"class":2119},[2113,21330,21331],{"class":2133},"getVtcElectectricL",[2113,21333,2423],{"class":2119},[2113,21335,184],{"class":2330},[2113,21337,2553],{"class":2119},[2113,21339,21340,21342,21345,21347,21349,21351,21353,21356,21358,21361,21363,21365],{"class":2115,"line":5042},[2113,21341,19244],{"class":2326},[2113,21343,21344],{"class":2414}," oldStocks",[2113,21346,2153],{"class":2334},[2113,21348,17521],{"class":2326},[2113,21350,15832],{"class":2414},[2113,21352,179],{"class":2119},[2113,21354,21355],{"class":2414},"decathlonService",[2113,21357,179],{"class":2119},[2113,21359,21360],{"class":2133},"findOneById",[2113,21362,2423],{"class":2119},[2113,21364,184],{"class":2330},[2113,21366,2553],{"class":2119},[2113,21368,21369,21371,21374,21376,21378,21380,21383,21385,21388,21391,21393,21395,21397],{"class":2115,"line":5057},[2113,21370,19244],{"class":2326},[2113,21372,21373],{"class":2414}," diffStocks",[2113,21375,2153],{"class":2334},[2113,21377,15832],{"class":2414},[2113,21379,179],{"class":2119},[2113,21381,21382],{"class":2133},"diffStocks",[2113,21384,2423],{"class":2119},[2113,21386,21387],{"class":2414},"oldStocks",[2113,21389,21390],{"class":2119},"?.",[2113,21392,20823],{"class":2330},[2113,21394,932],{"class":2119},[2113,21396,19546],{"class":2330},[2113,21398,2553],{"class":2119},[2113,21400,21401,21403,21405,21407,21409,21412,21414,21417,21419,21421,21423,21425,21427,21430],{"class":2115,"line":5062},[2113,21402,20377],{"class":2414},[2113,21404,179],{"class":2119},[2113,21406,20382],{"class":2414},[2113,21408,179],{"class":2119},[2113,21410,21411],{"class":2133},"debug",[2113,21413,2423],{"class":2119},[2113,21415,21416],{"class":2149},"`The are ",[2113,21418,15877],{"class":2326},[2113,21420,21382],{"class":2414},[2113,21422,179],{"class":2119},[2113,21424,20410],{"class":2330},[2113,21426,15888],{"class":2326},[2113,21428,21429],{"class":2149}," stocks that change`",[2113,21431,2553],{"class":2119},[2113,21433,21434],{"class":2115,"line":5098},[2113,21435,2125],{"emptyLinePlaceholder":1767},[2113,21437,21438,21440,21442],{"class":2115,"line":5117},[2113,21439,3917],{"class":2326},[2113,21441,21091],{"class":2330},[2113,21443,2487],{"class":2119},[2113,21445,21446,21448,21450,21452,21454,21456],{"class":2115,"line":5142},[2113,21447,7394],{"class":2326},[2113,21449,2495],{"class":2119},[2113,21451,21382],{"class":2414},[2113,21453,179],{"class":2119},[2113,21455,20410],{"class":2330},[2113,21457,2433],{"class":2119},[2113,21459,21460,21463,21465],{"class":2115,"line":5148},[2113,21461,21462],{"class":2330},"      message",[2113,21464,2153],{"class":2334},[2113,21466,21467],{"class":2330}," diffStocks\n",[2113,21469,21470,21472,21474,21476,21479,21481,21483,21486,21488,21490,21492,21495,21497,21499,21501,21503,21505,21507,21509,21511],{"class":2115,"line":5163},[2113,21471,10331],{"class":2119},[2113,21473,6644],{"class":2133},[2113,21475,8879],{"class":2119},[2113,21477,21478],{"class":2429},"value",[2113,21480,5709],{"class":2119},[2113,21482,8063],{"class":2326},[2113,21484,21485],{"class":2149}," `  ",[2113,21487,15877],{"class":2326},[2113,21489,21478],{"class":2414},[2113,21491,179],{"class":2119},[2113,21493,21494],{"class":2330},"type",[2113,21496,15888],{"class":2326},[2113,21498,10873],{"class":2149},[2113,21500,15877],{"class":2326},[2113,21502,21478],{"class":2414},[2113,21504,179],{"class":2119},[2113,21506,19546],{"class":2330},[2113,21508,15888],{"class":2326},[2113,21510,20427],{"class":2149},[2113,21512,4660],{"class":2119},[2113,21514,21515,21517,21519,21521,21523,21525,21527],{"class":2115,"line":5169},[2113,21516,10331],{"class":2119},[2113,21518,7230],{"class":2133},[2113,21520,2423],{"class":2119},[2113,21522,21212],{"class":2149},[2113,21524,20418],{"class":2334},[2113,21526,21212],{"class":2149},[2113,21528,2553],{"class":2119},[2113,21530,21531],{"class":2115,"line":5175},[2113,21532,4665],{"class":2119},[2113,21534,21535],{"class":2115,"line":5180},[2113,21536,2125],{"emptyLinePlaceholder":1767},[2113,21538,21539,21541,21543,21545,21547,21549,21552,21554,21556,21558,21560],{"class":2115,"line":5199},[2113,21540,20239],{"class":2326},[2113,21542,15832],{"class":2414},[2113,21544,179],{"class":2119},[2113,21546,21355],{"class":2414},[2113,21548,179],{"class":2119},[2113,21550,21551],{"class":2133},"update",[2113,21553,2423],{"class":2119},[2113,21555,184],{"class":2330},[2113,21557,932],{"class":2119},[2113,21559,19546],{"class":2330},[2113,21561,2553],{"class":2119},[2113,21563,21564,21566,21568,21570,21572,21574,21576,21578,21580,21582,21584,21586,21588,21590],{"class":2115,"line":5204},[2113,21565,3137],{"class":2326},[2113,21567,21091],{"class":2330},[2113,21569,2506],{"class":2334},[2113,21571,17813],{"class":2149},[2113,21573,15877],{"class":2326},[2113,21575,21011],{"class":2330},[2113,21577,15888],{"class":2326},[2113,21579,15746],{"class":2149},[2113,21581,20418],{"class":2334},[2113,21583,15877],{"class":2326},[2113,21585,16052],{"class":2330},[2113,21587,15888],{"class":2326},[2113,21589,20427],{"class":2149},[2113,21591,2487],{"class":2119},[2113,21593,21594],{"class":2115,"line":5209},[2113,21595,2572],{"class":2119},[2113,21597,21598],{"class":2115,"line":5232},[2113,21599,2125],{"emptyLinePlaceholder":1767},[2113,21601,21602,21605,21607,21610,21612,21615,21617,21620,21622,21624],{"class":2115,"line":5237},[2113,21603,21604],{"class":2133},"  diffStocks",[2113,21606,2423],{"class":2119},[2113,21608,21609],{"class":2429},"oldInfos",[2113,21611,4429],{"class":2119},[2113,21613,21614],{"class":2414},"StockInformations",[2113,21616,932],{"class":2119},[2113,21618,21619],{"class":2429},"newInfos",[2113,21621,4429],{"class":2119},[2113,21623,21614],{"class":2414},[2113,21625,2433],{"class":2119},[2113,21627,21628,21631,21633,21635,21637,21639],{"class":2115,"line":5242},[2113,21629,21630],{"class":2330},"    oldInfos",[2113,21632,4140],{"class":2334},[2113,21634,2495],{"class":2119},[2113,21636,21609],{"class":2330},[2113,21638,2153],{"class":2334},[2113,21640,21641],{"class":2119}," []);\n",[2113,21643,21644,21647,21649,21651,21653,21655],{"class":2115,"line":5267},[2113,21645,21646],{"class":2330},"    newInfos",[2113,21648,4140],{"class":2334},[2113,21650,2495],{"class":2119},[2113,21652,21619],{"class":2330},[2113,21654,2153],{"class":2334},[2113,21656,21641],{"class":2119},[2113,21658,21659],{"class":2115,"line":5282},[2113,21660,2125],{"emptyLinePlaceholder":1767},[2113,21662,21663,21665,21668,21670,21673,21675,21678,21680,21683,21685,21687,21689,21691],{"class":2115,"line":5295},[2113,21664,19244],{"class":2326},[2113,21666,21667],{"class":2414}," oldMap",[2113,21669,2153],{"class":2334},[2113,21671,21672],{"class":2414}," oldInfos",[2113,21674,179],{"class":2119},[2113,21676,21677],{"class":2133},"reduce",[2113,21679,8879],{"class":2119},[2113,21681,21682],{"class":2429},"acc",[2113,21684,932],{"class":2119},[2113,21686,7919],{"class":2429},[2113,21688,5709],{"class":2119},[2113,21690,8063],{"class":2326},[2113,21692,2647],{"class":2119},[2113,21694,21695,21698,21700,21702,21704,21706,21708,21710,21712],{"class":2115,"line":5310},[2113,21696,21697],{"class":2330},"      acc",[2113,21699,4124],{"class":2119},[2113,21701,7919],{"class":2414},[2113,21703,179],{"class":2119},[2113,21705,21494],{"class":2330},[2113,21707,4129],{"class":2119},[2113,21709,2335],{"class":2334},[2113,21711,7910],{"class":2330},[2113,21713,2487],{"class":2119},[2113,21715,21716,21718,21721],{"class":2115,"line":5315},[2113,21717,20652],{"class":2326},[2113,21719,21720],{"class":2330}," acc",[2113,21722,2487],{"class":2119},[2113,21724,21725,21728,21730,21733,21735,21737,21739,21741],{"class":2115,"line":5320},[2113,21726,21727],{"class":2119},"    }, {} ",[2113,21729,6242],{"class":2326},[2113,21731,21732],{"class":2414}," Record",[2113,21734,3109],{"class":2119},[2113,21736,19225],{"class":2414},[2113,21738,932],{"class":2119},[2113,21740,19236],{"class":2414},[2113,21742,21743],{"class":2119},">);\n",[2113,21745,21746],{"class":2115,"line":8625},[2113,21747,2125],{"emptyLinePlaceholder":1767},[2113,21749,21750,21752,21755,21757,21759,21761,21764,21766,21768],{"class":2115,"line":8631},[2113,21751,3137],{"class":2326},[2113,21753,21754],{"class":2414}," newInfos",[2113,21756,179],{"class":2119},[2113,21758,21186],{"class":2133},[2113,21760,8879],{"class":2119},[2113,21762,21763],{"class":2429},"info",[2113,21765,5709],{"class":2119},[2113,21767,8063],{"class":2326},[2113,21769,2647],{"class":2119},[2113,21771,21772,21774,21776,21778,21780,21782,21784,21786,21788,21790,21792,21794,21796],{"class":2115,"line":8637},[2113,21773,20579],{"class":2326},[2113,21775,21344],{"class":2414},[2113,21777,2153],{"class":2334},[2113,21779,21667],{"class":2330},[2113,21781,4124],{"class":2119},[2113,21783,21763],{"class":2414},[2113,21785,179],{"class":2119},[2113,21787,21494],{"class":2330},[2113,21789,19328],{"class":2119},[2113,21791,19546],{"class":2330},[2113,21793,4140],{"class":2334},[2113,21795,3208],{"class":2166},[2113,21797,2487],{"class":2119},[2113,21799,21800,21802,21804,21807,21809,21811,21813,21815,21817,21819],{"class":2115,"line":8652},[2113,21801,20652],{"class":2326},[2113,21803,21344],{"class":2330},[2113,21805,21806],{"class":2334}," !==",[2113,21808,2495],{"class":2119},[2113,21810,21763],{"class":2414},[2113,21812,21390],{"class":2119},[2113,21814,19546],{"class":2330},[2113,21816,4140],{"class":2334},[2113,21818,3208],{"class":2166},[2113,21820,2553],{"class":2119},[2113,21822,21823],{"class":2115,"line":8674},[2113,21824,19378],{"class":2119},[2113,21826,21827],{"class":2115,"line":8706},[2113,21828,2572],{"class":2119},[2113,21830,21831],{"class":2115,"line":8724},[2113,21832,2599],{"class":2119},[12,21834,21835],{},"Cela me permet de recevoir un SMS de la forme :",[12,21837,21838],{},[121,21839],{"alt":21840,"className":21841,"src":21842},"Les SMS",[126],"\u002FProgrammation\u002Fachat_velo\u002FSMS.png",[128,21844,21846],{"id":21845},"déploiement-de-la-notification","Déploiement de la notification",[12,21848,21849],{},"Pour déployer la notification, il me faut deux endpoints (un pour la partie web, et l'autre pour la notification).",[12,21851,21852,21853,21856,21857,21860],{},"Je garde donc dans le dossier ",[184,21854,21855],{},"src"," le fichier ",[184,21858,21859],{},"main.ts"," pour la partie web et je créé un point d'entrée spécial pour la lambda de notification:",[2105,21862,21864],{"className":15749,"code":21863,"language":15751,"meta":1646,"style":1646},"\nimport { HttpStatus } from '@nestjs\u002Fcommon';\nimport { NestFactory } from '@nestjs\u002Fcore';\nimport { Handler } from 'aws-lambda';\n\nimport { CheckAvailabilityModule } from '.\u002Fcheck-availability.module';\nimport { AvailibilityCheckerService } from '.\u002Favailability\u002Favailibility-checker.service';\n\nlet service: AvailibilityCheckerService;\n\nasync function bootstrap(): Promise\u003CAvailibilityCheckerService> {\n  const appContext = await NestFactory.createApplicationContext(\n    CheckAvailabilityModule,\n  );\n  return appContext.get(AvailibilityCheckerService);\n}\n\nexport const handler: Handler = async () => {\n  service = service ?? (await bootstrap());\n\n  await service.checkAvailibility();\n\n  return {\n    statusCode: HttpStatus.OK,\n  };\n};\n",[184,21865,21866,21870,21888,21906,21924,21928,21946,21963,21967,21979,21983,22004,22025,22032,22036,22052,22056,22060,22083,22104,22108,22122,22126,22132,22148,22153],{"__ignoreMap":1646},[2113,21867,21868],{"class":2115,"line":2116},[2113,21869,2125],{"emptyLinePlaceholder":1767},[2113,21871,21872,21874,21876,21879,21881,21883,21886],{"class":2115,"line":1647},[2113,21873,15758],{"class":2326},[2113,21875,7616],{"class":2119},[2113,21877,21878],{"class":2330},"HttpStatus",[2113,21880,7840],{"class":2119},[2113,21882,15787],{"class":2326},[2113,21884,21885],{"class":2149}," '@nestjs\u002Fcommon'",[2113,21887,2487],{"class":2119},[2113,21889,21890,21892,21894,21897,21899,21901,21904],{"class":2115,"line":1652},[2113,21891,15758],{"class":2326},[2113,21893,7616],{"class":2119},[2113,21895,21896],{"class":2330},"NestFactory",[2113,21898,7840],{"class":2119},[2113,21900,15787],{"class":2326},[2113,21902,21903],{"class":2149}," '@nestjs\u002Fcore'",[2113,21905,2487],{"class":2119},[2113,21907,21908,21910,21912,21915,21917,21919,21922],{"class":2115,"line":2185},[2113,21909,15758],{"class":2326},[2113,21911,7616],{"class":2119},[2113,21913,21914],{"class":2330},"Handler",[2113,21916,7840],{"class":2119},[2113,21918,15787],{"class":2326},[2113,21920,21921],{"class":2149}," 'aws-lambda'",[2113,21923,2487],{"class":2119},[2113,21925,21926],{"class":2115,"line":2230},[2113,21927,2125],{"emptyLinePlaceholder":1767},[2113,21929,21930,21932,21934,21937,21939,21941,21944],{"class":2115,"line":2235},[2113,21931,15758],{"class":2326},[2113,21933,7616],{"class":2119},[2113,21935,21936],{"class":2330},"CheckAvailabilityModule",[2113,21938,7840],{"class":2119},[2113,21940,15787],{"class":2326},[2113,21942,21943],{"class":2149}," '.\u002Fcheck-availability.module'",[2113,21945,2487],{"class":2119},[2113,21947,21948,21950,21952,21954,21956,21958,21961],{"class":2115,"line":2241},[2113,21949,15758],{"class":2326},[2113,21951,7616],{"class":2119},[2113,21953,21006],{"class":2330},[2113,21955,7840],{"class":2119},[2113,21957,15787],{"class":2326},[2113,21959,21960],{"class":2149}," '.\u002Favailability\u002Favailibility-checker.service'",[2113,21962,2487],{"class":2119},[2113,21964,21965],{"class":2115,"line":2246},[2113,21966,2125],{"emptyLinePlaceholder":1767},[2113,21968,21969,21971,21973,21975,21977],{"class":2115,"line":2464},[2113,21970,7907],{"class":2326},[2113,21972,21031],{"class":2330},[2113,21974,4429],{"class":2119},[2113,21976,21006],{"class":2414},[2113,21978,2487],{"class":2119},[2113,21980,21981],{"class":2115,"line":2085},[2113,21982,2125],{"emptyLinePlaceholder":1767},[2113,21984,21985,21988,21990,21993,21996,21998,22000,22002],{"class":2115,"line":2514},[2113,21986,21987],{"class":2326},"async",[2113,21989,17728],{"class":2326},[2113,21991,21992],{"class":2133}," bootstrap",[2113,21994,21995],{"class":2119},"(): ",[2113,21997,19231],{"class":2414},[2113,21999,3109],{"class":2119},[2113,22001,21006],{"class":2414},[2113,22003,4449],{"class":2119},[2113,22005,22006,22008,22011,22013,22015,22018,22020,22023],{"class":2115,"line":2533},[2113,22007,2995],{"class":2326},[2113,22009,22010],{"class":2414}," appContext",[2113,22012,2153],{"class":2334},[2113,22014,17521],{"class":2326},[2113,22016,22017],{"class":2414}," NestFactory",[2113,22019,179],{"class":2119},[2113,22021,22022],{"class":2133},"createApplicationContext",[2113,22024,3146],{"class":2119},[2113,22026,22027,22030],{"class":2115,"line":2556},[2113,22028,22029],{"class":2330},"    CheckAvailabilityModule",[2113,22031,4706],{"class":2119},[2113,22033,22034],{"class":2115,"line":2569},[2113,22035,9185],{"class":2119},[2113,22037,22038,22040,22042,22044,22046,22048,22050],{"class":2115,"line":2575},[2113,22039,2578],{"class":2326},[2113,22041,22010],{"class":2414},[2113,22043,179],{"class":2119},[2113,22045,19283],{"class":2133},[2113,22047,2423],{"class":2119},[2113,22049,21006],{"class":2330},[2113,22051,2553],{"class":2119},[2113,22053,22054],{"class":2115,"line":2596},[2113,22055,2599],{"class":2119},[2113,22057,22058],{"class":2115,"line":3192},[2113,22059,2125],{"emptyLinePlaceholder":1767},[2113,22061,22062,22064,22066,22069,22071,22073,22075,22077,22079,22081],{"class":2115,"line":3213},[2113,22063,2327],{"class":2326},[2113,22065,2839],{"class":2326},[2113,22067,22068],{"class":2133}," handler",[2113,22070,4429],{"class":2119},[2113,22072,21914],{"class":2414},[2113,22074,2153],{"class":2334},[2113,22076,17725],{"class":2326},[2113,22078,8060],{"class":2119},[2113,22080,8063],{"class":2326},[2113,22082,2647],{"class":2119},[2113,22084,22085,22088,22090,22092,22095,22097,22100,22102],{"class":2115,"line":3236},[2113,22086,22087],{"class":2330},"  service",[2113,22089,2153],{"class":2334},[2113,22091,21031],{"class":2330},[2113,22093,22094],{"class":2334}," ??",[2113,22096,2495],{"class":2119},[2113,22098,22099],{"class":2326},"await",[2113,22101,21992],{"class":2133},[2113,22103,2593],{"class":2119},[2113,22105,22106],{"class":2115,"line":3248},[2113,22107,2125],{"emptyLinePlaceholder":1767},[2113,22109,22110,22113,22115,22117,22120],{"class":2115,"line":4899},[2113,22111,22112],{"class":2326},"  await",[2113,22114,21031],{"class":2414},[2113,22116,179],{"class":2119},[2113,22118,22119],{"class":2133},"checkAvailibility",[2113,22121,3944],{"class":2119},[2113,22123,22124],{"class":2115,"line":1777},[2113,22125,2125],{"emptyLinePlaceholder":1767},[2113,22127,22128,22130],{"class":2115,"line":4931},[2113,22129,2578],{"class":2326},[2113,22131,2647],{"class":2119},[2113,22133,22134,22137,22139,22141,22143,22146],{"class":2115,"line":4961},[2113,22135,22136],{"class":2330},"    statusCode",[2113,22138,4429],{"class":2119},[2113,22140,21878],{"class":2414},[2113,22142,179],{"class":2119},[2113,22144,22145],{"class":2330},"OK",[2113,22147,4706],{"class":2119},[2113,22149,22150],{"class":2115,"line":4976},[2113,22151,22152],{"class":2119},"  };\n",[2113,22154,22155],{"class":2115,"line":4993},[2113,22156,2787],{"class":2119},[12,22158,22159],{},"Contrairement à la partie WEB ce service ne démarre pas de serveur Web (ni la partie express).",[12,22161,22162],{},"Le paramétrage de la lambda se fait donc avec le fichier serverless.yml dont voici les principales modifications:",[2105,22164,22166],{"className":19667,"code":22165,"language":19669,"meta":1646,"style":1646},"service: decathlon\nframeworkVersion: \"2\"\n\nplugins:\n  - serverless-dynamodb-local\n  - serverless-plugin-log-retention\n  - serverless-offline\n  - serverless-domain-manager\n\ncustom:\n  region: eu-central-1\n  logRetentionInDays: 7\n  serverless-offline:\n    noPrependStageInUrl: true\n    allowCache: false\n  decathlonTable:\n    name: ${self:provider.stage}-decathlon\n    arn: !GetAtt decathlonTable.Arn\n  dynamodb:\n    stages:\n      - ${self:provider.stage}\n    start:\n      migrate: true\n  endpoints:\n    dynamodbURL: \"http:\u002F\u002Flocalhost:8000\"\n  customDomain: ...\n\nprovider:\n  name: aws\n  region: ${self:custom.region}\n  runtime: nodejs12.x\n  lambdaHashingVersion: \"20201221\"\n  stage: ${opt:stage, 'dev'}\n  environment:\n    DYNAMODB_ENDPOINT: ${self:custom.endpoints.dynamodbURL}\n    DECATHLON_TABLE_NAME: ${self:custom.decathlonTable.name}\n    NODE_OPTIONS: \"--max-http-header-size=80000\"\n  iamRoleStatements: ${file(delivery\u002Froles.yml)}\n\npackage:\n  individually: true\n  patterns:\n    - \"!.\u002F**\"\n\nfunctions:\n  api:\n    handler: dist\u002Fmain.handler\n    environment:\n      region: ${self:custom.region}\n    events:\n      - http:\n          path: \u002F\n          method: ANY\n      - http:\n          path: \u002F{proxy+}\n          method: ANY\n    package:\n      patterns:\n        - dist\u002Fmain.js\n        - views\u002F**\n\n  check-availability:\n    handler: dist\u002Fcheck-availability.handler\n    events:\n      - schedule: rate(30 minutes)\n    package:\n      patterns:\n        - dist\u002Fcheck-availability.js\n\nresources:\n  Resources:\n    decathlonTable: ${file(delivery\u002FdecathlonTable.yml):decathlonTable}\n",[184,22167,22168,22177,22185,22189,22195,22202,22208,22214,22220,22224,22230,22238,22246,22252,22260,22268,22275,22285,22298,22305,22312,22319,22326,22335,22342,22352,22360,22364,22370,22378,22386,22394,22402,22410,22416,22426,22436,22444,22454,22458,22464,22473,22479,22485,22489,22495,22501,22509,22515,22523,22529,22537,22545,22553,22561,22569,22577,22584,22591,22599,22605,22609,22616,22625,22631,22643,22649,22655,22662,22666,22673,22680],{"__ignoreMap":1646},[2113,22169,22170,22172,22174],{"class":2115,"line":2116},[2113,22171,16847],{"class":2330},[2113,22173,4429],{"class":2119},[2113,22175,22176],{"class":2149},"decathlon\n",[2113,22178,22179,22181,22183],{"class":2115,"line":1647},[2113,22180,19685],{"class":2330},[2113,22182,4429],{"class":2119},[2113,22184,19690],{"class":2149},[2113,22186,22187],{"class":2115,"line":1652},[2113,22188,2125],{"emptyLinePlaceholder":1767},[2113,22190,22191,22193],{"class":2115,"line":2185},[2113,22192,19699],{"class":2330},[2113,22194,19702],{"class":2119},[2113,22196,22197,22199],{"class":2115,"line":2230},[2113,22198,19707],{"class":2119},[2113,22200,22201],{"class":2149},"serverless-dynamodb-local\n",[2113,22203,22204,22206],{"class":2115,"line":2235},[2113,22205,19707],{"class":2119},[2113,22207,19710],{"class":2149},[2113,22209,22210,22212],{"class":2115,"line":2241},[2113,22211,19707],{"class":2119},[2113,22213,19717],{"class":2149},[2113,22215,22216,22218],{"class":2115,"line":2246},[2113,22217,19707],{"class":2119},[2113,22219,19724],{"class":2149},[2113,22221,22222],{"class":2115,"line":2464},[2113,22223,2125],{"emptyLinePlaceholder":1767},[2113,22225,22226,22228],{"class":2115,"line":2085},[2113,22227,19733],{"class":2330},[2113,22229,19702],{"class":2119},[2113,22231,22232,22234,22236],{"class":2115,"line":2514},[2113,22233,19740],{"class":2330},[2113,22235,4429],{"class":2119},[2113,22237,19745],{"class":2149},[2113,22239,22240,22242,22244],{"class":2115,"line":2533},[2113,22241,19750],{"class":2330},[2113,22243,4429],{"class":2119},[2113,22245,19755],{"class":2166},[2113,22247,22248,22250],{"class":2115,"line":2556},[2113,22249,19760],{"class":2330},[2113,22251,19702],{"class":2119},[2113,22253,22254,22256,22258],{"class":2115,"line":2569},[2113,22255,19767],{"class":2330},[2113,22257,4429],{"class":2119},[2113,22259,19772],{"class":2166},[2113,22261,22262,22264,22266],{"class":2115,"line":2575},[2113,22263,19777],{"class":2330},[2113,22265,4429],{"class":2119},[2113,22267,19782],{"class":2166},[2113,22269,22270,22273],{"class":2115,"line":2596},[2113,22271,22272],{"class":2330},"  decathlonTable",[2113,22274,19702],{"class":2119},[2113,22276,22277,22280,22282],{"class":2115,"line":3192},[2113,22278,22279],{"class":2330},"    name",[2113,22281,4429],{"class":2119},[2113,22283,22284],{"class":2149},"${self:provider.stage}-decathlon\n",[2113,22286,22287,22290,22292,22295],{"class":2115,"line":3213},[2113,22288,22289],{"class":2330},"    arn",[2113,22291,4429],{"class":2119},[2113,22293,22294],{"class":2326},"!GetAtt",[2113,22296,22297],{"class":2149}," decathlonTable.Arn\n",[2113,22299,22300,22303],{"class":2115,"line":3236},[2113,22301,22302],{"class":2330},"  dynamodb",[2113,22304,19702],{"class":2119},[2113,22306,22307,22310],{"class":2115,"line":3248},[2113,22308,22309],{"class":2330},"    stages",[2113,22311,19702],{"class":2119},[2113,22313,22314,22316],{"class":2115,"line":4899},[2113,22315,20039],{"class":2119},[2113,22317,22318],{"class":2149},"${self:provider.stage}\n",[2113,22320,22321,22324],{"class":2115,"line":1777},[2113,22322,22323],{"class":2330},"    start",[2113,22325,19702],{"class":2119},[2113,22327,22328,22331,22333],{"class":2115,"line":4931},[2113,22329,22330],{"class":2330},"      migrate",[2113,22332,4429],{"class":2119},[2113,22334,19772],{"class":2166},[2113,22336,22337,22340],{"class":2115,"line":4961},[2113,22338,22339],{"class":2330},"  endpoints",[2113,22341,19702],{"class":2119},[2113,22343,22344,22347,22349],{"class":2115,"line":4976},[2113,22345,22346],{"class":2330},"    dynamodbURL",[2113,22348,4429],{"class":2119},[2113,22350,22351],{"class":2149},"\"http:\u002F\u002Flocalhost:8000\"\n",[2113,22353,22354,22356,22358],{"class":2115,"line":4993},[2113,22355,19787],{"class":2330},[2113,22357,4429],{"class":2119},[2113,22359,4902],{"class":2166},[2113,22361,22362],{"class":2115,"line":5013},[2113,22363,2125],{"emptyLinePlaceholder":1767},[2113,22365,22366,22368],{"class":2115,"line":5018},[2113,22367,19876],{"class":2330},[2113,22369,19702],{"class":2119},[2113,22371,22372,22374,22376],{"class":2115,"line":5042},[2113,22373,19883],{"class":2330},[2113,22375,4429],{"class":2119},[2113,22377,19888],{"class":2149},[2113,22379,22380,22382,22384],{"class":2115,"line":5057},[2113,22381,19740],{"class":2330},[2113,22383,4429],{"class":2119},[2113,22385,19897],{"class":2149},[2113,22387,22388,22390,22392],{"class":2115,"line":5062},[2113,22389,19902],{"class":2330},[2113,22391,4429],{"class":2119},[2113,22393,19907],{"class":2149},[2113,22395,22396,22398,22400],{"class":2115,"line":5098},[2113,22397,19912],{"class":2330},[2113,22399,4429],{"class":2119},[2113,22401,19917],{"class":2149},[2113,22403,22404,22406,22408],{"class":2115,"line":5117},[2113,22405,19922],{"class":2330},[2113,22407,4429],{"class":2119},[2113,22409,19927],{"class":2149},[2113,22411,22412,22414],{"class":2115,"line":5142},[2113,22413,19932],{"class":2330},[2113,22415,19702],{"class":2119},[2113,22417,22418,22421,22423],{"class":2115,"line":5148},[2113,22419,22420],{"class":2330},"    DYNAMODB_ENDPOINT",[2113,22422,4429],{"class":2119},[2113,22424,22425],{"class":2149},"${self:custom.endpoints.dynamodbURL}\n",[2113,22427,22428,22431,22433],{"class":2115,"line":5163},[2113,22429,22430],{"class":2330},"    DECATHLON_TABLE_NAME",[2113,22432,4429],{"class":2119},[2113,22434,22435],{"class":2149},"${self:custom.decathlonTable.name}\n",[2113,22437,22438,22440,22442],{"class":2115,"line":5169},[2113,22439,19939],{"class":2330},[2113,22441,4429],{"class":2119},[2113,22443,19944],{"class":2149},[2113,22445,22446,22449,22451],{"class":2115,"line":5175},[2113,22447,22448],{"class":2330},"  iamRoleStatements",[2113,22450,4429],{"class":2119},[2113,22452,22453],{"class":2149},"${file(delivery\u002Froles.yml)}\n",[2113,22455,22456],{"class":2115,"line":5180},[2113,22457,2125],{"emptyLinePlaceholder":1767},[2113,22459,22460,22462],{"class":2115,"line":5199},[2113,22461,15998],{"class":2330},[2113,22463,19702],{"class":2119},[2113,22465,22466,22469,22471],{"class":2115,"line":5204},[2113,22467,22468],{"class":2330},"  individually",[2113,22470,4429],{"class":2119},[2113,22472,19772],{"class":2166},[2113,22474,22475,22477],{"class":2115,"line":5209},[2113,22476,19959],{"class":2330},[2113,22478,19702],{"class":2119},[2113,22480,22481,22483],{"class":2115,"line":5232},[2113,22482,19966],{"class":2119},[2113,22484,19969],{"class":2149},[2113,22486,22487],{"class":2115,"line":5237},[2113,22488,2125],{"emptyLinePlaceholder":1767},[2113,22490,22491,22493],{"class":2115,"line":5242},[2113,22492,19992],{"class":2330},[2113,22494,19702],{"class":2119},[2113,22496,22497,22499],{"class":2115,"line":5267},[2113,22498,19999],{"class":2330},[2113,22500,19702],{"class":2119},[2113,22502,22503,22505,22507],{"class":2115,"line":5282},[2113,22504,20006],{"class":2330},[2113,22506,4429],{"class":2119},[2113,22508,20011],{"class":2149},[2113,22510,22511,22513],{"class":2115,"line":5295},[2113,22512,20016],{"class":2330},[2113,22514,19702],{"class":2119},[2113,22516,22517,22519,22521],{"class":2115,"line":5310},[2113,22518,20023],{"class":2330},[2113,22520,4429],{"class":2119},[2113,22522,19897],{"class":2149},[2113,22524,22525,22527],{"class":2115,"line":5315},[2113,22526,20032],{"class":2330},[2113,22528,19702],{"class":2119},[2113,22530,22531,22533,22535],{"class":2115,"line":5320},[2113,22532,20039],{"class":2119},[2113,22534,18039],{"class":2330},[2113,22536,19702],{"class":2119},[2113,22538,22539,22541,22543],{"class":2115,"line":8625},[2113,22540,20048],{"class":2330},[2113,22542,4429],{"class":2119},[2113,22544,20053],{"class":2149},[2113,22546,22547,22549,22551],{"class":2115,"line":8631},[2113,22548,20058],{"class":2330},[2113,22550,4429],{"class":2119},[2113,22552,20063],{"class":2149},[2113,22554,22555,22557,22559],{"class":2115,"line":8637},[2113,22556,20039],{"class":2119},[2113,22558,18039],{"class":2330},[2113,22560,19702],{"class":2119},[2113,22562,22563,22565,22567],{"class":2115,"line":8652},[2113,22564,20048],{"class":2330},[2113,22566,4429],{"class":2119},[2113,22568,20080],{"class":2149},[2113,22570,22571,22573,22575],{"class":2115,"line":8674},[2113,22572,20058],{"class":2330},[2113,22574,4429],{"class":2119},[2113,22576,20063],{"class":2149},[2113,22578,22579,22582],{"class":2115,"line":8706},[2113,22580,22581],{"class":2330},"    package",[2113,22583,19702],{"class":2119},[2113,22585,22586,22589],{"class":2115,"line":8724},[2113,22587,22588],{"class":2330},"      patterns",[2113,22590,19702],{"class":2119},[2113,22592,22593,22596],{"class":2115,"line":8729},[2113,22594,22595],{"class":2119},"        - ",[2113,22597,22598],{"class":2149},"dist\u002Fmain.js\n",[2113,22600,22601,22603],{"class":2115,"line":8734},[2113,22602,22595],{"class":2119},[2113,22604,19983],{"class":2149},[2113,22606,22607],{"class":2115,"line":8740},[2113,22608,2125],{"emptyLinePlaceholder":1767},[2113,22610,22611,22614],{"class":2115,"line":8772},[2113,22612,22613],{"class":2330},"  check-availability",[2113,22615,19702],{"class":2119},[2113,22617,22618,22620,22622],{"class":2115,"line":8778},[2113,22619,20006],{"class":2330},[2113,22621,4429],{"class":2119},[2113,22623,22624],{"class":2149},"dist\u002Fcheck-availability.handler\n",[2113,22626,22627,22629],{"class":2115,"line":8789},[2113,22628,20032],{"class":2330},[2113,22630,19702],{"class":2119},[2113,22632,22633,22635,22638,22640],{"class":2115,"line":8795},[2113,22634,20039],{"class":2119},[2113,22636,22637],{"class":2330},"schedule",[2113,22639,4429],{"class":2119},[2113,22641,22642],{"class":2149},"rate(30 minutes)\n",[2113,22644,22645,22647],{"class":2115,"line":8817},[2113,22646,22581],{"class":2330},[2113,22648,19702],{"class":2119},[2113,22650,22651,22653],{"class":2115,"line":8822},[2113,22652,22588],{"class":2330},[2113,22654,19702],{"class":2119},[2113,22656,22657,22659],{"class":2115,"line":8857},[2113,22658,22595],{"class":2119},[2113,22660,22661],{"class":2149},"dist\u002Fcheck-availability.js\n",[2113,22663,22664],{"class":2115,"line":8896},[2113,22665,2125],{"emptyLinePlaceholder":1767},[2113,22667,22668,22671],{"class":2115,"line":8901},[2113,22669,22670],{"class":2330},"resources",[2113,22672,19702],{"class":2119},[2113,22674,22675,22678],{"class":2115,"line":8907},[2113,22676,22677],{"class":2330},"  Resources",[2113,22679,19702],{"class":2119},[2113,22681,22682,22685,22687],{"class":2115,"line":8913},[2113,22683,22684],{"class":2330},"    decathlonTable",[2113,22686,4429],{"class":2119},[2113,22688,22689],{"class":2149},"${file(delivery\u002FdecathlonTable.yml):decathlonTable}\n",[12,22691,22692],{},"Il ne reste plus qu'à déployer et profiter de l'envoi de SMS.",[128,22694,22696],{"id":22695},"optimisation","Optimisation",[12,22698,22699],{},"Sur AWS, le coût d'une lambda est lié au nombre d'exécutions mais aussi au temps d'exécution. Il est important que le temps d'exécution soit le plus\nrapide possible.",[12,22701,22702],{},"Ici dans notre cas le temps le temps de traitement sera lié au temps de réponse de la requête HTTP extérieur à notre site. Afin de parfaire notre petite\napplication de bonne pratique, nous allons utiliser webpack (côté serveur) afin d'optimiser le temps de démarrage de notre application.",[12,22704,22705,22706,22709],{},"Grâce au framework Nest.JS, cela est très facile à faire, dans le fichier ",[184,22707,22708],{},"nest-cli.json",", nous allons demander à Nest.JS de générer un bundle webpack:",[2105,22711,22713],{"className":18051,"code":22712,"language":18053,"meta":1646,"style":1646},"{\n  \"collection\": \"@nestjs\u002Fschematics\",\n  \"sourceRoot\": \"src\",\n  \"compilerOptions\": {\n    \"webpack\": true\n  }\n}\n",[184,22714,22715,22719,22731,22743,22750,22759,22763],{"__ignoreMap":1646},[2113,22716,22717],{"class":2115,"line":2116},[2113,22718,18060],{"class":2119},[2113,22720,22721,22724,22726,22729],{"class":2115,"line":1647},[2113,22722,22723],{"class":2330},"  \"collection\"",[2113,22725,4429],{"class":2119},[2113,22727,22728],{"class":2149},"\"@nestjs\u002Fschematics\"",[2113,22730,4706],{"class":2119},[2113,22732,22733,22736,22738,22741],{"class":2115,"line":1652},[2113,22734,22735],{"class":2330},"  \"sourceRoot\"",[2113,22737,4429],{"class":2119},[2113,22739,22740],{"class":2149},"\"src\"",[2113,22742,4706],{"class":2119},[2113,22744,22745,22748],{"class":2115,"line":2185},[2113,22746,22747],{"class":2330},"  \"compilerOptions\"",[2113,22749,8091],{"class":2119},[2113,22751,22752,22755,22757],{"class":2115,"line":2230},[2113,22753,22754],{"class":2330},"    \"webpack\"",[2113,22756,4429],{"class":2119},[2113,22758,19772],{"class":2166},[2113,22760,22761],{"class":2115,"line":2235},[2113,22762,2572],{"class":2119},[2113,22764,22765],{"class":2115,"line":2241},[2113,22766,2599],{"class":2119},[12,22768,22769,22770,3938],{},"Par défaut, webpack ne crée un bundle qu'avec le source de notre application (et donc sans les modules node). Ce qui nous intéresse c'est de faire un bundle\ncomprenant notre application ainsi que les modules node (on peut voir un bench sur la ",[49,22771,22773],{"href":19662,"rel":22772},[347],"FAQ Nest.JS",[12,22775,22776,22777,22780],{},"Pour cela nous allons créer un fichier ",[184,22778,22779],{},"webpack.config.js"," qui sera pris automatiquement (inspiré de la documentation mais modifié pour posséder plusieurs points\nd'entrées, on exclut aussi le framework AWS qui est déjà inclus dans AWS):",[2105,22782,22784],{"className":15749,"code":22783,"language":15751,"meta":1646,"style":1646},"module.exports = (options, webpack) => {\n  const lazyImports = [\n    \"@nestjs\u002Fmicroservices\u002Fmicroservices-module\",\n    \"@nestjs\u002Fwebsockets\u002Fsocket-module\",\n  ];\n\n  return {\n    ...options,\n    entry: {\n      main: options.entry,\n      \"check-availability\": options.entry.replace(\"main\", \"check-availability\"),\n    },\n    externals: [\"aws-sdk\"],\n    output: {\n      filename: \"[name].js\",\n      libraryTarget: \"commonjs2\",\n    },\n    plugins: [\n      ...options.plugins,\n      new webpack.IgnorePlugin({\n        checkResource(resource) {\n          if (lazyImports.includes(resource)) {\n            try {\n              require.resolve(resource);\n            } catch (err) {\n              return true;\n            }\n          }\n          return false;\n        },\n      }),\n    ],\n  };\n};\n",[184,22785,22786,22813,22825,22832,22839,22844,22848,22854,22863,22870,22886,22916,22920,22932,22939,22951,22963,22967,22974,22986,23001,23013,23035,23042,23058,23071,23080,23084,23089,23098,23102,23107,23111,23115],{"__ignoreMap":1646},[2113,22787,22788,22791,22793,22796,22798,22800,22802,22804,22807,22809,22811],{"class":2115,"line":2116},[2113,22789,22790],{"class":2414},"module",[2113,22792,179],{"class":2119},[2113,22794,22795],{"class":2414},"exports",[2113,22797,2153],{"class":2334},[2113,22799,2495],{"class":2119},[2113,22801,17539],{"class":2429},[2113,22803,932],{"class":2119},[2113,22805,22806],{"class":2429},"webpack",[2113,22808,5709],{"class":2119},[2113,22810,8063],{"class":2326},[2113,22812,2647],{"class":2119},[2113,22814,22815,22817,22820,22822],{"class":2115,"line":1647},[2113,22816,2995],{"class":2326},[2113,22818,22819],{"class":2414}," lazyImports",[2113,22821,2153],{"class":2334},[2113,22823,22824],{"class":2119}," [\n",[2113,22826,22827,22830],{"class":2115,"line":1652},[2113,22828,22829],{"class":2149},"    \"@nestjs\u002Fmicroservices\u002Fmicroservices-module\"",[2113,22831,4706],{"class":2119},[2113,22833,22834,22837],{"class":2115,"line":2185},[2113,22835,22836],{"class":2149},"    \"@nestjs\u002Fwebsockets\u002Fsocket-module\"",[2113,22838,4706],{"class":2119},[2113,22840,22841],{"class":2115,"line":2230},[2113,22842,22843],{"class":2119},"  ];\n",[2113,22845,22846],{"class":2115,"line":2235},[2113,22847,2125],{"emptyLinePlaceholder":1767},[2113,22849,22850,22852],{"class":2115,"line":2241},[2113,22851,2578],{"class":2326},[2113,22853,2647],{"class":2119},[2113,22855,22856,22859,22861],{"class":2115,"line":2246},[2113,22857,22858],{"class":2119},"    ...",[2113,22860,17539],{"class":2330},[2113,22862,4706],{"class":2119},[2113,22864,22865,22868],{"class":2115,"line":2464},[2113,22866,22867],{"class":2330},"    entry",[2113,22869,8091],{"class":2119},[2113,22871,22872,22875,22877,22879,22881,22884],{"class":2115,"line":2085},[2113,22873,22874],{"class":2330},"      main",[2113,22876,4429],{"class":2119},[2113,22878,17539],{"class":2414},[2113,22880,179],{"class":2119},[2113,22882,22883],{"class":2330},"entry",[2113,22885,4706],{"class":2119},[2113,22887,22888,22891,22893,22895,22897,22899,22901,22904,22906,22909,22911,22914],{"class":2115,"line":2514},[2113,22889,22890],{"class":2149},"      \"check-availability\"",[2113,22892,4429],{"class":2119},[2113,22894,17539],{"class":2414},[2113,22896,179],{"class":2119},[2113,22898,22883],{"class":2414},[2113,22900,179],{"class":2119},[2113,22902,22903],{"class":2133},"replace",[2113,22905,2423],{"class":2119},[2113,22907,22908],{"class":2149},"\"main\"",[2113,22910,932],{"class":2119},[2113,22912,22913],{"class":2149},"\"check-availability\"",[2113,22915,7142],{"class":2119},[2113,22917,22918],{"class":2115,"line":2533},[2113,22919,8237],{"class":2119},[2113,22921,22922,22925,22927,22930],{"class":2115,"line":2556},[2113,22923,22924],{"class":2330},"    externals",[2113,22926,6155],{"class":2119},[2113,22928,22929],{"class":2149},"\"aws-sdk\"",[2113,22931,7173],{"class":2119},[2113,22933,22934,22937],{"class":2115,"line":2569},[2113,22935,22936],{"class":2330},"    output",[2113,22938,8091],{"class":2119},[2113,22940,22941,22944,22946,22949],{"class":2115,"line":2575},[2113,22942,22943],{"class":2330},"      filename",[2113,22945,4429],{"class":2119},[2113,22947,22948],{"class":2149},"\"[name].js\"",[2113,22950,4706],{"class":2119},[2113,22952,22953,22956,22958,22961],{"class":2115,"line":2596},[2113,22954,22955],{"class":2330},"      libraryTarget",[2113,22957,4429],{"class":2119},[2113,22959,22960],{"class":2149},"\"commonjs2\"",[2113,22962,4706],{"class":2119},[2113,22964,22965],{"class":2115,"line":3192},[2113,22966,8237],{"class":2119},[2113,22968,22969,22972],{"class":2115,"line":3213},[2113,22970,22971],{"class":2330},"    plugins",[2113,22973,18219],{"class":2119},[2113,22975,22976,22978,22980,22982,22984],{"class":2115,"line":3236},[2113,22977,19449],{"class":2119},[2113,22979,17539],{"class":2414},[2113,22981,179],{"class":2119},[2113,22983,19699],{"class":2330},[2113,22985,4706],{"class":2119},[2113,22987,22988,22991,22994,22996,22999],{"class":2115,"line":3248},[2113,22989,22990],{"class":2326},"      new",[2113,22992,22993],{"class":2414}," webpack",[2113,22995,179],{"class":2119},[2113,22997,22998],{"class":2133},"IgnorePlugin",[2113,23000,19349],{"class":2119},[2113,23002,23003,23006,23008,23011],{"class":2115,"line":4899},[2113,23004,23005],{"class":2133},"        checkResource",[2113,23007,2423],{"class":2119},[2113,23009,23010],{"class":2429},"resource",[2113,23012,2433],{"class":2119},[2113,23014,23015,23018,23020,23023,23025,23028,23030,23032],{"class":2115,"line":1777},[2113,23016,23017],{"class":2326},"          if",[2113,23019,2495],{"class":2119},[2113,23021,23022],{"class":2414},"lazyImports",[2113,23024,179],{"class":2119},[2113,23026,23027],{"class":2133},"includes",[2113,23029,2423],{"class":2119},[2113,23031,23010],{"class":2330},[2113,23033,23034],{"class":2119},")) {\n",[2113,23036,23037,23040],{"class":2115,"line":4931},[2113,23038,23039],{"class":2326},"            try",[2113,23041,2647],{"class":2119},[2113,23043,23044,23047,23049,23052,23054,23056],{"class":2115,"line":4961},[2113,23045,23046],{"class":2414},"              require",[2113,23048,179],{"class":2119},[2113,23050,23051],{"class":2133},"resolve",[2113,23053,2423],{"class":2119},[2113,23055,23010],{"class":2330},[2113,23057,2553],{"class":2119},[2113,23059,23060,23063,23065,23067,23069],{"class":2115,"line":4976},[2113,23061,23062],{"class":2119},"            } ",[2113,23064,8973],{"class":2326},[2113,23066,2495],{"class":2119},[2113,23068,6794],{"class":2330},[2113,23070,2433],{"class":2119},[2113,23072,23073,23076,23078],{"class":2115,"line":4993},[2113,23074,23075],{"class":2326},"              return",[2113,23077,4872],{"class":2166},[2113,23079,2487],{"class":2119},[2113,23081,23082],{"class":2115,"line":5013},[2113,23083,5172],{"class":2119},[2113,23085,23086],{"class":2115,"line":5018},[2113,23087,23088],{"class":2119},"          }\n",[2113,23090,23091,23094,23096],{"class":2115,"line":5042},[2113,23092,23093],{"class":2326},"          return",[2113,23095,5052],{"class":2166},[2113,23097,2487],{"class":2119},[2113,23099,23100],{"class":2115,"line":5057},[2113,23101,12744],{"class":2119},[2113,23103,23104],{"class":2115,"line":5062},[2113,23105,23106],{"class":2119},"      }),\n",[2113,23108,23109],{"class":2115,"line":5098},[2113,23110,19140],{"class":2119},[2113,23112,23113],{"class":2115,"line":5117},[2113,23114,22152],{"class":2119},[2113,23116,23117],{"class":2115,"line":5142},[2113,23118,2787],{"class":2119},[12,23120,23121],{},"A l'heure actuelle (mais principalement à cause du temps d'exécution de la requête), la requête prend environ 1s:",[2105,23123,23126],{"className":23124,"code":23125,"language":8748,"meta":1646,"style":1646},"language-log shiki shiki-themes one-dark-pro","REPORT RequestId: eaa2b5a3-a754-4b1f-adcc-22ca3c6045aa Duration: 984.41 ms Billed Duration: 985 ms Memory Size: 1024 MB Max Memory Used: 109 MB Init Duration: 540.30 ms\n",[184,23127,23128],{"__ignoreMap":1646},[2113,23129,23130,23133,23136,23139,23142,23144,23147,23150,23153,23156,23159,23162,23165,23168,23171,23173,23176],{"class":2115,"line":2116},[2113,23131,23132],{"class":2119},"REPORT RequestId: ",[2113,23134,23135],{"class":2166},"eaa2b5a3-a754-4b1f-adcc-22ca3c6045aa",[2113,23137,23138],{"class":2119}," Duration: ",[2113,23140,23141],{"class":2166},"984",[2113,23143,179],{"class":2119},[2113,23145,23146],{"class":2166},"41",[2113,23148,23149],{"class":2119}," ms Billed Duration: ",[2113,23151,23152],{"class":2166},"985",[2113,23154,23155],{"class":2119}," ms Memory Size: ",[2113,23157,23158],{"class":2166},"1024",[2113,23160,23161],{"class":2119}," MB Max Memory Used: ",[2113,23163,23164],{"class":2166},"109",[2113,23166,23167],{"class":2119}," MB Init Duration: ",[2113,23169,23170],{"class":2166},"540",[2113,23172,179],{"class":2119},[2113,23174,23175],{"class":2166},"30",[2113,23177,23178],{"class":2119}," ms\n",[128,23180,23182],{"id":23181},"le-coût-du-projet","Le coût du projet",[12,23184,23185],{},"Grâce au Free-Tiers d'AWS, ce qui me coûte réellement de l'argent dans ce projet est SNS (qui sert à l'envoi de SMS). L'autre coût est relatif à Route 53 car j'ai déployé le site\ndans un sous-domaine.",[12,23187,23188],{},"Au 29 Août 2021, je vais avoir dépensé 7,34$ pour me faire plaisir (j'aurais pu le déployer sur mes propres serveurs dédiés) et pour pouvoir m'acheter un vélo sans\nattendre que le site m'envoie un mail ou qu'il y ait suffisamment de stock pendant suffisamment longtemps pour que je puisse voir le vélo, ou l'acheter en magasin.",[12,23190,23191],{},[121,23192],{"alt":23193,"className":23194,"src":23195},"Coût du projet",[126],"\u002FProgrammation\u002Fachat_velo\u002Fcout.png",[128,23197,23199],{"id":23198},"bilan","Bilan",[12,23201,23202],{},"On est fin septembre, j'ai reçu mon vélo et j'ai pu rouler plusieurs semaines avec (Je l'ai recu début septembre).",[12,23204,23205],{},"Cela a commencé par une notification à 11h59 indiquant que 4 exemplaires avaient été ajoutés au site. J'ai pu le commander dans la foulée. A 14h, tous les exemplaires avaient été vendus.\nJe devais partir manger quand j'ai reçu la notification.",[12,23207,23208],{},"Si je n'avais pas développé ce petit programme, je n'aurais jamais su que des vélos étaient en stock, car je n'ai jamais reçu le fameux mail. De plus en travaillant\n(car je suis revenu de vacances depuis) je ne peux pas rafraîchir la page en continu (1 fois le matin, 1 fois le soir).",[12,23210,23211],{},"Bref, est-ce que le mail que doit nous envoyer le site est de la poudre aux yeux ? Se sert-il d'une file d'attente (et dans ce cas je m'excuse auprès de celui que\nj'ai doublé) ? Ou n'est-il envoyé que quand il y a plus de 10 exemplaires qui arrivent sur le site ?",[12,23213,23214],{},"Depuis j'ai désactivé le programme. Avant j'avais pu voir le stock bouger rapidement sur les VTT en XL et sur le VTC en XL et en S. Ce que je peux dire c'est qu'une\ntrentaine de vélo ajoutés sur le VTT le soir à 21h partent comme des petits pains. Il n'en restait plus le lendemin à 12h...",[12,23216,23217],{},"Au delà de l'achat du vélo, j'ai été content de pouvoir mettre un site en production rapidement et quasi-gratuitement (si on ne compte pas les SMS). Je me demande ce\nque m'aurais coûté le même site au delà de la période des 1 an (après la fin du free-tiers).",[23219,23220,23223,23228],"section",{"className":23221,"dataFootnotes":1646},[23222],"footnotes",[128,23224,23227],{"className":23225,"id":17406},[23226],"sr-only","Footnotes",[367,23229,23230,23241],{},[370,23231,23233,23234],{"id":23232},"user-content-fn-1","Certains me conseillent des petits magasins de vélo, d'autres des VTCAE à plus de 5 000 €. Bref, nous ne sommes pas là pour parler de mon choix d'enseigne. Mais si vous voulez tout savoir, c'est une question pratique. Il est à coté de chez moi. ",[49,23235,23240],{"href":23236,"ariaLabel":23237,"className":23238,"dataFootnoteBackref":1646},"#user-content-fnref-1","Back to reference 1",[23239],"data-footnote-backref","↩",[370,23242,23244,23249],{"id":23243},"user-content-fn-2",[1886,23245,23246],{},[12,23247,23248],{},"Hello friend jsdom only handles static html, to make the click event you will need a headless browser for scraping the web. There are several one of them is the Puppeter that uses the chorme in the background.",[49,23250,23240],{"href":23251,"ariaLabel":23252,"className":23253,"dataFootnoteBackref":1646},"#user-content-fnref-2","Back to reference 2",[23239],[3358,23255,23256],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sLaUg, html code.shiki .sLaUg{--shiki-default:#FFFFFF}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .shdRp, html code.shiki .shdRp{--shiki-default:#C678DD;--shiki-default-font-style:italic}html pre.shiki code .sKU4T, html code.shiki .sKU4T{--shiki-default:#E5C07B;--shiki-default-font-style:italic}",{"title":1646,"searchDepth":1647,"depth":1647,"links":23258},[23259,23260,23262,23263,23264,23265,23266,23267,23268,23269,23270,23271,23272],{"id":17426,"depth":1647,"text":17427},{"id":17457,"depth":1647,"text":23261},"La 1er version",{"id":18006,"depth":1647,"text":18007},{"id":18149,"depth":1647,"text":18150},{"id":18501,"depth":1647,"text":18502},{"id":19647,"depth":1647,"text":19648},{"id":20113,"depth":1647,"text":20114},{"id":20138,"depth":1647,"text":20139},{"id":21845,"depth":1647,"text":21846},{"id":22695,"depth":1647,"text":22696},{"id":23181,"depth":1647,"text":23182},{"id":23198,"depth":1647,"text":23199},{"id":17406,"depth":1647,"text":23227},"2021-09-27",{"type":9,"value":23275},[23276,23278,23282,23289,23291,23295,23297],[12,23277,17388],{},[12,23279,17391,23280,17395],{},[1892,23281,17394],{},[12,23283,17398,23284],{},[17400,23285,23286],{},[49,23287,3686],{"href":17404,"ariaDescribedBy":23288,"dataFootnoteRef":1646,"id":17407},[17406],[12,23290,17410],{},[12,23292,17413,23293,17417],{},[362,23294,17416],{},[12,23296,17420],{},[12,23298,17423],{},{"planet":1767},"\u002Fpost\u002Fachat_velo",{"title":17383,"description":17388},"achat_velo","posts\u002FProgrammation\u002F2021-09-27_achat_velo",[3510,3511,23305,23306,23307,23308],"aws","web","velo","sms","p5JgMwprkltEBjhdZdpkmC59vL1CqDYiHhoE51Qhs0U",{"id":23311,"title":23312,"author":7,"body":23313,"category":24866,"categorySlug":24867,"date":24868,"description":24869,"excerpt":24870,"extension":1764,"location":1765,"meta":24891,"navigation":1767,"path":24892,"published":1767,"seo":24893,"slug":24894,"stem":24895,"tags":24896,"timeToRead":2514,"__hash__":24898},"posts\u002Fposts\u002FLogiciels\u002F2021-03-02-gridsome-mise-a-jour-du-blog.md","Mise à jour du blog vers gridsome",{"type":9,"value":23314,"toc":24857},[23315,23329,23343,23346,23349,23363,23366,23370,23373,23376,23404,23407,23411,23414,23442,23445,23448,23476,23479,23504,23507,23565,23568,23637,23641,23648,23656,23659,23753,23771,23839,23842,23882,23889,23962,23965,24023,24032,24035,24102,24105,24187,24190,24194,24197,24200,24203,24206,24215,24218,24225,24475,24484,24488,24491,24494,24504,24507,24537,24540,24543,24546,24554,24563,24572,24580,24583,24593,24766,24769,24821,24824,24826,24829,24840,24854],[12,23316,23317,23318,23322,23323,23328],{},"J'avais migré en Janvier 2013 mon ",[49,23319,23321],{"href":23320},"\u002Fpages\u002Fmigration-to-pelican","blog vers Pelican"," en venant de\n",[49,23324,23327],{"href":23325,"rel":23326},"https:\u002F\u002Ffr.dotclear.org\u002F",[347],"Dotclear",". Pelican est un générateur de site statique en Python. J'avais\nalors dû faire l'impasse sur les commentaires mais au bénéfice d'un site performant et avec une\nsurface d'attaque plus faible.",[12,23330,23331,23332,23337,23338,179],{},"Plus tard, en décembre 2016, j'avais changé le thème pour ",[49,23333,23336],{"href":23334,"rel":23335},"https:\u002F\u002Fgithub.com\u002Falexandrevicenzi\u002FFlex",[347],"alexandrevicenzi\u002FFlex",".\nAujoud'hui je vais vous montrer la migration de mon blog vers ",[49,23339,23342],{"href":23340,"rel":23341},"https:\u002F\u002Fgridsome.org\u002F",[347],"Gridsome",[12,23344,23345],{},"Je reste sur un générateur de site statique car, j'aime l'idée d'un site à la fois rapide et\nimmuable tant que je ne décide pas de modifier le contenu mon blog. Alors pourquoi changer de Pelican\nvers Gridsome ?",[12,23347,23348],{},"Gridsome est un générateur de site statique écrit en Javascript. Il a la particularité de pouvoir générer\ndes pages depuis des fichiers au format Markdown mais aussi depuis des CMS Headless, ou de n'importe\nquelles autres API et ce grace à une API commune en GraphQL.",[12,23350,23351,23352,23357,23358,179],{},"En plus de générer le site statique au format HTML, Gridsome fonctionne comme une SPA",[17400,23353,23354],{},[49,23355,3686],{"href":17404,"ariaDescribedBy":23356,"dataFootnoteRef":1646,"id":17407},[17406],". Dans une SPA\nles pages sont générées côté client et sont mises à jour lors d'appel API pour récupérer les données. Ainsi\nseul le contenu modifié est rafraichit. Le framework Gridsome se base sur ",[49,23359,23362],{"href":23360,"rel":23361},"https:\u002F\u002Ffr.vuejs.org\u002F",[347],"Vue.Js",[12,23364,23365],{},"Ce qui m'a plus dans Gridsome, c'est l'utilisation de Vue.JS pour générer les pages statiques, mais aussi\nl'utilisation de GraphQL pour requêter le contenu des pages d'une même et unique façon.",[128,23367,23369],{"id":23368},"alternatives","Alternatives",[12,23371,23372],{},"Lors de la migration de mon blog, je voulais une technologie basée sur Vue.Js, car c'est un framework que\nj'apprécie. Mais le plus important, c'est que j'avais besoin de changement (pour le plaisir quoi ;)).",[12,23374,23375],{},"Les générateurs de site statique en Vue.Js sont :",[506,23377,23378,23385,23392,23399],{},[370,23379,23380],{},[49,23381,23384],{"href":23382,"rel":23383},"https:\u002F\u002Fvuepress.vuejs.org\u002F",[347],"VuePress",[370,23386,23387],{},[49,23388,23391],{"href":23389,"rel":23390},"https:\u002F\u002Ffr.nuxtjs.org\u002F",[347],"Nuxt.js",[370,23393,23394],{},[49,23395,23398],{"href":23396,"rel":23397},"https:\u002F\u002Fsaber.land\u002F",[347],"Saber",[370,23400,23401],{},[49,23402,23342],{"href":23340,"rel":23403},[347],[12,23405,23406],{},"Je vous laisse regarder et vous faire votre avis sur les différents framework. J'ai surtout testé VuePress mais\nj'ai changé pour Gridsome quand j'ai vu la simplicité d'implémentation.",[128,23408,23410],{"id":23409},"début-de-limplémentation","Début de l'implémentation",[12,23412,23413],{},"Je voulais changer de technologie, mais j'aimais bien le thème Pelican que j'utilisais. Je n'avais donc pas\nenvie de le changer. Je me suis imposé également les contraintes suivantes:",[506,23415,23416,23419,23430,23433,23436,23439],{},[370,23417,23418],{},"Je ne voulais pas changer les différents chemins d'accès au site (tant que faire se peut).",[370,23420,23421,23422],{},"Je voulais garder les fonctionnalités suivantes:\n",[506,23423,23424,23427],{},[370,23425,23426],{},"Sitemap",[370,23428,23429],{},"Atom",[370,23431,23432],{},"J'avais des billets écrits en Markdown mais aussi 3 pages que je ne voulais pas réécrire en HTML",[370,23434,23435],{},"Garder le même thème",[370,23437,23438],{},"Avoir une page par catégorie et une page avec toutes les catégories",[370,23440,23441],{},"Avoir une page par tag et une page avec tous les tags",[12,23443,23444],{},"Je commence donc à initialiser un projet de zéro avec mes différents billets de blog, les pages (contact,\nanciens-projets, et contact) et les fichiers statiques (les images, les zips, ...).",[12,23446,23447],{},"J'ai du retravailler les différents billets de blog au format Markdown:",[506,23449,23450,23453,23469],{},[370,23451,23452],{},"J'ai dû déplacer les différents attributs du fichier pour les placer dans un frontmatter au format YAML.",[370,23454,23455,23456,23459,23460,23462,23463,179,23466,23468],{},"Les fichiers ont un nom qui porte de l'information. Ainsi, le dossier dans lequel se trouve le fichier\nconstitue le nom de la catégorie, et le nom du fichier porte la date de publication et le ",[184,23457,23458],{},"slug"," (utilisé\npour le chemin dans l'URL).",[15567,23461],{},"Par exemple, avec le fichier: ",[184,23464,23465],{},"Logiciels\u002F2009-02-15_debian-lenny-est-sortie.md",[15567,23467],{},"Avec le changement de paradigme de Gridsome, j'ai dû intégrer ces différentes informations dans le frontmatter.",[370,23470,23471,23472,23475],{},"J'ai également du retravailler les emplacements de fichiers qui étaient préfixés dans Pelican\nde ",[184,23473,23474],{},"|static|\u002Fpublic\u002F...",". Dans Gridsome, il n'y pas de syntaxe particulière, juste le nom du fichier absolu.",[12,23477,23478],{},"Avant j'avais:",[2105,23480,23483],{"className":23481,"code":23482,"language":1764,"meta":1646,"style":1646},"language-md shiki shiki-themes one-dark-pro","Title: Debian Lenny est sortie\nTags: debian, kde\n\n![Logo](|static|\u002Fpublic\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[184,23484,23485,23490,23495,23499],{"__ignoreMap":1646},[2113,23486,23487],{"class":2115,"line":2116},[2113,23488,23489],{},"Title: Debian Lenny est sortie\n",[2113,23491,23492],{"class":2115,"line":1647},[2113,23493,23494],{},"Tags: debian, kde\n",[2113,23496,23497],{"class":2115,"line":1652},[2113,23498,2125],{"emptyLinePlaceholder":1767},[2113,23500,23501],{"class":2115,"line":2185},[2113,23502,23503],{},"![Logo](|static|\u002Fpublic\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[12,23505,23506],{},"et j'ai corrigé cela en",[2105,23508,23510],{"className":23481,"code":23509,"language":1764,"meta":1646,"style":1646},"---\ntitle: Debian Lenny est sortie\ndate: 2009-02-15\ntags:\n  - debian\n  - kde\npublished: True\ncategory: Logiciels\n---\n\n![Logo](\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[184,23511,23512,23517,23522,23527,23532,23537,23542,23547,23552,23556,23560],{"__ignoreMap":1646},[2113,23513,23514],{"class":2115,"line":2116},[2113,23515,23516],{},"---\n",[2113,23518,23519],{"class":2115,"line":1647},[2113,23520,23521],{},"title: Debian Lenny est sortie\n",[2113,23523,23524],{"class":2115,"line":1652},[2113,23525,23526],{},"date: 2009-02-15\n",[2113,23528,23529],{"class":2115,"line":2185},[2113,23530,23531],{},"tags:\n",[2113,23533,23534],{"class":2115,"line":2230},[2113,23535,23536],{},"  - debian\n",[2113,23538,23539],{"class":2115,"line":2235},[2113,23540,23541],{},"  - kde\n",[2113,23543,23544],{"class":2115,"line":2241},[2113,23545,23546],{},"published: True\n",[2113,23548,23549],{"class":2115,"line":2246},[2113,23550,23551],{},"category: Logiciels\n",[2113,23553,23554],{"class":2115,"line":2464},[2113,23555,23516],{},[2113,23557,23558],{"class":2115,"line":2085},[2113,23559,2125],{"emptyLinePlaceholder":1767},[2113,23561,23562],{"class":2115,"line":2514},[2113,23563,23564],{},"![Logo](\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[12,23566,23567],{},"Afin de pouvoir reprendre mes fichiers au format markdown sans trop de modification, j'ai également installé les\nplugins remark suivants (ce sur quoi Gridsome se base pour transformer le Markdown en HTML) :",[506,23569,23570,23576,23610,23625,23631],{},[370,23571,23572,23575],{},[184,23573,23574],{},"@gridsome\u002Fremark-prismjs",": pour effectuer une coloration syntaxique.",[370,23577,23578,23581,23582],{},[184,23579,23580],{},"remark-inline-links",": pour pouvoir gérer les liens inclus (qui sont ensuite reportés en bas du fichier)",[2105,23583,23585],{"className":23481,"code":23584,"language":1764,"meta":1646,"style":1646},"[foo], [foo][], [bar][foo].\n\n![foo], ![foo][], ![bar][foo].\n\n[foo]: http:\u002F\u002Fexample.com \"Example Domain\"\n",[184,23586,23587,23592,23596,23601,23605],{"__ignoreMap":1646},[2113,23588,23589],{"class":2115,"line":2116},[2113,23590,23591],{},"[foo], [foo][], [bar][foo].\n",[2113,23593,23594],{"class":2115,"line":1647},[2113,23595,2125],{"emptyLinePlaceholder":1767},[2113,23597,23598],{"class":2115,"line":1652},[2113,23599,23600],{},"![foo], ![foo][], ![bar][foo].\n",[2113,23602,23603],{"class":2115,"line":2185},[2113,23604,2125],{"emptyLinePlaceholder":1767},[2113,23606,23607],{"class":2115,"line":2230},[2113,23608,23609],{},"[foo]: http:\u002F\u002Fexample.com \"Example Domain\"\n",[370,23611,23612,23615,23616],{},[184,23613,23614],{},"remark-attr",": pour pouvoir ajouter des attributs à une image ou à un objet.",[2105,23617,23619],{"className":23481,"code":23618,"language":1764,"meta":1646,"style":1646},"![alt](img){attrs} \u002F ![alt](img){ height=50 }\n",[184,23620,23621],{"__ignoreMap":1646},[2113,23622,23623],{"class":2115,"line":2116},[2113,23624,23618],{},[370,23626,23627,23630],{},[184,23628,23629],{},"remark-toc",": pour pouvoir ajouter un sommaire à certain billet.",[370,23632,23633,23636],{},[184,23634,23635],{},"remark-footnotes",": pour pouvoir ajouter les footnotes.",[128,23638,23640],{"id":23639},"création-du-thème","Création du thème",[12,23642,23643,23644,23647],{},"Le thème Pelican de mon blog est basé sur ",[49,23645,23336],{"href":23334,"rel":23646},[347],".\nJ'ai donc créé un thème gridsome en reprenant le style du thème précédent mais en convertissant les templates\nen Vue.Js.",[12,23649,23650,23651,179],{},"J'ai donc créé ce thème que j'ai mis à disposition ici : ",[49,23652,23655],{"href":23653,"rel":23654},"https:\u002F\u002Fgithub.com\u002Fphoenix741\u002Fgridsome-flex-markdown-starter",[347],"phoenix741\u002Fgridsome-flex-markdown-starter",[12,23657,23658],{},"Voici la configuration de gridsome que j'ai implémenté pour reconstruire le thème du blog:",[2105,23660,23662],{"className":7776,"code":23661,"language":7778,"meta":1646,"style":1646},"  plugins: [\n    {\n      use: \"@gridsome\u002Fsource-filesystem\",\n      options: {\n        baseDir: \"content\u002Fposts\",\n        path: \"**\u002F*.md\",\n        typeName: \"Post\",\n        refs: {\n          tags: {\n            typeName: \"Tag\",\n            create: true,\n          },\n          category: {\n            typeName: \"Category\",\n            create: true,\n          },\n        },\n      },\n    },\n",[184,23663,23664,23669,23673,23678,23683,23688,23693,23698,23703,23708,23713,23718,23723,23728,23733,23737,23741,23745,23749],{"__ignoreMap":1646},[2113,23665,23666],{"class":2115,"line":2116},[2113,23667,23668],{},"  plugins: [\n",[2113,23670,23671],{"class":2115,"line":1647},[2113,23672,18224],{},[2113,23674,23675],{"class":2115,"line":1652},[2113,23676,23677],{},"      use: \"@gridsome\u002Fsource-filesystem\",\n",[2113,23679,23680],{"class":2115,"line":2185},[2113,23681,23682],{},"      options: {\n",[2113,23684,23685],{"class":2115,"line":2230},[2113,23686,23687],{},"        baseDir: \"content\u002Fposts\",\n",[2113,23689,23690],{"class":2115,"line":2235},[2113,23691,23692],{},"        path: \"**\u002F*.md\",\n",[2113,23694,23695],{"class":2115,"line":2241},[2113,23696,23697],{},"        typeName: \"Post\",\n",[2113,23699,23700],{"class":2115,"line":2246},[2113,23701,23702],{},"        refs: {\n",[2113,23704,23705],{"class":2115,"line":2464},[2113,23706,23707],{},"          tags: {\n",[2113,23709,23710],{"class":2115,"line":2085},[2113,23711,23712],{},"            typeName: \"Tag\",\n",[2113,23714,23715],{"class":2115,"line":2514},[2113,23716,23717],{},"            create: true,\n",[2113,23719,23720],{"class":2115,"line":2533},[2113,23721,23722],{},"          },\n",[2113,23724,23725],{"class":2115,"line":2556},[2113,23726,23727],{},"          category: {\n",[2113,23729,23730],{"class":2115,"line":2569},[2113,23731,23732],{},"            typeName: \"Category\",\n",[2113,23734,23735],{"class":2115,"line":2575},[2113,23736,23717],{},[2113,23738,23739],{"class":2115,"line":2596},[2113,23740,23722],{},[2113,23742,23743],{"class":2115,"line":3192},[2113,23744,12744],{},[2113,23746,23747],{"class":2115,"line":3213},[2113,23748,12282],{},[2113,23750,23751],{"class":2115,"line":3236},[2113,23752,8237],{},[12,23754,23755,23756,23759,23760,23763,23764,23767,23768,179],{},"Pour commencer, on utilise le module ",[184,23757,23758],{},"@gridsome\u002Fsource-filesystem"," pour lire le contenu du dossier\n",[184,23761,23762],{},"content\u002Fposts\u002F**\u002F*.md",". On définit au plugin comment on créé les ",[184,23765,23766],{},"Tag"," et les ",[184,23769,23770],{},"Category",[2105,23772,23774],{"className":7776,"code":23773,"language":7778,"meta":1646,"style":1646},"    {\n      use: \"@microflash\u002Fgridsome-plugin-feed\",\n      options: {\n        contentTypes: [\"Post\"],\n        rss: {\n          enabled: true,\n          output: \"\u002Ffeed.xml\",\n        },\n        atom: {\n          enabled: true,\n          output: \"\u002Ffeed.atom\",\n        },\n      },\n    },\n",[184,23775,23776,23780,23785,23789,23794,23799,23804,23809,23813,23818,23822,23827,23831,23835],{"__ignoreMap":1646},[2113,23777,23778],{"class":2115,"line":2116},[2113,23779,18224],{},[2113,23781,23782],{"class":2115,"line":1647},[2113,23783,23784],{},"      use: \"@microflash\u002Fgridsome-plugin-feed\",\n",[2113,23786,23787],{"class":2115,"line":1652},[2113,23788,23682],{},[2113,23790,23791],{"class":2115,"line":2185},[2113,23792,23793],{},"        contentTypes: [\"Post\"],\n",[2113,23795,23796],{"class":2115,"line":2230},[2113,23797,23798],{},"        rss: {\n",[2113,23800,23801],{"class":2115,"line":2235},[2113,23802,23803],{},"          enabled: true,\n",[2113,23805,23806],{"class":2115,"line":2241},[2113,23807,23808],{},"          output: \"\u002Ffeed.xml\",\n",[2113,23810,23811],{"class":2115,"line":2246},[2113,23812,12744],{},[2113,23814,23815],{"class":2115,"line":2464},[2113,23816,23817],{},"        atom: {\n",[2113,23819,23820],{"class":2115,"line":2085},[2113,23821,23803],{},[2113,23823,23824],{"class":2115,"line":2514},[2113,23825,23826],{},"          output: \"\u002Ffeed.atom\",\n",[2113,23828,23829],{"class":2115,"line":2533},[2113,23830,12744],{},[2113,23832,23833],{"class":2115,"line":2556},[2113,23834,12282],{},[2113,23836,23837],{"class":2115,"line":2569},[2113,23838,8237],{},[12,23840,23841],{},"Ensuite on crée un flux RSS et un flux ATOM avec ces posts, afin que les lecteurs qui utilisent\nencore un lecteur de flux, ou les programmes qui s'abonnent directement à mon site fonctionnent encore.",[2105,23843,23845],{"className":7776,"code":23844,"language":7778,"meta":1646,"style":1646},"    {\n      use: \"@gridsome\u002Fsource-filesystem\",\n      options: {\n        baseDir: \"content\u002Fpages\",\n        path: \"*.md\",\n        typeName: \"BlogPage\",\n      },\n    },\n",[184,23846,23847,23851,23855,23859,23864,23869,23874,23878],{"__ignoreMap":1646},[2113,23848,23849],{"class":2115,"line":2116},[2113,23850,18224],{},[2113,23852,23853],{"class":2115,"line":1647},[2113,23854,23677],{},[2113,23856,23857],{"class":2115,"line":1652},[2113,23858,23682],{},[2113,23860,23861],{"class":2115,"line":2185},[2113,23862,23863],{},"        baseDir: \"content\u002Fpages\",\n",[2113,23865,23866],{"class":2115,"line":2230},[2113,23867,23868],{},"        path: \"*.md\",\n",[2113,23870,23871],{"class":2115,"line":2235},[2113,23872,23873],{},"        typeName: \"BlogPage\",\n",[2113,23875,23876],{"class":2115,"line":2241},[2113,23877,12282],{},[2113,23879,23880],{"class":2115,"line":2246},[2113,23881,8237],{},[12,23883,23884,23885,23888],{},"On recommence avec les pages du site, que l'on type comme ",[184,23886,23887],{},"BlogPage",". Ces pages ne seront\npas accessibles via des catégories ou des pages. Il faudra faire des liens directement vers les\npages depuis un menu.",[2105,23890,23892],{"className":7776,"code":23891,"language":7778,"meta":1646,"style":1646},"    {\n      use: \"@gridsome\u002Fplugin-sitemap\",\n      options: {\n        config: {\n          \"\u002Fpost\u002F*\": {\n            changefreq: \"weekly\",\n            priority: 0.5,\n          },\n          \"\u002Fpage\u002F*\": {\n            changefreq: \"monthly\",\n            priority: 0.7,\n          },\n        },\n      },\n    },\n",[184,23893,23894,23898,23903,23907,23912,23917,23922,23927,23931,23936,23941,23946,23950,23954,23958],{"__ignoreMap":1646},[2113,23895,23896],{"class":2115,"line":2116},[2113,23897,18224],{},[2113,23899,23900],{"class":2115,"line":1647},[2113,23901,23902],{},"      use: \"@gridsome\u002Fplugin-sitemap\",\n",[2113,23904,23905],{"class":2115,"line":1652},[2113,23906,23682],{},[2113,23908,23909],{"class":2115,"line":2185},[2113,23910,23911],{},"        config: {\n",[2113,23913,23914],{"class":2115,"line":2230},[2113,23915,23916],{},"          \"\u002Fpost\u002F*\": {\n",[2113,23918,23919],{"class":2115,"line":2235},[2113,23920,23921],{},"            changefreq: \"weekly\",\n",[2113,23923,23924],{"class":2115,"line":2241},[2113,23925,23926],{},"            priority: 0.5,\n",[2113,23928,23929],{"class":2115,"line":2246},[2113,23930,23722],{},[2113,23932,23933],{"class":2115,"line":2464},[2113,23934,23935],{},"          \"\u002Fpage\u002F*\": {\n",[2113,23937,23938],{"class":2115,"line":2085},[2113,23939,23940],{},"            changefreq: \"monthly\",\n",[2113,23942,23943],{"class":2115,"line":2514},[2113,23944,23945],{},"            priority: 0.7,\n",[2113,23947,23948],{"class":2115,"line":2533},[2113,23949,23722],{},[2113,23951,23952],{"class":2115,"line":2556},[2113,23953,12744],{},[2113,23955,23956],{"class":2115,"line":2569},[2113,23957,12282],{},[2113,23959,23960],{"class":2115,"line":2575},[2113,23961,8237],{},[12,23963,23964],{},"Après, on définit un sitemap qui pourra être utilisé par les moteurs de recherches.",[2105,23966,23968],{"className":7776,"code":23967,"language":7778,"meta":1646,"style":1646},"  ],\n  templates: {\n    Post: (obj) => {\n      return (\n        \"\u002Fpost\u002F\" + obj.fileInfo.name.replace(\u002F^\\d{4}-\\d{2}-\\d{2}[_\\-]\u002F, \"\")\n      );\n    },\n    BlogPage: \"\u002Fpages\u002F:fileInfo__name\",\n    Tag: \"\u002Ftag\u002F:id\",\n    Category: \"\u002Fcategory\u002F:title\",\n  },\n",[184,23969,23970,23975,23980,23985,23990,23995,24000,24004,24009,24014,24019],{"__ignoreMap":1646},[2113,23971,23972],{"class":2115,"line":2116},[2113,23973,23974],{},"  ],\n",[2113,23976,23977],{"class":2115,"line":1647},[2113,23978,23979],{},"  templates: {\n",[2113,23981,23982],{"class":2115,"line":1652},[2113,23983,23984],{},"    Post: (obj) => {\n",[2113,23986,23987],{"class":2115,"line":2185},[2113,23988,23989],{},"      return (\n",[2113,23991,23992],{"class":2115,"line":2230},[2113,23993,23994],{},"        \"\u002Fpost\u002F\" + obj.fileInfo.name.replace(\u002F^\\d{4}-\\d{2}-\\d{2}[_\\-]\u002F, \"\")\n",[2113,23996,23997],{"class":2115,"line":2235},[2113,23998,23999],{},"      );\n",[2113,24001,24002],{"class":2115,"line":2241},[2113,24003,8237],{},[2113,24005,24006],{"class":2115,"line":2246},[2113,24007,24008],{},"    BlogPage: \"\u002Fpages\u002F:fileInfo__name\",\n",[2113,24010,24011],{"class":2115,"line":2464},[2113,24012,24013],{},"    Tag: \"\u002Ftag\u002F:id\",\n",[2113,24015,24016],{"class":2115,"line":2085},[2113,24017,24018],{},"    Category: \"\u002Fcategory\u002F:title\",\n",[2113,24020,24021],{"class":2115,"line":2514},[2113,24022,8490],{},[12,24024,24025,24026,24028,24029,24031],{},"On définit les différentes chemins d'accès pour nos billets, nos pages, nos ",[184,24027,23766],{},",\net nos ",[184,24030,23770],{},". Comme je ne souhaite pas changer les adresses de mes billets sur mon site,\nj'ai défini les adresses sur les mêmes chemins que mon blog sous Pelican.",[12,24033,24034],{},"Comme les noms des fichiers des billets portent la date et le nom du billet, pour retrouver\nle nom du chemin d'origine, je supprime la date pour la génération du chemin.",[2105,24036,24038],{"className":7776,"code":24037,"language":7778,"meta":1646,"style":1646},"  transformers: {\n    remark: {\n      plugins: [\n        [\"@gridsome\u002Fremark-prismjs\", { showLineNumbers: true }],\n        \"remark-inline-links\",\n        [\"remark-toc\", { heading: \"sommaire\" }],\n        \"remark-attr\",\n      ],\n      config: {\n        footnotes: true,\n      },\n    },\n  },\n",[184,24039,24040,24045,24050,24055,24060,24065,24070,24075,24080,24085,24090,24094,24098],{"__ignoreMap":1646},[2113,24041,24042],{"class":2115,"line":2116},[2113,24043,24044],{},"  transformers: {\n",[2113,24046,24047],{"class":2115,"line":1647},[2113,24048,24049],{},"    remark: {\n",[2113,24051,24052],{"class":2115,"line":1652},[2113,24053,24054],{},"      plugins: [\n",[2113,24056,24057],{"class":2115,"line":2185},[2113,24058,24059],{},"        [\"@gridsome\u002Fremark-prismjs\", { showLineNumbers: true }],\n",[2113,24061,24062],{"class":2115,"line":2230},[2113,24063,24064],{},"        \"remark-inline-links\",\n",[2113,24066,24067],{"class":2115,"line":2235},[2113,24068,24069],{},"        [\"remark-toc\", { heading: \"sommaire\" }],\n",[2113,24071,24072],{"class":2115,"line":2241},[2113,24073,24074],{},"        \"remark-attr\",\n",[2113,24076,24077],{"class":2115,"line":2246},[2113,24078,24079],{},"      ],\n",[2113,24081,24082],{"class":2115,"line":2464},[2113,24083,24084],{},"      config: {\n",[2113,24086,24087],{"class":2115,"line":2085},[2113,24088,24089],{},"        footnotes: true,\n",[2113,24091,24092],{"class":2115,"line":2514},[2113,24093,12282],{},[2113,24095,24096],{"class":2115,"line":2533},[2113,24097,8237],{},[2113,24099,24100],{"class":2115,"line":2556},[2113,24101,8490],{},[12,24103,24104],{},"Enfin, je définis les différents plugins dont j'ai besoin pour que mes différents billets\nfonctionnent correctement.",[2105,24106,24108],{"className":7776,"code":24107,"language":7778,"meta":1646,"style":1646},"  permalinks: {\n    trailingSlash: \"false\",\n    slugify: {\n      use: \"@sindresorhus\u002Fslugify\",\n      options: {\n        decamelize: false,\n      },\n    },\n  },\n  css: {\n    loaderOptions: {\n      less: {\n        \u002F\u002F options here will be passed to less-loader\n      },\n    },\n  },\n};\n",[184,24109,24110,24115,24120,24125,24130,24134,24139,24143,24147,24151,24156,24161,24166,24171,24175,24179,24183],{"__ignoreMap":1646},[2113,24111,24112],{"class":2115,"line":2116},[2113,24113,24114],{},"  permalinks: {\n",[2113,24116,24117],{"class":2115,"line":1647},[2113,24118,24119],{},"    trailingSlash: \"false\",\n",[2113,24121,24122],{"class":2115,"line":1652},[2113,24123,24124],{},"    slugify: {\n",[2113,24126,24127],{"class":2115,"line":2185},[2113,24128,24129],{},"      use: \"@sindresorhus\u002Fslugify\",\n",[2113,24131,24132],{"class":2115,"line":2230},[2113,24133,23682],{},[2113,24135,24136],{"class":2115,"line":2235},[2113,24137,24138],{},"        decamelize: false,\n",[2113,24140,24141],{"class":2115,"line":2241},[2113,24142,12282],{},[2113,24144,24145],{"class":2115,"line":2246},[2113,24146,8237],{},[2113,24148,24149],{"class":2115,"line":2464},[2113,24150,8490],{},[2113,24152,24153],{"class":2115,"line":2085},[2113,24154,24155],{},"  css: {\n",[2113,24157,24158],{"class":2115,"line":2514},[2113,24159,24160],{},"    loaderOptions: {\n",[2113,24162,24163],{"class":2115,"line":2533},[2113,24164,24165],{},"      less: {\n",[2113,24167,24168],{"class":2115,"line":2556},[2113,24169,24170],{},"        \u002F\u002F options here will be passed to less-loader\n",[2113,24172,24173],{"class":2115,"line":2569},[2113,24174,12282],{},[2113,24176,24177],{"class":2115,"line":2575},[2113,24178,8237],{},[2113,24180,24181],{"class":2115,"line":2596},[2113,24182,8490],{},[2113,24184,24185],{"class":2115,"line":3192},[2113,24186,2787],{},[12,24188,24189],{},"Je termine par modifier la manière dont Gridsome transforme les adresses, toujours pour\nque les chemins de mon blog ne changent pas.",[128,24191,24193],{"id":24192},"développement-des-pages","Développement des pages",[12,24195,24196],{},"Viens ensuite l'écriture des différentes pages, composants et layout pour définir notre thème.\nLe plus compliqué, c'est que le résumé (excerpt) fourni par Gridsome supprime le rendu HTML et\nles retours chariots.",[12,24198,24199],{},"Je voulais que sur la page principale de mon application, je puisse générer un résumé de chaque\nbillet avec le texte et le rendu original du billet.",[12,24201,24202],{},"Pour cela malheureusement Gridsome ne fournit pas une manière simple de générer un résumé. Je vais\nvous présenter mon implémentation afin d'avoir un résumé au format Markdown.",[12,24204,24205],{},"Ma première version a été de créer à partir de contenu HTML le résumé dans un composant vue dédié.\nLors de la génération des pages statiques, le résultat était comme attendu. Par contre lors du\nchangement des pages (en mode SPA), le billet intégral du blog est chargé, pour être tronqué dynamiquement. Ce\nqui est long et pas performant quand cela arrive sur 10 billets à la suite. J'ai donc modifié le système pour\ngénérer les pages côté serveur.",[12,24207,24208,24209,24212,24213,3938],{},"Pour cela j'ai donc modifié le fichier ",[184,24210,24211],{},"gridsome.server.js",". Le problème, c'est que Gridsome ne permet\npas d'appeler un transformer (remark) directement, et ne permet pas de convertir un text en markdown (autrement\nqu'en passant par le plugin ",[184,24214,23758],{},[12,24216,24217],{},"J'ai donc dû implémenter l'appel à remark (et les différents plugins) directement dans la partie\nserveur manuellement.",[12,24219,24220,24221,24224],{},"Une fois le fichier HTML obtenu, j'appelle une librairie dédiée ",[184,24222,24223],{},"truncatise"," afin de réduire le contenu\nà quelques paragraphes (4).",[2105,24226,24228],{"className":7776,"code":24227,"language":7778,"meta":1646,"style":1646},"const truncatise = require(\"truncatise\");\n\nconst remark = require(\"remark\");\nconst html = require(\"remark-html\");\n\nconst remarkPrism = require(\"@gridsome\u002Fremark-prismjs\");\nconst remarkInlineLinks = require(\"remark-inline-links\");\nconst remarkToc = require(\"remark-toc\");\nconst remarkAttr = require(\"remark-attr\");\nconst remarkFN = require(\"remark-footnotes\");\n\nmodule.exports = function(api) {\n  api.loadSource(({ addSchemaResolvers }) => {\n    addSchemaResolvers({\n      Post: {\n        excerpt: {\n          type: \"String\",\n          async resolve(obj) {\n            return new Promise((resolve, reject) => {\n              remark()\n                \u002F\u002F Appel des différents plugins utilisé coté config.\n                .use(remarkPrism)\n                .use(remarkInlineLinks)\n                .use(remarkToc, { heading: \"sommaire\" })\n                .use(remarkAttr)\n                .use(remarkFN)\n                \u002F\u002F Génération HTML\n                .use(html)\n                .process(obj.content, function(err, file) {\n                  if (err) {\n                    return reject(err);\n                  }\n\n                  \u002F\u002F On tronque en ne gardant que les 4 premiers paragraphes\n                  resolve(\n                    truncatise(String(file), {\n                      TruncateLength: 4,\n                      TruncateBy: \"paragraphs\",\n                      Strict: false,\n                      StripHTML: false,\n                      Suffix: \" ...\",\n                    })\n                  );\n                });\n            });\n          },\n        },\n      },\n    });\n  });\n};\n",[184,24229,24230,24235,24239,24244,24249,24253,24258,24263,24268,24273,24278,24282,24287,24292,24297,24302,24307,24312,24317,24322,24327,24332,24337,24342,24347,24352,24357,24362,24367,24372,24377,24382,24387,24391,24396,24401,24406,24411,24416,24421,24426,24431,24436,24441,24446,24450,24454,24458,24462,24466,24471],{"__ignoreMap":1646},[2113,24231,24232],{"class":2115,"line":2116},[2113,24233,24234],{},"const truncatise = require(\"truncatise\");\n",[2113,24236,24237],{"class":2115,"line":1647},[2113,24238,2125],{"emptyLinePlaceholder":1767},[2113,24240,24241],{"class":2115,"line":1652},[2113,24242,24243],{},"const remark = require(\"remark\");\n",[2113,24245,24246],{"class":2115,"line":2185},[2113,24247,24248],{},"const html = require(\"remark-html\");\n",[2113,24250,24251],{"class":2115,"line":2230},[2113,24252,2125],{"emptyLinePlaceholder":1767},[2113,24254,24255],{"class":2115,"line":2235},[2113,24256,24257],{},"const remarkPrism = require(\"@gridsome\u002Fremark-prismjs\");\n",[2113,24259,24260],{"class":2115,"line":2241},[2113,24261,24262],{},"const remarkInlineLinks = require(\"remark-inline-links\");\n",[2113,24264,24265],{"class":2115,"line":2246},[2113,24266,24267],{},"const remarkToc = require(\"remark-toc\");\n",[2113,24269,24270],{"class":2115,"line":2464},[2113,24271,24272],{},"const remarkAttr = require(\"remark-attr\");\n",[2113,24274,24275],{"class":2115,"line":2085},[2113,24276,24277],{},"const remarkFN = require(\"remark-footnotes\");\n",[2113,24279,24280],{"class":2115,"line":2514},[2113,24281,2125],{"emptyLinePlaceholder":1767},[2113,24283,24284],{"class":2115,"line":2533},[2113,24285,24286],{},"module.exports = function(api) {\n",[2113,24288,24289],{"class":2115,"line":2556},[2113,24290,24291],{},"  api.loadSource(({ addSchemaResolvers }) => {\n",[2113,24293,24294],{"class":2115,"line":2569},[2113,24295,24296],{},"    addSchemaResolvers({\n",[2113,24298,24299],{"class":2115,"line":2575},[2113,24300,24301],{},"      Post: {\n",[2113,24303,24304],{"class":2115,"line":2596},[2113,24305,24306],{},"        excerpt: {\n",[2113,24308,24309],{"class":2115,"line":3192},[2113,24310,24311],{},"          type: \"String\",\n",[2113,24313,24314],{"class":2115,"line":3213},[2113,24315,24316],{},"          async resolve(obj) {\n",[2113,24318,24319],{"class":2115,"line":3236},[2113,24320,24321],{},"            return new Promise((resolve, reject) => {\n",[2113,24323,24324],{"class":2115,"line":3248},[2113,24325,24326],{},"              remark()\n",[2113,24328,24329],{"class":2115,"line":4899},[2113,24330,24331],{},"                \u002F\u002F Appel des différents plugins utilisé coté config.\n",[2113,24333,24334],{"class":2115,"line":1777},[2113,24335,24336],{},"                .use(remarkPrism)\n",[2113,24338,24339],{"class":2115,"line":4931},[2113,24340,24341],{},"                .use(remarkInlineLinks)\n",[2113,24343,24344],{"class":2115,"line":4961},[2113,24345,24346],{},"                .use(remarkToc, { heading: \"sommaire\" })\n",[2113,24348,24349],{"class":2115,"line":4976},[2113,24350,24351],{},"                .use(remarkAttr)\n",[2113,24353,24354],{"class":2115,"line":4993},[2113,24355,24356],{},"                .use(remarkFN)\n",[2113,24358,24359],{"class":2115,"line":5013},[2113,24360,24361],{},"                \u002F\u002F Génération HTML\n",[2113,24363,24364],{"class":2115,"line":5018},[2113,24365,24366],{},"                .use(html)\n",[2113,24368,24369],{"class":2115,"line":5042},[2113,24370,24371],{},"                .process(obj.content, function(err, file) {\n",[2113,24373,24374],{"class":2115,"line":5057},[2113,24375,24376],{},"                  if (err) {\n",[2113,24378,24379],{"class":2115,"line":5062},[2113,24380,24381],{},"                    return reject(err);\n",[2113,24383,24384],{"class":2115,"line":5098},[2113,24385,24386],{},"                  }\n",[2113,24388,24389],{"class":2115,"line":5117},[2113,24390,2125],{"emptyLinePlaceholder":1767},[2113,24392,24393],{"class":2115,"line":5142},[2113,24394,24395],{},"                  \u002F\u002F On tronque en ne gardant que les 4 premiers paragraphes\n",[2113,24397,24398],{"class":2115,"line":5148},[2113,24399,24400],{},"                  resolve(\n",[2113,24402,24403],{"class":2115,"line":5163},[2113,24404,24405],{},"                    truncatise(String(file), {\n",[2113,24407,24408],{"class":2115,"line":5169},[2113,24409,24410],{},"                      TruncateLength: 4,\n",[2113,24412,24413],{"class":2115,"line":5175},[2113,24414,24415],{},"                      TruncateBy: \"paragraphs\",\n",[2113,24417,24418],{"class":2115,"line":5180},[2113,24419,24420],{},"                      Strict: false,\n",[2113,24422,24423],{"class":2115,"line":5199},[2113,24424,24425],{},"                      StripHTML: false,\n",[2113,24427,24428],{"class":2115,"line":5204},[2113,24429,24430],{},"                      Suffix: \" ...\",\n",[2113,24432,24433],{"class":2115,"line":5209},[2113,24434,24435],{},"                    })\n",[2113,24437,24438],{"class":2115,"line":5232},[2113,24439,24440],{},"                  );\n",[2113,24442,24443],{"class":2115,"line":5237},[2113,24444,24445],{},"                });\n",[2113,24447,24448],{"class":2115,"line":5242},[2113,24449,14698],{},[2113,24451,24452],{"class":2115,"line":5267},[2113,24453,23722],{},[2113,24455,24456],{"class":2115,"line":5282},[2113,24457,12744],{},[2113,24459,24460],{"class":2115,"line":5295},[2113,24461,12282],{},[2113,24463,24464],{"class":2115,"line":5310},[2113,24465,19378],{},[2113,24467,24468],{"class":2115,"line":5315},[2113,24469,24470],{},"  });\n",[2113,24472,24473],{"class":2115,"line":5320},[2113,24474,2787],{},[12,24476,24477,24478,24480,24481,3938],{},"C'est ainsi que j'ai converti mon blog vers le framework ",[184,24479,23342],{},". Je vous passe tous les\ndétails (vu que le thème est disponible sur Github sur ",[49,24482,23655],{"href":23653,"rel":24483},[347],[128,24485,24487],{"id":24486},"ajout-dun-système-de-commentaire","Ajout d'un système de commentaire",[12,24489,24490],{},"Ce qu'il manquait sur mon blog précédent c'était un système de commentaires. J'ai donc ajouté\nce système de commentaires à mon blog.",[12,24492,24493],{},"Je ne souhaitais pas ajouter n'importe quel système de commentaires. J'ai donc regardé\nce que je pouvais ajouter sur un site statique. On va retrouver les systèmes de commentaires\nnon libres, et faciles à implémenter (et pas forcément très compatible RGPD).",[506,24495,24496,24499,24502],{},[370,24497,24498],{},"Disqus",[370,24500,24501],{},"Facebook comments",[370,24503,3699],{},[12,24505,24506],{},"De mon coté je me suis dirigé vers les systèmes de commentaires libres:",[506,24508,24509,24516,24523,24530],{},[370,24510,24511],{},[49,24512,24515],{"href":24513,"rel":24514},"https:\u002F\u002Fvssue.js.org\u002F",[347],"Vssue",[370,24517,24518],{},[49,24519,24522],{"href":24520,"rel":24521},"https:\u002F\u002Fgithub.com\u002Fimsun\u002Fgitment",[347],"Gitment",[370,24524,24525],{},[49,24526,24529],{"href":24527,"rel":24528},"https:\u002F\u002Futteranc.es\u002F",[347],"Utterances",[370,24531,24532],{},[49,24533,24536],{"href":24534,"rel":24535},"https:\u002F\u002Fgithub.com\u002Fumputun\u002Fremark42",[347],"Remark42",[12,24538,24539],{},"Remark42 nécessite l'installation d'un programme sur mon serveur. Je l'ai testé mais j'ai été à quelques\nproblème lors de l'intégration avec Gridsome.",[12,24541,24542],{},"Vssue, Gitment et Utterances sont intéressants car ils permettent d'ajouter un système de commentaires\nse basant sur le système de ticketing de Github. Github n'est en lui même pas OpenSource mais les frameworks\nci-dessus le sont.",[12,24544,24545],{},"Je considère que la plupart des personnes lisant mon blog ont un compte Github. Mais n'hésitez pas à commenter\nsi ce n'est pas le cas ;).",[12,24547,24548,24549,179],{},"Vssue était mon système préféré, beau, simple compatible avec Gitea (que j'utilise pour mes sources), Gitlab,\net avec Github également. Mais dû aux limitations de Github et Gitea, il m'était nécessaire de fournir mon\n",[49,24550,24553],{"href":24551,"rel":24552},"https:\u002F\u002Fvssue.js.org\u002Fguide\u002Fgithub.html",[347],"ClientSecret OAuth2",[12,24555,24556,24557,24562],{},"Je n'ai pas apprécié cette idée mais j'ai tout de même voulu tester. Là je me suis rendu compte qu'il\ny avait des problèmes CORS. Le composant passe par ",[49,24558,24561],{"href":24559,"rel":24560},"https:\u002F\u002Fgithub.com\u002FRob--W\u002Fcors-anywhere",[347],"CORS Anywhere"," mais\nce dernier ne peut être utilisé que pour le développement. Ce qui signifie que je dois installer un proxy équivalent\nsur mon serveur. J'ai donc regardé pour autre chose.",[12,24564,24565,24566,24571],{},"J'ai alors testé Utterances. Le problème est que cette librairie n'est pas compatible avec les\napplications de type SPA. On peut voir d'ailleurs quelques commentaires sur le ticket ",[49,24567,24570],{"href":24568,"rel":24569},"https:\u002F\u002Fgithub.com\u002Futterance\u002Futterances\u002Fissues\u002F231",[347],"Github #231","\nde personnes ayant tenté de faire une version du client pour Vue.Js.",[12,24573,24574,24575,3938],{},"Je me suis donc inspiré d'un composant Vue.JS écrit par quelqu'un d'autre (dans le 1er commentaire) pour inclure\nce système sur mon site.\nCe composant a dû être légèrement adapté pour fonctionner (je vous invite à regarder\n",[49,24576,24579],{"href":24577,"rel":24578},"https:\u002F\u002Fgithub.com\u002Fphoenix741\u002Fgridsome-flex-markdown-starter\u002Fblob\u002Fmain\u002Fsrc\u002Fcomponents\u002FGithubComponent.vue",[347],"GithubComponent.vue",[12,24581,24582],{},"Comme le plugin utilise directement l'objet window, il ne peut fonctionner avec le mode SSR (Server Side Rendering) de\nVue.Js. J'ai dû donc désactiver le mode commentaires pour le côté SSR (la génération). Ce n'est pas déconnant car\nde toutes façon les commentaires sont gérés sur Github.",[12,24584,24585,24586,24589,24590,179],{},"Voici comment on fait avec Gridsome. On commence par encadrer le bloc qui ne doit pas faire partie de la génération\ncôté serveur (et qui est donc ",[184,24587,24588],{},"Client Only","). Ce bloc contient notre composant ",[184,24591,24592],{},"GithubComponent",[2105,24594,24596],{"className":18156,"code":24595,"language":18158,"meta":1646,"style":1646},"\u003Carticle>\n  ...\n  \u003CClientOnly>\n    \u003Cdiv class=\"commentswrap\">\n      \u003Cdiv id=\"comments\">\n        \u003CGithubComponent\n          :title=\"$page.post.title\"\n          :repo=\"$page.metadata.utterances.repo\"\n          :pathname=\"this.$route.path\"\n          :url=\"url\"\n          :issueTerm=\"$page.metadata.utterances.issueTerm\"\n          :label=\"$page.metadata.utterances.label\"\n        >\u003C\u002FGithubComponent>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002FClientOnly>\n\u003C\u002Farticle>\n",[184,24597,24598,24607,24611,24621,24638,24654,24662,24672,24682,24692,24702,24712,24722,24731,24740,24749,24758],{"__ignoreMap":1646},[2113,24599,24600,24602,24605],{"class":2115,"line":2116},[2113,24601,3109],{"class":2119},[2113,24603,24604],{"class":2330},"article",[2113,24606,18185],{"class":2119},[2113,24608,24609],{"class":2115,"line":1647},[2113,24610,18190],{"class":2119},[2113,24612,24613,24616,24619],{"class":2115,"line":1652},[2113,24614,24615],{"class":2119},"  \u003C",[2113,24617,24618],{"class":2330},"ClientOnly",[2113,24620,18185],{"class":2119},[2113,24622,24623,24626,24629,24631,24633,24636],{"class":2115,"line":2185},[2113,24624,24625],{"class":2119},"    \u003C",[2113,24627,24628],{"class":2330},"div",[2113,24630,19179],{"class":2166},[2113,24632,2335],{"class":2119},[2113,24634,24635],{"class":2149},"\"commentswrap\"",[2113,24637,18185],{"class":2119},[2113,24639,24640,24643,24645,24647,24649,24652],{"class":2115,"line":2230},[2113,24641,24642],{"class":2119},"      \u003C",[2113,24644,24628],{"class":2330},[2113,24646,18170],{"class":2166},[2113,24648,2335],{"class":2119},[2113,24650,24651],{"class":2149},"\"comments\"",[2113,24653,18185],{"class":2119},[2113,24655,24656,24659],{"class":2115,"line":2235},[2113,24657,24658],{"class":2119},"        \u003C",[2113,24660,24661],{"class":2330},"GithubComponent\n",[2113,24663,24664,24667,24669],{"class":2115,"line":2241},[2113,24665,24666],{"class":2166},"          :title",[2113,24668,2335],{"class":2119},[2113,24670,24671],{"class":2149},"\"$page.post.title\"\n",[2113,24673,24674,24677,24679],{"class":2115,"line":2246},[2113,24675,24676],{"class":2166},"          :repo",[2113,24678,2335],{"class":2119},[2113,24680,24681],{"class":2149},"\"$page.metadata.utterances.repo\"\n",[2113,24683,24684,24687,24689],{"class":2115,"line":2464},[2113,24685,24686],{"class":2166},"          :pathname",[2113,24688,2335],{"class":2119},[2113,24690,24691],{"class":2149},"\"this.$route.path\"\n",[2113,24693,24694,24697,24699],{"class":2115,"line":2085},[2113,24695,24696],{"class":2166},"          :url",[2113,24698,2335],{"class":2119},[2113,24700,24701],{"class":2149},"\"url\"\n",[2113,24703,24704,24707,24709],{"class":2115,"line":2514},[2113,24705,24706],{"class":2166},"          :issueTerm",[2113,24708,2335],{"class":2119},[2113,24710,24711],{"class":2149},"\"$page.metadata.utterances.issueTerm\"\n",[2113,24713,24714,24717,24719],{"class":2115,"line":2533},[2113,24715,24716],{"class":2166},"          :label",[2113,24718,2335],{"class":2119},[2113,24720,24721],{"class":2149},"\"$page.metadata.utterances.label\"\n",[2113,24723,24724,24727,24729],{"class":2115,"line":2556},[2113,24725,24726],{"class":2119},"        >\u003C\u002F",[2113,24728,24592],{"class":2330},[2113,24730,18185],{"class":2119},[2113,24732,24733,24736,24738],{"class":2115,"line":2569},[2113,24734,24735],{"class":2119},"      \u003C\u002F",[2113,24737,24628],{"class":2330},[2113,24739,18185],{"class":2119},[2113,24741,24742,24745,24747],{"class":2115,"line":2575},[2113,24743,24744],{"class":2119},"    \u003C\u002F",[2113,24746,24628],{"class":2330},[2113,24748,18185],{"class":2119},[2113,24750,24751,24754,24756],{"class":2115,"line":2596},[2113,24752,24753],{"class":2119},"  \u003C\u002F",[2113,24755,24618],{"class":2330},[2113,24757,18185],{"class":2119},[2113,24759,24760,24762,24764],{"class":2115,"line":3192},[2113,24761,18195],{"class":2119},[2113,24763,24604],{"class":2330},[2113,24765,18185],{"class":2119},[12,24767,24768],{},"Enfin on charge le composant dynamiquement dans la partie Javascript.",[2105,24770,24772],{"className":7776,"code":24771,"language":7778,"meta":1646,"style":1646},"export default {\n  components: {\n    PostHeaderTitle,\n    GithubComponent: () =>\n      import(\"..\u002Fcomponents\u002FGithubComponent\")\n        .then((m) => m.default)\n        .catch(),\n  },\n  ...\n}\n",[184,24773,24774,24779,24784,24789,24794,24799,24804,24809,24813,24817],{"__ignoreMap":1646},[2113,24775,24776],{"class":2115,"line":2116},[2113,24777,24778],{},"export default {\n",[2113,24780,24781],{"class":2115,"line":1647},[2113,24782,24783],{},"  components: {\n",[2113,24785,24786],{"class":2115,"line":1652},[2113,24787,24788],{},"    PostHeaderTitle,\n",[2113,24790,24791],{"class":2115,"line":2185},[2113,24792,24793],{},"    GithubComponent: () =>\n",[2113,24795,24796],{"class":2115,"line":2230},[2113,24797,24798],{},"      import(\"..\u002Fcomponents\u002FGithubComponent\")\n",[2113,24800,24801],{"class":2115,"line":2235},[2113,24802,24803],{},"        .then((m) => m.default)\n",[2113,24805,24806],{"class":2115,"line":2241},[2113,24807,24808],{},"        .catch(),\n",[2113,24810,24811],{"class":2115,"line":2246},[2113,24812,8490],{},[2113,24814,24815],{"class":2115,"line":2464},[2113,24816,18190],{},[2113,24818,24819],{"class":2115,"line":2085},[2113,24820,2599],{},[12,24822,24823],{},"Ces deux opérations permettent d'exclure cette partie lors de la génération des pages statique, mais de\nl'ajouter dynamiquement après le chargement de la page coté client.",[128,24825,1621],{"id":1620},[12,24827,24828],{},"Voici donc comment j'ai mis en place la version Vue.JS de mon blog. N'hésitez pas à m'envoyer un mail\nou lâcher un commentaire.",[12,24830,24831,24832,24835,24836,179],{},"Vous pouvez retrouver le kit de démarrage que j'ai fait pour mon blog sur github: ",[49,24833,23655],{"href":23653,"rel":24834},[347],". Vous pouvez également consulter l'intégration d'utterances sur un site\nen vue ici: ",[49,24837,24839],{"href":24577,"rel":24838},[347],"phoenix741\u002Fgridsome-flex-markdown-starter:GithubComponent.vue",[23219,24841,24843,24846],{"className":24842,"dataFootnotes":1646},[23222],[128,24844,23227],{"className":24845,"id":17406},[23226],[367,24847,24848],{},[370,24849,24850,24851],{"id":23232},"Single Page Application ",[49,24852,23240],{"href":23236,"ariaLabel":23237,"className":24853,"dataFootnoteBackref":1646},[23239],[3358,24855,24856],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}",{"title":1646,"searchDepth":1647,"depth":1647,"links":24858},[24859,24860,24861,24862,24863,24864,24865],{"id":23368,"depth":1647,"text":23369},{"id":23409,"depth":1647,"text":23410},{"id":23639,"depth":1647,"text":23640},{"id":24192,"depth":1647,"text":24193},{"id":24486,"depth":1647,"text":24487},{"id":1620,"depth":1647,"text":1621},{"id":17406,"depth":1647,"text":23227},"Logiciels","logiciels","2021-03-02","J'avais migré en Janvier 2013 mon blog vers Pelican en venant de\nDotclear. Pelican est un générateur de site statique en Python. J'avais\nalors dû faire l'impasse sur les commentaires mais au bénéfice d'un site performant et avec une\nsurface d'attaque plus faible.",{"type":9,"value":24871},[24872,24879,24887,24889],[12,24873,23317,24874,23322,24876,23328],{},[49,24875,23321],{"href":23320},[49,24877,23327],{"href":23325,"rel":24878},[347],[12,24880,23331,24881,23337,24884,179],{},[49,24882,23336],{"href":23334,"rel":24883},[347],[49,24885,23342],{"href":23340,"rel":24886},[347],[12,24888,23345],{},[12,24890,23348],{},{"planet":1767},"\u002Fpost\u002Fgridsome-mise-a-jour-du-blog",{"title":23312,"description":24869},"gridsome-mise-a-jour-du-blog","posts\u002FLogiciels\u002F2021-03-02-gridsome-mise-a-jour-du-blog",[24897,3510],"dedie","92imksbvFvyNB9g-BgsuOp-f1qDd_PCUcic_SG3Gf2o",{"id":24900,"title":24901,"author":7,"body":24902,"category":1682,"categorySlug":1683,"date":69,"description":14,"excerpt":26057,"extension":1764,"location":1765,"meta":26083,"navigation":1767,"path":65,"published":1767,"seo":26084,"slug":26085,"stem":26086,"tags":26087,"timeToRead":2556,"__hash__":26089},"posts\u002Fposts\u002FWoodstock\u002F2021-01-12_woodstock_brtfs.md","Woodstock Backup - Utilisation de Btrfs et son remplacement",{"type":9,"value":24903,"toc":26043},[24904,24906,24909,24912,24938,24946,24949,24952,24955,24958,24962,24966,24969,24972,24980,24983,25054,25057,25305,25308,25311,25315,25318,25321,25324,25335,25338,25341,25344,25347,25358,25361,25364,25367,25370,25373,25382,25396,25399,25402,25411,25420,25423,25426,25429,25432,25435,25438,25487,25490,25494,25497,25501,25504,25507,25542,25545,25693,25696,25699,25706,25709,25713,25716,25719,25722,25725,25734,26011,26018,26026,26029,26032,26034,26040],[12,24905,14],{},[12,24907,24908],{},"La version 1 de mon programme de sauvegarde Woodstock Backup utlise Btrfs et Rsync pour effectuer une sauvegarde. Je\nl'utilise depuis quelques mois pour sauvegarder mes differentes machines (7 machines).",[12,24910,24911],{},"Voici un premier compte-rendu de l'utilisation de la première version de cet outil dont je suis l'auteur:",[506,24913,24914,24917,24926],{},[370,24915,24916],{},"Lors de mon utilisation la sauvegarde fonctionne très bien, et cela c'est cool :). Je suis aux alentours de 200 snapshots.",[370,24918,24919,24920,24922,24923,24925],{},"J'ai eu un problème d'espace disque. Lors du déplacement de plusieurs énormes fichiers sur un serveur. La taille de\nl'espace de stockage à augmenté énormément.",[15567,24921],{},"En effet rsync ne permet pas de détecter les déplacements de fichiers et btrfs ne permet pas de dédupliquer à la volée\nles données.",[15567,24924],{},"Les fichiers ont donc été considérés comme étant nouveau.",[370,24927,24928,24929,24931,24932,24934,24935,24937],{},"L'espace disque étant tombé à zéro, j'ai voulu supprimer la dernière snapshot pour tester un déplacement de fichiers\ndans btrfs (à la main).",[15567,24930],{},"La suppression de la snapshot a commencé à prendre énormément de temps, puis la machine est devenue inaccessible.",[15567,24933],{},"En me connectant en direct sur la machine (KVM), j'ai découvert que la suppression du dernier volume Btrfs remplissait\nla mémore. Les 8Go octets de mémoire ont été remplis. Et le noyaux linux a utilisé OOM Killer pour détruire tous les\nprocessus.",[15567,24936],{},"Bref la machine n'était plus dans un état lui permettant de faire les sauvegardes.",[12,24939,24940,24941,24945],{},"On parle déjà de problème avec btrfs et la suppression de snapshot (",[49,24942,24943],{"href":24943,"rel":24944},"https:\u002F\u002Fwww.spinics.net\u002Flists\u002Flinux-btrfs\u002Fmsg52088.html",[347],").\nOn trouve aussi pas mal d'article si les problèmes de btrfs quand il n'y a plus d'espace.",[12,24947,24948],{},"Cela me fait penser que btrfs ne me permettrait pas de gérer mes sauvegardes sur le long terme (tant sur le nombre de snapshots\nque cela allait créér que sur la déduplication).",[12,24950,24951],{},"Cela m'amène à la conclusion suivante, il faut que je repense la gestion du pool et de la déduplication. C'est d'ailleurs\nce que font beaucoup de logiciel de sauvegarde (UrBackup proposer Btrfs et une version interne ; BackupPC gère son pool interne,\nBorg gère son pool interne ...)",[12,24953,24954],{},"Je vais donc repenser la manière dont je gère la déduplication dans l'application de sauvegarde. Si je réécris la manière\ndont est géré le stockage, je ne pourrais plus me baser sur rsync pour la sauvegarde. Il faudra donc que je développe mon\npropre outil pour générer la sauvegarde.",[12,24956,24957],{},"Par contre cela à l'avantage de ne plus être dépendant d'un système de fichier.",[128,24959,24961],{"id":24960},"le-pool","Le pool",[133,24963,24965],{"id":24964},"diviser-pour-réigner","Diviser pour réigner",[12,24967,24968],{},"Nous allons donc commencer par construire ce qui doit être le pool des données. Permettant de faire la déduplication.",[12,24970,24971],{},"Afin de faire de la déduplication, nous allons découper les fichiers en plusieurs morceaux. Cela permettra de faire des\nsauvegardes de gros fichiers dont seule une partie change:",[506,24973,24974,24977],{},[370,24975,24976],{},"Comme les images de machine virtuelle par exemple",[370,24978,24979],{},"Comme les fichiers de logs par exemple",[12,24981,24982],{},"Quelle est la bonne taille pour un morceau de fichier. Si on découpe le fichier de taille trop petite alors le découpage ne\nsert à rien. J'ai donc pris un échantillon de tous les fichiers que j'ai sur mes différentes machines pour déterminer la taille\nde cet échantillon.",[2105,24984,24986],{"className":2317,"code":24985,"language":2319,"meta":1646,"style":1646},"find . -type f -print0 | \\\nxargs -0 ls -l | \\\nawk '{ n=int(log($5)\u002Flog(2)); if (n\u003C10) { n=10; } size[n]++ } END { for (i in size) printf(\"%d %d\\n\", 2^i, size[i]) }' | \\\nsort -n | \\\nawk 'function human(x) { x[1]\u002F=1024; if (x[1]>=1024) { x[2]++; human(x) } } { a[1]=$1; a[2]=0; human(a); printf(\"%3d%s: %6d\\n\", a[1],substr(\"kMGTEPYZ\",a[2]+1,1),$2) }'\n",[184,24987,24988,25006,25024,25036,25047],{"__ignoreMap":1646},[2113,24989,24990,24992,24995,24997,24999,25001,25003],{"class":2115,"line":2116},[2113,24991,7343],{"class":2133},[2113,24993,24994],{"class":2149}," .",[2113,24996,7349],{"class":2166},[2113,24998,3920],{"class":2149},[2113,25000,7354],{"class":2166},[2113,25002,7357],{"class":2119},[2113,25004,25005],{"class":2334},"\\\n",[2113,25007,25008,25011,25014,25017,25020,25022],{"class":2115,"line":1647},[2113,25009,25010],{"class":2133},"xargs",[2113,25012,25013],{"class":2166}," -0",[2113,25015,25016],{"class":2149}," ls",[2113,25018,25019],{"class":2166}," -l",[2113,25021,7357],{"class":2119},[2113,25023,25005],{"class":2334},[2113,25025,25026,25029,25032,25034],{"class":2115,"line":1652},[2113,25027,25028],{"class":2133},"awk",[2113,25030,25031],{"class":2149}," '{ n=int(log($5)\u002Flog(2)); if (n\u003C10) { n=10; } size[n]++ } END { for (i in size) printf(\"%d %d\\n\", 2^i, size[i]) }'",[2113,25033,7357],{"class":2119},[2113,25035,25005],{"class":2334},[2113,25037,25038,25040,25043,25045],{"class":2115,"line":2185},[2113,25039,19527],{"class":2133},[2113,25041,25042],{"class":2166}," -n",[2113,25044,7357],{"class":2119},[2113,25046,25005],{"class":2334},[2113,25048,25049,25051],{"class":2115,"line":2230},[2113,25050,25028],{"class":2133},[2113,25052,25053],{"class":2149}," 'function human(x) { x[1]\u002F=1024; if (x[1]>=1024) { x[2]++; human(x) } } { a[1]=$1; a[2]=0; human(a); printf(\"%3d%s: %6d\\n\", a[1],substr(\"kMGTEPYZ\",a[2]+1,1),$2) }'\n",[12,25055,25056],{},"Voici le resultat de ce test d'échantillon.",[22,25058,25059,25072],{},[25,25060,25061],{},[28,25062,25063,25066,25069],{},[31,25064,25065],{},"File size",[31,25067,25068],{},"Number",[31,25070,25071],{},"Repartition",[41,25073,25074,25085,25096,25107,25118,25129,25140,25151,25162,25173,25184,25195,25206,25217,25228,25239,25250,25261,25272,25283,25294],{},[28,25075,25076,25079,25082],{},[46,25077,25078],{},"1k",[46,25080,25081],{},"29126558",[46,25083,25084],{},"37,36 %",[28,25086,25087,25090,25093],{},[46,25088,25089],{},"2k",[46,25091,25092],{},"8649088",[46,25094,25095],{},"48,45 %",[28,25097,25098,25101,25104],{},[46,25099,25100],{},"4k",[46,25102,25103],{},"7915884",[46,25105,25106],{},"58,60 %",[28,25108,25109,25112,25115],{},[46,25110,25111],{},"8k",[46,25113,25114],{},"6394302",[46,25116,25117],{},"66,81 %",[28,25119,25120,25123,25126],{},[46,25121,25122],{},"16k",[46,25124,25125],{},"4839627",[46,25127,25128],{},"73,01 %",[28,25130,25131,25134,25137],{},[46,25132,25133],{},"32k",[46,25135,25136],{},"3606949",[46,25138,25139],{},"77,64 %",[28,25141,25142,25145,25148],{},[46,25143,25144],{},"64k",[46,25146,25147],{},"3477900",[46,25149,25150],{},"82,10 %",[28,25152,25153,25156,25159],{},[46,25154,25155],{},"128k",[46,25157,25158],{},"5158625",[46,25160,25161],{},"88,72 %",[28,25163,25164,25167,25170],{},[46,25165,25166],{},"256k",[46,25168,25169],{},"3601985",[46,25171,25172],{},"93,34 %",[28,25174,25175,25178,25181],{},[46,25176,25177],{},"512k",[46,25179,25180],{},"971108",[46,25182,25183],{},"94,58 %",[28,25185,25186,25189,25192],{},[46,25187,25188],{},"1M",[46,25190,25191],{},"875574",[46,25193,25194],{},"95,71 %",[28,25196,25197,25200,25203],{},[46,25198,25199],{},"2M",[46,25201,25202],{},"1698194",[46,25204,25205],{},"97,88 %",[28,25207,25208,25211,25214],{},[46,25209,25210],{},"4M",[46,25212,25213],{},"1046430",[46,25215,25216],{},"99,23 %",[28,25218,25219,25222,25225],{},[46,25220,25221],{},"8M",[46,25223,25224],{},"309027",[46,25226,25227],{},"99,62 %",[28,25229,25230,25233,25236],{},[46,25231,25232],{},"16M",[46,25234,25235],{},"105271",[46,25237,25238],{},"99,76 %",[28,25240,25241,25244,25247],{},[46,25242,25243],{},"32M",[46,25245,25246],{},"65211",[46,25248,25249],{},"99,84 %",[28,25251,25252,25255,25258],{},[46,25253,25254],{},"64M",[46,25256,25257],{},"50832",[46,25259,25260],{},"99,91 %",[28,25262,25263,25266,25269],{},[46,25264,25265],{},"128M",[46,25267,25268],{},"33947",[46,25270,25271],{},"99,95 %",[28,25273,25274,25277,25280],{},[46,25275,25276],{},"256M",[46,25278,25279],{},"21338",[46,25281,25282],{},"99,98 %",[28,25284,25285,25288,25291],{},[46,25286,25287],{},"512M",[46,25289,25290],{},"8066",[46,25292,25293],{},"99,99 %",[28,25295,25296,25299,25302],{},[46,25297,25298],{},"> 1G",[46,25300,25301],{},"10068",[46,25303,25304],{},"100,00 %",[12,25306,25307],{},"La conclusion que l'on peut tirer de cela est que si on prend un chunk de 4Mo, alors 99% des fichiers ne seront pas découpés. Dans\nle lot rentrerons la plupart des fichiers textes, des photos, ... . Tous des fichiers qui s'ils ne sont pas des copies, sont normalement\nunique.",[12,25309,25310],{},"Dans une première version, je pense que cette taille ne sera pas paramétrable, mais il faudra la rendre dynamiquement paramétrable dans le\nfutur (si le type des fichiers d'autres personnes a pour conséquence une répartition différente).",[128,25312,25314],{"id":25313},"la-table-de-hashage","La table de hashage",[12,25316,25317],{},"Ces différents morceaux de fichiers (chunk) doivent ensuite pouvoir être identifiés facilement sans pour autant relire le contenu du fichier à\nchaque fois.",[12,25319,25320],{},"L'idée est d'utiliser une table de hashage où avec une clé on peut retrouver le contenu d'un morceau de fichier.",[12,25322,25323],{},"Il faut pouvoir, à partir de la clé:",[506,25325,25326,25329,25332],{},[370,25327,25328],{},"savoir si le morceau de fichier existe,",[370,25330,25331],{},"lire le contenu du morceau de fichier,",[370,25333,25334],{},"écrire le contenu du morceau de fichier.",[12,25336,25337],{},"Quelle clé peut-on utiliser ?",[12,25339,25340],{},"Après quelque recherches je vais partir sur un SHA-256. Quels sont les avantages et les inconvénients d'une telle clé ?",[12,25342,25343],{},"L'avantage est qu'il est possible, quand on connait le hash de faire le lien direct avec le morceau de fichier associé. La clé est\nfacile à générer et le lien entre la clé et le chunk est facile à connaître.",[12,25345,25346],{},"Le plus gros inconvénient est le risque de collision. Le but d'une fonction de hash est de calculer un nombre \"court\" qui permet\nde representer un texte beaucoup plus long. Ce type de fonction de hashage est généralement utilisé pour:",[506,25348,25349,25352,25355],{},[370,25350,25351],{},"quand elle est coupler à une clé public\u002Fprivé, signer un document,",[370,25353,25354],{},"verifier l'intégrité d'un document (en cas d'erreur transfert réseau),",[370,25356,25357],{},"envoyer séparement permet de vérifier que le fichier n'a pas été corrompu.",[12,25359,25360],{},"Ce qui est le propre d'une fonction de hash est sa non réversibilité. En effet s'il était possible de retrouver facilement à partir d'un\nhash le texte contenu associé, les algo de compression n'utiliseraient que ca ;).\nMais la plus grosse conséquence de tout cela est que deux textes (de contenus et de longueurs différentes) peuvent arriver au même hash.",[12,25362,25363],{},"Pour un logiciel de sauvegarde, cela pose alors un gros problème. Si parcequ'il possède le même hash qu'un autre fichier, on ne sauvegarde\npas un document, la restauration ne sera pas faite à l'identique et cela engendrera de la perte de fichier.",[12,25365,25366],{},"Je me suis donc renseigné sur ce qui se faisait ailleurs.",[133,25368,15449],{"id":25369},"borg",[12,25371,25372],{},"Je trouve borg intéressant dans son fonctionnement, ses performances, ainsi que son stockage interne. Ce qui est dommage, c'est que dans\nmon cas, je n'ai pas une vue centralisée des machines dont j'organise la sauvegarde.",[12,25374,25375,25376,25381],{},"Dans la partie ",[49,25377,25380],{"href":25378,"rel":25379},"https:\u002F\u002Fborgbackup.readthedocs.io\u002Fen\u002Fstable\u002Finternals.html",[347],"Internals"," on retrouve la structure interne du pool de Borg.",[12,25383,25384,25385,25390,25391,179],{},"Borg utilise ",[49,25386,25389],{"href":25387,"rel":25388},"https:\u002F\u002Fborgbackup.readthedocs.io\u002Fen\u002Fstable\u002Finternals\u002Fdata-structures.html#hashindex",[347],"HashIndex",", qui si j'ai bien compris\ncalcule un hash sur un contenu dont la taille peut varier. Des utilisateurs se sont déjà posés la question du ",[49,25392,25395],{"href":25393,"rel":25394},"https:\u002F\u002Fgithub.com\u002Fborgbackup\u002Fborg\u002Fissues\u002F170",[347],"risque de collision",[12,25397,25398],{},"La réponse faite par l'équipe de Borg a été de rejeter la demande. L'explication qui est faite est que la probabilité qu'une telle\ncollision arrive est faible sur un hash de 256. Très faible.",[133,25400,15400],{"id":25401},"urbackup",[12,25403,25404,25405,25410],{},"Même chose pour UrBackup. Dans le ",[49,25406,25409],{"href":25407,"rel":25408},"https:\u002F\u002Fwww.urbackup.org\u002Fadministration_manual.html",[347],"manuel d'administration, partie 6.3",",\non retrouve l'information suivante:",[1886,25412,25413],{},[12,25414,25415,25416,25419],{},"UrBackup uses SHA512 to hash the files before file deduplication. In comparison ZFS uses SHA256 for block deduplication.\nThe choice of SHA512 is safer. The Wikipedia page for “Birthday attack” has a probability table for SHA512. According to\nit one needs 1.6 ",[362,25417,25418],{},"1068 different files (of same size) to reach a probability of 10-18 of a collision. It also states\nthat 10-18 is the best case uncorrectable bit error rate of a typical hard disk. To have 1.6"," 1068 different files of\n1KB you need 1.4551915 * 1056 EB of hard disk space. So it is ridiculously more likely that the hard disk returns bad\ndata or the data gets corrupted in RAM, rather than UrBackup linking the wrong files to each other.",[12,25421,25422],{},"Bref, on y explique sur la probabilité de collision pour un SHA512 est plus faible que celle utilisé pour la déduplication\ndans ZFS. On y explique également qu'elle est ridicule par rapport au risque de panne d'un disque dur.",[133,25424,1013],{"id":25425},"backuppc",[12,25427,25428],{},"BackupPC utilise un MD5 pour gérer son les clés de son pool. En cas de collision (ce qui est beaucoup plus probable avec un MD5 qu'avec\nun SHA-256) une extension est ajoutée à la fin du fichier.",[12,25430,25431],{},"Cela necessite alors de ré-envoyer le fichier sur le réseau pour comparer, mais permet de s'assurer qu'en cas de collision on ait une\nsolution de repli.",[12,25433,25434],{},"Un MD5 fait 4 octets contrairement à un SHA-256 qui en fait 32.",[12,25436,25437],{},"Du coup, est-ce qu'avec un md5, j'ai beaucoup de collision sur mon instance BackupPC ? Pour un pool qui contient 3 464 397 fichiers, j'ai\n0 collision.",[2105,25439,25441],{"className":2317,"code":25440,"language":2319,"meta":1646,"style":1646},"> find . -type f | wc -l\n3464397\n> find . -type f | awk 'length($0) > 40' | wc -l\n0\n",[184,25442,25443,25459,25464,25483],{"__ignoreMap":1646},[2113,25444,25445,25448,25450,25453,25456],{"class":2115,"line":2116},[2113,25446,25447],{"class":2119},"> find ",[2113,25449,179],{"class":2334},[2113,25451,25452],{"class":2119}," -type f | ",[2113,25454,25455],{"class":2133},"wc",[2113,25457,25458],{"class":2166}," -l\n",[2113,25460,25461],{"class":2115,"line":1647},[2113,25462,25463],{"class":2133},"3464397\n",[2113,25465,25466,25468,25470,25472,25474,25477,25479,25481],{"class":2115,"line":1652},[2113,25467,25447],{"class":2119},[2113,25469,179],{"class":2334},[2113,25471,25452],{"class":2119},[2113,25473,25028],{"class":2133},[2113,25475,25476],{"class":2149}," 'length($0) > 40'",[2113,25478,7357],{"class":2119},[2113,25480,25455],{"class":2133},[2113,25482,25458],{"class":2166},[2113,25484,25485],{"class":2115,"line":2185},[2113,25486,18077],{"class":2133},[12,25488,25489],{},"Ce qui est plutôt rassurant sur le risque de collision sur un SHA-256",[133,25491,25493],{"id":25492},"woodstock-backup","Woodstock Backup",[12,25495,25496],{},"Pour mon propre logiciel, je vais partir sur un SHA-256. Je vais partir du principe qu'il n'y a pas de collision (et je ferais un test\npour vérifier cela).\nDans une version future il pourrait être intéressant d'ajouter une extension au fichier dans le cas où il y a une collision, et laisser\nla possibilité à l'utilisateur de demander à retransférer le fichier pour être sûr qu'il n'y a pas de collision.",[128,25498,25500],{"id":25499},"structure-du-pool","Structure du pool",[12,25502,25503],{},"Maintenant que l'on connaît la taille des différents morceau de fichier ainsi que la clé qui nous servira à les classer. Nous allons\nmaintenant voir comment les organiser dans notre système de fichier.",[12,25505,25506],{},"Derrière le stockage j'ai les idées suivantes:",[506,25508,25509,25516,25519,25536,25539],{},[370,25510,25511,25512,25515],{},"Si on stocke tout les chunks (morceau de fichier) dans un seul dossier, nous pourrions au plus avoir ",[184,25513,25514],{},"1.3 x 10^154"," fichiers. Mais\ncela dépendra plus du nombre de fichier (chunk) que du nombre du nombre de possibilités.",[370,25517,25518],{},"Un système de fichier est en lui-même déjà une table de correspondance où la clé est le nom du fichier.",[370,25520,25521,25522],{},"Il y a une limite sur le nombre de fichiers par dossier:\n",[506,25523,25524,25527,25530,25533],{},[370,25525,25526],{},"FAT32: 65 536 (donc non utilisable)",[370,25528,25529],{},"NTFS: à priori pas de limit.",[370,25531,25532],{},"EXT2: ~1.3 x 10^20 (mais problème de perf au delà de 10 000) - (donc je déconseille son utilisation).",[370,25534,25535],{},"EXT4: pas de limit de nombre de fichier par dossier",[370,25537,25538],{},"J'aimerai à terme (un jour peut-être) pouvoir proposer l'utilisation d'un stockage objet (exemple S3, minio, ...) afin de pouvoir\nutiliser ce stockage depuis plusieurs serveurs. Il n'y a pas de limite du nombre de fichier dans un bucket S3.",[370,25540,25541],{},"Sur une structure à deux niveaux (et 255 possibilité par niveau), on peut espérer que les 3,5 millions de fichiers seront répartis\nce qui ferait un peu plus d'une 50 de fichier par dossier. (le nombre de fichier par dossier dépend du nombre de fichier et de leurs\ntaille).",[12,25543,25544],{},"Voici la structure que j'imagine utiliser pour le stockage des chunks.",[2105,25546,25548],{"className":2317,"code":25547,"language":2319,"meta":1646,"style":1646}," pool\n   ├── aa\n   │    ├── aa\n   │    │    ├── aa\n   │    │    │    ├── REFCNT\n   │    │    │    │     ├── sha256 cnt\n   │    │    │    │     ├── sha256 cnt\n   │    │    │    │     └── sha256 cnt\n   │    │    │    ├── LOCK\n   │    │    │    │     └── host backupNumber\n   │    │    │    └── aaaaaacdefghih-sha256.zlib\n",[184,25549,25550,25555,25563,25573,25584,25597,25615,25631,25648,25661,25679],{"__ignoreMap":1646},[2113,25551,25552],{"class":2115,"line":2116},[2113,25553,25554],{"class":2133}," pool\n",[2113,25556,25557,25560],{"class":2115,"line":1647},[2113,25558,25559],{"class":2133},"   ├──",[2113,25561,25562],{"class":2149}," aa\n",[2113,25564,25565,25568,25571],{"class":2115,"line":1652},[2113,25566,25567],{"class":2133},"   │",[2113,25569,25570],{"class":2149},"    ├──",[2113,25572,25562],{"class":2149},[2113,25574,25575,25577,25580,25582],{"class":2115,"line":2185},[2113,25576,25567],{"class":2133},[2113,25578,25579],{"class":2149},"    │",[2113,25581,25570],{"class":2149},[2113,25583,25562],{"class":2149},[2113,25585,25586,25588,25590,25592,25594],{"class":2115,"line":2230},[2113,25587,25567],{"class":2133},[2113,25589,25579],{"class":2149},[2113,25591,25579],{"class":2149},[2113,25593,25570],{"class":2149},[2113,25595,25596],{"class":2149}," REFCNT\n",[2113,25598,25599,25601,25603,25605,25607,25610,25612],{"class":2115,"line":2235},[2113,25600,25567],{"class":2133},[2113,25602,25579],{"class":2149},[2113,25604,25579],{"class":2149},[2113,25606,25579],{"class":2149},[2113,25608,25609],{"class":2149},"     ├──",[2113,25611,10145],{"class":2149},[2113,25613,25614],{"class":2149}," cnt\n",[2113,25616,25617,25619,25621,25623,25625,25627,25629],{"class":2115,"line":2241},[2113,25618,25567],{"class":2133},[2113,25620,25579],{"class":2149},[2113,25622,25579],{"class":2149},[2113,25624,25579],{"class":2149},[2113,25626,25609],{"class":2149},[2113,25628,10145],{"class":2149},[2113,25630,25614],{"class":2149},[2113,25632,25633,25635,25637,25639,25641,25644,25646],{"class":2115,"line":2246},[2113,25634,25567],{"class":2133},[2113,25636,25579],{"class":2149},[2113,25638,25579],{"class":2149},[2113,25640,25579],{"class":2149},[2113,25642,25643],{"class":2149},"     └──",[2113,25645,10145],{"class":2149},[2113,25647,25614],{"class":2149},[2113,25649,25650,25652,25654,25656,25658],{"class":2115,"line":2464},[2113,25651,25567],{"class":2133},[2113,25653,25579],{"class":2149},[2113,25655,25579],{"class":2149},[2113,25657,25570],{"class":2149},[2113,25659,25660],{"class":2149}," LOCK\n",[2113,25662,25663,25665,25667,25669,25671,25673,25676],{"class":2115,"line":2085},[2113,25664,25567],{"class":2133},[2113,25666,25579],{"class":2149},[2113,25668,25579],{"class":2149},[2113,25670,25579],{"class":2149},[2113,25672,25643],{"class":2149},[2113,25674,25675],{"class":2149}," host",[2113,25677,25678],{"class":2149}," backupNumber\n",[2113,25680,25681,25683,25685,25687,25690],{"class":2115,"line":2514},[2113,25682,25567],{"class":2133},[2113,25684,25579],{"class":2149},[2113,25686,25579],{"class":2149},[2113,25688,25689],{"class":2149},"    └──",[2113,25691,25692],{"class":2149}," aaaaaacdefghih-sha256.zlib\n",[12,25694,25695],{},"Pour les 3 premiers niveaux, nous allons avoir une structure de dossier qui est constituée des 3 premiers octets du SHA-256. Ce qui permet\nde réduire le nombre de fichiers par dossier, mais aussi de limiter le nombre de LOCK lors de la création du pool.",[12,25697,25698],{},"Ensuite dans chaque dossier, nous aurons un fichier de LOCK qui sera créé lors de l'ajout d'un nouveau morceau de fichier. Il sera également\nutilisé lors de la lecture.",[12,25700,25701,25702,25705],{},"Un fichier ",[184,25703,25704],{},"REFCNT"," sera utilisé pour compter le nombre de référence. Cela permettra de supprimer les morceaux quand ces derniers ne seront\nplus utilisés.",[12,25707,25708],{},"Enfin les morceaux de fichiers sont stockés dans des fichiers dont le nom contient le hash. Ces fichiers peuvent être compressés pour réduire\nl'espace utilisé.",[128,25710,25712],{"id":25711},"structure-des-fichiers-de-sauvegardes","Structure des fichiers de sauvegardes",[12,25714,25715],{},"Une fois les morceaux de fichiers déposés dans le pool, il nous faut un fichier permettant de référencer l'ensemble des fichiers qui composent\nla sauvegarde.",[12,25717,25718],{},"Il y aura un fichier par sauvegarde.",[12,25720,25721],{},"Ces fichiers de sauvegarde seront stockés dans les dossiers des différents serveurs sauvegardés. Ce fichier contient l'ensemble des fichiers\nd'une sauvegarde et pour chaque fichier, le hash de l'enssemble des morceaux de fichiers.",[12,25723,25724],{},"Ce fichier doit avoir un format lisible facilement et rapidement par un programme. Ce fichier sera dans un format binaire (pour que le fichier\nsoit lisible rapidement et qu'il ne prenne pas trop de place).",[12,25726,25727,25728,25733],{},"Afin de simplifier la mise en place de ce fichier, nous allons utiliser ",[49,25729,25732],{"href":25730,"rel":25731},"https:\u002F\u002Fdevelopers.google.com\u002Fprotocol-buffers",[347],"protocol-buffers",". Ce\ndernier permet d'avoir un format de fichier compatible quel que soit le language de l'application. Voici un premier jet de format de fichier qui\nsera utilisé pour stocker chaque fichier d'une sauvegarde.",[2105,25735,25737],{"className":15974,"code":25736,"language":15970,"meta":1646,"style":1646},"message FileManifest {\n  message FileManifestStat {\n    int32 ownerId = 1;\n    int32 groupId = 2;\n    int64 size = 3;\n    int64 lastRead = 4;\n    int64 lastModified = 5;\n    int64 created = 6;\n    int32 mode = 7;\n  }\n\n  message FileManifestAcl {\n    string user = 1;\n    string group = 2;\n    int32 mask = 3;\n    int32 other = 4;\n  }\n\n  bytes path = 1;\n  FileManifestStat stats = 2;\n  map\u003Cstring, bytes> xattr = 5;\n  repeated FileManifestAcl acl = 6;\n  repeated bytes chunks = 3;\n  bytes sha256 = 4;\n}\n",[184,25738,25739,25747,25755,25767,25779,25791,25803,25815,25827,25839,25843,25847,25856,25870,25883,25896,25909,25913,25917,25929,25941,25966,25981,25995,26007],{"__ignoreMap":1646},[2113,25740,25741,25743,25745],{"class":2115,"line":2116},[2113,25742,16052],{"class":2326},[2113,25744,16055],{"class":2414},[2113,25746,2647],{"class":2119},[2113,25748,25749,25751,25753],{"class":2115,"line":1647},[2113,25750,16062],{"class":2326},[2113,25752,16065],{"class":2414},[2113,25754,2647],{"class":2119},[2113,25756,25757,25759,25761,25763,25765],{"class":2115,"line":1652},[2113,25758,16072],{"class":2326},[2113,25760,16075],{"class":2330},[2113,25762,2153],{"class":2334},[2113,25764,2850],{"class":2166},[2113,25766,2487],{"class":2119},[2113,25768,25769,25771,25773,25775,25777],{"class":2115,"line":2185},[2113,25770,16072],{"class":2326},[2113,25772,16088],{"class":2330},[2113,25774,2153],{"class":2334},[2113,25776,2484],{"class":2166},[2113,25778,2487],{"class":2119},[2113,25780,25781,25783,25785,25787,25789],{"class":2115,"line":2230},[2113,25782,16099],{"class":2326},[2113,25784,7924],{"class":2330},[2113,25786,2153],{"class":2334},[2113,25788,16106],{"class":2166},[2113,25790,2487],{"class":2119},[2113,25792,25793,25795,25797,25799,25801],{"class":2115,"line":2235},[2113,25794,16099],{"class":2326},[2113,25796,16115],{"class":2330},[2113,25798,2153],{"class":2334},[2113,25800,2894],{"class":2166},[2113,25802,2487],{"class":2119},[2113,25804,25805,25807,25809,25811,25813],{"class":2115,"line":2241},[2113,25806,16099],{"class":2326},[2113,25808,16128],{"class":2330},[2113,25810,2153],{"class":2334},[2113,25812,16133],{"class":2166},[2113,25814,2487],{"class":2119},[2113,25816,25817,25819,25821,25823,25825],{"class":2115,"line":2246},[2113,25818,16099],{"class":2326},[2113,25820,16142],{"class":2330},[2113,25822,2153],{"class":2334},[2113,25824,16147],{"class":2166},[2113,25826,2487],{"class":2119},[2113,25828,25829,25831,25833,25835,25837],{"class":2115,"line":2464},[2113,25830,16072],{"class":2326},[2113,25832,16156],{"class":2330},[2113,25834,2153],{"class":2334},[2113,25836,6408],{"class":2166},[2113,25838,2487],{"class":2119},[2113,25840,25841],{"class":2115,"line":2085},[2113,25842,2572],{"class":2119},[2113,25844,25845],{"class":2115,"line":2514},[2113,25846,2125],{"emptyLinePlaceholder":1767},[2113,25848,25849,25851,25854],{"class":2115,"line":2533},[2113,25850,16062],{"class":2326},[2113,25852,25853],{"class":2414}," FileManifestAcl",[2113,25855,2647],{"class":2119},[2113,25857,25858,25861,25864,25866,25868],{"class":2115,"line":2556},[2113,25859,25860],{"class":2326},"    string",[2113,25862,25863],{"class":2330}," user",[2113,25865,2153],{"class":2334},[2113,25867,2850],{"class":2166},[2113,25869,2487],{"class":2119},[2113,25871,25872,25874,25877,25879,25881],{"class":2115,"line":2569},[2113,25873,25860],{"class":2326},[2113,25875,25876],{"class":2330}," group",[2113,25878,2153],{"class":2334},[2113,25880,2484],{"class":2166},[2113,25882,2487],{"class":2119},[2113,25884,25885,25887,25890,25892,25894],{"class":2115,"line":2575},[2113,25886,16072],{"class":2326},[2113,25888,25889],{"class":2330}," mask",[2113,25891,2153],{"class":2334},[2113,25893,16106],{"class":2166},[2113,25895,2487],{"class":2119},[2113,25897,25898,25900,25903,25905,25907],{"class":2115,"line":2596},[2113,25899,16072],{"class":2326},[2113,25901,25902],{"class":2330}," other",[2113,25904,2153],{"class":2334},[2113,25906,2894],{"class":2166},[2113,25908,2487],{"class":2119},[2113,25910,25911],{"class":2115,"line":3192},[2113,25912,2572],{"class":2119},[2113,25914,25915],{"class":2115,"line":3213},[2113,25916,2125],{"emptyLinePlaceholder":1767},[2113,25918,25919,25921,25923,25925,25927],{"class":2115,"line":3236},[2113,25920,16175],{"class":2326},[2113,25922,6584],{"class":2330},[2113,25924,2153],{"class":2334},[2113,25926,2850],{"class":2166},[2113,25928,2487],{"class":2119},[2113,25930,25931,25933,25935,25937,25939],{"class":2115,"line":3248},[2113,25932,16188],{"class":2326},[2113,25934,9087],{"class":2330},[2113,25936,2153],{"class":2334},[2113,25938,2484],{"class":2166},[2113,25940,2487],{"class":2119},[2113,25942,25943,25946,25948,25950,25952,25955,25957,25960,25962,25964],{"class":2115,"line":4899},[2113,25944,25945],{"class":2326},"  map",[2113,25947,3109],{"class":2119},[2113,25949,19225],{"class":2326},[2113,25951,932],{"class":2119},[2113,25953,25954],{"class":2326},"bytes",[2113,25956,4435],{"class":2119},[2113,25958,25959],{"class":2330},"xattr",[2113,25961,2153],{"class":2334},[2113,25963,16133],{"class":2166},[2113,25965,2487],{"class":2119},[2113,25967,25968,25970,25972,25975,25977,25979],{"class":2115,"line":1777},[2113,25969,16201],{"class":2326},[2113,25971,25853],{"class":2326},[2113,25973,25974],{"class":2330}," acl",[2113,25976,2153],{"class":2334},[2113,25978,16147],{"class":2166},[2113,25980,2487],{"class":2119},[2113,25982,25983,25985,25987,25989,25991,25993],{"class":2115,"line":4931},[2113,25984,16201],{"class":2326},[2113,25986,16204],{"class":2326},[2113,25988,16207],{"class":2330},[2113,25990,2153],{"class":2334},[2113,25992,16106],{"class":2166},[2113,25994,2487],{"class":2119},[2113,25996,25997,25999,26001,26003,26005],{"class":2115,"line":4961},[2113,25998,16175],{"class":2326},[2113,26000,10145],{"class":2330},[2113,26002,2153],{"class":2334},[2113,26004,2894],{"class":2166},[2113,26006,2487],{"class":2119},[2113,26008,26009],{"class":2115,"line":4976},[2113,26010,2599],{"class":2119},[12,26012,26013,26014,26017],{},"Le fichier sera constitué d'une liste de ",[184,26015,26016],{},"FileManifest",". Ce fichier est de la forme :",[2105,26019,26024],{"className":26020,"code":26022,"language":26023},[26021],"language-text","int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest\nint32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest\n(vous avez compris le principe)\n","text",[184,26025,26022],{"__ignoreMap":1646},[12,26027,26028],{},"où chaque int32 est la taille de chaque FileManifest.",[12,26030,26031],{},"Lors de la sauvegarde, le serveur repartira de la sauvegarde précédente et écrira toute modification dans un journal. Le journal\nindiquera, les fichiers ajoutés, supprimés, et modifiés. Une fois la sauvegarde terminée, le journal sera fusionné dans le fichier de\nsauvegarde (et le comptage de référence sera mis à jour).",[128,26033,1621],{"id":1620},[12,26035,26036,26037,179],{},"Maintenant que j'ai une idée de la forme du nouveau de format de stockage, il ne me reste plus qu'à développer le pool de stockage.\nSi vous avez des retours à me faire sur le format de stockage alors n'hésitez pas à me contacter. Vous pouvez le faire par exemple sur\n",[49,26038,26039],{"href":25492},"https:\u002F\u002Fgithub.com\u002Fphoenix741\u002Fwoodstock-backup",[3358,26041,26042],{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}",{"title":1646,"searchDepth":1647,"depth":1647,"links":26044},[26045,26048,26054,26055,26056],{"id":24960,"depth":1647,"text":24961,"children":26046},[26047],{"id":24964,"depth":1652,"text":24965},{"id":25313,"depth":1647,"text":25314,"children":26049},[26050,26051,26052,26053],{"id":25369,"depth":1652,"text":15449},{"id":25401,"depth":1652,"text":15400},{"id":25425,"depth":1652,"text":1013},{"id":25492,"depth":1652,"text":25493},{"id":25499,"depth":1647,"text":25500},{"id":25711,"depth":1647,"text":25712},{"id":1620,"depth":1647,"text":1621},{"type":9,"value":26058},[26059,26061,26063,26065],[12,26060,14],{},[12,26062,24908],{},[12,26064,24911],{},[506,26066,26067,26069,26075],{},[370,26068,24916],{},[370,26070,24919,26071,24922,26073,24925],{},[15567,26072],{},[15567,26074],{},[370,26076,24928,26077,24931,26079,24934,26081,24937],{},[15567,26078],{},[15567,26080],{},[15567,26082],{},{"planet":1767},{"title":24901,"description":14},"woodstock_brtfs","posts\u002FWoodstock\u002F2021-01-12_woodstock_brtfs",[1773,1774,26088,983,3510,3511,1683],"btrfs","f12bC0YlcreB0kEuvkJiJsi0nYRb0O3yavG9pPjrXUM",{"id":26091,"title":26092,"author":7,"body":26093,"category":2057,"categorySlug":2058,"date":29132,"description":26097,"excerpt":29133,"extension":1764,"location":1765,"meta":29155,"navigation":1767,"path":29156,"published":1767,"seo":29157,"slug":29158,"stem":29159,"tags":29160,"timeToRead":1777,"__hash__":29163},"posts\u002Fposts\u002FProgrammation\u002F2020-11-29_creation-api-3.md","Comment créer une bonne API Web - Partie 3",{"type":9,"value":26094,"toc":29111},[26095,26098,26101,26121,26124,26128,26131,26139,26142,26148,26151,26154,26157,26288,26294,26300,26303,26469,26472,26475,26483,26487,26490,26493,26496,26499,26502,26510,26513,26517,26520,26523,26633,26636,26639,26646,26649,26657,26661,26668,26676,26680,26683,26946,26949,26966,26975,26978,27010,27013,27030,27033,27036,27112,27115,27126,27138,27182,27185,27334,27337,27341,27344,27347,27350,27353,27356,27360,27425,27428,27431,27437,27441,27444,27519,27532,27537,27540,27669,27676,27680,27683,27691,27704,27713,27716,27720,27723,27726,27729,27740,27746,27773,27889,27897,27901,27904,27907,27913,27924,28092,28101,28105,28114,28117,28182,28185,28194,28252,28255,28351,28357,28361,28366,28369,28380,28383,28391,28395,28398,28416,28420,28426,28429,28525,28528,28531,28534,28573,28576,28756,28762,28765,28773,28776,28785,28788,28796,28800,28807,28810,28813,28816,28859,28862,28870,29044,29051,29053,29056,29059,29063,29108],[12,26096,26097],{},"Bonjour,",[12,26099,26100],{},"Cet article fait partie d'un ensemble:",[506,26102,26103,26109,26115],{},[370,26104,26105],{},[49,26106,26108],{"href":26107},"\u002Fpost\u002Fcreation-api-1\u002F","Généralités sur l'écriture d'une bonne API",[370,26110,26111],{},[49,26112,26114],{"href":26113},"\u002Fpost\u002Fcreation-api-2\u002F","Qu'est ce qu'une API REST",[370,26116,26117],{},[49,26118,26120],{"href":26119},"\u002Fpost\u002Fcreation-api-3\u002F","Qu'est ce qu'une API GraphQL",[12,26122,26123],{},"Il y a quelques années de cela, j'ai souhaité résoudre un problème que j'ai depuis longtemps avec les API REST: comment bien\nnormaliser les tris, les projections, et les filtres. En effectuant mes recherches je suis tombé sur deux frameworks qui permettent\nde résoudre le problème des projections.",[128,26125,26127],{"id":26126},"quest-quune-api-falcor","Qu'est qu'une API Falcor",[12,26129,26130],{},"Je ne vais parler que succinctement de Falcor. C'est un framework que je n'ai pas utilisé mais j'ai tout de même été très intéressé\npar ce dernier et je vais écrire quelques lignes sur ce Framework.",[12,26132,26133,26134,179],{},"Pour plus d'informations, vous pourrez vous référer à la ",[49,26135,26138],{"href":26136,"rel":26137},"https:\u002F\u002Fnetflix.github.io\u002Ffalcor\u002Fstarter\u002Fwhat-is-falcor.html",[347],"documentation",[12,26140,26141],{},"Pour reprendre les explications de la documentation, Falcor est un middleware de votre application qui permet d'interroger des ressources\nau format JSON sur le serveur, comme votre application le ferait sur des données en mémoire.",[12,26143,26144],{},[121,26145],{"alt":26146,"src":26147},"Falcor","https:\u002F\u002Fnetflix.github.io\u002Ffalcor\u002Fdocumentation\u002Fnetwork-diagram.png",[12,26149,26150],{},"La requête envoyée alors au serveur ne contient que les champs demandés par le client, ce qui permet au serveur de sélectionner les champs\net de ne retourner que ces champs. De plus le serveur peut n'exposer qu'un seul modèle contenant toutes les ressources (que ce soit pour\nretourner des listes ou des items particuliers).",[12,26152,26153],{},"En une seule requête, le client peut alors demander l'ensemble des données dont il a besoin, et l'afficher.",[12,26155,26156],{},"Nous n'avons plus alors le dilemne \"faut-il créér une sous-resource, ou l'intégrer dans la ressource actuelle ?\". Du point de vue du client:\nplus besoin d'effectuer plusieurs requêtes complexes pour récupérer plusieurs ressources, un seul appel suffit.",[2105,26158,26160],{"className":7776,"code":26159,"language":7778,"meta":1646,"style":1646},"\u002Fmodel.json?paths=[\"user.name\", \"user.surname\", \"user.address\"]\n\nGET \u002Fmodel.json?paths=[\"user.name\", \"user.surname\", \"user.address\"]\n{\n  user: {\n    name: \"Frank\",\n    surname: \"Underwood\",\n    address: \"1600 Pennsylvania Avenue, Washington, DC\"\n  }\n}\n",[184,26161,26162,26199,26203,26236,26240,26247,26258,26270,26280,26284],{"__ignoreMap":1646},[2113,26163,26164,26166,26169,26171,26173,26176,26179,26181,26183,26186,26188,26191,26193,26196],{"class":2115,"line":2116},[2113,26165,2520],{"class":2334},[2113,26167,26168],{"class":2414},"model",[2113,26170,179],{"class":2119},[2113,26172,18053],{"class":2330},[2113,26174,26175],{"class":2326},"?",[2113,26177,26178],{"class":2330},"paths",[2113,26180,2335],{"class":2334},[2113,26182,4124],{"class":2119},[2113,26184,26185],{"class":2149},"\"user.name\"",[2113,26187,932],{"class":2119},[2113,26189,26190],{"class":2149},"\"user.surname\"",[2113,26192,932],{"class":2119},[2113,26194,26195],{"class":2149},"\"user.address\"",[2113,26197,26198],{"class":2119},"]\n",[2113,26200,26201],{"class":2115,"line":1647},[2113,26202,2125],{"emptyLinePlaceholder":1767},[2113,26204,26205,26208,26210,26212,26214,26216,26218,26220,26222,26224,26226,26228,26230,26232,26234],{"class":2115,"line":1652},[2113,26206,26207],{"class":2414},"GET",[2113,26209,9175],{"class":2334},[2113,26211,26168],{"class":2414},[2113,26213,179],{"class":2119},[2113,26215,18053],{"class":2330},[2113,26217,26175],{"class":2326},[2113,26219,26178],{"class":2330},[2113,26221,2335],{"class":2334},[2113,26223,4124],{"class":2119},[2113,26225,26185],{"class":2149},[2113,26227,932],{"class":2119},[2113,26229,26190],{"class":2149},[2113,26231,932],{"class":2119},[2113,26233,26195],{"class":2149},[2113,26235,26198],{"class":2119},[2113,26237,26238],{"class":2115,"line":2185},[2113,26239,18060],{"class":2119},[2113,26241,26242,26245],{"class":2115,"line":2230},[2113,26243,26244],{"class":2330},"  user",[2113,26246,8091],{"class":2119},[2113,26248,26249,26251,26253,26256],{"class":2115,"line":2235},[2113,26250,22279],{"class":2330},[2113,26252,4429],{"class":2119},[2113,26254,26255],{"class":2149},"\"Frank\"",[2113,26257,4706],{"class":2119},[2113,26259,26260,26263,26265,26268],{"class":2115,"line":2241},[2113,26261,26262],{"class":2330},"    surname",[2113,26264,4429],{"class":2119},[2113,26266,26267],{"class":2149},"\"Underwood\"",[2113,26269,4706],{"class":2119},[2113,26271,26272,26275,26277],{"class":2115,"line":2246},[2113,26273,26274],{"class":2330},"    address",[2113,26276,4429],{"class":2119},[2113,26278,26279],{"class":2149},"\"1600 Pennsylvania Avenue, Washington, DC\"\n",[2113,26281,26282],{"class":2115,"line":2464},[2113,26283,2572],{"class":2119},[2113,26285,26286],{"class":2115,"line":2085},[2113,26287,2599],{"class":2119},[12,26289,1583,26290,26293],{},[184,26291,26292],{},"Router"," falcor côté serveur, s'occupe alors de dispatcher les différentes parties demandées par le client à différents backends, qui peuvent\nalors répondre indépendament les uns des autres.\nFalcor va s'occuper alors d'aggréger le résultat.",[12,26295,26296,179],{},[121,26297],{"alt":26298,"src":26299},"Diagramme de service","https:\u002F\u002Fnetflix.github.io\u002Ffalcor\u002Fimages\u002Fservices-diagram.png",[12,26301,26302],{},"Pour récupérer des données depuis le front:",[2105,26304,26306],{"className":7776,"code":26305,"language":7778,"meta":1646,"style":1646},"\u002F\u002F ask for name and age of user with id = 5\nmodel.get(\"users[5]['name','age']\");\u002F\u002F which will eventually return something like\n{\n  \"users\": {\n    \"5\": {\n      \"name\": \"John Doe\",\n      \"age\": 33\n    }\n  }\n}\u002F\u002F you can also ask for ranges\nmodel.get(\"users[5..7].name);\u002F\u002F which eventually returns the following\n{\n  \"users\": {\n    \"5\": { \"name\": \"John Doe\" },\n    \"6\": { \"name\": \"Jane Doe\" },\n    \"7\": { \"name\": \"Mary Poppins\" }\n  }\n}\n",[184,26307,26308,26313,26332,26336,26343,26350,26362,26372,26376,26380,26387,26403,26407,26413,26428,26444,26461,26465],{"__ignoreMap":1646},[2113,26309,26310],{"class":2115,"line":2116},[2113,26311,26312],{"class":2396},"\u002F\u002F ask for name and age of user with id = 5\n",[2113,26314,26315,26317,26319,26321,26323,26326,26329],{"class":2115,"line":1647},[2113,26316,26168],{"class":2414},[2113,26318,179],{"class":2119},[2113,26320,19283],{"class":2133},[2113,26322,2423],{"class":2119},[2113,26324,26325],{"class":2149},"\"users[5]['name','age']\"",[2113,26327,26328],{"class":2119},");",[2113,26330,26331],{"class":2396},"\u002F\u002F which will eventually return something like\n",[2113,26333,26334],{"class":2115,"line":1652},[2113,26335,18060],{"class":2119},[2113,26337,26338,26341],{"class":2115,"line":2185},[2113,26339,26340],{"class":2149},"  \"users\"",[2113,26342,8091],{"class":2119},[2113,26344,26345,26348],{"class":2115,"line":2230},[2113,26346,26347],{"class":2149},"    \"5\"",[2113,26349,8091],{"class":2119},[2113,26351,26352,26355,26357,26360],{"class":2115,"line":2235},[2113,26353,26354],{"class":2149},"      \"name\"",[2113,26356,4429],{"class":2119},[2113,26358,26359],{"class":2149},"\"John Doe\"",[2113,26361,4706],{"class":2119},[2113,26363,26364,26367,26369],{"class":2115,"line":2241},[2113,26365,26366],{"class":2149},"      \"age\"",[2113,26368,4429],{"class":2119},[2113,26370,26371],{"class":2166},"33\n",[2113,26373,26374],{"class":2115,"line":2246},[2113,26375,4665],{"class":2119},[2113,26377,26378],{"class":2115,"line":2464},[2113,26379,2572],{"class":2119},[2113,26381,26382,26384],{"class":2115,"line":2085},[2113,26383,15888],{"class":2119},[2113,26385,26386],{"class":2396},"\u002F\u002F you can also ask for ranges\n",[2113,26388,26389,26391,26393,26395,26397,26400],{"class":2115,"line":2514},[2113,26390,26168],{"class":2414},[2113,26392,179],{"class":2119},[2113,26394,19283],{"class":2133},[2113,26396,2423],{"class":2119},[2113,26398,26399],{"class":2149},"\"users[5..7].name);\u002F\u002F which eventually returns the followin",[2113,26401,26402],{"class":19134},"g\n",[2113,26404,26405],{"class":2115,"line":2533},[2113,26406,18060],{"class":2119},[2113,26408,26409,26411],{"class":2115,"line":2556},[2113,26410,26340],{"class":2149},[2113,26412,8091],{"class":2119},[2113,26414,26415,26417,26419,26422,26424,26426],{"class":2115,"line":2569},[2113,26416,26347],{"class":2149},[2113,26418,8099],{"class":2119},[2113,26420,26421],{"class":2149},"\"name\"",[2113,26423,4429],{"class":2119},[2113,26425,26359],{"class":2149},[2113,26427,8129],{"class":2119},[2113,26429,26430,26433,26435,26437,26439,26442],{"class":2115,"line":2575},[2113,26431,26432],{"class":2149},"    \"6\"",[2113,26434,8099],{"class":2119},[2113,26436,26421],{"class":2149},[2113,26438,4429],{"class":2119},[2113,26440,26441],{"class":2149},"\"Jane Doe\"",[2113,26443,8129],{"class":2119},[2113,26445,26446,26449,26451,26453,26455,26458],{"class":2115,"line":2596},[2113,26447,26448],{"class":2149},"    \"7\"",[2113,26450,8099],{"class":2119},[2113,26452,26421],{"class":2149},[2113,26454,4429],{"class":2119},[2113,26456,26457],{"class":2149},"\"Mary Poppins\"",[2113,26459,26460],{"class":2119}," }\n",[2113,26462,26463],{"class":2115,"line":3192},[2113,26464,2572],{"class":2119},[2113,26466,26467],{"class":2115,"line":3213},[2113,26468,2599],{"class":2119},[12,26470,26471],{},"L'avantage de ce framework, est qu'il permet de simplifier l'écriture des projections et de la partie lecture d'une API.",[12,26473,26474],{},"Ce pourquoi je n'ai pas choisi ce framework vient en deux choses:",[506,26476,26477,26480],{},[370,26478,26479],{},"Il est disponible principalement pour Javascript (NodeJS + Front) ; Il existe une version Java même qui n'a pas été mise à jour\ndepuis 2018 et donc je ne retrouve pas les sources.",[370,26481,26482],{},"J'ai trouvé mieux en GraphQL.",[128,26484,26486],{"id":26485},"quest-quune-api-graphql","Qu'est qu'une API GraphQL",[12,26488,26489],{},"Venant du constat que Falcor répondait à mon besoin de pouvoir normaliser la projection mais avec quelques limites, j'ai continué mes\nrecherches et je suis tombé sur GraphQL.",[12,26491,26492],{},"GraphQL répond à la même problèmatique: pouvoir laisser au client choisir la projection qu'il souhaite des données. Comme Falcor,\nGraphQL permet de ramener plusieurs ressources en une requête. Et comme Falcor, GraphQL permet d'aggréger le résultat côté serveur\nde façon asynchrone.",[12,26494,26495],{},"L'avantage de GraphQL sur Falcor est que GraphQL est une norme écrite par Facebook alors que Falcor est une librairie Javascript.\nDe la norme GraphQL, découle une implémentation officielle en Javascript mais aussi dans plein d'autres languages.",[12,26497,26498],{},"Par contre GraphQL n'est qu'un language de requête. Il ne décrit pas comment doit être transportée la requête sur le réseau, ni\ncomment doit être transférée la réponse. Seul le contenu est normalisé. Les frameworks sont tout de même compatibles entre eux.",[12,26500,26501],{},"Par contre cela permet d'utiliser GraphQL pour autre chose que des requêtes réseaux. On pourrait envisager de faire un service qui\nne répond qu'à des requêtes GraphQL. Ce service est alors rattaché au contrôleur pour une exposition mais aussi directement\nappelable en interne par d'autres services. Cela pourrait permettre de faire une couche d'abstraction interne.",[12,26503,26504,26505,179],{},"Il existe plusieurs frameworks ajoutant la couche de transport à GraphQL. Dans la suite je parlerai d'une des implémentations qui se\nnomme ",[49,26506,26509],{"href":26507,"rel":26508},"https:\u002F\u002Fwww.apollographql.com\u002F",[347],"Apollo",[12,26511,26512],{},"Maintenant passons au vif du sujet.",[133,26514,26516],{"id":26515},"query-introduction"," Query - Introduction",[12,26518,26519],{},"Le système de requête de graphql permet au client de décrire ce qu'il souhaite récupérer. C'est au client de décider des éléments\nqu'il souhaite et de construire sa requête.",[12,26521,26522],{},"Voici un exemple de requête de requête:",[2105,26524,26528],{"className":26525,"code":26526,"language":26527,"meta":1646,"style":1646},"language-graphql shiki shiki-themes one-dark-pro","query Dashboard {\n  queueStats {\n    waiting\n    active\n    failed\n    lastExecution\n    nextWakeup\n  }\n  diskUsageStats {\n    currentRepartition {\n      host\n      total\n    }\n    currentSpace {\n      size\n      used\n    }\n  }\n}\n","graphql",[184,26529,26530,26540,26547,26552,26557,26562,26567,26572,26576,26583,26590,26595,26600,26604,26611,26616,26621,26625,26629],{"__ignoreMap":1646},[2113,26531,26532,26535,26538],{"class":2115,"line":2116},[2113,26533,26534],{"class":2326},"query",[2113,26536,26537],{"class":2133}," Dashboard",[2113,26539,2647],{"class":2119},[2113,26541,26542,26545],{"class":2115,"line":1647},[2113,26543,26544],{"class":2330},"  queueStats",[2113,26546,2647],{"class":2119},[2113,26548,26549],{"class":2115,"line":1652},[2113,26550,26551],{"class":2330},"    waiting\n",[2113,26553,26554],{"class":2115,"line":2185},[2113,26555,26556],{"class":2330},"    active\n",[2113,26558,26559],{"class":2115,"line":2230},[2113,26560,26561],{"class":2330},"    failed\n",[2113,26563,26564],{"class":2115,"line":2235},[2113,26565,26566],{"class":2330},"    lastExecution\n",[2113,26568,26569],{"class":2115,"line":2241},[2113,26570,26571],{"class":2330},"    nextWakeup\n",[2113,26573,26574],{"class":2115,"line":2246},[2113,26575,2572],{"class":2119},[2113,26577,26578,26581],{"class":2115,"line":2464},[2113,26579,26580],{"class":2330},"  diskUsageStats",[2113,26582,2647],{"class":2119},[2113,26584,26585,26588],{"class":2115,"line":2085},[2113,26586,26587],{"class":2330},"    currentRepartition",[2113,26589,2647],{"class":2119},[2113,26591,26592],{"class":2115,"line":2514},[2113,26593,26594],{"class":2330},"      host\n",[2113,26596,26597],{"class":2115,"line":2533},[2113,26598,26599],{"class":2330},"      total\n",[2113,26601,26602],{"class":2115,"line":2556},[2113,26603,4665],{"class":2119},[2113,26605,26606,26609],{"class":2115,"line":2569},[2113,26607,26608],{"class":2330},"    currentSpace",[2113,26610,2647],{"class":2119},[2113,26612,26613],{"class":2115,"line":2575},[2113,26614,26615],{"class":2330},"      size\n",[2113,26617,26618],{"class":2115,"line":2596},[2113,26619,26620],{"class":2330},"      used\n",[2113,26622,26623],{"class":2115,"line":3192},[2113,26624,4665],{"class":2119},[2113,26626,26627],{"class":2115,"line":3213},[2113,26628,2572],{"class":2119},[2113,26630,26631],{"class":2115,"line":3236},[2113,26632,2599],{"class":2119},[12,26634,26635],{},"Derrière chaqu'un des membres ci-dessus, on peut retrouver un resolver. Un resolver c'est l'équivalent du contrôleur pour une API REST.\nC'est le resolver qui va récupérer les informations demandées par le client (arguments, projection, ...) et les transférer au service.",[12,26637,26638],{},"Il nous faudra alors faire un lien entre le schéma et ces resolvers. Pour le client, peu importe qu'il faille, pour une requête, exécuter\nen tâche de fond 1 resolver ou 10 resolver. Le client n'a pas besoin de le savoir.",[12,26640,26641,26642,26645],{},"Imaginons que dans l'exemple ci-dessous le champs ",[184,26643,26644],{},"nextWakeup"," nécessite un calcul complexe. Il suffit de créer un resolver qui\neffectue ce calcul.\nSi jamais le client n'a pas besoin de l'afficher et du coup, ne le demande pas, alors ce calcul complexe ne sera pas fait et c'est du temps\nde traitement gagné sur le serveur.",[12,26647,26648],{},"En REST, un champ qui nécessite un calcul complexe et soit",[506,26650,26651,26654],{},[370,26652,26653],{},"inclus dans la resource et donc toujours effectué quelque soit le besoin du client",[370,26655,26656],{},"séparé dans une ressource séparée, au risque de complexifier l'API et de demander au client de faire plusieurs requêtes si nécessaire (surtout si\non a plus d'un champ calculé).",[133,26658,26660],{"id":26659},"nommer-ces-requêtes","Nommer ces requêtes",[12,26662,26663,26664,26667],{},"Dans l'exemple ci-dessus, on peut remarquer que la requête possède un nom. C'est une bonne pratique de toujours nommer l'opération\n(ici ",[184,26665,26666],{},"Dashboard","). Nommer les opérations permet de :",[506,26669,26670,26673],{},[370,26671,26672],{},"mettre plusieurs opérations nommées dans une seule requête.",[370,26674,26675],{},"mais aussi de mieux tracer et débugger les requêtes côté serveur dans les logs.",[133,26677,26679],{"id":26678},"query-coté-serveur","Query - Coté serveur",[12,26681,26682],{},"Côté serveur nous allons commencer par décrire le schéma que pourra alors utiliser le client pour effectuer ces requêtes. Par exemple :",[2105,26684,26686],{"className":26525,"code":26685,"language":26527,"meta":1646,"style":1646},"scalar Date\n\n\"\"\"\nDefine the state of the queue\n\"\"\"\ntype QueueStats {\n  \"\"\"\n  Number of task waiting in queue\n  \"\"\"\n  waiting: Int!\n  \"\"\"\n  Number of task active in queue\n  \"\"\"\n  active: Int!\n  \"\"\"\n  Number of task that have failed\n  \"\"\"\n  failed: Int!\n  \"\"\"\n  Date of the last execution\n  \"\"\"\n  lastExecution: Date!\n  \"\"\"\n  Date of the next wakeup\n  \"\"\"\n  nextWakeup: Date!\n}\n\ntype DiskCurrentRepartition {\n  host: Int!\n  total: Int!\n}\n\ntype DiskCurrentSpace {\n  size: Int!\n  used: Int!\n}\n\ntype DiskUsageStats {\n  currentRepartition: [DiskCurrentRepartition!]!\n  currentSpace: DiskCurrentSpace!\n}\n\ntype Query {\n  queueStats: QueueStats!\n  diskUsageState: DiskUsageStats!\n}\n",[184,26687,26688,26696,26700,26705,26710,26714,26721,26726,26731,26735,26743,26747,26752,26756,26763,26767,26772,26776,26783,26787,26792,26796,26804,26808,26813,26817,26824,26828,26832,26839,26846,26853,26857,26861,26868,26874,26881,26885,26889,26896,26904,26912,26916,26920,26927,26934,26942],{"__ignoreMap":1646},[2113,26689,26690,26693],{"class":2115,"line":2116},[2113,26691,26692],{"class":2326},"scalar",[2113,26694,26695],{"class":2119}," Date\n",[2113,26697,26698],{"class":2115,"line":1647},[2113,26699,2125],{"emptyLinePlaceholder":1767},[2113,26701,26702],{"class":2115,"line":1652},[2113,26703,26704],{"class":2396},"\"\"\"\n",[2113,26706,26707],{"class":2115,"line":2185},[2113,26708,26709],{"class":2396},"Define the state of the queue\n",[2113,26711,26712],{"class":2115,"line":2230},[2113,26713,26704],{"class":2396},[2113,26715,26716,26718],{"class":2115,"line":2235},[2113,26717,21494],{"class":2326},[2113,26719,26720],{"class":2119}," QueueStats {\n",[2113,26722,26723],{"class":2115,"line":2241},[2113,26724,26725],{"class":2396},"  \"\"\"\n",[2113,26727,26728],{"class":2115,"line":2246},[2113,26729,26730],{"class":2396},"  Number of task waiting in queue\n",[2113,26732,26733],{"class":2115,"line":2464},[2113,26734,26725],{"class":2396},[2113,26736,26737,26740],{"class":2115,"line":2085},[2113,26738,26739],{"class":2330},"  waiting",[2113,26741,26742],{"class":2119},": Int!\n",[2113,26744,26745],{"class":2115,"line":2514},[2113,26746,26725],{"class":2396},[2113,26748,26749],{"class":2115,"line":2533},[2113,26750,26751],{"class":2396},"  Number of task active in queue\n",[2113,26753,26754],{"class":2115,"line":2556},[2113,26755,26725],{"class":2396},[2113,26757,26758,26761],{"class":2115,"line":2569},[2113,26759,26760],{"class":2330},"  active",[2113,26762,26742],{"class":2119},[2113,26764,26765],{"class":2115,"line":2575},[2113,26766,26725],{"class":2396},[2113,26768,26769],{"class":2115,"line":2596},[2113,26770,26771],{"class":2396},"  Number of task that have failed\n",[2113,26773,26774],{"class":2115,"line":3192},[2113,26775,26725],{"class":2396},[2113,26777,26778,26781],{"class":2115,"line":3213},[2113,26779,26780],{"class":2330},"  failed",[2113,26782,26742],{"class":2119},[2113,26784,26785],{"class":2115,"line":3236},[2113,26786,26725],{"class":2396},[2113,26788,26789],{"class":2115,"line":3248},[2113,26790,26791],{"class":2396},"  Date of the last execution\n",[2113,26793,26794],{"class":2115,"line":4899},[2113,26795,26725],{"class":2396},[2113,26797,26798,26801],{"class":2115,"line":1777},[2113,26799,26800],{"class":2330},"  lastExecution",[2113,26802,26803],{"class":2119},": Date!\n",[2113,26805,26806],{"class":2115,"line":4931},[2113,26807,26725],{"class":2396},[2113,26809,26810],{"class":2115,"line":4961},[2113,26811,26812],{"class":2396},"  Date of the next wakeup\n",[2113,26814,26815],{"class":2115,"line":4976},[2113,26816,26725],{"class":2396},[2113,26818,26819,26822],{"class":2115,"line":4993},[2113,26820,26821],{"class":2330},"  nextWakeup",[2113,26823,26803],{"class":2119},[2113,26825,26826],{"class":2115,"line":5013},[2113,26827,2599],{"class":2119},[2113,26829,26830],{"class":2115,"line":5018},[2113,26831,2125],{"emptyLinePlaceholder":1767},[2113,26833,26834,26836],{"class":2115,"line":5042},[2113,26835,21494],{"class":2326},[2113,26837,26838],{"class":2119}," DiskCurrentRepartition {\n",[2113,26840,26841,26844],{"class":2115,"line":5057},[2113,26842,26843],{"class":2330},"  host",[2113,26845,26742],{"class":2119},[2113,26847,26848,26851],{"class":2115,"line":5062},[2113,26849,26850],{"class":2330},"  total",[2113,26852,26742],{"class":2119},[2113,26854,26855],{"class":2115,"line":5098},[2113,26856,2599],{"class":2119},[2113,26858,26859],{"class":2115,"line":5117},[2113,26860,2125],{"emptyLinePlaceholder":1767},[2113,26862,26863,26865],{"class":2115,"line":5142},[2113,26864,21494],{"class":2326},[2113,26866,26867],{"class":2119}," DiskCurrentSpace {\n",[2113,26869,26870,26872],{"class":2115,"line":5148},[2113,26871,11661],{"class":2330},[2113,26873,26742],{"class":2119},[2113,26875,26876,26879],{"class":2115,"line":5163},[2113,26877,26878],{"class":2330},"  used",[2113,26880,26742],{"class":2119},[2113,26882,26883],{"class":2115,"line":5169},[2113,26884,2599],{"class":2119},[2113,26886,26887],{"class":2115,"line":5175},[2113,26888,2125],{"emptyLinePlaceholder":1767},[2113,26890,26891,26893],{"class":2115,"line":5180},[2113,26892,21494],{"class":2326},[2113,26894,26895],{"class":2119}," DiskUsageStats {\n",[2113,26897,26898,26901],{"class":2115,"line":5199},[2113,26899,26900],{"class":2330},"  currentRepartition",[2113,26902,26903],{"class":2119},": [DiskCurrentRepartition!]!\n",[2113,26905,26906,26909],{"class":2115,"line":5204},[2113,26907,26908],{"class":2330},"  currentSpace",[2113,26910,26911],{"class":2119},": DiskCurrentSpace!\n",[2113,26913,26914],{"class":2115,"line":5209},[2113,26915,2599],{"class":2119},[2113,26917,26918],{"class":2115,"line":5232},[2113,26919,2125],{"emptyLinePlaceholder":1767},[2113,26921,26922,26924],{"class":2115,"line":5237},[2113,26923,21494],{"class":2326},[2113,26925,26926],{"class":2119}," Query {\n",[2113,26928,26929,26931],{"class":2115,"line":5242},[2113,26930,26544],{"class":2330},[2113,26932,26933],{"class":2119},": QueueStats!\n",[2113,26935,26936,26939],{"class":2115,"line":5267},[2113,26937,26938],{"class":2330},"  diskUsageState",[2113,26940,26941],{"class":2119},": DiskUsageStats!\n",[2113,26943,26944],{"class":2115,"line":5282},[2113,26945,2599],{"class":2119},[12,26947,26948],{},"On peut remarquer plusieurs choses:",[506,26950,26951,26954,26957,26960,26963],{},[370,26952,26953],{},"Tous les champs ont un type (String, Int, Float, Array, Date)",[370,26955,26956],{},"Il est possible de créer de nouveaux types simples, scalaires: par exemple champ de type Date.",[370,26958,26959],{},"Il est possible de créer de nouveaux types complexes, ce qui en fait nos structures.",[370,26961,26962],{},"Le point d'exclamation permet d'indiquer que le champ retourné ne sera jamais null (et donc le client peut compter dessus s'il le souhaite).",[370,26964,26965],{},"Il est possible de mettre de la documentation au niveau de chaque champ mais aussi au niveau des types.",[12,26967,26968,26969,26974],{},"Ce schéma (qui peut aussi être généré à l'aide d'annotations en Typescript, ",[49,26970,26973],{"href":26971,"rel":26972},"https:\u002F\u002Fdocs.nestjs.com\u002Fgraphql\u002Fresolvers#code-first-resolver",[347],"par exemple",")\nsert également de documentation (un peu comme swagger).",[12,26976,26977],{},"Il est alors possible d'utiliser des outils comme",[506,26979,26980,26988,26995,27003],{},[370,26981,26982,26987],{},[49,26983,26986],{"href":26984,"rel":26985},"https:\u002F\u002F2fd.github.io\u002Fgraphdoc\u002F",[347],"Graph Doc",",",[370,26989,26990,26987],{},[49,26991,26994],{"href":26992,"rel":26993},"https:\u002F\u002Fdocql.io\u002F",[347],"DocQL",[370,26996,26997,27002],{},[49,26998,27001],{"href":26999,"rel":27000},"https:\u002F\u002Fgithub.com\u002FAPIs-guru\u002Fgraphql-voyager",[347],"GraphQL Voyager",", et",[370,27004,27005],{},[49,27006,27009],{"href":27007,"rel":27008},"https:\u002F\u002Fgithub.com\u002Fwayfair\u002Fdociql",[347],"Doc iQL",[12,27011,27012],{},"pour générer de la documentation mais aussi des outils comme",[506,27014,27015,27022],{},[370,27016,27017],{},[49,27018,27021],{"href":27019,"rel":27020},"https:\u002F\u002Fgithub.com\u002Fgraphql\u002Fgraphql-playground",[347],"GraphQL Playground",[370,27023,27024,27029],{},[49,27025,27028],{"href":27026,"rel":27027},"https:\u002F\u002Fgithub.com\u002Fgraphql\u002Fgraphiql",[347],"GraphiQL"," qui sont des IDE permettant de faire des requêtes (avec documentation et auto-completion).",[12,27031,27032],{},"Une fois le schéma écrit, il peut être communiqué aux équipes front (si les équipes sont séparées). Pendant que l'on développe alors le front, côté\nserveur on peut alors implémenter le schéma.",[12,27034,27035],{},"Pour implémenter le schéma ci-dessus, nous allons écrire un resolver (en Javascript pour l'exemple).",[2105,27037,27039],{"className":7776,"code":27038,"language":7778,"meta":1646,"style":1646},"const resolvers = {\n  Query: {\n    queueStats() {\n      return getQueueStats();\n    },\n    async diskUsageState() {\n      return await getDiskUsageState();\n    },\n  },\n};\n",[184,27040,27041,27052,27059,27066,27075,27079,27089,27100,27104,27108],{"__ignoreMap":1646},[2113,27042,27043,27045,27048,27050],{"class":2115,"line":2116},[2113,27044,7785],{"class":2326},[2113,27046,27047],{"class":2414}," resolvers",[2113,27049,2153],{"class":2334},[2113,27051,2647],{"class":2119},[2113,27053,27054,27057],{"class":2115,"line":1647},[2113,27055,27056],{"class":2330},"  Query",[2113,27058,8091],{"class":2119},[2113,27060,27061,27064],{"class":2115,"line":1652},[2113,27062,27063],{"class":2133},"    queueStats",[2113,27065,3912],{"class":2119},[2113,27067,27068,27070,27073],{"class":2115,"line":2185},[2113,27069,20652],{"class":2326},[2113,27071,27072],{"class":2133}," getQueueStats",[2113,27074,3944],{"class":2119},[2113,27076,27077],{"class":2115,"line":2230},[2113,27078,8237],{"class":2119},[2113,27080,27081,27084,27087],{"class":2115,"line":2235},[2113,27082,27083],{"class":2326},"    async",[2113,27085,27086],{"class":2133}," diskUsageState",[2113,27088,3912],{"class":2119},[2113,27090,27091,27093,27095,27098],{"class":2115,"line":2241},[2113,27092,20652],{"class":2326},[2113,27094,17521],{"class":2326},[2113,27096,27097],{"class":2133}," getDiskUsageState",[2113,27099,3944],{"class":2119},[2113,27101,27102],{"class":2115,"line":2246},[2113,27103,8237],{"class":2119},[2113,27105,27106],{"class":2115,"line":2464},[2113,27107,8490],{"class":2119},[2113,27109,27110],{"class":2115,"line":2085},[2113,27111,2787],{"class":2119},[12,27113,27114],{},"Un resolver peut pour un champ:",[506,27116,27117,27120,27123],{},[370,27118,27119],{},"retourner une valeur",[370,27121,27122],{},"retourner une fonction qui retourne la valeur",[370,27124,27125],{},"retourner une fonction qui retourne une promesse avec la valeur",[12,27127,27128,27129,27132,27133,792,27135,2384],{},"Ainsi imaginons que la méthode ",[184,27130,27131],{},"getQueueStats()"," retourne l'objet suivant, dans lequel il manque ",[184,27134,26644],{},[184,27136,27137],{},"lastExecution",[2105,27139,27141],{"className":18051,"code":27140,"language":18053,"meta":1646,"style":1646},"{\n  \"waiting\": 0,\n  \"active\": 2,\n  \"failed\": 0\n}\n",[184,27142,27143,27147,27158,27169,27178],{"__ignoreMap":1646},[2113,27144,27145],{"class":2115,"line":2116},[2113,27146,18060],{"class":2119},[2113,27148,27149,27152,27154,27156],{"class":2115,"line":1647},[2113,27150,27151],{"class":2330},"  \"waiting\"",[2113,27153,4429],{"class":2119},[2113,27155,3095],{"class":2166},[2113,27157,4706],{"class":2119},[2113,27159,27160,27163,27165,27167],{"class":2115,"line":1652},[2113,27161,27162],{"class":2330},"  \"active\"",[2113,27164,4429],{"class":2119},[2113,27166,2547],{"class":2166},[2113,27168,4706],{"class":2119},[2113,27170,27171,27174,27176],{"class":2115,"line":2185},[2113,27172,27173],{"class":2330},"  \"failed\"",[2113,27175,4429],{"class":2119},[2113,27177,18077],{"class":2166},[2113,27179,27180],{"class":2115,"line":2230},[2113,27181,2599],{"class":2119},[12,27183,27184],{},"Il est possible d'écrire un resolver :",[2105,27186,27188],{"className":7776,"code":27187,"language":7778,"meta":1646,"style":1646},"const resolvers = {\n  Query: {\n    queueStats() {\n      return getQueueStats();\n    },\n    async diskUsageState() {\n      return await getDiskUsageState();\n    },\n  },\n  QueueStats: {\n    lastExecution(parent \u002F*: QueueStats *\u002F) {\n      const { active } = parent; \u002F\u002F Ici pour l'exemple on peut récuperer un attribut de parent.\n      return getLastExecution();\n    },\n    nextWakeup() {\n      return getNextWakup();\n    },\n  },\n};\n",[184,27189,27190,27200,27206,27212,27220,27224,27232,27242,27246,27250,27257,27272,27293,27302,27306,27313,27322,27326,27330],{"__ignoreMap":1646},[2113,27191,27192,27194,27196,27198],{"class":2115,"line":2116},[2113,27193,7785],{"class":2326},[2113,27195,27047],{"class":2414},[2113,27197,2153],{"class":2334},[2113,27199,2647],{"class":2119},[2113,27201,27202,27204],{"class":2115,"line":1647},[2113,27203,27056],{"class":2330},[2113,27205,8091],{"class":2119},[2113,27207,27208,27210],{"class":2115,"line":1652},[2113,27209,27063],{"class":2133},[2113,27211,3912],{"class":2119},[2113,27213,27214,27216,27218],{"class":2115,"line":2185},[2113,27215,20652],{"class":2326},[2113,27217,27072],{"class":2133},[2113,27219,3944],{"class":2119},[2113,27221,27222],{"class":2115,"line":2230},[2113,27223,8237],{"class":2119},[2113,27225,27226,27228,27230],{"class":2115,"line":2235},[2113,27227,27083],{"class":2326},[2113,27229,27086],{"class":2133},[2113,27231,3912],{"class":2119},[2113,27233,27234,27236,27238,27240],{"class":2115,"line":2241},[2113,27235,20652],{"class":2326},[2113,27237,17521],{"class":2326},[2113,27239,27097],{"class":2133},[2113,27241,3944],{"class":2119},[2113,27243,27244],{"class":2115,"line":2246},[2113,27245,8237],{"class":2119},[2113,27247,27248],{"class":2115,"line":2464},[2113,27249,8490],{"class":2119},[2113,27251,27252,27255],{"class":2115,"line":2085},[2113,27253,27254],{"class":2330},"  QueueStats",[2113,27256,8091],{"class":2119},[2113,27258,27259,27262,27264,27267,27270],{"class":2115,"line":2514},[2113,27260,27261],{"class":2133},"    lastExecution",[2113,27263,2423],{"class":2119},[2113,27265,27266],{"class":2429},"parent",[2113,27268,27269],{"class":2396}," \u002F*: QueueStats *\u002F",[2113,27271,2433],{"class":2119},[2113,27273,27274,27276,27278,27281,27283,27285,27288,27290],{"class":2115,"line":2533},[2113,27275,20579],{"class":2326},[2113,27277,7616],{"class":2119},[2113,27279,27280],{"class":2414},"active",[2113,27282,7840],{"class":2119},[2113,27284,2335],{"class":2334},[2113,27286,27287],{"class":2330}," parent",[2113,27289,6160],{"class":2119},[2113,27291,27292],{"class":2396},"\u002F\u002F Ici pour l'exemple on peut récuperer un attribut de parent.\n",[2113,27294,27295,27297,27300],{"class":2115,"line":2556},[2113,27296,20652],{"class":2326},[2113,27298,27299],{"class":2133}," getLastExecution",[2113,27301,3944],{"class":2119},[2113,27303,27304],{"class":2115,"line":2569},[2113,27305,8237],{"class":2119},[2113,27307,27308,27311],{"class":2115,"line":2575},[2113,27309,27310],{"class":2133},"    nextWakeup",[2113,27312,3912],{"class":2119},[2113,27314,27315,27317,27320],{"class":2115,"line":2596},[2113,27316,20652],{"class":2326},[2113,27318,27319],{"class":2133}," getNextWakup",[2113,27321,3944],{"class":2119},[2113,27323,27324],{"class":2115,"line":3192},[2113,27325,8237],{"class":2119},[2113,27327,27328],{"class":2115,"line":3213},[2113,27329,8490],{"class":2119},[2113,27331,27332],{"class":2115,"line":3236},[2113,27333,2787],{"class":2119},[12,27335,27336],{},"Il est alors possible de créer son schéma en pensant à comment le client va intéroger ce dernier, et lors de l'implémentation ajouter des structures\ncomplexes qui sont issues du calcul synchrone ou asynchrone des données.",[133,27338,27340],{"id":27339},"query-nullable","Query - Nullable",[12,27342,27343],{},"On a vu précédement qu'on pouvait utiliser le point d'exclamation pour indiquer qu'un champ ne sera jamais NULL. Cela peut avoir des avantages pour le client\nmais cela a aussi de grandes implications côté serveur.",[12,27345,27346],{},"Par défaut pour GraphQL, tous les champs sont nullable par défaut. En effet, si un resolver n'arrive pas à récupérer la donnée (erreur côté serveur, back HS, problème\nde base de données, problème réseau, droits d'accès différents selon les champs ...), le serveur pourra retourner NULL à la place de la valeur (et une erreur en\nparallèle du json). Cela permet au client une vue partielle même si certains services ne sont pas disponibles.",[12,27348,27349],{},"Si le champ ne peut pas être null, alors le json ne pourra pas du tout être envoyé et c'est la requête complète qui est en ereur.",[12,27351,27352],{},"C'est pour cela qu'en GraphQL chaque champ peut, par défaut, obtenir la valeur null.",[12,27354,27355],{},"Lors de la conception d'un schéma GraphQL, il faut utiliser la possibilité de rendre le champ non nullable avec réflexion et uniquement pour les champs dont on\nsouhaite garantir la non nullité.",[133,27357,27359],{"id":27358},"query-ajout-darguments","Query - Ajout d'arguments",[2105,27361,27363],{"className":26525,"code":27362,"language":26527,"meta":1646,"style":1646},"query HeroNameAndFriends($episode: Episode) {\n  hero(episode: $episode) {\n    name\n    friends {\n      name\n    }\n  }\n}\n",[184,27364,27365,27380,27396,27401,27408,27413,27417,27421],{"__ignoreMap":1646},[2113,27366,27367,27369,27372,27374,27377],{"class":2115,"line":2116},[2113,27368,26534],{"class":2326},[2113,27370,27371],{"class":2133}," HeroNameAndFriends",[2113,27373,2423],{"class":2119},[2113,27375,27376],{"class":2429},"$episode",[2113,27378,27379],{"class":2119},": Episode) {\n",[2113,27381,27382,27385,27387,27390,27392,27394],{"class":2115,"line":1647},[2113,27383,27384],{"class":2330},"  hero",[2113,27386,2423],{"class":2119},[2113,27388,27389],{"class":2429},"episode",[2113,27391,4429],{"class":2119},[2113,27393,27376],{"class":2330},[2113,27395,2433],{"class":2119},[2113,27397,27398],{"class":2115,"line":1652},[2113,27399,27400],{"class":2330},"    name\n",[2113,27402,27403,27406],{"class":2115,"line":2185},[2113,27404,27405],{"class":2330},"    friends",[2113,27407,2647],{"class":2119},[2113,27409,27410],{"class":2115,"line":2230},[2113,27411,27412],{"class":2330},"      name\n",[2113,27414,27415],{"class":2115,"line":2235},[2113,27416,4665],{"class":2119},[2113,27418,27419],{"class":2115,"line":2241},[2113,27420,2572],{"class":2119},[2113,27422,27423],{"class":2115,"line":2246},[2113,27424,2599],{"class":2119},[12,27426,27427],{},"Il est possible en GraphQL de définir certain champs comme ayant des paramètres. Ils seront alors passés au resolver. Il est également possible d'avoir\ndes paramètres à différents niveaux du schéma (pas seulement au niveau le plus haut).",[12,27429,27430],{},"Le passage de paramètres permet d'écrire son opération une fois et ensuite de l'appeler avec des paramètres. C'est important de définir les saisie utilisateurs\ncomme des paramètres pour éviter les injections GraphQL (comme en SQL, ou autre).",[12,27432,27433,27436],{},[155,27434,27435],{},"Règle n°1",": Ne jamais faire confiance à l'utilisateur.",[133,27438,27440],{"id":27439},"query-création-dalias","Query - Création d'alias",[12,27442,27443],{},"Si on souhaite récupérer plusieurs valeurs d'un attribut en fonction de ses paramètres, il est possible de le demander plusieurs fois et de lui associer\nun alias.",[2105,27445,27447],{"className":26525,"code":27446,"language":26527,"meta":1646,"style":1646},"query aliasQuery {\n  empireHero: hero(episode: EMPIRE) {\n    name\n  }\n  jediHero: hero(episode: JEDI) {\n    name\n  }\n}\n",[184,27448,27449,27458,27479,27483,27487,27507,27511,27515],{"__ignoreMap":1646},[2113,27450,27451,27453,27456],{"class":2115,"line":2116},[2113,27452,26534],{"class":2326},[2113,27454,27455],{"class":2133}," aliasQuery",[2113,27457,2647],{"class":2119},[2113,27459,27460,27463,27465,27468,27470,27472,27474,27477],{"class":2115,"line":1647},[2113,27461,27462],{"class":2149},"  empireHero",[2113,27464,4429],{"class":2119},[2113,27466,27467],{"class":2330},"hero",[2113,27469,2423],{"class":2119},[2113,27471,27389],{"class":2429},[2113,27473,15746],{"class":2119},[2113,27475,27476],{"class":2166}," EMPIRE",[2113,27478,2433],{"class":2119},[2113,27480,27481],{"class":2115,"line":1652},[2113,27482,27400],{"class":2330},[2113,27484,27485],{"class":2115,"line":2185},[2113,27486,2572],{"class":2119},[2113,27488,27489,27492,27494,27496,27498,27500,27502,27505],{"class":2115,"line":2230},[2113,27490,27491],{"class":2149},"  jediHero",[2113,27493,4429],{"class":2119},[2113,27495,27467],{"class":2330},[2113,27497,2423],{"class":2119},[2113,27499,27389],{"class":2429},[2113,27501,15746],{"class":2119},[2113,27503,27504],{"class":2166}," JEDI",[2113,27506,2433],{"class":2119},[2113,27508,27509],{"class":2115,"line":2235},[2113,27510,27400],{"class":2330},[2113,27512,27513],{"class":2115,"line":2241},[2113,27514,2572],{"class":2119},[2113,27516,27517],{"class":2115,"line":2246},[2113,27518,2599],{"class":2119},[12,27520,27521,27522,792,27525,27528,27529,27531],{},"Dans le JSON résultant, on retrouve alors les deux attributs ",[184,27523,27524],{},"empireHero",[184,27526,27527],{},"jediHero"," qui sont tous les deux issus du membre ",[184,27530,27467],{}," avec un paramètre\ndifférent. Cela peut aussi être utilisé pour simplement renommer un champ.",[27533,27534,27536],"h4",{"id":27535},"query-fragment","Query - Fragment",[12,27538,27539],{},"Les fragments permettent de factoriser et de créer des morceaux de requêtes réutilisables.",[2105,27541,27543],{"className":26525,"code":27542,"language":26527,"meta":1646,"style":1646},"# Dans le fichier FragmentJob.graphql\n\nfragment FragmentJob on Job {\n  id\n  state\n  failedReason\n  data {\n    host\n  }\n}\n\n# Dans le fichier QueueTasks.graphql\n\n#import \".\u002FFragmentJob.graphql\"\n\nquery QueueTasks($state: [String!]) {\n  queue(state: $state) {\n    ...FragmentJob\n  }\n}\n",[184,27544,27545,27550,27554,27567,27572,27577,27582,27588,27593,27597,27601,27605,27610,27614,27619,27623,27638,27654,27661,27665],{"__ignoreMap":1646},[2113,27546,27547],{"class":2115,"line":2116},[2113,27548,27549],{"class":2396},"# Dans le fichier FragmentJob.graphql\n",[2113,27551,27552],{"class":2115,"line":1647},[2113,27553,2125],{"emptyLinePlaceholder":1767},[2113,27555,27556,27559,27562,27564],{"class":2115,"line":1652},[2113,27557,27558],{"class":2326},"fragment",[2113,27560,27561],{"class":2119}," FragmentJob ",[2113,27563,9030],{"class":2326},[2113,27565,27566],{"class":2119}," Job {\n",[2113,27568,27569],{"class":2115,"line":2185},[2113,27570,27571],{"class":2330},"  id\n",[2113,27573,27574],{"class":2115,"line":2230},[2113,27575,27576],{"class":2330},"  state\n",[2113,27578,27579],{"class":2115,"line":2235},[2113,27580,27581],{"class":2330},"  failedReason\n",[2113,27583,27584,27586],{"class":2115,"line":2241},[2113,27585,11787],{"class":2330},[2113,27587,2647],{"class":2119},[2113,27589,27590],{"class":2115,"line":2246},[2113,27591,27592],{"class":2330},"    host\n",[2113,27594,27595],{"class":2115,"line":2464},[2113,27596,2572],{"class":2119},[2113,27598,27599],{"class":2115,"line":2085},[2113,27600,2599],{"class":2119},[2113,27602,27603],{"class":2115,"line":2514},[2113,27604,2125],{"emptyLinePlaceholder":1767},[2113,27606,27607],{"class":2115,"line":2533},[2113,27608,27609],{"class":2396},"# Dans le fichier QueueTasks.graphql\n",[2113,27611,27612],{"class":2115,"line":2556},[2113,27613,2125],{"emptyLinePlaceholder":1767},[2113,27615,27616],{"class":2115,"line":2569},[2113,27617,27618],{"class":2396},"#import \".\u002FFragmentJob.graphql\"\n",[2113,27620,27621],{"class":2115,"line":2575},[2113,27622,2125],{"emptyLinePlaceholder":1767},[2113,27624,27625,27627,27630,27632,27635],{"class":2115,"line":2596},[2113,27626,26534],{"class":2326},[2113,27628,27629],{"class":2133}," QueueTasks",[2113,27631,2423],{"class":2119},[2113,27633,27634],{"class":2429},"$state",[2113,27636,27637],{"class":2119},": [String!]) {\n",[2113,27639,27640,27643,27645,27648,27650,27652],{"class":2115,"line":3192},[2113,27641,27642],{"class":2330},"  queue",[2113,27644,2423],{"class":2119},[2113,27646,27647],{"class":2429},"state",[2113,27649,4429],{"class":2119},[2113,27651,27634],{"class":2330},[2113,27653,2433],{"class":2119},[2113,27655,27656,27658],{"class":2115,"line":3213},[2113,27657,22858],{"class":2119},[2113,27659,27660],{"class":2330},"FragmentJob\n",[2113,27662,27663],{"class":2115,"line":3236},[2113,27664,2572],{"class":2119},[2113,27666,27667],{"class":2115,"line":3248},[2113,27668,2599],{"class":2119},[12,27670,27671,27672,27675],{},"Le Fragment ",[184,27673,27674],{},"FragmentJob"," peut alors être réutilisé dans différentes requêtes, voir même plusieurs fois dans la même requête.",[133,27677,27679],{"id":27678},"query-gestion-de-version","Query - Gestion de version",[12,27681,27682],{},"J'en parlais dans les articles précédents, il existe plusieurs manières de versionner une API. GraphQL n'y échappe pas. On peut:",[506,27684,27685,27688],{},[370,27686,27687],{},"ajouter un numéro de version dans le path de l'url, dans le nom de l'hôte, dans un header http.",[370,27689,27690],{},"ou décider de gérer une seule API rétro-compatible où on décommissionne au fur et à mesure les champs.",[12,27692,27693,27694,27699,27700,27703],{},"Les créateurs de GraphQL partagent ",[49,27695,27698],{"href":27696,"rel":27697},"https:\u002F\u002Fgraphql.org\u002Flearn\u002Fbest-practices\u002F#versioning",[347],"leur opinions",". Comme c'est le client qui décide\ndes champs qu'il souhaite rappatrier, toute évolution du schéma ajoutant de nouveaux attributs ne pose aucun problème et ne surchargera pas\nplus le client.\nLes suppressions doivent passer par une phase d'obsolescence ou les attributs sont marqués avec l'annotation ",[184,27701,27702],{},"@deprecated"," et leur décommissionnement\nautomatique.",[12,27705,27706,27707,27712],{},"Afin de pouvoir facilement décommissionner des attributs, le mieux est de savoir quels attributs sont utilisés ou non. La librairie Apollo permet de\nse connecter au ",[49,27708,27711],{"href":27709,"rel":27710},"https:\u002F\u002Fstudio.apollographql.com\u002F",[347],"studio d'appolo"," et de visualiser l'utilisation des attributs. Savoir qu'un attribut n'est pas\nutilisé permet de le décommissionner ou de le modifier. Bien sûr et malheureusement cette partie n'est pas open source. Je n'ai pas encore trouvé de\ndashboard OpenSource permettant d'analyser l'utilisation d'un champ.",[12,27714,27715],{},"Ce qui est important c'est d'avoir une politique de versionning.",[133,27717,27719],{"id":27718},"query-pagination","Query - Pagination",[12,27721,27722],{},"GraphQL n'a pas défini de règle concernant la pagination car il y a plein de manières de la gérer. Pour des listes contenant peu d'éléments, il n'y a\npar exemple pas lieu de gérer la pagination.",[12,27724,27725],{},"Quand on envisage la pagination, on peut en faire une par page avec un nombre d'éléments à passer (skip), ou une basée sur l'id du premier élément\nà afficher.",[12,27727,27728],{},"Il existe également des patterns que l'on peut utiliser pour ne pas réinventer une nouvelle facon de faire.",[12,27730,27731,27732,27735,27736,27739],{},"Par exemple l'un de ses patterns s'appele ",[184,27733,27734],{},"Connections"," et des librairies comme ",[184,27737,27738],{},"Relay"," savent comment gérer automatiquement ce pattern.",[12,27741,27742,27743,27745],{},"Dans le pattern ",[184,27744,27734],{},", nous retrouvons les notions:",[506,27747,27748,27759],{},[370,27749,27750,27751],{},"En entrée",[506,27752,27753,27756],{},[370,27754,27755],{},"nombre d'éléments à récupérer",[370,27757,27758],{},"le curseur à partir duquel retourner les données",[370,27760,27761,27762],{},"En sortie",[506,27763,27764,27767,27770],{},[370,27765,27766],{},"Le nombre d'éléments total",[370,27768,27769],{},"Les informations sur la page comme le prochain curseur et si on a une page suivante",[370,27771,27772],{},"Les différents éléments de la page (avec le curseur associé)",[2105,27774,27776],{"className":26525,"code":27775,"language":26527,"meta":1646,"style":1646},"{\n  hero {\n    name\n    friendsConnection(first: 2, after: \"Y3Vyc29yMQ==\") {\n      totalCount\n      edges {\n        node {\n          name\n        }\n        cursor\n      }\n      pageInfo {\n        endCursor\n        hasNextPage\n      }\n    }\n  }\n}\n",[184,27777,27778,27782,27788,27792,27818,27823,27830,27837,27842,27846,27851,27856,27863,27868,27873,27877,27881,27885],{"__ignoreMap":1646},[2113,27779,27780],{"class":2115,"line":2116},[2113,27781,18060],{"class":2119},[2113,27783,27784,27786],{"class":2115,"line":1647},[2113,27785,27384],{"class":2330},[2113,27787,2647],{"class":2119},[2113,27789,27790],{"class":2115,"line":1652},[2113,27791,27400],{"class":2330},[2113,27793,27794,27797,27799,27802,27804,27806,27808,27811,27813,27816],{"class":2115,"line":2185},[2113,27795,27796],{"class":2330},"    friendsConnection",[2113,27798,2423],{"class":2119},[2113,27800,27801],{"class":2429},"first",[2113,27803,4429],{"class":2119},[2113,27805,2547],{"class":2166},[2113,27807,932],{"class":2119},[2113,27809,27810],{"class":2429},"after",[2113,27812,4429],{"class":2119},[2113,27814,27815],{"class":2149},"\"Y3Vyc29yMQ==\"",[2113,27817,2433],{"class":2119},[2113,27819,27820],{"class":2115,"line":2230},[2113,27821,27822],{"class":2330},"      totalCount\n",[2113,27824,27825,27828],{"class":2115,"line":2235},[2113,27826,27827],{"class":2330},"      edges",[2113,27829,2647],{"class":2119},[2113,27831,27832,27835],{"class":2115,"line":2241},[2113,27833,27834],{"class":2330},"        node",[2113,27836,2647],{"class":2119},[2113,27838,27839],{"class":2115,"line":2246},[2113,27840,27841],{"class":2330},"          name\n",[2113,27843,27844],{"class":2115,"line":2464},[2113,27845,4578],{"class":2119},[2113,27847,27848],{"class":2115,"line":2085},[2113,27849,27850],{"class":2330},"        cursor\n",[2113,27852,27853],{"class":2115,"line":2514},[2113,27854,27855],{"class":2119},"      }\n",[2113,27857,27858,27861],{"class":2115,"line":2533},[2113,27859,27860],{"class":2330},"      pageInfo",[2113,27862,2647],{"class":2119},[2113,27864,27865],{"class":2115,"line":2556},[2113,27866,27867],{"class":2330},"        endCursor\n",[2113,27869,27870],{"class":2115,"line":2569},[2113,27871,27872],{"class":2330},"        hasNextPage\n",[2113,27874,27875],{"class":2115,"line":2575},[2113,27876,27855],{"class":2119},[2113,27878,27879],{"class":2115,"line":2596},[2113,27880,4665],{"class":2119},[2113,27882,27883],{"class":2115,"line":3192},[2113,27884,2572],{"class":2119},[2113,27886,27887],{"class":2115,"line":3213},[2113,27888,2599],{"class":2119},[12,27890,27891,27892],{},"On peut retrouver la spécification de ce modèle de pagination sur le site de ",[49,27893,27896],{"href":27894,"rel":27895},"https:\u002F\u002Frelay.dev\u002Fgraphql\u002Fconnections.htm",[347],"relay.dev",[133,27898,27900],{"id":27899},"query-cache-control","Query - Cache - Control",[12,27902,27903],{},"Contrairement au REST où on peut peut utiliser les header HTTP, pour contrôler le cache, en GraphQL une requête peut avoir des limites d'âge différentes sur les différentes resources\nappelées. Il faut donc trouver d'autres moyens de gérer le cache.",[12,27905,27906],{},"C'est pour moi un des points les plus gênants de GraphQL.",[12,27908,27909,27910,179],{},"Sur le serveur, il y a plusieurs manières complémentaires (à différentes fins) de gérer le cache. Nous allons nous concentrer sur la librairie ",[184,27911,27912],{},"apollo-server",[12,27914,27915,27916,27919,27920,27923],{},"La 1ère manière s'apparentant au cache-control des requêtes HTTP peut être utilisée avec la directive ",[184,27917,27918],{},"@cacheControl"," ou au niveau du resolver. Elle ne permet pas de cacher directement la donnée\nmais de donner une indication au client sur combien de temps cette donnée peut-être cachée. Si la librairie utilisée côté client est également ",[184,27921,27922],{},"apollo"," elle utilisera alors ces indications pour\ngérer le cache de la donnée (et donc ne pas ré-effectuer la requête.)",[2105,27925,27927],{"className":26525,"code":27926,"language":26527,"meta":1646,"style":1646},"scalar Date\n\n\"\"\"\nDefine the state of the queue\n\"\"\"\ntype QueueStats @cacheControl(maxAge: 60) {\n  \"\"\"\n  Number of task waiting in queue\n  \"\"\"\n  waiting: Int!\n  \"\"\"\n  Number of task active in queue\n  \"\"\"\n  active: Int!\n  \"\"\"\n  Number of task that have failed\n  \"\"\"\n  failed: Int!\n  \"\"\"\n  Date of the last execution\n  \"\"\"\n  lastExecution: Date! @cacheControl(maxAge: 3600)\n  \"\"\"\n  Date of the next wakeup\n  \"\"\"\n  nextWakeup: Date! @cacheControl(maxAge: 3600)\n}\n",[184,27928,27929,27935,27939,27943,27947,27951,27972,27976,27980,27984,27990,27994,27998,28002,28008,28012,28016,28020,28026,28030,28034,28038,28058,28062,28066,28070,28088],{"__ignoreMap":1646},[2113,27930,27931,27933],{"class":2115,"line":2116},[2113,27932,26692],{"class":2326},[2113,27934,26695],{"class":2119},[2113,27936,27937],{"class":2115,"line":1647},[2113,27938,2125],{"emptyLinePlaceholder":1767},[2113,27940,27941],{"class":2115,"line":1652},[2113,27942,26704],{"class":2396},[2113,27944,27945],{"class":2115,"line":2185},[2113,27946,26709],{"class":2396},[2113,27948,27949],{"class":2115,"line":2230},[2113,27950,26704],{"class":2396},[2113,27952,27953,27955,27958,27960,27962,27965,27967,27970],{"class":2115,"line":2235},[2113,27954,21494],{"class":2326},[2113,27956,27957],{"class":2119}," QueueStats ",[2113,27959,27918],{"class":2133},[2113,27961,2423],{"class":2119},[2113,27963,27964],{"class":2429},"maxAge",[2113,27966,4429],{"class":2119},[2113,27968,27969],{"class":2166},"60",[2113,27971,2433],{"class":2119},[2113,27973,27974],{"class":2115,"line":2241},[2113,27975,26725],{"class":2396},[2113,27977,27978],{"class":2115,"line":2246},[2113,27979,26730],{"class":2396},[2113,27981,27982],{"class":2115,"line":2464},[2113,27983,26725],{"class":2396},[2113,27985,27986,27988],{"class":2115,"line":2085},[2113,27987,26739],{"class":2330},[2113,27989,26742],{"class":2119},[2113,27991,27992],{"class":2115,"line":2514},[2113,27993,26725],{"class":2396},[2113,27995,27996],{"class":2115,"line":2533},[2113,27997,26751],{"class":2396},[2113,27999,28000],{"class":2115,"line":2556},[2113,28001,26725],{"class":2396},[2113,28003,28004,28006],{"class":2115,"line":2569},[2113,28005,26760],{"class":2330},[2113,28007,26742],{"class":2119},[2113,28009,28010],{"class":2115,"line":2575},[2113,28011,26725],{"class":2396},[2113,28013,28014],{"class":2115,"line":2596},[2113,28015,26771],{"class":2396},[2113,28017,28018],{"class":2115,"line":3192},[2113,28019,26725],{"class":2396},[2113,28021,28022,28024],{"class":2115,"line":3213},[2113,28023,26780],{"class":2330},[2113,28025,26742],{"class":2119},[2113,28027,28028],{"class":2115,"line":3236},[2113,28029,26725],{"class":2396},[2113,28031,28032],{"class":2115,"line":3248},[2113,28033,26791],{"class":2396},[2113,28035,28036],{"class":2115,"line":4899},[2113,28037,26725],{"class":2396},[2113,28039,28040,28042,28045,28047,28049,28051,28053,28056],{"class":2115,"line":1777},[2113,28041,26800],{"class":2330},[2113,28043,28044],{"class":2119},": Date! ",[2113,28046,27918],{"class":2133},[2113,28048,2423],{"class":2119},[2113,28050,27964],{"class":2429},[2113,28052,4429],{"class":2119},[2113,28054,28055],{"class":2166},"3600",[2113,28057,4660],{"class":2119},[2113,28059,28060],{"class":2115,"line":4931},[2113,28061,26725],{"class":2396},[2113,28063,28064],{"class":2115,"line":4961},[2113,28065,26812],{"class":2396},[2113,28067,28068],{"class":2115,"line":4976},[2113,28069,26725],{"class":2396},[2113,28071,28072,28074,28076,28078,28080,28082,28084,28086],{"class":2115,"line":4993},[2113,28073,26821],{"class":2330},[2113,28075,28044],{"class":2119},[2113,28077,27918],{"class":2133},[2113,28079,2423],{"class":2119},[2113,28081,27964],{"class":2429},[2113,28083,4429],{"class":2119},[2113,28085,28055],{"class":2166},[2113,28087,4660],{"class":2119},[2113,28089,28090],{"class":2115,"line":5013},[2113,28091,2599],{"class":2119},[12,28093,28094,28095,28100],{},"Je vous invite à lire la ",[49,28096,28099],{"href":28097,"rel":28098},"https:\u002F\u002Fwww.apollographql.com\u002Fdocs\u002Fapollo-server\u002Fperformance\u002Fcaching\u002F",[347],"documentation d'apollo"," qui contient énormément d'informations. Cette méthode permet d'ajouter\ndes header http pour cacher les requêtes dans un CDN intérmédiaire, ou dans une base redis attachée au serveur, ou dans le cache navigateur, voir également au niveau même du client.",[133,28102,28104],{"id":28103},"query-cache-dataloader","Query - Cache - DataLoader",[12,28106,28107,28108,28113],{},"La 2nd méthode de cache consiste à l'utilisation de ",[49,28109,28112],{"href":28110,"rel":28111},"https:\u002F\u002Fgithub.com\u002Fgraphql\u002Fdataloader",[347],"DataLoader",". L'ajout d'un dataloader a pour but de cacher le temps d'une requête les différents éléments pour éviter de multiplier les appels.\nLe but n'étant pas de réellement faire du cache mais plutôt de pouvoir traiter une requête batch le plus rapidement possible.",[12,28115,28116],{},"Imaginons par exemple la requête suivante :",[2105,28118,28120],{"className":26525,"code":28119,"language":26527,"meta":1646,"style":1646},"query TestDataLoader {\n  backups {\n    id\n    startDate\n    endDate\n    user {\n      firstName\n      lastName\n    }\n  }\n}\n",[184,28121,28122,28131,28138,28143,28148,28153,28160,28165,28170,28174,28178],{"__ignoreMap":1646},[2113,28123,28124,28126,28129],{"class":2115,"line":2116},[2113,28125,26534],{"class":2326},[2113,28127,28128],{"class":2133}," TestDataLoader",[2113,28130,2647],{"class":2119},[2113,28132,28133,28136],{"class":2115,"line":1647},[2113,28134,28135],{"class":2330},"  backups",[2113,28137,2647],{"class":2119},[2113,28139,28140],{"class":2115,"line":1652},[2113,28141,28142],{"class":2330},"    id\n",[2113,28144,28145],{"class":2115,"line":2185},[2113,28146,28147],{"class":2330},"    startDate\n",[2113,28149,28150],{"class":2115,"line":2230},[2113,28151,28152],{"class":2330},"    endDate\n",[2113,28154,28155,28158],{"class":2115,"line":2235},[2113,28156,28157],{"class":2330},"    user",[2113,28159,2647],{"class":2119},[2113,28161,28162],{"class":2115,"line":2241},[2113,28163,28164],{"class":2330},"      firstName\n",[2113,28166,28167],{"class":2115,"line":2246},[2113,28168,28169],{"class":2330},"      lastName\n",[2113,28171,28172],{"class":2115,"line":2464},[2113,28173,4665],{"class":2119},[2113,28175,28176],{"class":2115,"line":2085},[2113,28177,2572],{"class":2119},[2113,28179,28180],{"class":2115,"line":2514},[2113,28181,2599],{"class":2119},[12,28183,28184],{},"Dans le cas présent, on souhaite récupérer les backups, mais également pour chaque backup récupérer l'utilisateur. Si l'utilisateur est le même pour chaque backup, on se retouve alors à charger\nl'utilisateur plusieurs fois. Le fait d'utiliser un dataloader permettra dans le cas présent de ne les charger qu'une fois.",[12,28186,28187,28188,28193],{},"Pour utiliser le système de data loader, on peut utiliser la bibliothèque du même nom: [",[49,28189,28192],{"href":28190,"rel":28191},"https:\u002F\u002Fgithub.com\u002Fgraphql\u002Fdataloader%5D(Github",[347],"https:\u002F\u002Fgithub.com\u002Fgraphql\u002Fdataloader](Github",": graphql\u002Fdataloader). Il nous faut alors créer un dataloader pour\nun type d'objet.",[2105,28195,28197],{"className":7776,"code":28196,"language":7778,"meta":1646,"style":1646},"import DataLoader from \"dataloader\";\n\nconst userLoader = new DataLoader((keys) => service.getUsers(keys));\n",[184,28198,28199,28213,28217],{"__ignoreMap":1646},[2113,28200,28201,28203,28206,28208,28211],{"class":2115,"line":2116},[2113,28202,15758],{"class":2326},[2113,28204,28205],{"class":2330}," DataLoader",[2113,28207,15768],{"class":2326},[2113,28209,28210],{"class":2149}," \"dataloader\"",[2113,28212,2487],{"class":2119},[2113,28214,28215],{"class":2115,"line":1647},[2113,28216,2125],{"emptyLinePlaceholder":1767},[2113,28218,28219,28221,28224,28226,28228,28230,28232,28235,28237,28239,28241,28243,28246,28248,28250],{"class":2115,"line":1652},[2113,28220,7785],{"class":2326},[2113,28222,28223],{"class":2414}," userLoader",[2113,28225,2153],{"class":2334},[2113,28227,4778],{"class":2326},[2113,28229,28205],{"class":2133},[2113,28231,8879],{"class":2119},[2113,28233,28234],{"class":2429},"keys",[2113,28236,5709],{"class":2119},[2113,28238,8063],{"class":2326},[2113,28240,21031],{"class":2414},[2113,28242,179],{"class":2119},[2113,28244,28245],{"class":2133},"getUsers",[2113,28247,2423],{"class":2119},[2113,28249,28234],{"class":2330},[2113,28251,5818],{"class":2119},[12,28253,28254],{},"Ensuite dans les resolvers, au lieu de récupérer les utilisateurs directement à partir du service, on utilise le data loader pour récupérer la valeur :",[2105,28256,28258],{"className":7776,"code":28257,"language":7778,"meta":1646,"style":1646},"const resolvers = {\n  async backups() {\n    return service.find();\n  },\n  Backup: {\n    async user(ctx, { id }) {\n      return await userLoader.load(id);\n    },\n  },\n};\n",[184,28259,28260,28270,28278,28290,28294,28301,28320,28339,28343,28347],{"__ignoreMap":1646},[2113,28261,28262,28264,28266,28268],{"class":2115,"line":2116},[2113,28263,7785],{"class":2326},[2113,28265,27047],{"class":2414},[2113,28267,2153],{"class":2334},[2113,28269,2647],{"class":2119},[2113,28271,28272,28274,28276],{"class":2115,"line":1647},[2113,28273,19213],{"class":2326},[2113,28275,6686],{"class":2133},[2113,28277,3912],{"class":2119},[2113,28279,28280,28282,28284,28286,28288],{"class":2115,"line":1652},[2113,28281,3137],{"class":2326},[2113,28283,21031],{"class":2414},[2113,28285,179],{"class":2119},[2113,28287,7343],{"class":2133},[2113,28289,3944],{"class":2119},[2113,28291,28292],{"class":2115,"line":2185},[2113,28293,8490],{"class":2119},[2113,28295,28296,28299],{"class":2115,"line":2230},[2113,28297,28298],{"class":2330},"  Backup",[2113,28300,8091],{"class":2119},[2113,28302,28303,28305,28307,28309,28312,28315,28317],{"class":2115,"line":2235},[2113,28304,27083],{"class":2326},[2113,28306,25863],{"class":2133},[2113,28308,2423],{"class":2119},[2113,28310,28311],{"class":2429},"ctx",[2113,28313,28314],{"class":2119},", { ",[2113,28316,20628],{"class":2429},[2113,28318,28319],{"class":2119}," }) {\n",[2113,28321,28322,28324,28326,28328,28330,28333,28335,28337],{"class":2115,"line":2241},[2113,28323,20652],{"class":2326},[2113,28325,17521],{"class":2326},[2113,28327,28223],{"class":2414},[2113,28329,179],{"class":2119},[2113,28331,28332],{"class":2133},"load",[2113,28334,2423],{"class":2119},[2113,28336,20628],{"class":2330},[2113,28338,2553],{"class":2119},[2113,28340,28341],{"class":2115,"line":2246},[2113,28342,8237],{"class":2119},[2113,28344,28345],{"class":2115,"line":2464},[2113,28346,8490],{"class":2119},[2113,28348,28349],{"class":2115,"line":2085},[2113,28350,2787],{"class":2119},[12,28352,28353,28356],{},[184,28354,28355],{},"Dataloader"," n'est par contre pas fait pour être utilisé en tant que cache applicatif et ne remplace donc pas un memcached ou un redis.",[133,28358,28360],{"id":28359},"query-batch","Query - Batch",[12,28362,28363,28365],{},[184,28364,26509],{}," côté client propose de pouvoir faire du batching de requêtes. Cela consiste à attendre un léger laps de temps pour regrouper en une seule requête plusieurs requêtes.",[12,28367,28368],{},"Attention néanmoins, mettre en place le batching de requêtes implique:",[506,28370,28371,28374,28377],{},[370,28372,28373],{},"qu'on attend (pas très longtemps) pour envoyer les requêtes",[370,28375,28376],{},"qu'on attend l'aggrégat des réponses avant de les recevoir",[370,28378,28379],{},"qu'on ne bénéficie pas du multiplexing des requêtes de HTTP\u002F2.",[12,28381,28382],{},"Il est alors conseillé d'abord de faire d'autres types d'optimisations (comme utiliser les requêtes persistentes, un cache dans un CDN des réponses, utiliser HTTP\u002F2 jusqu'au NodeJS)\navant de mettre en place le système de batch.",[12,28384,28385,28386,179],{},"Si vous voulez le mettre tout de même en place, vous pouvez regarder la ",[49,28387,28390],{"href":28388,"rel":28389},"https:\u002F\u002Fwww.apollographql.com\u002Fdocs\u002Flink\u002Flinks\u002Fbatch-http\u002F",[347],"documentation d'Apollo sur ce sujet",[133,28392,28394],{"id":28393},"query-transfert-réseau","Query - Transfert réseau",[12,28396,28397],{},"Pour une API Web, les requêtes et les réponses sont transférées en utilisant le protocole HTTP. Pour améliorer les performances il est\npossible d'utiliser plusieurs méthodes:",[506,28399,28400,28403,28410,28413],{},[370,28401,28402],{},"Comme en REST, activer la compression GZIP coté serveur, permet de réduire les flux réseaux",[370,28404,28405,28406],{},"Utiliser les requêtes persistentes (stocké coté serveur) : ",[49,28407,28408],{"href":28408,"rel":28409},"https:\u002F\u002Fwww.apollographql.com\u002Fdocs\u002Fapollo-server\u002Fperformance\u002Fapq\u002F",[347],[370,28411,28412],{},"Utiliser les fragments pour réutiliser certaines parties des requêtes qui peuvent se répéter.",[370,28414,28415],{},"Utiliser HTTP\u002F2 et le multiplexing des requêtes. Faire une seule grosse requête peut être contre-productif avec HTTP\u002F2. En effet il faut attendre la fin de la résolution de l'ensemble des resolvers\ncôté serveur avant d'avoir la réponse. Faire plusieurs requêtes peut permettre d'avoir certaines parties de la requête plus vite. De plus HTTP\u002F2 permet de mieux paralléliser les requêtes. Il faut donc\ntrouver le bon nombre de requêtes à executer pour avoir les meilleurs performances.",[133,28417,28419],{"id":28418},"mutation","Mutation",[12,28421,28422,28423,179],{},"Nous avons énormément parlé de toute la partie requête de GraphQL. GraphQL permet également de faire des modifications via des ",[184,28424,28425],{},"mutations",[12,28427,28428],{},"Une mutation est similaire à une requête :",[2105,28430,28432],{"className":26525,"code":28431,"language":26527,"meta":1646,"style":1646},"mutation CreateBackup($backup: CreateBackupInput!) {\n  createBackup(backup: $backup) {\n    statusCode\n    message\n    error\n    errorCode\n    backup {\n      id\n      state\n      user {\n        id\n      }\n    }\n  }\n",[184,28433,28434,28449,28464,28469,28474,28479,28484,28491,28496,28501,28508,28513,28517,28521],{"__ignoreMap":1646},[2113,28435,28436,28438,28441,28443,28446],{"class":2115,"line":2116},[2113,28437,28418],{"class":2326},[2113,28439,28440],{"class":2133}," CreateBackup",[2113,28442,2423],{"class":2119},[2113,28444,28445],{"class":2429},"$backup",[2113,28447,28448],{"class":2119},": CreateBackupInput!) {\n",[2113,28450,28451,28454,28456,28458,28460,28462],{"class":2115,"line":1647},[2113,28452,28453],{"class":2330},"  createBackup",[2113,28455,2423],{"class":2119},[2113,28457,1773],{"class":2429},[2113,28459,4429],{"class":2119},[2113,28461,28445],{"class":2330},[2113,28463,2433],{"class":2119},[2113,28465,28466],{"class":2115,"line":1652},[2113,28467,28468],{"class":2330},"    statusCode\n",[2113,28470,28471],{"class":2115,"line":2185},[2113,28472,28473],{"class":2330},"    message\n",[2113,28475,28476],{"class":2115,"line":2230},[2113,28477,28478],{"class":2330},"    error\n",[2113,28480,28481],{"class":2115,"line":2235},[2113,28482,28483],{"class":2330},"    errorCode\n",[2113,28485,28486,28489],{"class":2115,"line":2241},[2113,28487,28488],{"class":2330},"    backup",[2113,28490,2647],{"class":2119},[2113,28492,28493],{"class":2115,"line":2246},[2113,28494,28495],{"class":2330},"      id\n",[2113,28497,28498],{"class":2115,"line":2464},[2113,28499,28500],{"class":2330},"      state\n",[2113,28502,28503,28506],{"class":2115,"line":2085},[2113,28504,28505],{"class":2330},"      user",[2113,28507,2647],{"class":2119},[2113,28509,28510],{"class":2115,"line":2514},[2113,28511,28512],{"class":2330},"        id\n",[2113,28514,28515],{"class":2115,"line":2533},[2113,28516,27855],{"class":2119},[2113,28518,28519],{"class":2115,"line":2556},[2113,28520,4665],{"class":2119},[2113,28522,28523],{"class":2115,"line":2569},[2113,28524,2572],{"class":2119},[12,28526,28527],{},"Nous retrouvons la partie input, et la partie query (sur la réponse). Alors que généralement sur query on va retrouver des types primitifs en paramètres, on va plutôt retrouver\nen paramètre d'une mutation un objet de type Input. Sur le résultat le fonctionnement est lui le même que sur une Query.",[12,28529,28530],{},"Nous pouvons d'ailleurs utiliser les mêmes resolvers sur les réponses que dans les queries (et ainsi récupérer l'utilisateur de la backup de la même manière).",[12,28532,28533],{},"En bonne pratique, ce que nous pouvons retrouver sur les mutations sont:",[506,28535,28536,28539,28550,28555,28567],{},[370,28537,28538],{},"Ne pas renvoyer l'objet créé\u002Fmodifié directement. Renvoyer un objet permettant à l'utilisateur de récupérer l'erreur fonctionnelle ou l'objet selon le cas. Cela permettra de faire\névoluer plus facilement le résultat si nécessaire.",[370,28540,28541,28542,28545,28546,28549],{},"De la même manière, ",[155,28543,28544],{},"même"," si la mutation a besoin de très peu de paramètres en entrée, il vaut mieux créer un objet de type ",[184,28547,28548],{},"input"," afin de passer les paramètres. Ceci également\nafin de pouvoir évoluer facilement avec l'API au fur et à mesure des évolutions.",[370,28551,28552,28553,179],{},"Eviter pour le paramètre principal ou la sortie principale de réutiliser un objet. Il vaut mieux en créer un propre à la ",[184,28554,28418],{},[370,28556,28557,28558,28560,28561,28563,28564,28566],{},"Contrairement à REST, les ",[184,28559,28425],{}," GraphQL ressemblent plus à du RPC. Il vaut mieux alors éviter de penser ressources mais plutôt actions. Quels sont les actions que l'on souhaite\npouvoir faire depuis l'IHM.",[15567,28562],{},"Il ne faut pas hésiter à découper ces actions en petites actions que l'utilisateur peut choisir d'appeler ou non (il est possible d'appeler plusieurs ",[184,28565,28425],{}," en un seul appel).",[370,28568,28569,28570,28572],{},"Penser les ",[184,28571,28425],{}," en fonction des appels de vos clients.",[12,28574,28575],{},"Si on reprend le schéma suivant:",[2105,28577,28579],{"className":26525,"code":28578,"language":26527,"meta":1646,"style":1646},"input InputUser {\n  name: String!\n}\n\ninput CreateBackupInput {\n  host: String!\n  user: InputUser!\n}\n\ntype Backup {\n  host: String!\n  id: Int!\n  startDate: Date\n  endDate: Date\n  user: User\n}\n\ntype User {\n  name: String\n  backups: [Backup!]\n}\n\ntype CreateBackupResponse {\n  statusCode: Int!\n  message: String\n  error: String\n  errorCode: String\n  backup: Backup\n}\n",[184,28580,28581,28588,28595,28599,28603,28610,28616,28623,28627,28631,28638,28644,28651,28659,28666,28673,28677,28681,28688,28695,28702,28706,28710,28717,28724,28730,28737,28744,28752],{"__ignoreMap":1646},[2113,28582,28583,28585],{"class":2115,"line":2116},[2113,28584,28548],{"class":2326},[2113,28586,28587],{"class":2119}," InputUser {\n",[2113,28589,28590,28592],{"class":2115,"line":1647},[2113,28591,19883],{"class":2330},[2113,28593,28594],{"class":2119},": String!\n",[2113,28596,28597],{"class":2115,"line":1652},[2113,28598,2599],{"class":2119},[2113,28600,28601],{"class":2115,"line":2185},[2113,28602,2125],{"emptyLinePlaceholder":1767},[2113,28604,28605,28607],{"class":2115,"line":2230},[2113,28606,28548],{"class":2326},[2113,28608,28609],{"class":2119}," CreateBackupInput {\n",[2113,28611,28612,28614],{"class":2115,"line":2235},[2113,28613,26843],{"class":2330},[2113,28615,28594],{"class":2119},[2113,28617,28618,28620],{"class":2115,"line":2241},[2113,28619,26244],{"class":2330},[2113,28621,28622],{"class":2119},": InputUser!\n",[2113,28624,28625],{"class":2115,"line":2246},[2113,28626,2599],{"class":2119},[2113,28628,28629],{"class":2115,"line":2464},[2113,28630,2125],{"emptyLinePlaceholder":1767},[2113,28632,28633,28635],{"class":2115,"line":2085},[2113,28634,21494],{"class":2326},[2113,28636,28637],{"class":2119}," Backup {\n",[2113,28639,28640,28642],{"class":2115,"line":2514},[2113,28641,26843],{"class":2330},[2113,28643,28594],{"class":2119},[2113,28645,28646,28649],{"class":2115,"line":2533},[2113,28647,28648],{"class":2330},"  id",[2113,28650,26742],{"class":2119},[2113,28652,28653,28656],{"class":2115,"line":2556},[2113,28654,28655],{"class":2330},"  startDate",[2113,28657,28658],{"class":2119},": Date\n",[2113,28660,28661,28664],{"class":2115,"line":2569},[2113,28662,28663],{"class":2330},"  endDate",[2113,28665,28658],{"class":2119},[2113,28667,28668,28670],{"class":2115,"line":2575},[2113,28669,26244],{"class":2330},[2113,28671,28672],{"class":2119},": User\n",[2113,28674,28675],{"class":2115,"line":2596},[2113,28676,2599],{"class":2119},[2113,28678,28679],{"class":2115,"line":3192},[2113,28680,2125],{"emptyLinePlaceholder":1767},[2113,28682,28683,28685],{"class":2115,"line":3213},[2113,28684,21494],{"class":2326},[2113,28686,28687],{"class":2119}," User {\n",[2113,28689,28690,28692],{"class":2115,"line":3236},[2113,28691,19883],{"class":2330},[2113,28693,28694],{"class":2119},": String\n",[2113,28696,28697,28699],{"class":2115,"line":3248},[2113,28698,28135],{"class":2330},[2113,28700,28701],{"class":2119},": [Backup!]\n",[2113,28703,28704],{"class":2115,"line":4899},[2113,28705,2599],{"class":2119},[2113,28707,28708],{"class":2115,"line":1777},[2113,28709,2125],{"emptyLinePlaceholder":1767},[2113,28711,28712,28714],{"class":2115,"line":4931},[2113,28713,21494],{"class":2326},[2113,28715,28716],{"class":2119}," CreateBackupResponse {\n",[2113,28718,28719,28722],{"class":2115,"line":4961},[2113,28720,28721],{"class":2330},"  statusCode",[2113,28723,26742],{"class":2119},[2113,28725,28726,28728],{"class":2115,"line":4976},[2113,28727,16062],{"class":2330},[2113,28729,28694],{"class":2119},[2113,28731,28732,28735],{"class":2115,"line":4993},[2113,28733,28734],{"class":2330},"  error",[2113,28736,28694],{"class":2119},[2113,28738,28739,28742],{"class":2115,"line":5013},[2113,28740,28741],{"class":2330},"  errorCode",[2113,28743,28694],{"class":2119},[2113,28745,28746,28749],{"class":2115,"line":5018},[2113,28747,28748],{"class":2330},"  backup",[2113,28750,28751],{"class":2119},": Backup\n",[2113,28753,28754],{"class":2115,"line":5042},[2113,28755,2599],{"class":2119},[12,28757,28758,28759,28761],{},"Contrairement aux paramètres de sortie, il n'est pas possible sur les ",[184,28760,28548],{}," de faire des dépendances circulaires. Un input doit donc être lié à l'action faite\npar la mutation.\nCela veux dire aussi qu'il n'est pas possible de réutiliser un type de sortie en input pour l'entrée. Le point d'exclamation n'est plus un indicateur de non nullité\nmais un indicateur de paramètre obligatoire.",[12,28763,28764],{},"Lors de la conception des ces inputs il faut d'ailleurs penser à plusieurs choses:",[506,28766,28767,28770],{},[370,28768,28769],{},"Certain champs en sortie dans un type sont là pour representer un calcul ou un état qui fait sens mais que l'utilisateur ne peut pas modifier. Il ne faut donc pas\nle mettre en entrée.",[370,28771,28772],{},"Un paramètre qui peut être non nul en sortie n'est pas forcément obligatoire en entrée (et inversement).",[12,28774,28775],{},"Pour une application possédant beaucoup de formulaires et d'écrans de modifications ou d'actions, il peut être donc compliqué d'écrire les requêtes et les mutations associées.",[12,28777,28778,28779,28784],{},"Il peut peut-être être intéressant d'utiliser un transpiler comme ",[49,28780,28783],{"href":28781,"rel":28782},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fgraphql-s2s",[347],"graphql-s2s"," qui permet de simplifier certaines choses\ndans l'écriture du schéma.\nLors de son utilisation sur un projet possédant un gros schéma, il a permis la reprise d'une API Rest existante plus facilement.",[12,28786,28787],{},"Il permet entre autres:",[506,28789,28790,28793],{},[370,28791,28792],{},"de ne pas répéter les champs de l'interface lors de l'héritage d'un type.",[370,28794,28795],{},"de définir des types génériques (qui à la compilation créeront des vrais types).",[133,28797,28799],{"id":28798},"subscription","Subscription",[12,28801,28802,28803,28806],{},"Un des points que j'adore avec GraphQL c'est la facilité d'implémentation qu'apporte Apollo avec les ",[184,28804,28805],{},"Subscritpions",". Quand on souhaite remonter des informations\ndu serveur au client, on peut mettre en place des WebSockets ou des Server-Side-Events. Il faut alors définir un potocole entre le client et le serveur.",[12,28808,28809],{},"GraphQL utilise de façon transparente une WebSocket dans le cadre des souscriptions. L'avantage c'est que le protocole est le même que pour les query. On effectue une\ndemande et au lieu d'avoir une réponse, une fois, on reçois les mise à jours sur la souscription.",[12,28811,28812],{},"Cela permet de mettre en place facilement des pages dynamiques qui évoluent sans actions utilisateurs (progression, chat, ...) sans se casser la tête.",[12,28814,28815],{},"Pour définir une souscription on peut utiliser une requête comme celle-ci:",[2105,28817,28819],{"className":26525,"code":28818,"language":26527,"meta":1646,"style":1646},"#import \".\u002FFragmentJob.graphql\"\n\nsubscription QueueTasksJobUpdated {\n  jobUpdated {\n    ...FragmentJob\n  }\n}\n",[184,28820,28821,28825,28829,28838,28845,28851,28855],{"__ignoreMap":1646},[2113,28822,28823],{"class":2115,"line":2116},[2113,28824,27618],{"class":2396},[2113,28826,28827],{"class":2115,"line":1647},[2113,28828,2125],{"emptyLinePlaceholder":1767},[2113,28830,28831,28833,28836],{"class":2115,"line":1652},[2113,28832,28798],{"class":2133},[2113,28834,28835],{"class":2133}," QueueTasksJobUpdated",[2113,28837,2647],{"class":2119},[2113,28839,28840,28843],{"class":2115,"line":2185},[2113,28841,28842],{"class":2330},"  jobUpdated",[2113,28844,2647],{"class":2119},[2113,28846,28847,28849],{"class":2115,"line":2230},[2113,28848,22858],{"class":2119},[2113,28850,27660],{"class":2330},[2113,28852,28853],{"class":2115,"line":2235},[2113,28854,2572],{"class":2119},[2113,28856,28857],{"class":2115,"line":2241},[2113,28858,2599],{"class":2119},[12,28860,28861],{},"Dans l'exemple ci-dessous je réutilise le fragment d'un job défini dans une query. La souscription dans apollo va automatiquement mettre à jour le résultat et\nrafraichir l'IHM.",[12,28863,28864,28865,20415],{},"Par exemple avec vue cela donne (cf la doc de ",[49,28866,28869],{"href":28867,"rel":28868},"https:\u002F\u002Fapollo.vuejs.org\u002Fguide\u002Fapollo\u002Fsubscriptions.html#subscribe-to-more",[347],"vue-apollo",[2105,28871,28873],{"className":7776,"code":28872,"language":7778,"meta":1646,"style":1646},"apollo: {\n  tags: {\n    query: TAGS_QUERY,\n    subscribeToMore: {\n      document: gql`subscription name($param: String!) {\n        itemAdded(param: $param) {\n          id\n          label\n        }\n      }`,\n      \u002F\u002F Variables passed to the subscription. Since we're using a function,\n      \u002F\u002F they are reactive\n      variables () {\n        return {\n          param: this.param,\n        }\n      },\n      \u002F\u002F Mutate the previous result\n      updateQuery: (previousResult, { subscriptionData }) => {\n        \u002F\u002F Here, return the new result from the previous with the new data\n      },\n    }\n  }\n}\n",[184,28874,28875,28881,28888,28900,28907,28920,28925,28930,28935,28939,28946,28951,28956,28964,28971,28987,28991,28995,29000,29023,29028,29032,29036,29040],{"__ignoreMap":1646},[2113,28876,28877,28879],{"class":2115,"line":2116},[2113,28878,27922],{"class":2330},[2113,28880,8091],{"class":2119},[2113,28882,28883,28886],{"class":2115,"line":1647},[2113,28884,28885],{"class":2330},"  tags",[2113,28887,8091],{"class":2119},[2113,28889,28890,28893,28895,28898],{"class":2115,"line":1652},[2113,28891,28892],{"class":2330},"    query",[2113,28894,4429],{"class":2119},[2113,28896,28897],{"class":2414},"TAGS_QUERY",[2113,28899,4706],{"class":2119},[2113,28901,28902,28905],{"class":2115,"line":2185},[2113,28903,28904],{"class":2330},"    subscribeToMore",[2113,28906,8091],{"class":2119},[2113,28908,28909,28912,28914,28917],{"class":2115,"line":2230},[2113,28910,28911],{"class":2330},"      document",[2113,28913,4429],{"class":2119},[2113,28915,28916],{"class":2133},"gql",[2113,28918,28919],{"class":2149},"`subscription name($param: String!) {\n",[2113,28921,28922],{"class":2115,"line":2235},[2113,28923,28924],{"class":2149},"        itemAdded(param: $param) {\n",[2113,28926,28927],{"class":2115,"line":2241},[2113,28928,28929],{"class":2149},"          id\n",[2113,28931,28932],{"class":2115,"line":2246},[2113,28933,28934],{"class":2149},"          label\n",[2113,28936,28937],{"class":2115,"line":2464},[2113,28938,4578],{"class":2149},[2113,28940,28941,28944],{"class":2115,"line":2085},[2113,28942,28943],{"class":2149},"      }`",[2113,28945,4706],{"class":2119},[2113,28947,28948],{"class":2115,"line":2514},[2113,28949,28950],{"class":2396},"      \u002F\u002F Variables passed to the subscription. Since we're using a function,\n",[2113,28952,28953],{"class":2115,"line":2533},[2113,28954,28955],{"class":2396},"      \u002F\u002F they are reactive\n",[2113,28957,28958,28961],{"class":2115,"line":2556},[2113,28959,28960],{"class":2133},"      variables",[2113,28962,28963],{"class":2119}," () {\n",[2113,28965,28966,28969],{"class":2115,"line":2569},[2113,28967,28968],{"class":2326},"        return",[2113,28970,2647],{"class":2119},[2113,28972,28973,28976,28978,28980,28982,28985],{"class":2115,"line":2575},[2113,28974,28975],{"class":2330},"          param",[2113,28977,4429],{"class":2119},[2113,28979,15880],{"class":2414},[2113,28981,179],{"class":2119},[2113,28983,28984],{"class":2330},"param",[2113,28986,4706],{"class":2119},[2113,28988,28989],{"class":2115,"line":2596},[2113,28990,4578],{"class":2119},[2113,28992,28993],{"class":2115,"line":3192},[2113,28994,12282],{"class":2119},[2113,28996,28997],{"class":2115,"line":3213},[2113,28998,28999],{"class":2396},"      \u002F\u002F Mutate the previous result\n",[2113,29001,29002,29005,29008,29011,29013,29016,29019,29021],{"class":2115,"line":3236},[2113,29003,29004],{"class":2330},"      updateQuery",[2113,29006,29007],{"class":2119},": (",[2113,29009,29010],{"class":2429},"previousResult",[2113,29012,28314],{"class":2119},[2113,29014,29015],{"class":2429},"subscriptionData",[2113,29017,29018],{"class":2119}," }) ",[2113,29020,8063],{"class":2326},[2113,29022,2647],{"class":2119},[2113,29024,29025],{"class":2115,"line":3248},[2113,29026,29027],{"class":2396},"        \u002F\u002F Here, return the new result from the previous with the new data\n",[2113,29029,29030],{"class":2115,"line":4899},[2113,29031,12282],{"class":2119},[2113,29033,29034],{"class":2115,"line":1777},[2113,29035,4665],{"class":2119},[2113,29037,29038],{"class":2115,"line":4931},[2113,29039,2572],{"class":2119},[2113,29041,29042],{"class":2115,"line":4961},[2113,29043,2599],{"class":2119},[12,29045,29046,29047,29050],{},"La requête initiale est faite grâce à ",[184,29048,29049],{},"TAG_QUERY",", puis toutes les mises à jours se font via la souscription. L'IHM est alors automatiquement mise à jour.",[133,29052,1621],{"id":1620},[12,29054,29055],{},"GraphQL est super puissant et facilite l'écriture d'API et son utilisation par des clients. Par contre la montée en compétence pour faire du GraphQL est un\npeu plus élevée que pour faire du REST (Bien qu'il n'est pas facile de faire du bon REST).",[12,29057,29058],{},"Il peut toujours être utile de proposer des API Rest en plus des API GraphQL. Pour par exemple permettre au client de choisir ce qu'il souhaite utiliser ou\nmême par exemple pour des cas d'usage particuliers. (Par exemple le téléchargement\u002Fl'upload d'un document binaire est plus facile en REST que en GraphQL).",[133,29060,29062],{"id":29061},"références","Références",[506,29064,29065,29072,29078,29085,29091,29098,29103],{},[370,29066,29067],{},[49,29068,29071],{"href":29069,"rel":29070},"https:\u002F\u002Fgraphql.org\u002Flearn\u002Fqueries\u002F",[347],"GraphQL",[370,29073,29074,179],{},[49,29075,26509],{"href":29076,"rel":29077},"https:\u002F\u002Fwww.apollographql.com\u002Fdocs\u002Fapollo-server\u002Fdata\u002Fresolvers\u002F",[347],[370,29079,29080],{},[49,29081,29084],{"href":29082,"rel":29083},"https:\u002F\u002Fwww.moesif.com\u002Fblog\u002Ftechnical\u002Fapi-design\u002FBest-Practices-for-Versioning-REST-and-GraphQL-APIs\u002F",[347],"Best Practices for Versioning REST and GraphQL APIs",[370,29086,29087],{},[49,29088,29090],{"href":27696,"rel":29089},[347],"GraphQL Versioning",[370,29092,29093],{},[49,29094,29097],{"href":29095,"rel":29096},"https:\u002F\u002Ftowardsdatascience.com\u002Fgraphql-best-practices-3fda586538c4",[347],"GraphQL Best Practices",[370,29099,29100],{},[49,29101,29082],{"href":29082,"rel":29102},[347],[370,29104,29105],{},[49,29106,27696],{"href":27696,"rel":29107},[347],[3358,29109,29110],{},"html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sLaUg, html code.shiki .sLaUg{--shiki-default:#FFFFFF}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}",{"title":1646,"searchDepth":1647,"depth":1647,"links":29112},[29113,29114],{"id":26126,"depth":1647,"text":26127},{"id":26485,"depth":1647,"text":26486,"children":29115},[29116,29117,29118,29119,29120,29121,29122,29123,29124,29125,29126,29127,29128,29129,29130,29131],{"id":26515,"depth":1652,"text":26516},{"id":26659,"depth":1652,"text":26660},{"id":26678,"depth":1652,"text":26679},{"id":27339,"depth":1652,"text":27340},{"id":27358,"depth":1652,"text":27359},{"id":27439,"depth":1652,"text":27440},{"id":27678,"depth":1652,"text":27679},{"id":27718,"depth":1652,"text":27719},{"id":27899,"depth":1652,"text":27900},{"id":28103,"depth":1652,"text":28104},{"id":28359,"depth":1652,"text":28360},{"id":28393,"depth":1652,"text":28394},{"id":28418,"depth":1652,"text":28419},{"id":28798,"depth":1652,"text":28799},{"id":1620,"depth":1652,"text":1621},{"id":29061,"depth":1652,"text":29062},"2020-11-29",{"type":9,"value":29134},[29135,29137,29139,29153],[12,29136,26097],{},[12,29138,26100],{},[506,29140,29141,29145,29149],{},[370,29142,29143],{},[49,29144,26108],{"href":26107},[370,29146,29147],{},[49,29148,26114],{"href":26113},[370,29150,29151],{},[49,29152,26120],{"href":26119},[12,29154,26123],{},{"planet":1767},"\u002Fpost\u002Fcreation-api-3",{"title":26092,"description":26097},"creation-api-3","posts\u002FProgrammation\u002F2020-11-29_creation-api-3",[29161,26527,29162,3510,3511],"api","rest","HYw0wo6aBhxcGM0oSLGWN8-QiSEHsmV51HJU8dZLB8E",{"id":29165,"title":29166,"author":7,"body":29167,"category":2057,"categorySlug":2058,"date":33157,"description":29171,"excerpt":33158,"extension":1764,"location":1765,"meta":33190,"navigation":1767,"path":33191,"published":1767,"seo":33192,"slug":33193,"stem":33194,"tags":33195,"timeToRead":2514,"__hash__":33197},"posts\u002Fposts\u002FProgrammation\u002F2020-11-14_application_du_confinement.md","L'Application du confinement pour se déplacer",{"type":9,"value":29168,"toc":33147},[29169,29172,29175,29184,29192,29195,29198,29201,29215,29219,29238,29241,29255,29258,29261,29264,29271,29274,29278,29283,29389,29392,29658,29661,29664,29667,30066,30080,30089,30092,30274,30277,30409,30413,30416,30419,30484,30492,30534,30537,30645,30648,30889,30892,31155,31158,31408,31426,31437,31441,31448,31454,31576,31590,31593,31757,31760,31766,31773,31776,32060,32063,32066,32170,32180,32190,32520,32523,32529,33080,33083,33087,33090,33105,33108,33124,33144],[12,29170,29171],{},"Préambule: A cause du temps de validation du PlayStore, je publie cet article avec une semaine de retard.",[12,29173,29174],{},"Cela fait plus d'une semaine (quand j'écris ces lignes) que le re-confinement à commencé. Quand je vais courir, je dois me cantonner à\n1km autour de chez moi. Mais quand je cours j'aimerais que mon téléphone intelligent me prévienne quand j'approche du rayon de 1km ou\nquand je le dépasse. Je ne souhaite pas avoir le nez sur une carte de mon téléphone.",[12,29176,29177,29178,29183],{},"Je regarde ce qui se fait. J'ai trouvé l'application suivante sur le play store : ",[49,29179,29182],{"href":29180,"rel":29181},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.BETechnology.unkm",[347],"1km",".\nL'application m'avait l'air de répondre à mes critères mais ne fonctionnait pas lors de mon utilisation (en plus il y avait de la pub).",[12,29185,29186,29187,29191],{},"Une autre application ",[49,29188,29182],{"href":29189,"rel":29190},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.apps100101.a1km",[347]," pourrait répondre à mon besoin mais\nje ne l'ai pas testé.",[12,29193,29194],{},"Beaucoup d'applications ont pour but de dessiner un cercle sur une carte.",[12,29196,29197],{},"J'ai finalement décidé d'écrire ma propre application (en plus elle sera open source).",[12,29199,29200],{},"Voici ce dont j'ai besoin:",[506,29202,29203,29206,29209,29212],{},[370,29204,29205],{},"une application open source",[370,29207,29208],{},"un gros bouton pour démarrer la surveillance (à partir de mon point de départ)",[370,29210,29211],{},"un avertissement quand on approche du rayon max des 1km.",[370,29213,29214],{},"un avertissement régulier qand on dépasse les 1km.",[128,29216,29218],{"id":29217},"lapplication","L'application",[12,29220,29221,29222,29225,29226,29229,29230,29233,29234,29237],{},"Pour développer une application Android je vais démarrer ",[362,29223,29224],{},"Android Studio"," et commencer à développer le 1er écran. Je choisis le\nlanguage ",[362,29227,29228],{},"Java"," que je maîtrise plus que le language ",[362,29231,29232],{},"Kotlin"," et le SDK Minimum de ",[362,29235,29236],{},"Android 6.0"," pour toucher 85% des\nutilisateurs (si l'application peut interesser d'autres personnes).",[12,29239,29240],{},"J'imagine l'application découpée en deux parties:",[506,29242,29243,29252],{},[370,29244,29245,29246,29251],{},"L'activité",[17400,29247,29248],{},[49,29249,3686],{"href":17404,"ariaDescribedBy":29250,"dataFootnoteRef":1646,"id":17407},[17406]," principale de l'application qui contiendra mon gros bouton, la position de départ, la position courante, et la\ndistance à vol d'oiseau du point de départ.",[370,29253,29254],{},"Un service, dont le but est quand l'application est démarrée, de surveiller les déplacements et d'informer l'utilisateur.",[12,29256,29257],{},"Nous allons donc commencer par développer l'activité",[128,29259,29245],{"id":29260},"lactivité",[12,29262,29263],{},"Je ne suis pas graphiste ni UI\u002FUX designer. Le design de cette première interface va alors être très simple et très sobre. Un\ngros bouton + les différentes informations dont j'ai besoin quand l'application est démarrée :",[12,29265,29266],{},[121,29267],{"alt":29268,"className":29269,"src":29270},"Screenshoot page principale",[126],"\u002FProgrammation\u002F1kmarround\u002Fscreenshoot1.png",[12,29272,29273],{},"Lors du démarrage de l'application, j'ai besoin que celle-ci écoute le changement, les positions de l'utilisateur afin de\ndéfinir le point de départ. Comme ce service d'écoute me sera utile également pour le service, je développe une classe à coté.",[133,29275,29277],{"id":29276},"arroundlocationmanager","ArroundLocationManager",[12,29279,29280,29281,15746],{},"Voici donc la classe ",[155,29282,29277],{},[2105,29284,29288],{"className":29285,"code":29286,"language":29287,"meta":1646,"style":1646},"language-java shiki shiki-themes one-dark-pro","class ArroundLocationManager extends Thread {\n    private static final String TAG = \"ArroundLocationManager\";\n\n    private LocationManager mLocationManager = null;\n    private static final int LOCATION_INTERVAL = 1000;\n    private static final float LOCATION_DISTANCE = 50f;\n","java",[184,29289,29290,29305,29328,29332,29348,29368],{"__ignoreMap":1646},[2113,29291,29292,29294,29297,29300,29303],{"class":2115,"line":2116},[2113,29293,20538],{"class":2326},[2113,29295,29296],{"class":2414}," ArroundLocationManager",[2113,29298,29299],{"class":2326}," extends",[2113,29301,29302],{"class":2414}," Thread",[2113,29304,2647],{"class":2119},[2113,29306,29307,29309,29312,29315,29318,29321,29323,29326],{"class":2115,"line":1647},[2113,29308,21028],{"class":2326},[2113,29310,29311],{"class":2326}," static",[2113,29313,29314],{"class":2326}," final",[2113,29316,29317],{"class":2414}," String",[2113,29319,29320],{"class":2330}," TAG ",[2113,29322,2335],{"class":2334},[2113,29324,29325],{"class":2149}," \"ArroundLocationManager\"",[2113,29327,2487],{"class":2119},[2113,29329,29330],{"class":2115,"line":1652},[2113,29331,2125],{"emptyLinePlaceholder":1767},[2113,29333,29334,29336,29339,29342,29344,29346],{"class":2115,"line":2185},[2113,29335,21028],{"class":2326},[2113,29337,29338],{"class":2414}," LocationManager",[2113,29340,29341],{"class":2330}," mLocationManager ",[2113,29343,2335],{"class":2334},[2113,29345,20727],{"class":2166},[2113,29347,2487],{"class":2119},[2113,29349,29350,29352,29354,29356,29358,29361,29363,29366],{"class":2115,"line":2230},[2113,29351,21028],{"class":2326},[2113,29353,29311],{"class":2326},[2113,29355,29314],{"class":2326},[2113,29357,2842],{"class":2326},[2113,29359,29360],{"class":2330}," LOCATION_INTERVAL ",[2113,29362,2335],{"class":2334},[2113,29364,29365],{"class":2166}," 1000",[2113,29367,2487],{"class":2119},[2113,29369,29370,29372,29374,29376,29379,29382,29384,29387],{"class":2115,"line":2235},[2113,29371,21028],{"class":2326},[2113,29373,29311],{"class":2326},[2113,29375,29314],{"class":2326},[2113,29377,29378],{"class":2326}," float",[2113,29380,29381],{"class":2330}," LOCATION_DISTANCE ",[2113,29383,2335],{"class":2334},[2113,29385,29386],{"class":2166}," 50f",[2113,29388,2487],{"class":2119},[12,29390,29391],{},"Pour commencer définissons quelques constantes: je souhaite avoir la position, tous les 50m et au maximum toutes les secondes\n(m'enfin quelqu'un qui fait plus de 50m en une seconde à pied est trop fort pour moi).",[2105,29393,29395],{"className":29285,"code":29394,"language":29287,"meta":1646,"style":1646},"    private List\u003CArroundLocationManager.ArrroundLocationListener> listener = new ArrayList\u003C>();\n\n    public interface ArrroundLocationListener {\n        void updateLocation(Location startLocation);\n    }\n\n    private ArroundLocationManager.LocationListener[] mLocationListeners = new ArroundLocationManager.LocationListener[]{\n            new ArroundLocationManager.LocationListener(LocationManager.GPS_PROVIDER),\n            new ArroundLocationManager.LocationListener(LocationManager.NETWORK_PROVIDER)\n    };\n\n    public void addListener(ArroundLocationManager.ArrroundLocationListener l) {\n        listener.add(l);\n    }\n\n    private void callListener(Location location) {\n        for( ArroundLocationManager.ArrroundLocationListener l : listener) {\n            l.updateLocation(location);\n        }\n    }\n",[184,29396,29397,29433,29437,29450,29468,29472,29476,29503,29526,29547,29554,29558,29579,29592,29596,29600,29616,29637,29650,29654],{"__ignoreMap":1646},[2113,29398,29399,29401,29404,29406,29408,29410,29413,29416,29419,29421,29423,29426,29429,29431],{"class":2115,"line":2116},[2113,29400,21028],{"class":2326},[2113,29402,29403],{"class":2414}," List",[2113,29405,3109],{"class":2119},[2113,29407,29277],{"class":2414},[2113,29409,179],{"class":2119},[2113,29411,29412],{"class":2414},"ArrroundLocationListener",[2113,29414,29415],{"class":2119},">",[2113,29417,29418],{"class":2330}," listener ",[2113,29420,2335],{"class":2334},[2113,29422,4778],{"class":2326},[2113,29424,29425],{"class":2414}," ArrayList",[2113,29427,29428],{"class":2119},"\u003C>",[2113,29430,17772],{"class":2330},[2113,29432,2487],{"class":2119},[2113,29434,29435],{"class":2115,"line":1647},[2113,29436,2125],{"emptyLinePlaceholder":1767},[2113,29438,29439,29442,29445,29448],{"class":2115,"line":1652},[2113,29440,29441],{"class":2326},"    public",[2113,29443,29444],{"class":2326}," interface",[2113,29446,29447],{"class":2414}," ArrroundLocationListener",[2113,29449,2647],{"class":2119},[2113,29451,29452,29455,29458,29460,29463,29466],{"class":2115,"line":2185},[2113,29453,29454],{"class":2326},"        void",[2113,29456,29457],{"class":2133}," updateLocation",[2113,29459,2423],{"class":2119},[2113,29461,29462],{"class":2414},"Location",[2113,29464,29465],{"class":2429}," startLocation",[2113,29467,2553],{"class":2119},[2113,29469,29470],{"class":2115,"line":2230},[2113,29471,4665],{"class":2119},[2113,29473,29474],{"class":2115,"line":2235},[2113,29475,2125],{"emptyLinePlaceholder":1767},[2113,29477,29478,29480,29482,29484,29487,29490,29492,29494,29496,29498,29500],{"class":2115,"line":2241},[2113,29479,21028],{"class":2326},[2113,29481,29296],{"class":2414},[2113,29483,179],{"class":2119},[2113,29485,29486],{"class":2414},"LocationListener",[2113,29488,29489],{"class":2330},"[] mLocationListeners ",[2113,29491,2335],{"class":2334},[2113,29493,4778],{"class":2326},[2113,29495,29296],{"class":2414},[2113,29497,179],{"class":2119},[2113,29499,29486],{"class":2414},[2113,29501,29502],{"class":2330},"[]{\n",[2113,29504,29505,29508,29510,29512,29514,29516,29519,29521,29524],{"class":2115,"line":2246},[2113,29506,29507],{"class":2326},"            new",[2113,29509,29296],{"class":2330},[2113,29511,179],{"class":2119},[2113,29513,29486],{"class":2133},[2113,29515,2423],{"class":2119},[2113,29517,29518],{"class":2414},"LocationManager",[2113,29520,179],{"class":2119},[2113,29522,29523],{"class":2414},"GPS_PROVIDER",[2113,29525,7142],{"class":2119},[2113,29527,29528,29530,29532,29534,29536,29538,29540,29542,29545],{"class":2115,"line":2464},[2113,29529,29507],{"class":2326},[2113,29531,29296],{"class":2330},[2113,29533,179],{"class":2119},[2113,29535,29486],{"class":2133},[2113,29537,2423],{"class":2119},[2113,29539,29518],{"class":2414},[2113,29541,179],{"class":2119},[2113,29543,29544],{"class":2414},"NETWORK_PROVIDER",[2113,29546,4660],{"class":2119},[2113,29548,29549,29552],{"class":2115,"line":2085},[2113,29550,29551],{"class":2330},"    }",[2113,29553,2487],{"class":2119},[2113,29555,29556],{"class":2115,"line":2514},[2113,29557,2125],{"emptyLinePlaceholder":1767},[2113,29559,29560,29562,29565,29568,29570,29572,29574,29576],{"class":2115,"line":2533},[2113,29561,29441],{"class":2326},[2113,29563,29564],{"class":2326}," void",[2113,29566,29567],{"class":2133}," addListener",[2113,29569,2423],{"class":2330},[2113,29571,29277],{"class":2414},[2113,29573,179],{"class":2119},[2113,29575,29412],{"class":2414},[2113,29577,29578],{"class":2330}," l) {\n",[2113,29580,29581,29584,29586,29589],{"class":2115,"line":2556},[2113,29582,29583],{"class":2414},"        listener",[2113,29585,179],{"class":2119},[2113,29587,29588],{"class":2133},"add",[2113,29590,29591],{"class":2119},"(l);\n",[2113,29593,29594],{"class":2115,"line":2569},[2113,29595,4665],{"class":2330},[2113,29597,29598],{"class":2115,"line":2575},[2113,29599,2125],{"emptyLinePlaceholder":1767},[2113,29601,29602,29604,29606,29609,29611,29613],{"class":2115,"line":2596},[2113,29603,21028],{"class":2326},[2113,29605,29564],{"class":2326},[2113,29607,29608],{"class":2133}," callListener",[2113,29610,2423],{"class":2330},[2113,29612,29462],{"class":2414},[2113,29614,29615],{"class":2330}," location) {\n",[2113,29617,29618,29620,29623,29625,29627,29629,29632,29634],{"class":2115,"line":3192},[2113,29619,14355],{"class":2326},[2113,29621,29622],{"class":2330},"( ",[2113,29624,29277],{"class":2414},[2113,29626,179],{"class":2119},[2113,29628,29412],{"class":2414},[2113,29630,29631],{"class":2330}," l ",[2113,29633,15746],{"class":2326},[2113,29635,29636],{"class":2330}," listener) {\n",[2113,29638,29639,29642,29644,29647],{"class":2115,"line":3213},[2113,29640,29641],{"class":2414},"            l",[2113,29643,179],{"class":2119},[2113,29645,29646],{"class":2133},"updateLocation",[2113,29648,29649],{"class":2119},"(location);\n",[2113,29651,29652],{"class":2115,"line":3236},[2113,29653,4578],{"class":2330},[2113,29655,29656],{"class":2115,"line":3248},[2113,29657,4665],{"class":2330},[12,29659,29660],{},"Viens ensuite la définition d'un listener pour que les applications qui s'abonnent à cette classe puissent bénéficier d'un\nlistener et recevoir des notifications lors de la mise à jour des positions.",[12,29662,29663],{},"Comme on peut le constater je fais tourner cette classe dans un thread. Lors de mon développement je me suis rendu compte que\nlorsque je quittais l'application, le service était tué également. Une des raisons à cela est que le service est dans le même\nthread que l'activité principale. L'ajout de ce thread (ainsi que d'autre chose) ont résolu le problème. (Mais il est possible\nque ce soit plus lié aux autres choses qu'au thread lui même).",[12,29665,29666],{},"Voici le coeur du thread:",[2105,29668,29670],{"className":29285,"code":29669,"language":29287,"meta":1646,"style":1646},"    public void run() {\n        Looper.prepare();\n\n        initializeLocationManager();\n\n        Looper.loop();\n\n        \u002F\u002F Never called, loop is killed when activity or thread stopped\n        finalizeLocationManager();\n    }\n\n    private ArroundLocationManager.LocationListener[] mLocationListeners = new ArroundLocationManager.LocationListener[]{\n        new ArroundLocationManager.LocationListener(LocationManager.GPS_PROVIDER),\n        new ArroundLocationManager.LocationListener(LocationManager.NETWORK_PROVIDER)\n    };\n\n    public void initializeLocationManager() {\n        try {\n            mLocationManager.requestLocationUpdates(\n                    LocationManager.NETWORK_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE,\n                    mLocationListeners[1]);\n        } catch (java.lang.SecurityException ex) {\n            Log.i(TAG, \"fail to request location update, ignore\", ex);\n        } catch (IllegalArgumentException ex) {\n            Log.d(TAG, \"network provider does not exist, \" + ex.getMessage());\n        }\n\n        try {\n            mLocationManager.requestLocationUpdates(\n                    LocationManager.GPS_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE,\n                    mLocationListeners[0]);\n        } catch (java.lang.SecurityException ex) {\n            Log.i(TAG, \"fail to request location update, ignore\", ex);\n        } catch (IllegalArgumentException ex) {\n            Log.d(TAG, \"gps provider does not exist \" + ex.getMessage());\n        }\n    }\n",[184,29671,29672,29683,29695,29699,29708,29712,29723,29727,29732,29741,29745,29749,29773,29794,29814,29820,29824,29835,29842,29854,29866,29875,29892,29910,29925,29951,29955,29959,29965,29975,29985,29993,30007,30021,30035,30058,30062],{"__ignoreMap":1646},[2113,29673,29674,29676,29678,29681],{"class":2115,"line":2116},[2113,29675,29441],{"class":2326},[2113,29677,29564],{"class":2326},[2113,29679,29680],{"class":2133}," run",[2113,29682,3912],{"class":2330},[2113,29684,29685,29688,29690,29693],{"class":2115,"line":1647},[2113,29686,29687],{"class":2414},"        Looper",[2113,29689,179],{"class":2119},[2113,29691,29692],{"class":2133},"prepare",[2113,29694,3944],{"class":2119},[2113,29696,29697],{"class":2115,"line":1652},[2113,29698,2125],{"emptyLinePlaceholder":1767},[2113,29700,29701,29704,29706],{"class":2115,"line":2185},[2113,29702,29703],{"class":2133},"        initializeLocationManager",[2113,29705,17772],{"class":2330},[2113,29707,2487],{"class":2119},[2113,29709,29710],{"class":2115,"line":2230},[2113,29711,2125],{"emptyLinePlaceholder":1767},[2113,29713,29714,29716,29718,29721],{"class":2115,"line":2235},[2113,29715,29687],{"class":2414},[2113,29717,179],{"class":2119},[2113,29719,29720],{"class":2133},"loop",[2113,29722,3944],{"class":2119},[2113,29724,29725],{"class":2115,"line":2241},[2113,29726,2125],{"emptyLinePlaceholder":1767},[2113,29728,29729],{"class":2115,"line":2246},[2113,29730,29731],{"class":2396},"        \u002F\u002F Never called, loop is killed when activity or thread stopped\n",[2113,29733,29734,29737,29739],{"class":2115,"line":2464},[2113,29735,29736],{"class":2133},"        finalizeLocationManager",[2113,29738,17772],{"class":2330},[2113,29740,2487],{"class":2119},[2113,29742,29743],{"class":2115,"line":2085},[2113,29744,4665],{"class":2330},[2113,29746,29747],{"class":2115,"line":2514},[2113,29748,2125],{"emptyLinePlaceholder":1767},[2113,29750,29751,29753,29755,29757,29759,29761,29763,29765,29767,29769,29771],{"class":2115,"line":2533},[2113,29752,21028],{"class":2326},[2113,29754,29296],{"class":2414},[2113,29756,179],{"class":2119},[2113,29758,29486],{"class":2414},[2113,29760,29489],{"class":2330},[2113,29762,2335],{"class":2334},[2113,29764,4778],{"class":2326},[2113,29766,29296],{"class":2414},[2113,29768,179],{"class":2119},[2113,29770,29486],{"class":2414},[2113,29772,29502],{"class":2330},[2113,29774,29775,29778,29780,29782,29784,29786,29788,29790,29792],{"class":2115,"line":2556},[2113,29776,29777],{"class":2326},"        new",[2113,29779,29296],{"class":2330},[2113,29781,179],{"class":2119},[2113,29783,29486],{"class":2133},[2113,29785,2423],{"class":2119},[2113,29787,29518],{"class":2414},[2113,29789,179],{"class":2119},[2113,29791,29523],{"class":2414},[2113,29793,7142],{"class":2119},[2113,29795,29796,29798,29800,29802,29804,29806,29808,29810,29812],{"class":2115,"line":2569},[2113,29797,29777],{"class":2326},[2113,29799,29296],{"class":2330},[2113,29801,179],{"class":2119},[2113,29803,29486],{"class":2133},[2113,29805,2423],{"class":2119},[2113,29807,29518],{"class":2414},[2113,29809,179],{"class":2119},[2113,29811,29544],{"class":2414},[2113,29813,4660],{"class":2119},[2113,29815,29816,29818],{"class":2115,"line":2575},[2113,29817,29551],{"class":2330},[2113,29819,2487],{"class":2119},[2113,29821,29822],{"class":2115,"line":2596},[2113,29823,2125],{"emptyLinePlaceholder":1767},[2113,29825,29826,29828,29830,29833],{"class":2115,"line":3192},[2113,29827,29441],{"class":2326},[2113,29829,29564],{"class":2326},[2113,29831,29832],{"class":2133}," initializeLocationManager",[2113,29834,3912],{"class":2330},[2113,29836,29837,29840],{"class":2115,"line":3213},[2113,29838,29839],{"class":2326},"        try",[2113,29841,2647],{"class":2330},[2113,29843,29844,29847,29849,29852],{"class":2115,"line":3236},[2113,29845,29846],{"class":2414},"            mLocationManager",[2113,29848,179],{"class":2119},[2113,29850,29851],{"class":2133},"requestLocationUpdates",[2113,29853,3146],{"class":2119},[2113,29855,29856,29859,29861,29863],{"class":2115,"line":3248},[2113,29857,29858],{"class":2414},"                    LocationManager",[2113,29860,179],{"class":2119},[2113,29862,29544],{"class":2414},[2113,29864,29865],{"class":2119},", LOCATION_INTERVAL, LOCATION_DISTANCE,\n",[2113,29867,29868,29871,29873],{"class":2115,"line":4899},[2113,29869,29870],{"class":2119},"                    mLocationListeners[",[2113,29872,3686],{"class":2166},[2113,29874,6706],{"class":2119},[2113,29876,29877,29880,29882,29884,29887,29890],{"class":2115,"line":1777},[2113,29878,29879],{"class":2330},"        } ",[2113,29881,8973],{"class":2326},[2113,29883,2495],{"class":2330},[2113,29885,29886],{"class":2414},"java.lang.SecurityException",[2113,29888,29889],{"class":2429}," ex",[2113,29891,2433],{"class":2330},[2113,29893,29894,29897,29899,29901,29904,29907],{"class":2115,"line":4931},[2113,29895,29896],{"class":2414},"            Log",[2113,29898,179],{"class":2119},[2113,29900,7919],{"class":2133},[2113,29902,29903],{"class":2119},"(TAG, ",[2113,29905,29906],{"class":2149},"\"fail to request location update, ignore\"",[2113,29908,29909],{"class":2119},", ex);\n",[2113,29911,29912,29914,29916,29918,29921,29923],{"class":2115,"line":4961},[2113,29913,29879],{"class":2330},[2113,29915,8973],{"class":2326},[2113,29917,2495],{"class":2330},[2113,29919,29920],{"class":2414},"IllegalArgumentException",[2113,29922,29889],{"class":2429},[2113,29924,2433],{"class":2330},[2113,29926,29927,29929,29931,29934,29936,29939,29942,29944,29946,29949],{"class":2115,"line":4976},[2113,29928,29896],{"class":2414},[2113,29930,179],{"class":2119},[2113,29932,29933],{"class":2133},"d",[2113,29935,29903],{"class":2119},[2113,29937,29938],{"class":2149},"\"network provider does not exist, \"",[2113,29940,29941],{"class":2334}," +",[2113,29943,29889],{"class":2414},[2113,29945,179],{"class":2119},[2113,29947,29948],{"class":2133},"getMessage",[2113,29950,2593],{"class":2119},[2113,29952,29953],{"class":2115,"line":4993},[2113,29954,4578],{"class":2330},[2113,29956,29957],{"class":2115,"line":5013},[2113,29958,2125],{"emptyLinePlaceholder":1767},[2113,29960,29961,29963],{"class":2115,"line":5018},[2113,29962,29839],{"class":2326},[2113,29964,2647],{"class":2330},[2113,29966,29967,29969,29971,29973],{"class":2115,"line":5042},[2113,29968,29846],{"class":2414},[2113,29970,179],{"class":2119},[2113,29972,29851],{"class":2133},[2113,29974,3146],{"class":2119},[2113,29976,29977,29979,29981,29983],{"class":2115,"line":5057},[2113,29978,29858],{"class":2414},[2113,29980,179],{"class":2119},[2113,29982,29523],{"class":2414},[2113,29984,29865],{"class":2119},[2113,29986,29987,29989,29991],{"class":2115,"line":5062},[2113,29988,29870],{"class":2119},[2113,29990,3095],{"class":2166},[2113,29992,6706],{"class":2119},[2113,29994,29995,29997,29999,30001,30003,30005],{"class":2115,"line":5098},[2113,29996,29879],{"class":2330},[2113,29998,8973],{"class":2326},[2113,30000,2495],{"class":2330},[2113,30002,29886],{"class":2414},[2113,30004,29889],{"class":2429},[2113,30006,2433],{"class":2330},[2113,30008,30009,30011,30013,30015,30017,30019],{"class":2115,"line":5117},[2113,30010,29896],{"class":2414},[2113,30012,179],{"class":2119},[2113,30014,7919],{"class":2133},[2113,30016,29903],{"class":2119},[2113,30018,29906],{"class":2149},[2113,30020,29909],{"class":2119},[2113,30022,30023,30025,30027,30029,30031,30033],{"class":2115,"line":5142},[2113,30024,29879],{"class":2330},[2113,30026,8973],{"class":2326},[2113,30028,2495],{"class":2330},[2113,30030,29920],{"class":2414},[2113,30032,29889],{"class":2429},[2113,30034,2433],{"class":2330},[2113,30036,30037,30039,30041,30043,30045,30048,30050,30052,30054,30056],{"class":2115,"line":5148},[2113,30038,29896],{"class":2414},[2113,30040,179],{"class":2119},[2113,30042,29933],{"class":2133},[2113,30044,29903],{"class":2119},[2113,30046,30047],{"class":2149},"\"gps provider does not exist \"",[2113,30049,29941],{"class":2334},[2113,30051,29889],{"class":2414},[2113,30053,179],{"class":2119},[2113,30055,29948],{"class":2133},[2113,30057,2593],{"class":2119},[2113,30059,30060],{"class":2115,"line":5163},[2113,30061,4578],{"class":2330},[2113,30063,30064],{"class":2115,"line":5169},[2113,30065,4665],{"class":2330},[12,30067,30068,30069,30071,30072,30075,30076,30079],{},"On utilise le service ",[184,30070,29518],{}," d'android pour écouter la position de l'utilisateur. On écoute la position venant\ndu ",[362,30073,30074],{},"Network"," qui permet d'avoir une position moins fiable mais rapide, puis celle venant du ",[362,30077,30078],{},"GPS"," permettant d'avoir une\nposition fiable (mais lente à obtenir).",[12,30081,30082,30083,30088],{},"Afin d'avoir une position la plus précise aussi, je me suis inspiré du code suivant:\n",[49,30084,30087],{"href":30085,"rel":30086},"https:\u002F\u002Fstuff.mit.edu\u002Fafs\u002Fsipb\u002Fproject\u002Fandroid\u002Fdocs\u002Ftraining\u002Fbasics\u002Flocation\u002Fcurrentlocation.html",[347],"Obtaining the Current Location",".\nLe code en question permet de choisir entre deux positions la plus précise (entre la position NETWORK et la position GPS).",[12,30090,30091],{},"On notifie les appelants:",[2105,30093,30095],{"className":29285,"code":30094,"language":29287,"meta":1646,"style":1646},"    private class LocationListener implements android.location.LocationListener {\n        public LocationListener(String provider) {\n            Log.e(TAG, \"LocationListener \" + provider);\n            mLocation = new Location(provider);\n        }\n\n        @Override\n        public void onLocationChanged(Location location) {\n            Log.e(TAG, \"onLocationChanged: \" + location);\n            if (isBetterLocation(location, mLocation)) {\n                mLocation.set(location);\n            }\n\n            callListener(mLocation);\n        }\n        ...\n    }\n",[184,30096,30097,30116,30134,30152,30167,30171,30175,30183,30203,30221,30233,30245,30249,30253,30261,30265,30270],{"__ignoreMap":1646},[2113,30098,30099,30101,30103,30106,30109,30112,30114],{"class":2115,"line":2116},[2113,30100,21028],{"class":2326},[2113,30102,19179],{"class":2326},[2113,30104,30105],{"class":2414}," LocationListener",[2113,30107,30108],{"class":2326}," implements",[2113,30110,30111],{"class":2330}," android.location.",[2113,30113,29486],{"class":2414},[2113,30115,2647],{"class":2119},[2113,30117,30118,30121,30123,30125,30127,30130,30132],{"class":2115,"line":1647},[2113,30119,30120],{"class":2326},"        public",[2113,30122,30105],{"class":2133},[2113,30124,2423],{"class":2119},[2113,30126,13081],{"class":2414},[2113,30128,30129],{"class":2429}," provider",[2113,30131,17832],{"class":2119},[2113,30133,2647],{"class":2119},[2113,30135,30136,30138,30140,30142,30144,30147,30149],{"class":2115,"line":1652},[2113,30137,29896],{"class":2414},[2113,30139,179],{"class":2119},[2113,30141,5601],{"class":2133},[2113,30143,29903],{"class":2119},[2113,30145,30146],{"class":2149},"\"LocationListener \"",[2113,30148,29941],{"class":2334},[2113,30150,30151],{"class":2119}," provider);\n",[2113,30153,30154,30157,30159,30161,30164],{"class":2115,"line":2185},[2113,30155,30156],{"class":2119},"            mLocation ",[2113,30158,2335],{"class":2334},[2113,30160,4778],{"class":2326},[2113,30162,30163],{"class":2133}," Location",[2113,30165,30166],{"class":2119},"(provider);\n",[2113,30168,30169],{"class":2115,"line":2230},[2113,30170,4578],{"class":2119},[2113,30172,30173],{"class":2115,"line":2235},[2113,30174,2125],{"emptyLinePlaceholder":1767},[2113,30176,30177,30180],{"class":2115,"line":2241},[2113,30178,30179],{"class":2119},"        @",[2113,30181,30182],{"class":2414},"Override\n",[2113,30184,30185,30187,30189,30192,30194,30196,30199,30201],{"class":2115,"line":2246},[2113,30186,30120],{"class":2326},[2113,30188,29564],{"class":2326},[2113,30190,30191],{"class":2133}," onLocationChanged",[2113,30193,2423],{"class":2119},[2113,30195,29462],{"class":2414},[2113,30197,30198],{"class":2429}," location",[2113,30200,17832],{"class":2119},[2113,30202,2647],{"class":2119},[2113,30204,30205,30207,30209,30211,30213,30216,30218],{"class":2115,"line":2464},[2113,30206,29896],{"class":2414},[2113,30208,179],{"class":2119},[2113,30210,5601],{"class":2133},[2113,30212,29903],{"class":2119},[2113,30214,30215],{"class":2149},"\"onLocationChanged: \"",[2113,30217,29941],{"class":2334},[2113,30219,30220],{"class":2119}," location);\n",[2113,30222,30223,30225,30227,30230],{"class":2115,"line":2085},[2113,30224,5021],{"class":2326},[2113,30226,2495],{"class":2119},[2113,30228,30229],{"class":2133},"isBetterLocation",[2113,30231,30232],{"class":2119},"(location, mLocation)) {\n",[2113,30234,30235,30238,30240,30243],{"class":2115,"line":2514},[2113,30236,30237],{"class":2414},"                mLocation",[2113,30239,179],{"class":2119},[2113,30241,30242],{"class":2133},"set",[2113,30244,29649],{"class":2119},[2113,30246,30247],{"class":2115,"line":2533},[2113,30248,5172],{"class":2119},[2113,30250,30251],{"class":2115,"line":2556},[2113,30252,2125],{"emptyLinePlaceholder":1767},[2113,30254,30255,30258],{"class":2115,"line":2569},[2113,30256,30257],{"class":2133},"            callListener",[2113,30259,30260],{"class":2119},"(mLocation);\n",[2113,30262,30263],{"class":2115,"line":2575},[2113,30264,4578],{"class":2119},[2113,30266,30267],{"class":2115,"line":2596},[2113,30268,30269],{"class":2119},"        ...\n",[2113,30271,30272],{"class":2115,"line":3192},[2113,30273,4665],{"class":2119},[12,30275,30276],{},"Enfin on a une méthode pour calculer les distances avec Android.:",[2105,30278,30280],{"className":29285,"code":30279,"language":29287,"meta":1646,"style":1646},"    public static float getDistance(Location startLocation, Location lastLocation) {\n        float[] results = new float[1];\n        Location.distanceBetween(startLocation.getLatitude(), startLocation.getLongitude(), lastLocation.getLatitude(), lastLocation.getLongitude(), results);\n        float distance = results[0];\n        return distance;\n    }\n",[184,30281,30282,30306,30329,30378,30396,30405],{"__ignoreMap":1646},[2113,30283,30284,30286,30288,30290,30293,30295,30297,30299,30301,30303],{"class":2115,"line":2116},[2113,30285,29441],{"class":2326},[2113,30287,29311],{"class":2326},[2113,30289,29378],{"class":2326},[2113,30291,30292],{"class":2133}," getDistance",[2113,30294,2423],{"class":2330},[2113,30296,29462],{"class":2414},[2113,30298,29465],{"class":2330},[2113,30300,26987],{"class":2119},[2113,30302,30163],{"class":2414},[2113,30304,30305],{"class":2330}," lastLocation) {\n",[2113,30307,30308,30311,30314,30316,30318,30320,30322,30324,30327],{"class":2115,"line":1647},[2113,30309,30310],{"class":2326},"        float",[2113,30312,30313],{"class":2330},"[] results ",[2113,30315,2335],{"class":2334},[2113,30317,4778],{"class":2326},[2113,30319,29378],{"class":2326},[2113,30321,4124],{"class":2330},[2113,30323,3686],{"class":2166},[2113,30325,30326],{"class":2330},"]",[2113,30328,2487],{"class":2119},[2113,30330,30331,30334,30336,30339,30341,30344,30346,30349,30351,30353,30355,30358,30360,30363,30365,30367,30369,30371,30373,30375],{"class":2115,"line":1652},[2113,30332,30333],{"class":2414},"        Location",[2113,30335,179],{"class":2119},[2113,30337,30338],{"class":2133},"distanceBetween",[2113,30340,2423],{"class":2119},[2113,30342,30343],{"class":2414},"startLocation",[2113,30345,179],{"class":2119},[2113,30347,30348],{"class":2133},"getLatitude",[2113,30350,8110],{"class":2119},[2113,30352,30343],{"class":2414},[2113,30354,179],{"class":2119},[2113,30356,30357],{"class":2133},"getLongitude",[2113,30359,8110],{"class":2119},[2113,30361,30362],{"class":2414},"lastLocation",[2113,30364,179],{"class":2119},[2113,30366,30348],{"class":2133},[2113,30368,8110],{"class":2119},[2113,30370,30362],{"class":2414},[2113,30372,179],{"class":2119},[2113,30374,30357],{"class":2133},[2113,30376,30377],{"class":2119},"(), results);\n",[2113,30379,30380,30382,30385,30387,30390,30392,30394],{"class":2115,"line":2185},[2113,30381,30310],{"class":2326},[2113,30383,30384],{"class":2330}," distance ",[2113,30386,2335],{"class":2334},[2113,30388,30389],{"class":2330}," results[",[2113,30391,3095],{"class":2166},[2113,30393,30326],{"class":2330},[2113,30395,2487],{"class":2119},[2113,30397,30398,30400,30403],{"class":2115,"line":2230},[2113,30399,28968],{"class":2326},[2113,30401,30402],{"class":2330}," distance",[2113,30404,2487],{"class":2119},[2113,30406,30407],{"class":2115,"line":2235},[2113,30408,4665],{"class":2330},[133,30410,30412],{"id":30411},"mainactivity","MainActivity",[12,30414,30415],{},"Retournons dans notre activité principale. Je passe la création du layout qui est fort simple.",[12,30417,30418],{},"Dans l'activité, nous allons commencer par implémenter la phase de création du cycle de vie de notre activité:",[2105,30420,30422],{"className":29285,"code":30421,"language":29287,"meta":1646,"style":1646},"    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n",[184,30423,30424,30431,30448,30461],{"__ignoreMap":1646},[2113,30425,30426,30429],{"class":2115,"line":2116},[2113,30427,30428],{"class":2119},"    @",[2113,30430,30182],{"class":2414},[2113,30432,30433,30435,30437,30440,30442,30445],{"class":2115,"line":1647},[2113,30434,29441],{"class":2326},[2113,30436,29564],{"class":2326},[2113,30438,30439],{"class":2133}," onCreate",[2113,30441,2423],{"class":2330},[2113,30443,30444],{"class":2414},"Bundle",[2113,30446,30447],{"class":2330}," savedInstanceState) {\n",[2113,30449,30450,30453,30455,30458],{"class":2115,"line":1652},[2113,30451,30452],{"class":2414},"        super",[2113,30454,179],{"class":2119},[2113,30456,30457],{"class":2133},"onCreate",[2113,30459,30460],{"class":2119},"(savedInstanceState);\n",[2113,30462,30463,30466,30468,30470,30472,30475,30477,30480,30482],{"class":2115,"line":2185},[2113,30464,30465],{"class":2133},"        setContentView",[2113,30467,2423],{"class":2330},[2113,30469,4426],{"class":2414},[2113,30471,179],{"class":2119},[2113,30473,30474],{"class":2414},"layout",[2113,30476,179],{"class":2119},[2113,30478,30479],{"class":2414},"activity_main",[2113,30481,17832],{"class":2330},[2113,30483,2487],{"class":2119},[12,30485,30486,30487,179],{},"Depuis Android 6.0, il faut demander à l'utilisateur la permission d'utiliser la position de l'utilisateur. Du coup\non commence par demander la permission à l'utilisateur d'avoir accès à sa position",[17400,30488,30489],{},[49,30490,2547],{"href":17977,"ariaDescribedBy":30491,"dataFootnoteRef":1646,"id":17979},[17406],[2105,30493,30495],{"className":29285,"code":30494,"language":29287,"meta":1646,"style":1646},"        requestPermissionsIfNecessary(new String[]{\n                Manifest.permission.ACCESS_FINE_LOCATION,\n        });\n",[184,30496,30497,30510,30527],{"__ignoreMap":1646},[2113,30498,30499,30502,30504,30506,30508],{"class":2115,"line":2116},[2113,30500,30501],{"class":2133},"        requestPermissionsIfNecessary",[2113,30503,2423],{"class":2330},[2113,30505,3961],{"class":2326},[2113,30507,29317],{"class":2414},[2113,30509,29502],{"class":2330},[2113,30511,30512,30515,30517,30520,30522,30525],{"class":2115,"line":1647},[2113,30513,30514],{"class":2414},"                Manifest",[2113,30516,179],{"class":2119},[2113,30518,30519],{"class":2414},"permission",[2113,30521,179],{"class":2119},[2113,30523,30524],{"class":2414},"ACCESS_FINE_LOCATION",[2113,30526,4706],{"class":2119},[2113,30528,30529,30532],{"class":2115,"line":1652},[2113,30530,30531],{"class":2330},"        })",[2113,30533,2487],{"class":2119},[12,30535,30536],{},"Ensuite on appelle notre location manager et on écoute les changements de positions. Pour chaque changement de position\non met à jour la position et on met à jour les textes.",[2105,30538,30540],{"className":29285,"code":30539,"language":29287,"meta":1646,"style":1646},"        locationManager = new ArroundLocationManager(this);\n        locationManager.addListener((location) -> {\n            if (!isMyServiceRunning(ArroundService.class)) {\n                startLocation = location;\n            }\n            MainActivity.this.updateTextLocation();\n        });\n        locationManager.start();\n",[184,30541,30542,30561,30578,30600,30610,30614,30630,30634],{"__ignoreMap":1646},[2113,30543,30544,30547,30549,30551,30553,30555,30557,30559],{"class":2115,"line":2116},[2113,30545,30546],{"class":2330},"        locationManager ",[2113,30548,2335],{"class":2334},[2113,30550,4778],{"class":2326},[2113,30552,29296],{"class":2133},[2113,30554,2423],{"class":2330},[2113,30556,15880],{"class":2414},[2113,30558,17832],{"class":2330},[2113,30560,2487],{"class":2119},[2113,30562,30563,30566,30568,30571,30574,30576],{"class":2115,"line":1647},[2113,30564,30565],{"class":2414},"        locationManager",[2113,30567,179],{"class":2119},[2113,30569,30570],{"class":2133},"addListener",[2113,30572,30573],{"class":2119},"((location) ",[2113,30575,4115],{"class":2326},[2113,30577,2647],{"class":2119},[2113,30579,30580,30582,30584,30586,30589,30591,30594,30596,30598],{"class":2115,"line":1652},[2113,30581,5021],{"class":2326},[2113,30583,2495],{"class":2119},[2113,30585,3199],{"class":2334},[2113,30587,30588],{"class":2133},"isMyServiceRunning",[2113,30590,2423],{"class":2119},[2113,30592,30593],{"class":2414},"ArroundService",[2113,30595,179],{"class":2119},[2113,30597,20538],{"class":2414},[2113,30599,23034],{"class":2119},[2113,30601,30602,30605,30607],{"class":2115,"line":2185},[2113,30603,30604],{"class":2119},"                startLocation ",[2113,30606,2335],{"class":2334},[2113,30608,30609],{"class":2119}," location;\n",[2113,30611,30612],{"class":2115,"line":2230},[2113,30613,5172],{"class":2119},[2113,30615,30616,30619,30621,30623,30625,30628],{"class":2115,"line":2235},[2113,30617,30618],{"class":2414},"            MainActivity",[2113,30620,179],{"class":2119},[2113,30622,15880],{"class":2414},[2113,30624,179],{"class":2119},[2113,30626,30627],{"class":2133},"updateTextLocation",[2113,30629,3944],{"class":2119},[2113,30631,30632],{"class":2115,"line":2241},[2113,30633,10775],{"class":2119},[2113,30635,30636,30638,30640,30643],{"class":2115,"line":2246},[2113,30637,30565],{"class":2414},[2113,30639,179],{"class":2119},[2113,30641,30642],{"class":2133},"start",[2113,30644,3944],{"class":2119},[12,30646,30647],{},"Enfin on initialise l'IHM.",[2105,30649,30651],{"className":29285,"code":30650,"language":29287,"meta":1646,"style":1646},"        distanceText = findViewById(R.id.distance);\n        startText = findViewById(R.id.start);\n        locationText = findViewById(R.id.location);\n\n        goButton = findViewById(R.id.goButton);\n        stopButton = findViewById(R.id.stopButton);\n        goButton.setOnClickListener(v -> {\n            setStart(true);\n        });\n        stopButton.setOnClickListener(v -> {\n            setStart(false);\n        });\n        setStart(isMyServiceRunning(ArroundService.class));\n        MainActivity.this.updateTextLocation();\n    }\n",[184,30652,30653,30680,30705,30731,30735,30761,30787,30804,30815,30819,30834,30844,30848,30870,30885],{"__ignoreMap":1646},[2113,30654,30655,30658,30660,30663,30665,30667,30669,30671,30673,30676,30678],{"class":2115,"line":2116},[2113,30656,30657],{"class":2330},"        distanceText ",[2113,30659,2335],{"class":2334},[2113,30661,30662],{"class":2133}," findViewById",[2113,30664,2423],{"class":2330},[2113,30666,4426],{"class":2414},[2113,30668,179],{"class":2119},[2113,30670,20628],{"class":2414},[2113,30672,179],{"class":2119},[2113,30674,30675],{"class":2414},"distance",[2113,30677,17832],{"class":2330},[2113,30679,2487],{"class":2119},[2113,30681,30682,30685,30687,30689,30691,30693,30695,30697,30699,30701,30703],{"class":2115,"line":1647},[2113,30683,30684],{"class":2330},"        startText ",[2113,30686,2335],{"class":2334},[2113,30688,30662],{"class":2133},[2113,30690,2423],{"class":2330},[2113,30692,4426],{"class":2414},[2113,30694,179],{"class":2119},[2113,30696,20628],{"class":2414},[2113,30698,179],{"class":2119},[2113,30700,30642],{"class":2414},[2113,30702,17832],{"class":2330},[2113,30704,2487],{"class":2119},[2113,30706,30707,30710,30712,30714,30716,30718,30720,30722,30724,30727,30729],{"class":2115,"line":1652},[2113,30708,30709],{"class":2330},"        locationText ",[2113,30711,2335],{"class":2334},[2113,30713,30662],{"class":2133},[2113,30715,2423],{"class":2330},[2113,30717,4426],{"class":2414},[2113,30719,179],{"class":2119},[2113,30721,20628],{"class":2414},[2113,30723,179],{"class":2119},[2113,30725,30726],{"class":2414},"location",[2113,30728,17832],{"class":2330},[2113,30730,2487],{"class":2119},[2113,30732,30733],{"class":2115,"line":2185},[2113,30734,2125],{"emptyLinePlaceholder":1767},[2113,30736,30737,30740,30742,30744,30746,30748,30750,30752,30754,30757,30759],{"class":2115,"line":2230},[2113,30738,30739],{"class":2330},"        goButton ",[2113,30741,2335],{"class":2334},[2113,30743,30662],{"class":2133},[2113,30745,2423],{"class":2330},[2113,30747,4426],{"class":2414},[2113,30749,179],{"class":2119},[2113,30751,20628],{"class":2414},[2113,30753,179],{"class":2119},[2113,30755,30756],{"class":2414},"goButton",[2113,30758,17832],{"class":2330},[2113,30760,2487],{"class":2119},[2113,30762,30763,30766,30768,30770,30772,30774,30776,30778,30780,30783,30785],{"class":2115,"line":2235},[2113,30764,30765],{"class":2330},"        stopButton ",[2113,30767,2335],{"class":2334},[2113,30769,30662],{"class":2133},[2113,30771,2423],{"class":2330},[2113,30773,4426],{"class":2414},[2113,30775,179],{"class":2119},[2113,30777,20628],{"class":2414},[2113,30779,179],{"class":2119},[2113,30781,30782],{"class":2414},"stopButton",[2113,30784,17832],{"class":2330},[2113,30786,2487],{"class":2119},[2113,30788,30789,30792,30794,30797,30800,30802],{"class":2115,"line":2241},[2113,30790,30791],{"class":2414},"        goButton",[2113,30793,179],{"class":2119},[2113,30795,30796],{"class":2133},"setOnClickListener",[2113,30798,30799],{"class":2119},"(v ",[2113,30801,4115],{"class":2326},[2113,30803,2647],{"class":2119},[2113,30805,30806,30809,30811,30813],{"class":2115,"line":2246},[2113,30807,30808],{"class":2133},"            setStart",[2113,30810,2423],{"class":2119},[2113,30812,4819],{"class":2166},[2113,30814,2553],{"class":2119},[2113,30816,30817],{"class":2115,"line":2464},[2113,30818,10775],{"class":2119},[2113,30820,30821,30824,30826,30828,30830,30832],{"class":2115,"line":2085},[2113,30822,30823],{"class":2414},"        stopButton",[2113,30825,179],{"class":2119},[2113,30827,30796],{"class":2133},[2113,30829,30799],{"class":2119},[2113,30831,4115],{"class":2326},[2113,30833,2647],{"class":2119},[2113,30835,30836,30838,30840,30842],{"class":2115,"line":2514},[2113,30837,30808],{"class":2133},[2113,30839,2423],{"class":2119},[2113,30841,18557],{"class":2166},[2113,30843,2553],{"class":2119},[2113,30845,30846],{"class":2115,"line":2533},[2113,30847,10775],{"class":2119},[2113,30849,30850,30853,30855,30857,30859,30861,30863,30865,30868],{"class":2115,"line":2556},[2113,30851,30852],{"class":2133},"        setStart",[2113,30854,2423],{"class":2330},[2113,30856,30588],{"class":2133},[2113,30858,2423],{"class":2330},[2113,30860,30593],{"class":2414},[2113,30862,179],{"class":2119},[2113,30864,20538],{"class":2414},[2113,30866,30867],{"class":2330},"))",[2113,30869,2487],{"class":2119},[2113,30871,30872,30875,30877,30879,30881,30883],{"class":2115,"line":2569},[2113,30873,30874],{"class":2414},"        MainActivity",[2113,30876,179],{"class":2119},[2113,30878,15880],{"class":2414},[2113,30880,179],{"class":2119},[2113,30882,30627],{"class":2133},[2113,30884,3944],{"class":2119},[2113,30886,30887],{"class":2115,"line":2575},[2113,30888,4665],{"class":2330},[12,30890,30891],{},"Comme le LocationManager est un thread, nous devons faire attention à repasser dans le thread de l'UI afin de mettre\nà jour les labels :",[2105,30893,30895],{"className":29285,"code":30894,"language":29287,"meta":1646,"style":1646},"    public void updateTextLocation() {\n        runOnUiThread(() -> {\n            if (this.startLocation != null && this.runLocation != null) {\n                distanceText.setText(getString(R.string.distanceLabel, (int) ArroundLocationManager.getDistance(startLocation, runLocation)));\n            } else {\n                distanceText.setText(\"\");\n            }\n            if (this.startLocation != null) {\n                startText.setText(getAddress(startLocation));\n            } else {\n                startText.setText(\"\");\n            }\n            if (this.runLocation != null) {\n                locationText.setText(getAddress(runLocation));\n            } else {\n                locationText.setText(\"\");\n            }\n        });\n    }\n",[184,30896,30897,30908,30920,30951,30997,31005,31020,31024,31042,31059,31067,31081,31085,31103,31119,31127,31141,31145,31151],{"__ignoreMap":1646},[2113,30898,30899,30901,30903,30906],{"class":2115,"line":2116},[2113,30900,29441],{"class":2326},[2113,30902,29564],{"class":2326},[2113,30904,30905],{"class":2133}," updateTextLocation",[2113,30907,3912],{"class":2330},[2113,30909,30910,30913,30916,30918],{"class":2115,"line":1647},[2113,30911,30912],{"class":2133},"        runOnUiThread",[2113,30914,30915],{"class":2330},"(() ",[2113,30917,4115],{"class":2326},[2113,30919,2647],{"class":2330},[2113,30921,30922,30924,30926,30928,30930,30932,30934,30936,30938,30940,30942,30945,30947,30949],{"class":2115,"line":1652},[2113,30923,5021],{"class":2326},[2113,30925,2495],{"class":2330},[2113,30927,15880],{"class":2414},[2113,30929,179],{"class":2119},[2113,30931,30343],{"class":2414},[2113,30933,5638],{"class":2334},[2113,30935,20727],{"class":2166},[2113,30937,2506],{"class":2334},[2113,30939,15832],{"class":2414},[2113,30941,179],{"class":2119},[2113,30943,30944],{"class":2414},"runLocation",[2113,30946,5638],{"class":2334},[2113,30948,20727],{"class":2166},[2113,30950,2433],{"class":2330},[2113,30952,30953,30956,30958,30961,30963,30966,30968,30970,30972,30974,30976,30979,30982,30985,30987,30989,30991,30994],{"class":2115,"line":2185},[2113,30954,30955],{"class":2414},"                distanceText",[2113,30957,179],{"class":2119},[2113,30959,30960],{"class":2133},"setText",[2113,30962,2423],{"class":2119},[2113,30964,30965],{"class":2133},"getString",[2113,30967,2423],{"class":2119},[2113,30969,4426],{"class":2414},[2113,30971,179],{"class":2119},[2113,30973,19225],{"class":2414},[2113,30975,179],{"class":2119},[2113,30977,30978],{"class":2414},"distanceLabel",[2113,30980,30981],{"class":2119},", (",[2113,30983,30984],{"class":2326},"int",[2113,30986,5709],{"class":2119},[2113,30988,29277],{"class":2414},[2113,30990,179],{"class":2119},[2113,30992,30993],{"class":2133},"getDistance",[2113,30995,30996],{"class":2119},"(startLocation, runLocation)));\n",[2113,30998,30999,31001,31003],{"class":2115,"line":2230},[2113,31000,23062],{"class":2330},[2113,31002,4227],{"class":2326},[2113,31004,2647],{"class":2330},[2113,31006,31007,31009,31011,31013,31015,31018],{"class":2115,"line":2235},[2113,31008,30955],{"class":2414},[2113,31010,179],{"class":2119},[2113,31012,30960],{"class":2133},[2113,31014,2423],{"class":2119},[2113,31016,31017],{"class":2149},"\"\"",[2113,31019,2553],{"class":2119},[2113,31021,31022],{"class":2115,"line":2241},[2113,31023,5172],{"class":2330},[2113,31025,31026,31028,31030,31032,31034,31036,31038,31040],{"class":2115,"line":2246},[2113,31027,5021],{"class":2326},[2113,31029,2495],{"class":2330},[2113,31031,15880],{"class":2414},[2113,31033,179],{"class":2119},[2113,31035,30343],{"class":2414},[2113,31037,5638],{"class":2334},[2113,31039,20727],{"class":2166},[2113,31041,2433],{"class":2330},[2113,31043,31044,31047,31049,31051,31053,31056],{"class":2115,"line":2464},[2113,31045,31046],{"class":2414},"                startText",[2113,31048,179],{"class":2119},[2113,31050,30960],{"class":2133},[2113,31052,2423],{"class":2119},[2113,31054,31055],{"class":2133},"getAddress",[2113,31057,31058],{"class":2119},"(startLocation));\n",[2113,31060,31061,31063,31065],{"class":2115,"line":2085},[2113,31062,23062],{"class":2330},[2113,31064,4227],{"class":2326},[2113,31066,2647],{"class":2330},[2113,31068,31069,31071,31073,31075,31077,31079],{"class":2115,"line":2514},[2113,31070,31046],{"class":2414},[2113,31072,179],{"class":2119},[2113,31074,30960],{"class":2133},[2113,31076,2423],{"class":2119},[2113,31078,31017],{"class":2149},[2113,31080,2553],{"class":2119},[2113,31082,31083],{"class":2115,"line":2533},[2113,31084,5172],{"class":2330},[2113,31086,31087,31089,31091,31093,31095,31097,31099,31101],{"class":2115,"line":2556},[2113,31088,5021],{"class":2326},[2113,31090,2495],{"class":2330},[2113,31092,15880],{"class":2414},[2113,31094,179],{"class":2119},[2113,31096,30944],{"class":2414},[2113,31098,5638],{"class":2334},[2113,31100,20727],{"class":2166},[2113,31102,2433],{"class":2330},[2113,31104,31105,31108,31110,31112,31114,31116],{"class":2115,"line":2569},[2113,31106,31107],{"class":2414},"                locationText",[2113,31109,179],{"class":2119},[2113,31111,30960],{"class":2133},[2113,31113,2423],{"class":2119},[2113,31115,31055],{"class":2133},[2113,31117,31118],{"class":2119},"(runLocation));\n",[2113,31120,31121,31123,31125],{"class":2115,"line":2575},[2113,31122,23062],{"class":2330},[2113,31124,4227],{"class":2326},[2113,31126,2647],{"class":2330},[2113,31128,31129,31131,31133,31135,31137,31139],{"class":2115,"line":2596},[2113,31130,31107],{"class":2414},[2113,31132,179],{"class":2119},[2113,31134,30960],{"class":2133},[2113,31136,2423],{"class":2119},[2113,31138,31017],{"class":2149},[2113,31140,2553],{"class":2119},[2113,31142,31143],{"class":2115,"line":3192},[2113,31144,5172],{"class":2330},[2113,31146,31147,31149],{"class":2115,"line":3213},[2113,31148,30531],{"class":2330},[2113,31150,2487],{"class":2119},[2113,31152,31153],{"class":2115,"line":3236},[2113,31154,4665],{"class":2330},[12,31156,31157],{},"Pour ma part je ne connais pas la position GPS de ma maison par coeur, ni de là ou je me trouve. Cela tombe bien. Android\npropose une API pour geocoder une adresse. C'est à dire que l'on transforme une position en latitude, longitude en adresse\nlisible:",[2105,31159,31161],{"className":29285,"code":31160,"language":29287,"meta":1646,"style":1646},"    String getAddress(Location location) {\n        Geocoder geocoder;\n        List\u003CAddress> addresses;\n\n        try {\n            geocoder = new Geocoder(this, Locale.getDefault());\n\n            addresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1);\n\n            if (addresses.size() > 0 && addresses.get(0).getMaxAddressLineIndex() >= 0) {\n                return addresses.get(0).getAddressLine(0);\n            }\n            return \"Unknown\";\n        } catch (IOException e) {\n            return e.getMessage();\n        }\n    }\n",[184,31162,31163,31177,31187,31204,31208,31214,31246,31250,31286,31290,31334,31359,31363,31372,31388,31400,31404],{"__ignoreMap":1646},[2113,31164,31165,31168,31171,31173,31175],{"class":2115,"line":2116},[2113,31166,31167],{"class":2414},"    String",[2113,31169,31170],{"class":2133}," getAddress",[2113,31172,2423],{"class":2330},[2113,31174,29462],{"class":2414},[2113,31176,29615],{"class":2330},[2113,31178,31179,31182,31185],{"class":2115,"line":1647},[2113,31180,31181],{"class":2414},"        Geocoder",[2113,31183,31184],{"class":2330}," geocoder",[2113,31186,2487],{"class":2119},[2113,31188,31189,31192,31194,31197,31199,31202],{"class":2115,"line":1652},[2113,31190,31191],{"class":2414},"        List",[2113,31193,3109],{"class":2119},[2113,31195,31196],{"class":2414},"Address",[2113,31198,29415],{"class":2119},[2113,31200,31201],{"class":2330}," addresses",[2113,31203,2487],{"class":2119},[2113,31205,31206],{"class":2115,"line":2185},[2113,31207,2125],{"emptyLinePlaceholder":1767},[2113,31209,31210,31212],{"class":2115,"line":2230},[2113,31211,29839],{"class":2326},[2113,31213,2647],{"class":2330},[2113,31215,31216,31219,31221,31223,31226,31228,31230,31232,31235,31237,31240,31242,31244],{"class":2115,"line":2235},[2113,31217,31218],{"class":2330},"            geocoder ",[2113,31220,2335],{"class":2334},[2113,31222,4778],{"class":2326},[2113,31224,31225],{"class":2133}," Geocoder",[2113,31227,2423],{"class":2330},[2113,31229,15880],{"class":2414},[2113,31231,26987],{"class":2119},[2113,31233,31234],{"class":2414}," Locale",[2113,31236,179],{"class":2119},[2113,31238,31239],{"class":2133},"getDefault",[2113,31241,17772],{"class":2119},[2113,31243,17832],{"class":2330},[2113,31245,2487],{"class":2119},[2113,31247,31248],{"class":2115,"line":2241},[2113,31249,2125],{"emptyLinePlaceholder":1767},[2113,31251,31252,31255,31257,31259,31261,31264,31266,31268,31270,31272,31274,31276,31278,31280,31282,31284],{"class":2115,"line":2246},[2113,31253,31254],{"class":2330},"            addresses ",[2113,31256,2335],{"class":2334},[2113,31258,31184],{"class":2414},[2113,31260,179],{"class":2119},[2113,31262,31263],{"class":2133},"getFromLocation",[2113,31265,2423],{"class":2119},[2113,31267,30726],{"class":2414},[2113,31269,179],{"class":2119},[2113,31271,30348],{"class":2133},[2113,31273,8110],{"class":2119},[2113,31275,30726],{"class":2414},[2113,31277,179],{"class":2119},[2113,31279,30357],{"class":2133},[2113,31281,8110],{"class":2119},[2113,31283,3686],{"class":2166},[2113,31285,2553],{"class":2119},[2113,31287,31288],{"class":2115,"line":2464},[2113,31289,2125],{"emptyLinePlaceholder":1767},[2113,31291,31292,31294,31296,31299,31301,31303,31305,31307,31309,31311,31313,31315,31317,31319,31321,31323,31326,31328,31330,31332],{"class":2115,"line":2085},[2113,31293,5021],{"class":2326},[2113,31295,2495],{"class":2330},[2113,31297,31298],{"class":2414},"addresses",[2113,31300,179],{"class":2119},[2113,31302,7873],{"class":2133},[2113,31304,17772],{"class":2119},[2113,31306,5275],{"class":2334},[2113,31308,3208],{"class":2166},[2113,31310,2506],{"class":2334},[2113,31312,31201],{"class":2414},[2113,31314,179],{"class":2119},[2113,31316,19283],{"class":2133},[2113,31318,2423],{"class":2119},[2113,31320,3095],{"class":2166},[2113,31322,3938],{"class":2119},[2113,31324,31325],{"class":2133},"getMaxAddressLineIndex",[2113,31327,17772],{"class":2119},[2113,31329,6256],{"class":2334},[2113,31331,3208],{"class":2166},[2113,31333,2433],{"class":2330},[2113,31335,31336,31338,31340,31342,31344,31346,31348,31350,31353,31355,31357],{"class":2115,"line":2514},[2113,31337,5608],{"class":2326},[2113,31339,31201],{"class":2414},[2113,31341,179],{"class":2119},[2113,31343,19283],{"class":2133},[2113,31345,2423],{"class":2119},[2113,31347,3095],{"class":2166},[2113,31349,3938],{"class":2119},[2113,31351,31352],{"class":2133},"getAddressLine",[2113,31354,2423],{"class":2119},[2113,31356,3095],{"class":2166},[2113,31358,2553],{"class":2119},[2113,31360,31361],{"class":2115,"line":2533},[2113,31362,5172],{"class":2330},[2113,31364,31365,31367,31370],{"class":2115,"line":2556},[2113,31366,4559],{"class":2326},[2113,31368,31369],{"class":2149}," \"Unknown\"",[2113,31371,2487],{"class":2119},[2113,31373,31374,31376,31378,31380,31383,31386],{"class":2115,"line":2569},[2113,31375,29879],{"class":2330},[2113,31377,8973],{"class":2326},[2113,31379,2495],{"class":2330},[2113,31381,31382],{"class":2414},"IOException",[2113,31384,31385],{"class":2429}," e",[2113,31387,2433],{"class":2330},[2113,31389,31390,31392,31394,31396,31398],{"class":2115,"line":2575},[2113,31391,4559],{"class":2326},[2113,31393,31385],{"class":2414},[2113,31395,179],{"class":2119},[2113,31397,29948],{"class":2133},[2113,31399,3944],{"class":2119},[2113,31401,31402],{"class":2115,"line":2596},[2113,31403,4578],{"class":2330},[2113,31405,31406],{"class":2115,"line":3192},[2113,31407,4665],{"class":2330},[12,31409,31410,31411,31413,31414,31416,31417],{},"@Override\npublic void onRequestPermissionsResult(int requestCode, String",[2113,31412],{}," permissions, int",[2113,31415],{}," grantResults) {\nArrayList",[19225,31418,31419,31420,31422,31423,31425],{}," permissionsToRequest = new ArrayList\u003C>();\nfor (int i = 0; i \u003C grantResults.length; i++) {\npermissionsToRequest.add(permissions",[2113,31421,7919],{},");\n}\nif (permissionsToRequest.size() > 0) {\nActivityCompat.requestPermissions(this, permissionsToRequest.toArray(new String",[2113,31424,3095],{},"), REQUEST_PERMISSIONS_REQUEST_CODE);\n}\n}",[12,31427,31428,31429,31431,31432],{},"private void requestPermissionsIfNecessary(String",[2113,31430],{}," permissions) {\nArrayList",[19225,31433,31434,31435,31425],{}," permissionsToRequest = new ArrayList\u003C>();\nfor (String permission : permissions) {\nif (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {\n\u002F\u002F Permission is not granted\npermissionsToRequest.add(permission);\n}\n}\nif (permissionsToRequest.size() > 0) {\nActivityCompat.requestPermissions(this, permissionsToRequest.toArray(new String",[2113,31436,3095],{},[128,31438,31440],{"id":31439},"le-service","Le service",[12,31442,31443,31444,31447],{},"Le service est démarré par l'application et doit ensuite survivre à la fermeture de l'application. Pour cela nous\nallons créer un ",[362,31445,31446],{},"foreground service"," qui, contrairement aux services en tâche de fond qui sont déclenchés sur un évènement avec\nune durée de vie relativement courte, va tourner au premier plan en affichant une notification.",[12,31449,31450,31451,15746],{},"Pour démarrer le service depuis l'activité principale nous avons ajouté la méthode ",[184,31452,31453],{},"startServer",[2105,31455,31457],{"className":29285,"code":31456,"language":29287,"meta":1646,"style":1646},"    private void startServer() {\n        if (!mBounded) {\n            Intent mIntent = new Intent(this, ArroundService.class);\n            mIntent.putExtra(\"startLocation\", startLocation);\n            ContextCompat.startForegroundService(this, mIntent);\n            bindService(mIntent, mConnection, BIND_AUTO_CREATE);\n        }\n    }\n",[184,31458,31459,31470,31481,31513,31531,31548,31568,31572],{"__ignoreMap":1646},[2113,31460,31461,31463,31465,31468],{"class":2115,"line":2116},[2113,31462,21028],{"class":2326},[2113,31464,29564],{"class":2326},[2113,31466,31467],{"class":2133}," startServer",[2113,31469,3912],{"class":2330},[2113,31471,31472,31474,31476,31478],{"class":2115,"line":1647},[2113,31473,4518],{"class":2326},[2113,31475,2495],{"class":2330},[2113,31477,3199],{"class":2334},[2113,31479,31480],{"class":2330},"mBounded) {\n",[2113,31482,31483,31486,31489,31491,31493,31496,31498,31500,31502,31505,31507,31509,31511],{"class":2115,"line":1652},[2113,31484,31485],{"class":2414},"            Intent",[2113,31487,31488],{"class":2330}," mIntent ",[2113,31490,2335],{"class":2334},[2113,31492,4778],{"class":2326},[2113,31494,31495],{"class":2133}," Intent",[2113,31497,2423],{"class":2330},[2113,31499,15880],{"class":2414},[2113,31501,26987],{"class":2119},[2113,31503,31504],{"class":2414}," ArroundService",[2113,31506,179],{"class":2119},[2113,31508,20538],{"class":2414},[2113,31510,17832],{"class":2330},[2113,31512,2487],{"class":2119},[2113,31514,31515,31518,31520,31523,31525,31528],{"class":2115,"line":2185},[2113,31516,31517],{"class":2414},"            mIntent",[2113,31519,179],{"class":2119},[2113,31521,31522],{"class":2133},"putExtra",[2113,31524,2423],{"class":2119},[2113,31526,31527],{"class":2149},"\"startLocation\"",[2113,31529,31530],{"class":2119},", startLocation);\n",[2113,31532,31533,31536,31538,31541,31543,31545],{"class":2115,"line":2230},[2113,31534,31535],{"class":2414},"            ContextCompat",[2113,31537,179],{"class":2119},[2113,31539,31540],{"class":2133},"startForegroundService",[2113,31542,2423],{"class":2119},[2113,31544,15880],{"class":2414},[2113,31546,31547],{"class":2119},", mIntent);\n",[2113,31549,31550,31553,31556,31558,31561,31563,31566],{"class":2115,"line":2235},[2113,31551,31552],{"class":2133},"            bindService",[2113,31554,31555],{"class":2330},"(mIntent",[2113,31557,26987],{"class":2119},[2113,31559,31560],{"class":2330}," mConnection",[2113,31562,26987],{"class":2119},[2113,31564,31565],{"class":2330}," BIND_AUTO_CREATE)",[2113,31567,2487],{"class":2119},[2113,31569,31570],{"class":2115,"line":2241},[2113,31571,4578],{"class":2330},[2113,31573,31574],{"class":2115,"line":2246},[2113,31575,4665],{"class":2330},[12,31577,31578,31579,31581,31582,31585,31586,31589],{},"Ce qui est important c'est la méthode ",[184,31580,31540],{}," dont le but est de démarrer le service en mode ",[362,31583,31584],{},"Foreground",".\nCette méthode a son pendant dans le service qui est ",[184,31587,31588],{},"startForeground",". Si cette dernière n'est pas appelée dans le service\nune erreur sera remontée par Android.",[12,31591,31592],{},"Du coup on implémente le service et on commence par l'initialisation :",[2105,31594,31596],{"className":29285,"code":31595,"language":29287,"meta":1646,"style":1646},"    @Override\n    public void onCreate() {\n        super.onCreate();\n        notificationManager = new ArroundNotificationManager(this);\n        locationManager = new ArroundLocationManager(this);\n        locationManager.addListener(location -> {\n            runLocation = location;\n            callListener(location);\n            notificationManager.send(getDistance());\n            speakDistance();\n        });\n        locationManager.start();\n        textToSpeech = new TextToSpeech(this, this);\n    }\n",[184,31597,31598,31604,31614,31624,31644,31662,31677,31686,31692,31708,31715,31719,31729,31753],{"__ignoreMap":1646},[2113,31599,31600,31602],{"class":2115,"line":2116},[2113,31601,30428],{"class":2119},[2113,31603,30182],{"class":2414},[2113,31605,31606,31608,31610,31612],{"class":2115,"line":1647},[2113,31607,29441],{"class":2326},[2113,31609,29564],{"class":2326},[2113,31611,30439],{"class":2133},[2113,31613,3912],{"class":2330},[2113,31615,31616,31618,31620,31622],{"class":2115,"line":1652},[2113,31617,30452],{"class":2414},[2113,31619,179],{"class":2119},[2113,31621,30457],{"class":2133},[2113,31623,3944],{"class":2119},[2113,31625,31626,31629,31631,31633,31636,31638,31640,31642],{"class":2115,"line":2185},[2113,31627,31628],{"class":2330},"        notificationManager ",[2113,31630,2335],{"class":2334},[2113,31632,4778],{"class":2326},[2113,31634,31635],{"class":2133}," ArroundNotificationManager",[2113,31637,2423],{"class":2330},[2113,31639,15880],{"class":2414},[2113,31641,17832],{"class":2330},[2113,31643,2487],{"class":2119},[2113,31645,31646,31648,31650,31652,31654,31656,31658,31660],{"class":2115,"line":2230},[2113,31647,30546],{"class":2330},[2113,31649,2335],{"class":2334},[2113,31651,4778],{"class":2326},[2113,31653,29296],{"class":2133},[2113,31655,2423],{"class":2330},[2113,31657,15880],{"class":2414},[2113,31659,17832],{"class":2330},[2113,31661,2487],{"class":2119},[2113,31663,31664,31666,31668,31670,31673,31675],{"class":2115,"line":2235},[2113,31665,30565],{"class":2414},[2113,31667,179],{"class":2119},[2113,31669,30570],{"class":2133},[2113,31671,31672],{"class":2119},"(location ",[2113,31674,4115],{"class":2326},[2113,31676,2647],{"class":2119},[2113,31678,31679,31682,31684],{"class":2115,"line":2241},[2113,31680,31681],{"class":2119},"            runLocation ",[2113,31683,2335],{"class":2334},[2113,31685,30609],{"class":2119},[2113,31687,31688,31690],{"class":2115,"line":2246},[2113,31689,30257],{"class":2133},[2113,31691,29649],{"class":2119},[2113,31693,31694,31697,31699,31702,31704,31706],{"class":2115,"line":2464},[2113,31695,31696],{"class":2414},"            notificationManager",[2113,31698,179],{"class":2119},[2113,31700,31701],{"class":2133},"send",[2113,31703,2423],{"class":2119},[2113,31705,30993],{"class":2133},[2113,31707,2593],{"class":2119},[2113,31709,31710,31713],{"class":2115,"line":2085},[2113,31711,31712],{"class":2133},"            speakDistance",[2113,31714,3944],{"class":2119},[2113,31716,31717],{"class":2115,"line":2514},[2113,31718,10775],{"class":2119},[2113,31720,31721,31723,31725,31727],{"class":2115,"line":2533},[2113,31722,30565],{"class":2414},[2113,31724,179],{"class":2119},[2113,31726,30642],{"class":2133},[2113,31728,3944],{"class":2119},[2113,31730,31731,31734,31736,31738,31741,31743,31745,31747,31749,31751],{"class":2115,"line":2556},[2113,31732,31733],{"class":2330},"        textToSpeech ",[2113,31735,2335],{"class":2334},[2113,31737,4778],{"class":2326},[2113,31739,31740],{"class":2133}," TextToSpeech",[2113,31742,2423],{"class":2330},[2113,31744,15880],{"class":2414},[2113,31746,26987],{"class":2119},[2113,31748,15832],{"class":2414},[2113,31750,17832],{"class":2330},[2113,31752,2487],{"class":2119},[2113,31754,31755],{"class":2115,"line":2569},[2113,31756,4665],{"class":2330},[12,31758,31759],{},"On démarre le notification manager qui a pour but de notifier l'utilisateur de l'existence d'un service qui tourne au\n1er plan. La notification est d'ailleurs nécessaire pour un service foreground.",[12,31761,31762,31763,31765],{},"On écoute aussi notre ",[184,31764,29277],{}," qui lors des modifications s'occupe de mettre à jour la nouvelle position\net communique le changement de distance à l'utilisateur par voix et par notification.",[12,31767,31768,31769,31772],{},"On retrouve un ",[184,31770,31771],{},"callListener"," pour avertir l'utilisateur sur l'activité principale quand cette dernière est démarrée.",[12,31774,31775],{},"Ensuite le service démarre:",[2105,31777,31779],{"className":29285,"code":31778,"language":29287,"meta":1646,"style":1646},"    @Override\n    public int onStartCommand(Intent intent, int flags, int startId) {\n        Log.e(TAG, \"onStartCommand\");\n\n        PowerManager powerService = (PowerManager) getSystemService(Context.POWER_SERVICE);\n        wakeLock = powerService.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, \"ArroundService::lock\");\n        wakeLock.acquire();\n\n        Bundle extras = intent.getExtras();\n        Location location = (Location) extras.get(\"startLocation\");\n        if (location != null) {\n            this.startLocation = location;\n        }\n\n        Notification notification = notificationManager.notifyDistance(0);\n        startForeground(ArroundNotificationManager.ARROUND_ID, notification);\n\n        return START_STICKY;\n    }\n",[184,31780,31781,31787,31818,31834,31838,31868,31900,31912,31916,31935,31960,31974,31989,31993,31997,32021,32043,32047,32056],{"__ignoreMap":1646},[2113,31782,31783,31785],{"class":2115,"line":2116},[2113,31784,30428],{"class":2119},[2113,31786,30182],{"class":2414},[2113,31788,31789,31791,31793,31796,31798,31801,31804,31806,31808,31811,31813,31815],{"class":2115,"line":1647},[2113,31790,29441],{"class":2326},[2113,31792,2842],{"class":2326},[2113,31794,31795],{"class":2133}," onStartCommand",[2113,31797,2423],{"class":2330},[2113,31799,31800],{"class":2414},"Intent",[2113,31802,31803],{"class":2330}," intent",[2113,31805,26987],{"class":2119},[2113,31807,2842],{"class":2326},[2113,31809,31810],{"class":2330}," flags",[2113,31812,26987],{"class":2119},[2113,31814,2842],{"class":2326},[2113,31816,31817],{"class":2330}," startId) {\n",[2113,31819,31820,31823,31825,31827,31829,31832],{"class":2115,"line":1652},[2113,31821,31822],{"class":2414},"        Log",[2113,31824,179],{"class":2119},[2113,31826,5601],{"class":2133},[2113,31828,29903],{"class":2119},[2113,31830,31831],{"class":2149},"\"onStartCommand\"",[2113,31833,2553],{"class":2119},[2113,31835,31836],{"class":2115,"line":2185},[2113,31837,2125],{"emptyLinePlaceholder":1767},[2113,31839,31840,31843,31846,31848,31851,31854,31856,31859,31861,31864,31866],{"class":2115,"line":2230},[2113,31841,31842],{"class":2414},"        PowerManager",[2113,31844,31845],{"class":2330}," powerService ",[2113,31847,2335],{"class":2334},[2113,31849,31850],{"class":2330}," (PowerManager) ",[2113,31852,31853],{"class":2133},"getSystemService",[2113,31855,2423],{"class":2330},[2113,31857,31858],{"class":2414},"Context",[2113,31860,179],{"class":2119},[2113,31862,31863],{"class":2414},"POWER_SERVICE",[2113,31865,17832],{"class":2330},[2113,31867,2487],{"class":2119},[2113,31869,31870,31873,31875,31878,31880,31883,31885,31888,31890,31893,31895,31898],{"class":2115,"line":2235},[2113,31871,31872],{"class":2330},"        wakeLock ",[2113,31874,2335],{"class":2334},[2113,31876,31877],{"class":2414}," powerService",[2113,31879,179],{"class":2119},[2113,31881,31882],{"class":2133},"newWakeLock",[2113,31884,2423],{"class":2119},[2113,31886,31887],{"class":2414},"PowerManager",[2113,31889,179],{"class":2119},[2113,31891,31892],{"class":2414},"PARTIAL_WAKE_LOCK",[2113,31894,932],{"class":2119},[2113,31896,31897],{"class":2149},"\"ArroundService::lock\"",[2113,31899,2553],{"class":2119},[2113,31901,31902,31905,31907,31910],{"class":2115,"line":2241},[2113,31903,31904],{"class":2414},"        wakeLock",[2113,31906,179],{"class":2119},[2113,31908,31909],{"class":2133},"acquire",[2113,31911,3944],{"class":2119},[2113,31913,31914],{"class":2115,"line":2246},[2113,31915,2125],{"emptyLinePlaceholder":1767},[2113,31917,31918,31921,31924,31926,31928,31930,31933],{"class":2115,"line":2464},[2113,31919,31920],{"class":2414},"        Bundle",[2113,31922,31923],{"class":2330}," extras ",[2113,31925,2335],{"class":2334},[2113,31927,31803],{"class":2414},[2113,31929,179],{"class":2119},[2113,31931,31932],{"class":2133},"getExtras",[2113,31934,3944],{"class":2119},[2113,31936,31937,31939,31942,31944,31947,31950,31952,31954,31956,31958],{"class":2115,"line":2085},[2113,31938,30333],{"class":2414},[2113,31940,31941],{"class":2330}," location ",[2113,31943,2335],{"class":2334},[2113,31945,31946],{"class":2330}," (Location) ",[2113,31948,31949],{"class":2414},"extras",[2113,31951,179],{"class":2119},[2113,31953,19283],{"class":2133},[2113,31955,2423],{"class":2119},[2113,31957,31527],{"class":2149},[2113,31959,2553],{"class":2119},[2113,31961,31962,31964,31967,31970,31972],{"class":2115,"line":2514},[2113,31963,4518],{"class":2326},[2113,31965,31966],{"class":2330}," (location ",[2113,31968,31969],{"class":2334},"!=",[2113,31971,20727],{"class":2166},[2113,31973,2433],{"class":2330},[2113,31975,31976,31979,31981,31983,31985,31987],{"class":2115,"line":2533},[2113,31977,31978],{"class":2414},"            this",[2113,31980,179],{"class":2119},[2113,31982,30343],{"class":2414},[2113,31984,2153],{"class":2334},[2113,31986,30198],{"class":2330},[2113,31988,2487],{"class":2119},[2113,31990,31991],{"class":2115,"line":2556},[2113,31992,4578],{"class":2330},[2113,31994,31995],{"class":2115,"line":2569},[2113,31996,2125],{"emptyLinePlaceholder":1767},[2113,31998,31999,32002,32005,32007,32010,32012,32015,32017,32019],{"class":2115,"line":2575},[2113,32000,32001],{"class":2414},"        Notification",[2113,32003,32004],{"class":2330}," notification ",[2113,32006,2335],{"class":2334},[2113,32008,32009],{"class":2414}," notificationManager",[2113,32011,179],{"class":2119},[2113,32013,32014],{"class":2133},"notifyDistance",[2113,32016,2423],{"class":2119},[2113,32018,3095],{"class":2166},[2113,32020,2553],{"class":2119},[2113,32022,32023,32026,32028,32031,32033,32036,32038,32041],{"class":2115,"line":2596},[2113,32024,32025],{"class":2133},"        startForeground",[2113,32027,2423],{"class":2330},[2113,32029,32030],{"class":2414},"ArroundNotificationManager",[2113,32032,179],{"class":2119},[2113,32034,32035],{"class":2414},"ARROUND_ID",[2113,32037,26987],{"class":2119},[2113,32039,32040],{"class":2330}," notification)",[2113,32042,2487],{"class":2119},[2113,32044,32045],{"class":2115,"line":3192},[2113,32046,2125],{"emptyLinePlaceholder":1767},[2113,32048,32049,32051,32054],{"class":2115,"line":3213},[2113,32050,28968],{"class":2326},[2113,32052,32053],{"class":2330}," START_STICKY",[2113,32055,2487],{"class":2119},[2113,32057,32058],{"class":2115,"line":3236},[2113,32059,4665],{"class":2330},[12,32061,32062],{},"Afin qu'Android et son système de gestion d'nergie ne tuent pas notre service pendant que l'on court, nous commençons\npar mettre un PARTIAL_WAKE_LOCK. Ensuite nous récupérons la position (sauf si nous sommes issus d'un redémarrage de\nl'application) et appelons la méthode startForeground avec une notification persistante que nous avons créé.",[12,32064,32065],{},"Surtout après l'acquision du Wake Lock, il est important de le relâcher lors de la fermeture (quand l'utilisateur\nclique sur stop):",[2105,32067,32069],{"className":29285,"code":32068,"language":29287,"meta":1646,"style":1646},"    public void stop() {\n        if (wakeLock != null) {\n            if (wakeLock.isHeld()) {\n                wakeLock.release();\n                wakeLock = null;\n            }\n        }\n        stopForeground(true);\n        stopSelf();\n    }\n",[184,32070,32071,32082,32095,32113,32125,32136,32140,32144,32157,32166],{"__ignoreMap":1646},[2113,32072,32073,32075,32077,32080],{"class":2115,"line":2116},[2113,32074,29441],{"class":2326},[2113,32076,29564],{"class":2326},[2113,32078,32079],{"class":2133}," stop",[2113,32081,3912],{"class":2330},[2113,32083,32084,32086,32089,32091,32093],{"class":2115,"line":1647},[2113,32085,4518],{"class":2326},[2113,32087,32088],{"class":2330}," (wakeLock ",[2113,32090,31969],{"class":2334},[2113,32092,20727],{"class":2166},[2113,32094,2433],{"class":2330},[2113,32096,32097,32099,32101,32104,32106,32109,32111],{"class":2115,"line":1652},[2113,32098,5021],{"class":2326},[2113,32100,2495],{"class":2330},[2113,32102,32103],{"class":2414},"wakeLock",[2113,32105,179],{"class":2119},[2113,32107,32108],{"class":2133},"isHeld",[2113,32110,17772],{"class":2119},[2113,32112,2433],{"class":2330},[2113,32114,32115,32118,32120,32123],{"class":2115,"line":2185},[2113,32116,32117],{"class":2414},"                wakeLock",[2113,32119,179],{"class":2119},[2113,32121,32122],{"class":2133},"release",[2113,32124,3944],{"class":2119},[2113,32126,32127,32130,32132,32134],{"class":2115,"line":2230},[2113,32128,32129],{"class":2330},"                wakeLock ",[2113,32131,2335],{"class":2334},[2113,32133,20727],{"class":2166},[2113,32135,2487],{"class":2119},[2113,32137,32138],{"class":2115,"line":2235},[2113,32139,5172],{"class":2330},[2113,32141,32142],{"class":2115,"line":2241},[2113,32143,4578],{"class":2330},[2113,32145,32146,32149,32151,32153,32155],{"class":2115,"line":2246},[2113,32147,32148],{"class":2133},"        stopForeground",[2113,32150,2423],{"class":2330},[2113,32152,4819],{"class":2166},[2113,32154,17832],{"class":2330},[2113,32156,2487],{"class":2119},[2113,32158,32159,32162,32164],{"class":2115,"line":2464},[2113,32160,32161],{"class":2133},"        stopSelf",[2113,32163,17772],{"class":2330},[2113,32165,2487],{"class":2119},[2113,32167,32168],{"class":2115,"line":2085},[2113,32169,4665],{"class":2330},[12,32171,32172,32173,32176,32177,3938],{},"On en profite pour arrêter la notification (avec ̀",[184,32174,32175],{},"stopForeground",") et arrêter le service (avec ",[184,32178,32179],{},"stopSelf",[12,32181,32182,32183,32186,32187,15746],{},"Pour lire à l'utilisateur la distance, nous utilisons ",[184,32184,32185],{},"android.speech.tts.TextToSpeech",". Son utilisation\nest fort simple et se fait lors de l'appel à ",[184,32188,32189],{},"speakDistance",[2105,32191,32193],{"className":29285,"code":32192,"language":29287,"meta":1646,"style":1646},"    private void speakDistance() {\n        int distance = (int) getDistance();\n        if (Math.abs(lastDistance - distance) > 100) {\n            int stringId;\n            if (distance > 1000) {\n                stringId = R.string.speaker_meters_alert;\n            } else if (distance > 900) {\n                stringId = R.string.speaker_meters_warn;\n            } else {\n                stringId = R.string.speaker_meters_info;\n            }\n\n            String text = getString(stringId, distance);\n\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n                textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null, null);\n            } else {\n                textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null);\n            }\n\n            lastDistance = distance;\n        }\n    }\n",[184,32194,32195,32206,32227,32255,32265,32278,32299,32316,32335,32343,32362,32366,32370,32392,32396,32432,32463,32471,32493,32497,32501,32512,32516],{"__ignoreMap":1646},[2113,32196,32197,32199,32201,32204],{"class":2115,"line":2116},[2113,32198,21028],{"class":2326},[2113,32200,29564],{"class":2326},[2113,32202,32203],{"class":2133}," speakDistance",[2113,32205,3912],{"class":2330},[2113,32207,32208,32211,32213,32215,32217,32219,32221,32223,32225],{"class":2115,"line":1647},[2113,32209,32210],{"class":2326},"        int",[2113,32212,30384],{"class":2330},[2113,32214,2335],{"class":2334},[2113,32216,2495],{"class":2330},[2113,32218,30984],{"class":2326},[2113,32220,5709],{"class":2330},[2113,32222,30993],{"class":2133},[2113,32224,17772],{"class":2330},[2113,32226,2487],{"class":2119},[2113,32228,32229,32231,32233,32235,32237,32240,32243,32245,32248,32250,32253],{"class":2115,"line":1652},[2113,32230,4518],{"class":2326},[2113,32232,2495],{"class":2330},[2113,32234,7959],{"class":2414},[2113,32236,179],{"class":2119},[2113,32238,32239],{"class":2133},"abs",[2113,32241,32242],{"class":2119},"(lastDistance ",[2113,32244,3242],{"class":2334},[2113,32246,32247],{"class":2119}," distance)",[2113,32249,5275],{"class":2334},[2113,32251,32252],{"class":2166}," 100",[2113,32254,2433],{"class":2330},[2113,32256,32257,32260,32263],{"class":2115,"line":2185},[2113,32258,32259],{"class":2326},"            int",[2113,32261,32262],{"class":2330}," stringId",[2113,32264,2487],{"class":2119},[2113,32266,32267,32269,32272,32274,32276],{"class":2115,"line":2230},[2113,32268,5021],{"class":2326},[2113,32270,32271],{"class":2330}," (distance ",[2113,32273,29415],{"class":2334},[2113,32275,29365],{"class":2166},[2113,32277,2433],{"class":2330},[2113,32279,32280,32283,32285,32288,32290,32292,32294,32297],{"class":2115,"line":2235},[2113,32281,32282],{"class":2330},"                stringId ",[2113,32284,2335],{"class":2334},[2113,32286,32287],{"class":2414}," R",[2113,32289,179],{"class":2119},[2113,32291,19225],{"class":2414},[2113,32293,179],{"class":2119},[2113,32295,32296],{"class":2414},"speaker_meters_alert",[2113,32298,2487],{"class":2119},[2113,32300,32301,32303,32305,32307,32309,32311,32314],{"class":2115,"line":2241},[2113,32302,23062],{"class":2330},[2113,32304,4227],{"class":2326},[2113,32306,4230],{"class":2326},[2113,32308,32271],{"class":2330},[2113,32310,29415],{"class":2334},[2113,32312,32313],{"class":2166}," 900",[2113,32315,2433],{"class":2330},[2113,32317,32318,32320,32322,32324,32326,32328,32330,32333],{"class":2115,"line":2246},[2113,32319,32282],{"class":2330},[2113,32321,2335],{"class":2334},[2113,32323,32287],{"class":2414},[2113,32325,179],{"class":2119},[2113,32327,19225],{"class":2414},[2113,32329,179],{"class":2119},[2113,32331,32332],{"class":2414},"speaker_meters_warn",[2113,32334,2487],{"class":2119},[2113,32336,32337,32339,32341],{"class":2115,"line":2464},[2113,32338,23062],{"class":2330},[2113,32340,4227],{"class":2326},[2113,32342,2647],{"class":2330},[2113,32344,32345,32347,32349,32351,32353,32355,32357,32360],{"class":2115,"line":2085},[2113,32346,32282],{"class":2330},[2113,32348,2335],{"class":2334},[2113,32350,32287],{"class":2414},[2113,32352,179],{"class":2119},[2113,32354,19225],{"class":2414},[2113,32356,179],{"class":2119},[2113,32358,32359],{"class":2414},"speaker_meters_info",[2113,32361,2487],{"class":2119},[2113,32363,32364],{"class":2115,"line":2514},[2113,32365,5172],{"class":2330},[2113,32367,32368],{"class":2115,"line":2533},[2113,32369,2125],{"emptyLinePlaceholder":1767},[2113,32371,32372,32375,32378,32380,32383,32386,32388,32390],{"class":2115,"line":2556},[2113,32373,32374],{"class":2414},"            String",[2113,32376,32377],{"class":2330}," text ",[2113,32379,2335],{"class":2334},[2113,32381,32382],{"class":2133}," getString",[2113,32384,32385],{"class":2330},"(stringId",[2113,32387,26987],{"class":2119},[2113,32389,32247],{"class":2330},[2113,32391,2487],{"class":2119},[2113,32393,32394],{"class":2115,"line":2569},[2113,32395,2125],{"emptyLinePlaceholder":1767},[2113,32397,32398,32400,32402,32405,32407,32410,32412,32415,32417,32420,32422,32425,32427,32430],{"class":2115,"line":2575},[2113,32399,5021],{"class":2326},[2113,32401,2495],{"class":2330},[2113,32403,32404],{"class":2414},"Build",[2113,32406,179],{"class":2119},[2113,32408,32409],{"class":2414},"VERSION",[2113,32411,179],{"class":2119},[2113,32413,32414],{"class":2414},"SDK_INT",[2113,32416,6256],{"class":2334},[2113,32418,32419],{"class":2414}," Build",[2113,32421,179],{"class":2119},[2113,32423,32424],{"class":2414},"VERSION_CODES",[2113,32426,179],{"class":2119},[2113,32428,32429],{"class":2414},"LOLLIPOP",[2113,32431,2433],{"class":2330},[2113,32433,32434,32437,32439,32442,32445,32448,32450,32453,32455,32457,32459,32461],{"class":2115,"line":2596},[2113,32435,32436],{"class":2414},"                textToSpeech",[2113,32438,179],{"class":2119},[2113,32440,32441],{"class":2133},"speak",[2113,32443,32444],{"class":2119},"(text, ",[2113,32446,32447],{"class":2414},"TextToSpeech",[2113,32449,179],{"class":2119},[2113,32451,32452],{"class":2414},"QUEUE_FLUSH",[2113,32454,932],{"class":2119},[2113,32456,18569],{"class":2166},[2113,32458,932],{"class":2119},[2113,32460,18569],{"class":2166},[2113,32462,2553],{"class":2119},[2113,32464,32465,32467,32469],{"class":2115,"line":3192},[2113,32466,23062],{"class":2330},[2113,32468,4227],{"class":2326},[2113,32470,2647],{"class":2330},[2113,32472,32473,32475,32477,32479,32481,32483,32485,32487,32489,32491],{"class":2115,"line":3213},[2113,32474,32436],{"class":2414},[2113,32476,179],{"class":2119},[2113,32478,32441],{"class":2133},[2113,32480,32444],{"class":2119},[2113,32482,32447],{"class":2414},[2113,32484,179],{"class":2119},[2113,32486,32452],{"class":2414},[2113,32488,932],{"class":2119},[2113,32490,18569],{"class":2166},[2113,32492,2553],{"class":2119},[2113,32494,32495],{"class":2115,"line":3236},[2113,32496,5172],{"class":2330},[2113,32498,32499],{"class":2115,"line":3248},[2113,32500,2125],{"emptyLinePlaceholder":1767},[2113,32502,32503,32506,32508,32510],{"class":2115,"line":4899},[2113,32504,32505],{"class":2330},"            lastDistance ",[2113,32507,2335],{"class":2334},[2113,32509,30402],{"class":2330},[2113,32511,2487],{"class":2119},[2113,32513,32514],{"class":2115,"line":1777},[2113,32515,4578],{"class":2330},[2113,32517,32518],{"class":2115,"line":4931},[2113,32519,4665],{"class":2330},[12,32521,32522],{},"Enfin le dernier point concerne la création des notifications. Depuis la version Android O, l'application doit\nassocier une notification à un channel. Cela permet à Android de présenter à l'utilisateur une interface avec les\nnotifications possibles et de pouvoir désactiver\u002Factiver ces dernières au cas par cas.",[12,32524,32525,32528],{},[184,32526,32527],{},"AndroidNotificationManager"," est là pour ce but. Il va créer le channel de notification et notifier la distance à\nl'utilisateur.",[2105,32530,32532],{"className":29285,"code":32531,"language":29287,"meta":1646,"style":1646},"    private static final String CHANNEL_DEFAULT_IMPORTANCE = \"Running\";\n    public static final int ARROUND_ID = 1;\n\n    private void createNotificationChannel() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            CharSequence name = context.getString(R.string.channel_name);\n            String description = context.getString(R.string.channel_description);\n            NotificationChannel channel = new NotificationChannel(CHANNEL_DEFAULT_IMPORTANCE, name, NotificationManager.IMPORTANCE_DEFAULT);\n            channel.setDescription(description);\n            NotificationManager notificationManager = context.getSystemService(NotificationManager.class);\n            notificationManager.createNotificationChannel(channel);\n        }\n    }\n\n    public Notification createNotification(float distance) {\n        Intent notificationIntent = new Intent(context, MainActivity.class);\n        PendingIntent pendingIntent =\n                PendingIntent.getActivity(context, 0, notificationIntent, 0);\n\n        return new NotificationCompat.Builder(context, CHANNEL_DEFAULT_IMPORTANCE)\n                .setContentTitle(context.getText(R.string.notification_title))\n                .setContentText(context.getString(R.string.notification_message, (int) distance))\n                .setSmallIcon(android.R.drawable.ic_menu_mylocation)\n                .setContentIntent(pendingIntent)\n                .build();\n    }\n\n    public void notifyDistance(float distance) {\n        Notification n = this.createNotification(distance);\n        NotificationManager notificationManager = context.getSystemService(NotificationManager.class);\n        notificationManager.notify(ARROUND_ID, n);\n    }\n",[184,32533,32534,32554,32573,32577,32588,32619,32651,32681,32717,32730,32757,32769,32773,32777,32781,32799,32829,32839,32861,32865,32882,32915,32950,32977,32987,32996,33000,33004,33019,33038,33063,33076],{"__ignoreMap":1646},[2113,32535,32536,32538,32540,32542,32544,32547,32549,32552],{"class":2115,"line":2116},[2113,32537,21028],{"class":2326},[2113,32539,29311],{"class":2326},[2113,32541,29314],{"class":2326},[2113,32543,29317],{"class":2414},[2113,32545,32546],{"class":2330}," CHANNEL_DEFAULT_IMPORTANCE ",[2113,32548,2335],{"class":2334},[2113,32550,32551],{"class":2149}," \"Running\"",[2113,32553,2487],{"class":2119},[2113,32555,32556,32558,32560,32562,32564,32567,32569,32571],{"class":2115,"line":1647},[2113,32557,29441],{"class":2326},[2113,32559,29311],{"class":2326},[2113,32561,29314],{"class":2326},[2113,32563,2842],{"class":2326},[2113,32565,32566],{"class":2330}," ARROUND_ID ",[2113,32568,2335],{"class":2334},[2113,32570,2850],{"class":2166},[2113,32572,2487],{"class":2119},[2113,32574,32575],{"class":2115,"line":1652},[2113,32576,2125],{"emptyLinePlaceholder":1767},[2113,32578,32579,32581,32583,32586],{"class":2115,"line":2185},[2113,32580,21028],{"class":2326},[2113,32582,29564],{"class":2326},[2113,32584,32585],{"class":2133}," createNotificationChannel",[2113,32587,3912],{"class":2330},[2113,32589,32590,32592,32594,32596,32598,32600,32602,32604,32606,32608,32610,32612,32614,32617],{"class":2115,"line":2230},[2113,32591,4518],{"class":2326},[2113,32593,2495],{"class":2330},[2113,32595,32404],{"class":2414},[2113,32597,179],{"class":2119},[2113,32599,32409],{"class":2414},[2113,32601,179],{"class":2119},[2113,32603,32414],{"class":2414},[2113,32605,6256],{"class":2334},[2113,32607,32419],{"class":2414},[2113,32609,179],{"class":2119},[2113,32611,32424],{"class":2414},[2113,32613,179],{"class":2119},[2113,32615,32616],{"class":2414},"O",[2113,32618,2433],{"class":2330},[2113,32620,32621,32624,32627,32629,32632,32634,32636,32638,32640,32642,32644,32646,32649],{"class":2115,"line":2235},[2113,32622,32623],{"class":2414},"            CharSequence",[2113,32625,32626],{"class":2330}," name ",[2113,32628,2335],{"class":2334},[2113,32630,32631],{"class":2414}," context",[2113,32633,179],{"class":2119},[2113,32635,30965],{"class":2133},[2113,32637,2423],{"class":2119},[2113,32639,4426],{"class":2414},[2113,32641,179],{"class":2119},[2113,32643,19225],{"class":2414},[2113,32645,179],{"class":2119},[2113,32647,32648],{"class":2414},"channel_name",[2113,32650,2553],{"class":2119},[2113,32652,32653,32655,32658,32660,32662,32664,32666,32668,32670,32672,32674,32676,32679],{"class":2115,"line":2241},[2113,32654,32374],{"class":2414},[2113,32656,32657],{"class":2330}," description ",[2113,32659,2335],{"class":2334},[2113,32661,32631],{"class":2414},[2113,32663,179],{"class":2119},[2113,32665,30965],{"class":2133},[2113,32667,2423],{"class":2119},[2113,32669,4426],{"class":2414},[2113,32671,179],{"class":2119},[2113,32673,19225],{"class":2414},[2113,32675,179],{"class":2119},[2113,32677,32678],{"class":2414},"channel_description",[2113,32680,2553],{"class":2119},[2113,32682,32683,32686,32689,32691,32693,32696,32699,32701,32703,32705,32708,32710,32713,32715],{"class":2115,"line":2246},[2113,32684,32685],{"class":2414},"            NotificationChannel",[2113,32687,32688],{"class":2330}," channel ",[2113,32690,2335],{"class":2334},[2113,32692,4778],{"class":2326},[2113,32694,32695],{"class":2133}," NotificationChannel",[2113,32697,32698],{"class":2330},"(CHANNEL_DEFAULT_IMPORTANCE",[2113,32700,26987],{"class":2119},[2113,32702,16383],{"class":2330},[2113,32704,26987],{"class":2119},[2113,32706,32707],{"class":2414}," NotificationManager",[2113,32709,179],{"class":2119},[2113,32711,32712],{"class":2414},"IMPORTANCE_DEFAULT",[2113,32714,17832],{"class":2330},[2113,32716,2487],{"class":2119},[2113,32718,32719,32722,32724,32727],{"class":2115,"line":2464},[2113,32720,32721],{"class":2414},"            channel",[2113,32723,179],{"class":2119},[2113,32725,32726],{"class":2133},"setDescription",[2113,32728,32729],{"class":2119},"(description);\n",[2113,32731,32732,32735,32738,32740,32742,32744,32746,32748,32751,32753,32755],{"class":2115,"line":2085},[2113,32733,32734],{"class":2414},"            NotificationManager",[2113,32736,32737],{"class":2330}," notificationManager ",[2113,32739,2335],{"class":2334},[2113,32741,32631],{"class":2414},[2113,32743,179],{"class":2119},[2113,32745,31853],{"class":2133},[2113,32747,2423],{"class":2119},[2113,32749,32750],{"class":2414},"NotificationManager",[2113,32752,179],{"class":2119},[2113,32754,20538],{"class":2414},[2113,32756,2553],{"class":2119},[2113,32758,32759,32761,32763,32766],{"class":2115,"line":2514},[2113,32760,31696],{"class":2414},[2113,32762,179],{"class":2119},[2113,32764,32765],{"class":2133},"createNotificationChannel",[2113,32767,32768],{"class":2119},"(channel);\n",[2113,32770,32771],{"class":2115,"line":2533},[2113,32772,4578],{"class":2330},[2113,32774,32775],{"class":2115,"line":2556},[2113,32776,4665],{"class":2330},[2113,32778,32779],{"class":2115,"line":2569},[2113,32780,2125],{"emptyLinePlaceholder":1767},[2113,32782,32783,32785,32788,32791,32793,32796],{"class":2115,"line":2575},[2113,32784,29441],{"class":2326},[2113,32786,32787],{"class":2414}," Notification",[2113,32789,32790],{"class":2133}," createNotification",[2113,32792,2423],{"class":2330},[2113,32794,32795],{"class":2326},"float",[2113,32797,32798],{"class":2330}," distance) {\n",[2113,32800,32801,32804,32807,32809,32811,32813,32816,32818,32821,32823,32825,32827],{"class":2115,"line":2596},[2113,32802,32803],{"class":2414},"        Intent",[2113,32805,32806],{"class":2330}," notificationIntent ",[2113,32808,2335],{"class":2334},[2113,32810,4778],{"class":2326},[2113,32812,31495],{"class":2133},[2113,32814,32815],{"class":2330},"(context",[2113,32817,26987],{"class":2119},[2113,32819,32820],{"class":2414}," MainActivity",[2113,32822,179],{"class":2119},[2113,32824,20538],{"class":2414},[2113,32826,17832],{"class":2330},[2113,32828,2487],{"class":2119},[2113,32830,32831,32834,32837],{"class":2115,"line":3192},[2113,32832,32833],{"class":2414},"        PendingIntent",[2113,32835,32836],{"class":2330}," pendingIntent ",[2113,32838,7008],{"class":2334},[2113,32840,32841,32844,32846,32849,32852,32854,32857,32859],{"class":2115,"line":3213},[2113,32842,32843],{"class":2414},"                PendingIntent",[2113,32845,179],{"class":2119},[2113,32847,32848],{"class":2133},"getActivity",[2113,32850,32851],{"class":2119},"(context, ",[2113,32853,3095],{"class":2166},[2113,32855,32856],{"class":2119},", notificationIntent, ",[2113,32858,3095],{"class":2166},[2113,32860,2553],{"class":2119},[2113,32862,32863],{"class":2115,"line":3236},[2113,32864,2125],{"emptyLinePlaceholder":1767},[2113,32866,32867,32869,32871,32874,32876,32879],{"class":2115,"line":3248},[2113,32868,28968],{"class":2326},[2113,32870,4778],{"class":2326},[2113,32872,32873],{"class":2330}," NotificationCompat",[2113,32875,179],{"class":2119},[2113,32877,32878],{"class":2133},"Builder",[2113,32880,32881],{"class":2119},"(context, CHANNEL_DEFAULT_IMPORTANCE)\n",[2113,32883,32884,32887,32890,32892,32895,32897,32900,32902,32904,32906,32908,32910,32913],{"class":2115,"line":4899},[2113,32885,32886],{"class":2119},"                .",[2113,32888,32889],{"class":2133},"setContentTitle",[2113,32891,2423],{"class":2119},[2113,32893,32894],{"class":2414},"context",[2113,32896,179],{"class":2119},[2113,32898,32899],{"class":2133},"getText",[2113,32901,2423],{"class":2119},[2113,32903,4426],{"class":2414},[2113,32905,179],{"class":2119},[2113,32907,19225],{"class":2414},[2113,32909,179],{"class":2119},[2113,32911,32912],{"class":2414},"notification_title",[2113,32914,6775],{"class":2119},[2113,32916,32917,32919,32922,32924,32926,32928,32930,32932,32934,32936,32938,32940,32943,32945,32947],{"class":2115,"line":1777},[2113,32918,32886],{"class":2119},[2113,32920,32921],{"class":2133},"setContentText",[2113,32923,2423],{"class":2119},[2113,32925,32894],{"class":2414},[2113,32927,179],{"class":2119},[2113,32929,30965],{"class":2133},[2113,32931,2423],{"class":2119},[2113,32933,4426],{"class":2414},[2113,32935,179],{"class":2119},[2113,32937,19225],{"class":2414},[2113,32939,179],{"class":2119},[2113,32941,32942],{"class":2414},"notification_message",[2113,32944,30981],{"class":2119},[2113,32946,30984],{"class":2326},[2113,32948,32949],{"class":2119},") distance))\n",[2113,32951,32952,32954,32957,32959,32961,32963,32965,32967,32970,32972,32975],{"class":2115,"line":4931},[2113,32953,32886],{"class":2119},[2113,32955,32956],{"class":2133},"setSmallIcon",[2113,32958,2423],{"class":2119},[2113,32960,2084],{"class":2414},[2113,32962,179],{"class":2119},[2113,32964,4426],{"class":2414},[2113,32966,179],{"class":2119},[2113,32968,32969],{"class":2414},"drawable",[2113,32971,179],{"class":2119},[2113,32973,32974],{"class":2414},"ic_menu_mylocation",[2113,32976,4660],{"class":2119},[2113,32978,32979,32981,32984],{"class":2115,"line":4961},[2113,32980,32886],{"class":2119},[2113,32982,32983],{"class":2133},"setContentIntent",[2113,32985,32986],{"class":2119},"(pendingIntent)\n",[2113,32988,32989,32991,32994],{"class":2115,"line":4976},[2113,32990,32886],{"class":2119},[2113,32992,32993],{"class":2133},"build",[2113,32995,3944],{"class":2119},[2113,32997,32998],{"class":2115,"line":4993},[2113,32999,4665],{"class":2330},[2113,33001,33002],{"class":2115,"line":5013},[2113,33003,2125],{"emptyLinePlaceholder":1767},[2113,33005,33006,33008,33010,33013,33015,33017],{"class":2115,"line":5018},[2113,33007,29441],{"class":2326},[2113,33009,29564],{"class":2326},[2113,33011,33012],{"class":2133}," notifyDistance",[2113,33014,2423],{"class":2330},[2113,33016,32795],{"class":2326},[2113,33018,32798],{"class":2330},[2113,33020,33021,33023,33026,33028,33030,33032,33035],{"class":2115,"line":5042},[2113,33022,32001],{"class":2414},[2113,33024,33025],{"class":2330}," n ",[2113,33027,2335],{"class":2334},[2113,33029,15832],{"class":2414},[2113,33031,179],{"class":2119},[2113,33033,33034],{"class":2133},"createNotification",[2113,33036,33037],{"class":2119},"(distance);\n",[2113,33039,33040,33043,33045,33047,33049,33051,33053,33055,33057,33059,33061],{"class":2115,"line":5057},[2113,33041,33042],{"class":2414},"        NotificationManager",[2113,33044,32737],{"class":2330},[2113,33046,2335],{"class":2334},[2113,33048,32631],{"class":2414},[2113,33050,179],{"class":2119},[2113,33052,31853],{"class":2133},[2113,33054,2423],{"class":2119},[2113,33056,32750],{"class":2414},[2113,33058,179],{"class":2119},[2113,33060,20538],{"class":2414},[2113,33062,2553],{"class":2119},[2113,33064,33065,33068,33070,33073],{"class":2115,"line":5062},[2113,33066,33067],{"class":2414},"        notificationManager",[2113,33069,179],{"class":2119},[2113,33071,33072],{"class":2133},"notify",[2113,33074,33075],{"class":2119},"(ARROUND_ID, n);\n",[2113,33077,33078],{"class":2115,"line":5098},[2113,33079,4665],{"class":2330},[12,33081,33082],{},"Il est important lors de l'appel à notifyDistance de toujours utiliser le même identifiant de notification afin\nque cette dernière soit remplacée (et non ajouté). Cela permet de mettre à jour le contenu de la notification.",[128,33084,33086],{"id":33085},"pour-finir","Pour finir",[12,33088,33089],{},"Pour finir je suis content d'avoir développé cette application qui n'est pas exempte de bug, mais qui m'a pris\ntrès peu de temps de développement.",[12,33091,33092,33093,33098,33099,33104],{},"Vous pouvez retrouver le code source sur ",[49,33094,33097],{"href":33095,"rel":33096},"https:\u002F\u002Fgithub.com\u002Fphoenix741\u002F1kmarround",[347],"Github: phoenix741\u002F1kmarround","\net sur le ",[49,33100,33103],{"href":33101,"rel":33102},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=org.shadoware.a1kmarroud",[347],"PlayStore"," (à ce jour l'application\nn'a pas encore été validé dans les stores et n'est donc pas disponible).",[12,33106,33107],{},"Le plus long dans ce développement aura été:",[506,33109,33110,33118,33121],{},[370,33111,33112,33113,17832],{},"faire l'icône (à partir d'une image se trouvant sur ",[49,33114,33117],{"href":33115,"rel":33116},"https:\u002F\u002Fundraw.co\u002F",[347],"undraw.co",[370,33119,33120],{},"faire le layout (même si elle est simple)",[370,33122,33123],{},"remplir la fiche du playstore pour mettre l'application dans les stores.",[23219,33125,33127,33130],{"className":33126,"dataFootnotes":1646},[23222],[128,33128,23227],{"className":33129,"id":17406},[23226],[367,33131,33132,33138],{},[370,33133,33134,33135],{"id":23232},"Une activité est l'équivalent d'un écran dans Android. ",[49,33136,23240],{"href":23236,"ariaLabel":23237,"className":33137,"dataFootnoteBackref":1646},[23239],[370,33139,33140,33141],{"id":23243},"Pour demander les permissions je me suis basé sur le code suivant: ",[49,33142,23240],{"href":23251,"ariaLabel":23252,"className":33143,"dataFootnoteBackref":1646},[23239],[3358,33145,33146],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}",{"title":1646,"searchDepth":1647,"depth":1647,"links":33148},[33149,33150,33154,33155,33156],{"id":29217,"depth":1647,"text":29218},{"id":29260,"depth":1647,"text":29245,"children":33151},[33152,33153],{"id":29276,"depth":1652,"text":29277},{"id":30411,"depth":1652,"text":30412},{"id":31439,"depth":1647,"text":31440},{"id":33085,"depth":1647,"text":33086},{"id":17406,"depth":1647,"text":23227},"2020-11-14",{"type":9,"value":33159},[33160,33162,33164,33169,33174,33176,33178,33180],[12,33161,29171],{},[12,33163,29174],{},[12,33165,29177,33166,29183],{},[49,33167,29182],{"href":29180,"rel":33168},[347],[12,33170,29186,33171,29191],{},[49,33172,29182],{"href":29189,"rel":33173},[347],[12,33175,29194],{},[12,33177,29197],{},[12,33179,29200],{},[506,33181,33182,33184,33186,33188],{},[370,33183,29205],{},[370,33185,29208],{},[370,33187,29211],{},[370,33189,29214],{},{"planet":1767},"\u002Fpost\u002Fapplication_du_confinement",{"title":29166,"description":29171},"application_du_confinement","posts\u002FProgrammation\u002F2020-11-14_application_du_confinement",[2084,2058,29287,33196],"confinement","H4G4WeTzcsmG6glUDJrc_7H09bZb4hoavg3yFXRDN3A",{"id":33199,"title":33200,"author":7,"body":33201,"category":2057,"categorySlug":2058,"date":35027,"description":26097,"excerpt":35028,"extension":1764,"location":1765,"meta":35058,"navigation":1767,"path":35059,"published":1767,"seo":35060,"slug":35061,"stem":35062,"tags":35063,"timeToRead":3213,"__hash__":35064},"posts\u002Fposts\u002FProgrammation\u002F2020-11-02_creation-api-2.md","Comment créer une bonne API Web - Partie 2",{"type":9,"value":33202,"toc":35015},[33203,33205,33207,33221,33225,33228,33231,33238,33242,33249,33259,33262,33267,33304,33309,33378,33383,33449,33452,33456,33459,33461,33499,33506,33513,33516,33520,33529,33538,33542,33547,33553,33560,33563,33571,33584,33593,33596,33608,33610,33624,33632,33646,33651,33654,33666,33675,33732,33735,33738,33753,33756,33762,33992,33995,34003,34009,34074,34077,34085,34093,34097,34102,34114,34121,34130,34134,34144,34152,34156,34162,34165,34168,34185,34188,34191,34194,34198,34202,34205,34211,34214,34217,34222,34225,34228,34231,34234,34248,34251,34254,34267,34270,34276,34280,34283,34289,34293,34297,34303,34306,34310,34316,34325,34329,34337,34343,34346,34350,34364,34367,34388,34392,34398,34403,34407,34411,34414,34417,34421,34424,34427,34431,34434,34437,34440,34443,34446,34450,34453,34457,34460,34464,34470,34474,34477,34481,34485,34488,34491,34495,34498,34502,34505,34509,34512,34515,34518,34532,34535,34538,34876,34879,34883,34886,34889,34892,34896,34899,34902,34916,34922,34926,34929,34932,34935,34939,34971,35012],[12,33204,26097],{},[12,33206,26100],{},[506,33208,33209,33213,33217],{},[370,33210,33211],{},[49,33212,26108],{"href":26107},[370,33214,33215],{},[49,33216,26114],{"href":26113},[370,33218,33219],{},[49,33220,26120],{"href":26119},[128,33222,33224],{"id":33223},"quest-quune-api-rest","Qu'est qu'une API REST",[12,33226,33227],{},"REST est une norme dont voici les grandes lignes. Il n'est pas dans mon but de faire un cours sur REST (et il y en\ndéjà de très bons sur internet). Je souhaiterais surtout parler des points qui me semblent importants. N'hésitez pas à venir\nme dire si vous pensez qu'il manque des points importants.\nJe viendrai alors compléter mon article.",[12,33229,33230],{},"Le principe de REST est de séparer l'API en différentes ressources logiques qui peuvent être manipulées par les verbes\nHTTP (GET, POST, ...). La réponse de son côté se base également sur les codes http.",[12,33232,33233,33234,33237],{},"Le contenu de la requête et de la réponse peut être dans le format objet de votre choix (json, yaml, xml, ...). On\nutilise alors le header ",[184,33235,33236],{},"Content-Type"," pour définir le contenu. Une API peut d'ailleurs gérer plusieurs formats et\nrépondre au client le bon format en fonction de la demande du client.",[133,33239,33241],{"id":33240},"basé-sur-des-ressources","Basé sur des ressources",[12,33243,33244,33245,33248],{},"Quand on parle de REST, il faut penser ",[362,33246,33247],{},"ressources",". Mais qu'est qu'une ressource ?",[12,33250,33251,33252,33255,33256,33258],{},"Une ressource c'est un concept abstrait de REST. Une ressource c'est ce qui va être représenté par les données JSON\nque vous allez manipuler. Pour représenter une ressource on n'utilise pas un verbe, ou une action mais un ",[155,33253,33254],{},"nom",".\nL'utilisation du ",[155,33257,33254],{}," est important car le verbe est representé par la méthode HTTP.",[12,33260,33261],{},"Une ressource peut:",[506,33263,33264],{},[370,33265,33266],{},"être un singleton",[2105,33268,33270],{"className":18051,"code":33269,"language":18053,"meta":1646,"style":1646},"GET \u002Fapi\u002Fhosts\u002F:id\n\n{\n  \"id\": 1,\n  ...\n}\n",[184,33271,33272,33277,33281,33285,33296,33300],{"__ignoreMap":1646},[2113,33273,33274],{"class":2115,"line":2116},[2113,33275,33276],{"class":2119},"GET \u002Fapi\u002Fhosts\u002F:id\n",[2113,33278,33279],{"class":2115,"line":1647},[2113,33280,2125],{"emptyLinePlaceholder":1767},[2113,33282,33283],{"class":2115,"line":1652},[2113,33284,18060],{"class":2119},[2113,33286,33287,33290,33292,33294],{"class":2115,"line":2185},[2113,33288,33289],{"class":2330},"  \"id\"",[2113,33291,4429],{"class":2119},[2113,33293,3686],{"class":2166},[2113,33295,4706],{"class":2119},[2113,33297,33298],{"class":2115,"line":2230},[2113,33299,18190],{"class":19134},[2113,33301,33302],{"class":2115,"line":2235},[2113,33303,2599],{"class":2119},[506,33305,33306],{},[370,33307,33308],{},"être une collection",[2105,33310,33312],{"className":18051,"code":33311,"language":18053,"meta":1646,"style":1646},"GET \u002Fapi\u002Fhosts\n\n[\n  {\n    \"id\": 1,\n    ...\n  },\n  {\n    \"id\": 2,\n    ...\n  },\n]\n",[184,33313,33314,33319,33323,33327,33332,33343,33348,33352,33356,33366,33370,33374],{"__ignoreMap":1646},[2113,33315,33316],{"class":2115,"line":2116},[2113,33317,33318],{"class":2119},"GET \u002Fapi\u002Fhosts\n",[2113,33320,33321],{"class":2115,"line":1647},[2113,33322,2125],{"emptyLinePlaceholder":1767},[2113,33324,33325],{"class":2115,"line":1652},[2113,33326,10710],{"class":2119},[2113,33328,33329],{"class":2115,"line":2185},[2113,33330,33331],{"class":2119},"  {\n",[2113,33333,33334,33337,33339,33341],{"class":2115,"line":2230},[2113,33335,33336],{"class":2330},"    \"id\"",[2113,33338,4429],{"class":2119},[2113,33340,3686],{"class":2166},[2113,33342,4706],{"class":2119},[2113,33344,33345],{"class":2115,"line":2235},[2113,33346,33347],{"class":19134},"    ...\n",[2113,33349,33350],{"class":2115,"line":2241},[2113,33351,8490],{"class":2119},[2113,33353,33354],{"class":2115,"line":2246},[2113,33355,33331],{"class":2119},[2113,33357,33358,33360,33362,33364],{"class":2115,"line":2464},[2113,33359,33336],{"class":2330},[2113,33361,4429],{"class":2119},[2113,33363,2547],{"class":2166},[2113,33365,4706],{"class":2119},[2113,33367,33368],{"class":2115,"line":2085},[2113,33369,33347],{"class":19134},[2113,33371,33372],{"class":2115,"line":2514},[2113,33373,8490],{"class":2119},[2113,33375,33376],{"class":2115,"line":2533},[2113,33377,26198],{"class":2119},[506,33379,33380],{},[370,33381,33382],{},"être une sous-ressource",[2105,33384,33386],{"className":18051,"code":33385,"language":18053,"meta":1646,"style":1646},"GET \u002Fapi\u002Fhosts\u002F:id\u002Fbackups\n\n[\n  {\n    \"id\": 1,\n    ...\n  },\n  {\n    \"id\": 2,\n    ...\n  },\n]\n",[184,33387,33388,33393,33397,33401,33405,33415,33419,33423,33427,33437,33441,33445],{"__ignoreMap":1646},[2113,33389,33390],{"class":2115,"line":2116},[2113,33391,33392],{"class":2119},"GET \u002Fapi\u002Fhosts\u002F:id\u002Fbackups\n",[2113,33394,33395],{"class":2115,"line":1647},[2113,33396,2125],{"emptyLinePlaceholder":1767},[2113,33398,33399],{"class":2115,"line":1652},[2113,33400,10710],{"class":2119},[2113,33402,33403],{"class":2115,"line":2185},[2113,33404,33331],{"class":2119},[2113,33406,33407,33409,33411,33413],{"class":2115,"line":2230},[2113,33408,33336],{"class":2330},[2113,33410,4429],{"class":2119},[2113,33412,3686],{"class":2166},[2113,33414,4706],{"class":2119},[2113,33416,33417],{"class":2115,"line":2235},[2113,33418,33347],{"class":19134},[2113,33420,33421],{"class":2115,"line":2241},[2113,33422,8490],{"class":2119},[2113,33424,33425],{"class":2115,"line":2246},[2113,33426,33331],{"class":2119},[2113,33428,33429,33431,33433,33435],{"class":2115,"line":2464},[2113,33430,33336],{"class":2330},[2113,33432,4429],{"class":2119},[2113,33434,2547],{"class":2166},[2113,33436,4706],{"class":2119},[2113,33438,33439],{"class":2115,"line":2085},[2113,33440,33347],{"class":19134},[2113,33442,33443],{"class":2115,"line":2514},[2113,33444,8490],{"class":2119},[2113,33446,33447],{"class":2115,"line":2533},[2113,33448,26198],{"class":2119},[12,33450,33451],{},"La ressource est généralement nommée au pluriel et pour des raisons de consistance, doit toujours être nommée de\nla même manière même si le but est de récupérer un unique élément.",[133,33453,33455],{"id":33454},"utilise-les-verbes-http","Utilise les verbes HTTP",[12,33457,33458],{},"Donc une API REST ce sont des ressources qui peuvent être manipulées par les verbes HTTP. Du coup l'action n'est pas\nportée par le nom de la ressource mais par la méthode. Quels sont donc ces méthodes ?",[27533,33460,26207],{"id":19283},[12,33462,33463,33465,33466,33468,33469,33472,33473,33480,33481,33483,33484,33491,33492,932,33495,33498],{},[184,33464,26207],{}," est utilisé pour récupérer une ressource. En aucun cas ",[184,33467,26207],{}," doit être utilisé pour effectuer une modification\n(ce serait comme effectuer des modifications dans le ",[184,33470,33471],{},"getter"," d'une classe",[17400,33474,33475],{},[49,33476,3686],{"href":33477,"ariaDescribedBy":33478,"dataFootnoteRef":1646,"id":33479},"#user-content-fn-getter",[17406],"user-content-fnref-getter",").\n",[184,33482,26207],{}," ne doit pas modifier l'état de la ressource et doit être idempotent",[17400,33485,33486],{},[49,33487,2547],{"href":33488,"ariaDescribedBy":33489,"dataFootnoteRef":1646,"id":33490},"#user-content-fn-idempotent",[17406],"user-content-fnref-idempotent",". On peut donc appeler autant de fois que\nl'on souhaite la méthode GET, le résultat doit toujours être le même tant qu'une autre méthode (",[184,33493,33494],{},"POST",[184,33496,33497],{},"PUT",", ...)\nne vient pas modifier l'état de la ressource.",[12,33500,33501,33502,33505],{},"Si la ressource peut être produite par le serveur, le code HTTP de réponse doit être ",[184,33503,33504],{},"200 OK"," et contenir le contenu de\nla ressource de la réponse dans le corps.",[12,33507,33508,33509,33512],{},"Le client peut alors cacher la requête s'il le souhaite, et il est possible d'utiliser les headers HTTP standards pour lui\nindiquer combien de temps (cf header Cache-Control). Attention ce header peut être ignoré ou peut être utilisé par le\nclient afin d'agir sur son ",[362,33510,33511],{},"rate limit",". A aucun moment il ne faut faire confiance au client et il faut préférer cacher\nles requêtes en interne (via un mêmcached) et définir une limite d'appels plutôt que de solliciter son back si ce\ndernier ne peut pas tenir la charge.",[12,33514,33515],{},"ne doit pas avoir d'impact\u002Fd'effet de bord dans l'application.",[27533,33517,33519],{"id":33518},"head","HEAD",[12,33521,33522,33523,33525,33526,33528],{},"La méthode ",[184,33524,33519],{}," a le même effet que ",[184,33527,26207],{}," mais ne retourne pas de contenu. Le header peut-être utilisé pour vérifier\nl'état de la ressource (et voir si le cache doit être invalidé).",[12,33530,33531,33532,792,33535,179],{},"La méthode est considéré comme ",[362,33533,33534],{},"safe",[362,33536,33537],{},"idempotent",[27533,33539,33541],{"id":33540},"option","OPTION",[12,33543,33522,33544,33546],{},[184,33545,33541],{}," est utilisée pour vérifier les capacités de la méthode HTTP. Il est notament utilisé par les\nnavigateurs pour vérifier les entêtes CORS d'une API (en mode preflight) avant d'effectuer la véritable requête.",[12,33548,33549,33550,33552],{},"Pour interroger le serveur de facon générale ",[184,33551,2676],{}," peut être utilisé à la place de l'URI. Ce seront donc les capacités\nglobales du serveur qui seront retournées",[12,33554,33555,33556,792,33558,179],{},"La méthode est considérée comme ",[362,33557,33534],{},[362,33559,33537],{},[27533,33561,33494],{"id":33562},"post",[12,33564,33565,33567,33568,33570],{},[184,33566,33494],{}," doit être utilisé pour créer une nouvelle ressource associée à la requête demandée. ",[184,33569,33494],{}," est utilisé pour\najouter un nouvel élément à une collection.",[12,33572,33573,33574,33577,33578,33580,33581,33583],{},"Lorsque la ressource a été créé, la réponse doit être ",[184,33575,33576],{},"201 Created"," et le corps du message ne doit pas contenir\nd'informations. A la place on laisse le client récuperer la ressource en utilisant la méthode ",[184,33579,26207],{}," ci-dessus. Pour cela,\nle lien d'accès à la ressource doit être positionné dans le header ",[184,33582,29462],{}," de la réponse HTTP.",[12,33585,33586,33587,939,33590,33592],{},"Si toutefois la création de la ressource ne peut pas être liée à une ressource identifiable par un URI, la méthode peut\nalors retourner ",[184,33588,33589],{},"204 No Content",[184,33591,33504],{}," suivant si la réponse possède un contenu ou non.",[12,33594,33595],{},"La réponse ne peut pas être cachée (sauf indication contraire par header HTTP).",[12,33597,33598,33599,33601,33602,33604,33605,33607],{},"Les requêtes ",[184,33600,33494],{}," ne sont pas ",[362,33603,33537],{}," et peuvent changer l'état de la ressource. Dans ce cas le client devra\nd'ailleurs invalider son cache. Deux appels identiques ",[184,33606,33494],{}," peuvent génerer des résultats différents.",[27533,33609,33497],{"id":20854},[12,33611,33612,33613,792,33615,33617,33618,33620,33621,33623],{},"La différence la plus importante entre ",[184,33614,33494],{},[184,33616,33497],{}," se situe sur l'URI. La méthode ",[184,33619,33497],{}," est utilisée sur les URI\navec un identifiant (et donc unique), alors que la méthode ",[184,33622,33494],{}," est utilisée sur URI représentant une collection.",[12,33625,33626,33628,33629,179],{},[184,33627,33497],{}," doit alors être utilisé pour mettre à jour une ressource existante (voir en créer une nouvelle si elle n'existe\npas). La ressource intégrale doit être passée. Pour une mise à jour partielle, il faut voir la méthode ",[184,33630,33631],{},"PATCH",[12,33633,33634,33635,33637,33638,33640,33641,939,33643,33645],{},"S'il y a création d'une ressource, comme pour ",[184,33636,33494],{},", l'API doit retourner un ",[184,33639,33576],{},". Dans le cas d'une mise à\njour la méthode retourne alors ",[184,33642,33589],{},[184,33644,33504],{}," suivant que la réponse possède un contenu ou non.",[12,33647,33598,33648,33650],{},[184,33649,33497],{}," mettant à jour la ressource, le client devra invalider son cache sur la base de l'URI. La méthode est\nconsidérée comme idempotent car c'est la ressource entière qui est remplacée.",[27533,33652,33631],{"id":33653},"patch",[12,33655,33656,33658,33659,179],{},[184,33657,33631],{}," est utilisé pour mettre à jour une ressource partiellement. La mise à jour doit être également atomique",[17400,33660,33661],{},[49,33662,3696],{"href":33663,"ariaDescribedBy":33664,"dataFootnoteRef":1646,"id":33665},"#user-content-fn-atomic",[17406],"user-content-fnref-atomic",[12,33667,33668,33669,33674],{},"Dans la RFC ",[49,33670,33673],{"href":33671,"rel":33672},"https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc5789",[347],"RFC 5789"," de la méthode PATCH, il y est écrit que le contenu doit\ncontenir la description des modifications :",[2105,33676,33678],{"className":18051,"code":33677,"language":18053,"meta":1646,"style":1646},"PATCH \u002Ffile.txt HTTP\u002F1.1\nHost: www.example.com\nContent-Type: application\u002Fexample\nIf-Match: \"e0023aa4e\"\nContent-Length: 100\n\n[description of changes]\n",[184,33679,33680,33688,33693,33698,33706,33714,33718],{"__ignoreMap":1646},[2113,33681,33682,33685],{"class":2115,"line":2116},[2113,33683,33684],{"class":2119},"PATCH \u002Ffile.txt HTTP\u002F",[2113,33686,33687],{"class":2166},"1.1\n",[2113,33689,33690],{"class":2115,"line":1647},[2113,33691,33692],{"class":2119},"Host: www.example.com\n",[2113,33694,33695],{"class":2115,"line":1652},[2113,33696,33697],{"class":2119},"Content-Type: application\u002Fexample\n",[2113,33699,33700,33703],{"class":2115,"line":2185},[2113,33701,33702],{"class":2119},"If-Match: ",[2113,33704,33705],{"class":2149},"\"e0023aa4e\"\n",[2113,33707,33708,33711],{"class":2115,"line":2230},[2113,33709,33710],{"class":2119},"Content-Length: ",[2113,33712,33713],{"class":2166},"100\n",[2113,33715,33716],{"class":2115,"line":2235},[2113,33717,2125],{"emptyLinePlaceholder":1767},[2113,33719,33720,33722,33725,33727,33730],{"class":2115,"line":2241},[2113,33721,4124],{"class":2119},[2113,33723,33724],{"class":19134},"description",[2113,33726,2281],{"class":19134},[2113,33728,33729],{"class":19134}," changes",[2113,33731,26198],{"class":2119},[12,33733,33734],{},"Utiliser une implémentation free-style de la description des changements risque d'être source d'erreurs pour le client\ncomme pour le serveur. En effet, le client pourrait s'attendre dans certains cas à des ajouts dans un tableau, ou au\nremplacement du tableau.",[12,33736,33737],{},"Il existe différentes RFC décrivant la bonne manière de faire pour PATCHer un document JSON. Il vaut mieux alors se baser\ndessus pour développer nos API. Cela permet d'être clair sur le fonctionnement de la méthode.",[12,33739,33740,33741,33746,33747,33752],{},"On peut alors se baser sur les ",[49,33742,33745],{"href":33743,"rel":33744},"https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc6902",[347],"RFC 6902"," et\n",[49,33748,33751],{"href":33749,"rel":33750},"https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc7396",[347],"RFC 7396"," pour décrire le contenu de cette description des modifications.",[12,33754,33755],{},"Pour les API basées sur des XML, référez-vous aux RFC équivalents décrivant les méthodes de PATCH pour le XML.",[12,33757,33758,33759,33761],{},"Si on se base sur la ",[184,33760,33745],{}," on peut alors reprendre l'exemple qui décrit un changement comme étant une liste de\ndifférences à appliquer (noté le Content-Type):",[2105,33763,33765],{"className":18051,"code":33764,"language":18053,"meta":1646,"style":1646},"PATCH \u002Fmy\u002Fdata HTTP\u002F1.1\nHost: example.org\nContent-Length: 326\nContent-Type: application\u002Fjson-patch+json\nIf-Match: \"abc123\"\n\n[\n  { \"op\": \"test\", \"path\": \"\u002Fa\u002Fb\u002Fc\", \"value\": \"foo\" },\n  { \"op\": \"remove\", \"path\": \"\u002Fa\u002Fb\u002Fc\" },\n  { \"op\": \"add\", \"path\": \"\u002Fa\u002Fb\u002Fc\", \"value\": [ \"foo\", \"bar\" ] },\n  { \"op\": \"replace\", \"path\": \"\u002Fa\u002Fb\u002Fc\", \"value\": 42 },\n  { \"op\": \"move\", \"from\": \"\u002Fa\u002Fb\u002Fc\", \"path\": \"\u002Fa\u002Fb\u002Fd\" },\n  { \"op\": \"copy\", \"from\": \"\u002Fa\u002Fb\u002Fd\", \"path\": \"\u002Fa\u002Fb\u002Fe\" }\n]\n",[184,33766,33767,33774,33779,33786,33791,33798,33802,33806,33840,33861,33897,33927,33958,33988],{"__ignoreMap":1646},[2113,33768,33769,33772],{"class":2115,"line":2116},[2113,33770,33771],{"class":2119},"PATCH \u002Fmy\u002Fdata HTTP\u002F",[2113,33773,33687],{"class":2166},[2113,33775,33776],{"class":2115,"line":1647},[2113,33777,33778],{"class":2119},"Host: example.org\n",[2113,33780,33781,33783],{"class":2115,"line":1652},[2113,33782,33710],{"class":2119},[2113,33784,33785],{"class":2166},"326\n",[2113,33787,33788],{"class":2115,"line":2185},[2113,33789,33790],{"class":2119},"Content-Type: application\u002Fjson-patch+json\n",[2113,33792,33793,33795],{"class":2115,"line":2230},[2113,33794,33702],{"class":2119},[2113,33796,33797],{"class":2149},"\"abc123\"\n",[2113,33799,33800],{"class":2115,"line":2235},[2113,33801,2125],{"emptyLinePlaceholder":1767},[2113,33803,33804],{"class":2115,"line":2241},[2113,33805,10710],{"class":2119},[2113,33807,33808,33811,33814,33816,33818,33820,33823,33825,33828,33830,33833,33835,33838],{"class":2115,"line":2246},[2113,33809,33810],{"class":2119},"  { ",[2113,33812,33813],{"class":2330},"\"op\"",[2113,33815,4429],{"class":2119},[2113,33817,8963],{"class":2149},[2113,33819,932],{"class":2119},[2113,33821,33822],{"class":2330},"\"path\"",[2113,33824,4429],{"class":2119},[2113,33826,33827],{"class":2149},"\"\u002Fa\u002Fb\u002Fc\"",[2113,33829,932],{"class":2119},[2113,33831,33832],{"class":2330},"\"value\"",[2113,33834,4429],{"class":2119},[2113,33836,33837],{"class":2149},"\"foo\"",[2113,33839,8129],{"class":2119},[2113,33841,33842,33844,33846,33848,33851,33853,33855,33857,33859],{"class":2115,"line":2464},[2113,33843,33810],{"class":2119},[2113,33845,33813],{"class":2330},[2113,33847,4429],{"class":2119},[2113,33849,33850],{"class":2149},"\"remove\"",[2113,33852,932],{"class":2119},[2113,33854,33822],{"class":2330},[2113,33856,4429],{"class":2119},[2113,33858,33827],{"class":2149},[2113,33860,8129],{"class":2119},[2113,33862,33863,33865,33867,33869,33872,33874,33876,33878,33880,33882,33884,33887,33889,33891,33894],{"class":2115,"line":2085},[2113,33864,33810],{"class":2119},[2113,33866,33813],{"class":2330},[2113,33868,4429],{"class":2119},[2113,33870,33871],{"class":2149},"\"add\"",[2113,33873,932],{"class":2119},[2113,33875,33822],{"class":2330},[2113,33877,4429],{"class":2119},[2113,33879,33827],{"class":2149},[2113,33881,932],{"class":2119},[2113,33883,33832],{"class":2330},[2113,33885,33886],{"class":2119},": [ ",[2113,33888,33837],{"class":2149},[2113,33890,932],{"class":2119},[2113,33892,33893],{"class":2149},"\"bar\"",[2113,33895,33896],{"class":2119}," ] },\n",[2113,33898,33899,33901,33903,33905,33908,33910,33912,33914,33916,33918,33920,33922,33925],{"class":2115,"line":2514},[2113,33900,33810],{"class":2119},[2113,33902,33813],{"class":2330},[2113,33904,4429],{"class":2119},[2113,33906,33907],{"class":2149},"\"replace\"",[2113,33909,932],{"class":2119},[2113,33911,33822],{"class":2330},[2113,33913,4429],{"class":2119},[2113,33915,33827],{"class":2149},[2113,33917,932],{"class":2119},[2113,33919,33832],{"class":2330},[2113,33921,4429],{"class":2119},[2113,33923,33924],{"class":2166},"42",[2113,33926,8129],{"class":2119},[2113,33928,33929,33931,33933,33935,33938,33940,33943,33945,33947,33949,33951,33953,33956],{"class":2115,"line":2533},[2113,33930,33810],{"class":2119},[2113,33932,33813],{"class":2330},[2113,33934,4429],{"class":2119},[2113,33936,33937],{"class":2149},"\"move\"",[2113,33939,932],{"class":2119},[2113,33941,33942],{"class":2330},"\"from\"",[2113,33944,4429],{"class":2119},[2113,33946,33827],{"class":2149},[2113,33948,932],{"class":2119},[2113,33950,33822],{"class":2330},[2113,33952,4429],{"class":2119},[2113,33954,33955],{"class":2149},"\"\u002Fa\u002Fb\u002Fd\"",[2113,33957,8129],{"class":2119},[2113,33959,33960,33962,33964,33966,33969,33971,33973,33975,33977,33979,33981,33983,33986],{"class":2115,"line":2556},[2113,33961,33810],{"class":2119},[2113,33963,33813],{"class":2330},[2113,33965,4429],{"class":2119},[2113,33967,33968],{"class":2149},"\"copy\"",[2113,33970,932],{"class":2119},[2113,33972,33942],{"class":2330},[2113,33974,4429],{"class":2119},[2113,33976,33955],{"class":2149},[2113,33978,932],{"class":2119},[2113,33980,33822],{"class":2330},[2113,33982,4429],{"class":2119},[2113,33984,33985],{"class":2149},"\"\u002Fa\u002Fb\u002Fe\"",[2113,33987,26460],{"class":2119},[2113,33989,33990],{"class":2115,"line":2569},[2113,33991,26198],{"class":2119},[12,33993,33994],{},"Le format à l'avantage d'être complet et standard.",[12,33996,33997,33998,3938],{},"Pour gérer le format dans l'application, on peut retrouver des librairies capables de lire et de générer ce genre de\ndifférenciel (par exemple ",[49,33999,34002],{"href":34000,"rel":34001},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Ffast-json-patch",[347],"fast-json-patch",[12,34004,34005,34006,34008],{},"Avec la ",[184,34007,33751],{},", on peut adapter le Content-Type et avoir un format de patch plus lisible :",[2105,34010,34012],{"className":18051,"code":34011,"language":18053,"meta":1646,"style":1646},"PATCH \u002Ftarget HTTP\u002F1.1\nHost: example.org\nContent-Type: application\u002Fmerge-patch+json\n\n{\n  \"a\":\"z\",\n  \"c\": {\n    \"f\": null\n  }\n}\n",[184,34013,34014,34021,34025,34030,34034,34038,34050,34057,34066,34070],{"__ignoreMap":1646},[2113,34015,34016,34019],{"class":2115,"line":2116},[2113,34017,34018],{"class":2119},"PATCH \u002Ftarget HTTP\u002F",[2113,34020,33687],{"class":2166},[2113,34022,34023],{"class":2115,"line":1647},[2113,34024,33778],{"class":2119},[2113,34026,34027],{"class":2115,"line":1652},[2113,34028,34029],{"class":2119},"Content-Type: application\u002Fmerge-patch+json\n",[2113,34031,34032],{"class":2115,"line":2185},[2113,34033,2125],{"emptyLinePlaceholder":1767},[2113,34035,34036],{"class":2115,"line":2230},[2113,34037,18060],{"class":2119},[2113,34039,34040,34043,34045,34048],{"class":2115,"line":2235},[2113,34041,34042],{"class":2330},"  \"a\"",[2113,34044,15746],{"class":2119},[2113,34046,34047],{"class":2149},"\"z\"",[2113,34049,4706],{"class":2119},[2113,34051,34052,34055],{"class":2115,"line":2241},[2113,34053,34054],{"class":2330},"  \"c\"",[2113,34056,8091],{"class":2119},[2113,34058,34059,34062,34064],{"class":2115,"line":2246},[2113,34060,34061],{"class":2330},"    \"f\"",[2113,34063,4429],{"class":2119},[2113,34065,18749],{"class":2166},[2113,34067,34068],{"class":2115,"line":2464},[2113,34069,2572],{"class":2119},[2113,34071,34072],{"class":2115,"line":2085},[2113,34073,2599],{"class":2119},[12,34075,34076],{},"Par contre ce format a ses limitations :",[506,34078,34079,34082],{},[370,34080,34081],{},"il ne peut que mettre à jour des éléments (pas de suppression)",[370,34083,34084],{},"dans le cadre de valeurs comme des tableaux, c'est le contenu intégral qui est remplacé. Pas d'ajout ou de merge.",[12,34086,34087],{},[155,34088,34089,34090,34092],{},"Il est alors très important de définir dans sa documentation la liste des ",[184,34091,33236],{}," accepté par sont API",[27533,34094,34096],{"id":34095},"delete","DELETE",[12,34098,33522,34099,34101],{},[184,34100,34096],{}," porte bien son nom car elle sert à supprimer une ressource identifiée par son URI.",[12,34103,34104,34105,34107,34108,34110,34111,179],{},"Si la suppression a été effectuée, l'API retourne un ",[184,34106,33504],{}," si le contenu de la réponse inclut un status de la\nsuppression ou un ",[184,34109,33589],{}," sinon. Si la ressource n'existe pas, l'API retourne alors un ",[184,34112,34113],{},"404 Not Found",[12,34115,34116,34117,34120],{},"L'API peut retourner un ",[184,34118,34119],{},"202 Accepted"," si la demande a été prise en compte mais sera traitée\nultérieurement.",[12,34122,34123,34124,34126,34127,34129],{},"Plusieurs appels à la méthode ",[184,34125,34096],{}," sur une même URI ne doivent pas avoir d'effets de bord. La seule différence est\nsur la réponse qui passe à ",[184,34128,34113],{}," une fois que la ressource a été supprimée.",[27533,34131,34133],{"id":34132},"link-et-unlink","LINK et UNLINK",[12,34135,34136,34137,792,34140,34143],{},"Les méthodes ",[184,34138,34139],{},"LINK",[184,34141,34142],{},"UNLINK"," servent à lier\u002Fdélier des ressources entre elles.",[12,34145,34146,34147,179],{},"Je ne détaillerai pas leurs utilisations car elles sont peut utilisées. Vous pouvez en lire plus sur le site de\nl'",[49,34148,34151],{"href":34149,"rel":34150},"https:\u002F\u002Ftools.ietf.org\u002Fid\u002Fdraft-snell-link-method-01.html",[347],"IETF",[133,34153,34155],{"id":34154},"respecter-les-codes-retour-http","Respecter les codes retour HTTP",[12,34157,34158,34159,34161],{},"La norme REST se base sur les code de retour HTTP. Par exemple un code HTTP ",[184,34160,34113],{}," signifie que la ressource ne\npeut pas être trouvée. Il est important pour les applications clientes de respecter ses codes HTTP.",[12,34163,34164],{},"Commençons par un petit rappel sur les codes de retour.",[12,34166,34167],{},"Ces derniers sont classés en 5 catégories:",[506,34169,34170,34173,34176,34179,34182],{},[370,34171,34172],{},"1xx: Informationnel. Ces codes HTTP sont utilisés pour communiquer des informations sur l'état du transfert à un\nniveau protocolaire (donc pas au niveau applicatif).",[370,34174,34175],{},"2xx: Succès. Ces codes HTTP sont utilisés pour signifier le succès de la requête.",[370,34177,34178],{},"3xx: Redirection. Le client doit prendre des actions supplémentaires afin de terminer la requête.",[370,34180,34181],{},"4xx: Erreur dûe à la requête du client. C'est de la faute du client si la réponse ne peut pas être fournie. Il doit\nla reformuler (si la requête est mauvaise) ou attendre dans le cas d'un rate limit.",[370,34183,34184],{},"5xx: Erreur dûe au serveur. Le serveur n'a pas réussi à répondre au client, et c'est de son entière responsabilité.",[12,34186,34187],{},"Il est important de bien choisir son code de retour pour que le client puisse bien comprendre quelles actions il peut\nentreprendre pour avoir sa réponse. Envoyer un code 5xx alors que la requête ne contient pas tout les champs\nobligatoires est le meilleur moyen pour remplir votre boîte de support.",[12,34189,34190],{},"Inversement renvoyer un code 4xx pour une erreur côté serveur, risque de perturber l'utilisateur qui ne comprendra pas\npourquoi l'API ne fonctionne pas comme attendu.",[12,34192,34193],{},"Passons en revue quelques exemples de codes utilisables (j'ai fait uniquement une sélection de codes qui peuvent avoir un intérêt\npour une API REST) :",[27533,34195,34197],{"id":34196},"_2xx","2xx",[34199,34200,33504],"h5",{"id":34201},"_200-ok",[12,34203,34204],{},"Parfait, pas de problème, c'est le succès complet.",[12,34206,34207,34208,34210],{},"Contrairement à la réponse 204 ci-dessous, il est nécessaire de fournir un corps au message (sauf pour le header\n",[184,34209,33519],{}," qui ne renvoit jamais de corps de message).",[34199,34212,33576],{"id":34213},"_201-created",[12,34215,34216],{},"Une nouvelle ressource a été créé avec succès et ajoutée à la collection. Le corps du message doit être vide.\nLe client peut invalider le cache de la collection.",[12,34218,34219,34220,179],{},"Ce code de retour doit être utilisé une fois que la ressource a été créé par le serveur et l'emplacement de celle-ci\ndoit être retourné dans le header HTTP ",[184,34221,29462],{},[12,34223,34224],{},"Si la ressource ne peut être créé tout de suite, alors voir le header suivant.",[34199,34226,34119],{"id":34227},"_202-accepted",[12,34229,34230],{},"La requête a bien été reçue et sera traitée ultérieurement (via un batch par exemple). La création peut se produire, ou\nune erreur peut arriver ultérieurement.",[12,34232,34233],{},"Dans le corps du message le serveur doit retourner au client le maximum d'informations qui permettent à ce dernier de\nsavoir quand sa demande sera traitée. Par exemple on peut mettre dans le corps du message :",[506,34235,34236,34239,34242,34245],{},[370,34237,34238],{},"Le statut de la demande",[370,34240,34241],{},"La date d'exécution du process",[370,34243,34244],{},"L'URI pour accéder à la demande et obtenir son statut",[370,34246,34247],{},"Une estimation du temps de traitement.",[34199,34249,33589],{"id":34250},"_204-no-content",[12,34252,34253],{},"Le serveur a bien traité la demande mais aucune information ne sera retournée dans le body.",[12,34255,34256,34257,932,34259,932,34261,34263,34264,34266],{},"Ce code HTTP est généralement utilisé avec les méthodes ",[184,34258,33494],{},[184,34260,33497],{},[184,34262,34096],{}," quand le serveur n'a aucune information\n(au dela du header Location) à retourner au client. Il peut également être utilisé avec les méthodes ",[184,34265,26207],{}," pour indiquer\nque la ressource existe (contrairement au 404) mais aucune représentation de la ressource n'existe.",[12,34268,34269],{},"La réponse ne doit jamais contenir de contenu.",[12,34271,34272,34273,34275],{},"Sauf si un header (tel que ",[184,34274,29462],{},") l'indique, le client n'a pas besoin de modifier son cache.",[34199,34277,34279],{"id":34278},"_206-partial-content","206 Partial Content",[12,34281,34282],{},"Utilisé avec le header Range dans la requête pour n'envoyer qu'un résultat partiel.",[12,34284,34285,34286,179],{},"Cette réponse peut-être utilisée dans les requêtes de pagination en conjonction uniquement avec le header ",[184,34287,34288],{},"Range",[27533,34290,34292],{"id":34291},"_3xx","3xx",[34199,34294,34296],{"id":34295},"_301-moved-permanently","301 Moved Permanently",[12,34298,34299,34300,34302],{},"La ressource a été déplacée définitivement à un autre endroit. Le header ",[184,34301,29462],{}," contient le nouvel emplacement. Le\nrésultat de la requête peut être caché.",[12,34304,34305],{},"On peut utiliser ce retour pour signifier un changement de l'API suffisamment important pour que cette version ne soit\nplus à jour, mais il est préférable de versionner l'API, plutôt que d'utiliser les redirections.",[34199,34307,34309],{"id":34308},"_302-found","302 Found",[12,34311,34312,34313,34315],{},"La ressource a été déplacée temporairement à un autre endroit. Le header ",[184,34314,29462],{}," contient l'emplacement. La requête\nne peut être cachée que si les headers de caches sont présents.",[12,34317,34318,34319,34321,34322,34324],{},"Le client doit refaire exactement la même requête mais sur le nouvel emplacement défini dans le header ",[184,34320,29462],{},".\nCette règle n'est pas respectée par les navigateurs qui remplacent la méthode par ",[184,34323,26207],{}," et c'est pour ça que les codes\n303 et 307 ont été créés.",[34199,34326,34328],{"id":34327},"_303-see-other","303 See Other",[12,34330,34331,34332,939,34334,34336],{},"Ce code de retour, généralement utilisé avec des ",[184,34333,33497],{},[184,34335,33494],{}," indique que le serveur a terminé son travail mais\npréfère retourner au client une URI où trouver la ressource ou un message de status.",[12,34338,34339,34340,34342],{},"Le client peut alors décider ou non d'aller voir à l'adresse donnée via la méthode ",[184,34341,26207],{}," uniquement.",[12,34344,34345],{},"La réponse ne peut pas être modifiée.",[34199,34347,34349],{"id":34348},"_304-not-modified","304 Not Modified",[12,34351,34352,34353,939,34355,34357,34358,792,34361,3938],{},"Utilisé généralement avec une requête ",[184,34354,26207],{},[184,34356,33519],{}," pour indiquer que la ressource n'a pas été modifiée par rapport aux\ninformations spécifiées dans le header (",[184,34359,34360],{},"If-None-Match",[184,34362,34363],{},"If-Modified-Since",[12,34365,34366],{},"Cette réponse est utilisée pour économiser du temps de traitement et de la bande passsante côté serveur et côté client,\nvu que le client peut reprendre la réponse qui se présente dans son cache.",[12,34368,34369,34370,4706,34373,932,34376,932,34378,932,34381,34384,34385,179],{},"Généralement, une réponse précédente a été déjà envoyée au client avec les headers suivants: ",[184,34371,34372],{},"Cache-Control",[184,34374,34375],{},"Content-Location",[184,34377,36],{},[184,34379,34380],{},"ETag",[184,34382,34383],{},"Expires",", et ",[184,34386,34387],{},"Vary",[34199,34389,34391],{"id":34390},"_307-temporary-redirect","307 Temporary Redirect",[12,34393,34394,34395,34397],{},"Le serveur indique que la ressource doit être récupérée à un autre endroit en ré-envoyant la requête telle quelle à\nl'URI spécifiée dans le header ",[184,34396,29462],{},". Toute future requête doit continuer à être envoyée au endpoint actuel.",[12,34399,34400,34401,179],{},"La différence entre une réponse 302 et 307 réside dans le fait que la requête ne doit pas être modifiée lors de l'appel\nà l'URI de la réponse retournée par la 307. Avec une 302, certains clients changent incorectement cette valeur en ",[184,34402,26207],{},[27533,34404,34406],{"id":34405},"_4xx","4xx",[34199,34408,34410],{"id":34409},"_400-bad-request","400 Bad Request",[12,34412,34413],{},"Le serveur ne comprend pas la requête à cause de sa syntaxe. Le client ne doit pas répéter sa requête sans\nmodification.",[12,34415,34416],{},"Ce message peut être utilisé quand aucun autre message de type 4xx n'est approprié.",[34199,34418,34420],{"id":34419},"_401-unauthorized","401 Unauthorized",[12,34422,34423],{},"La ressource nécessite une authentification. Le client peut répéter sa requête s'il possède le bon header\nd'Authorization.",[12,34425,34426],{},"Si le header d'authentification est présent mais que le serveur retourne une 401, cela signifie que\nl'authentification a été refusée avec ces paramètres.",[34199,34428,34430],{"id":34429},"_403-forbidden","403 Forbidden",[12,34432,34433],{},"L'authentification du client est bien reconnue par le serveur, mais le client ne possède pas les autorisations\nnécessaires pour accéder à la ressource.",[12,34435,34436],{},"Le client ne doit pas répéter la demande.",[34199,34438,34113],{"id":34439},"_404-not-found",[12,34441,34442],{},"La ressource n'existe pas sur le serveur, pour le moment. Le client peut potentiellement refaire la requête plus tard.",[12,34444,34445],{},"Ce status est également utilisé lorsque le serveur ne veut pas révéler pourquoi la requête a été refusée.",[34199,34447,34449],{"id":34448},"_409-conflict","409 Conflict",[12,34451,34452],{},"La requête est bien formée (contrairement à 400) mais elle ne peut pas être complétée dû à l'état de la ressource.",[34199,34454,34456],{"id":34455},"_410-gone","410 Gone",[12,34458,34459],{},"La ressource n'existe pas sur le serveur, mais le serveur sait (par un moyen interne) que la ressource a existé par le\npassé et qu'il n'est pas dans la capacité de fournir une URI où trouver la nouvelle version.",[34199,34461,34463],{"id":34462},"_416-requested-range-not-satisfiable","416 Requested Range Not Satisfiable",[12,34465,34466,34467,34469],{},"L'intervalle utilisé dans le header ",[184,34468,34288],{}," ne peut pas être satisfait.",[34199,34471,34473],{"id":34472},"_429-too-many-requests","429 Too Many Requests",[12,34475,34476],{},"Utilisé pour prévenir le client qu'il a atteint ses limites (rate limit)",[27533,34478,34480],{"id":34479},"_5xx","5xx",[34199,34482,34484],{"id":34483},"_500-internal-server-error","500 Internal Server Error",[12,34486,34487],{},"Le serveur clame haut et fort \"Une erreur inattendue s’est produite\".",[12,34489,34490],{},"L'erreur 500 n'est jamais de la faute du client. Le client peut donc retenter sa requête plus tard.",[34199,34492,34494],{"id":34493},"_501-not-implemented","501 Not Implemented",[12,34496,34497],{},"Pratique pour les API en cours de développement, l'API existe mais n'a pas encore été implémentée.",[34199,34499,34501],{"id":34500},"_503-service-unavailable","503 Service Unavailable",[12,34503,34504],{},"Le serveur n'est pas encore prêt à répondre à la requête (par exemple il démarre ou il s'arrête).",[133,34506,34508],{"id":34507},"utilise-la-notion-de-lien-hypermedia-hateoas","Utilise la notion de lien hypermedia (HATEOAS)",[12,34510,34511],{},"HATEOAS, c'est pour Hypermedia As The Engine Of Application State.",[12,34513,34514],{},"Peu d'API sont REST HATEOAS. Le but est de REST HATEOAS est de lier les réponses REST les unes aux autres tout comme\nle Web lie les pages les unes aux autres. Le principe est donc d'ajouter la notion de lien hypermédia dans une API.",[12,34516,34517],{},"Dans la théorie, un client pourra donc s'adapter aux changement de l'API grâce à ces liens.",[12,34519,34520,34521,34526,34527,179],{},"Pour cela vous pouvez pouvez dans cadre du JSON vous baser sur\n",[49,34522,34525],{"href":34523,"rel":34524},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHypertext_Application_Language",[347],"HAL"," ou sur ",[49,34528,34531],{"href":34529,"rel":34530},"https:\u002F\u002Fjsonapi.org\u002F",[347],"JSON:API",[27533,34533,34531],{"id":34534},"jsonapi",[12,34536,34537],{},"Voici un exemple utilisant la syntaxe JSON:API.",[2105,34539,34541],{"className":18051,"code":34540,"language":18053,"meta":1646,"style":1646},"{\n  \"links\": {\n    \"self\": \"\u002Fapi\u002Fhosts\",\n    \"next\": \"\u002Fapi\u002Fhosts?page[offset]=2\",\n    \"last\": \"\u002Fapi\u002Fhosts?page[offset]=10\"\n  },\n  \"data\": [\n    {\n      \"type\": \"hosts\",\n      \"id\": \"pc-ulrich\",\n      \"attributes\": {\n        \"dhcp\": [\n          {\n            \"address\": \"192.168.101\",\n            \"start\": 0,\n            \"end\": 50\n          }\n        ]\n      },\n      \"relationships\": {\n        \"backups\": {\n          \"links\": {\n            \"self\": \"\u002Fapi\u002Fhosts\u002Fpc-ulrich\u002Frelationships\u002Fbackups\",\n            \"related\": \"\u002Fapi\u002Fhosts\u002Fpc-ulrich\u002Fbackups\"\n          },\n          \"data\": [\n            { \"type\": \"backups\", \"id\": 0 },\n            { \"type\": \"backups\", \"id\": 1 },\n            { \"type\": \"backups\", \"id\": 2 },\n            { \"type\": \"backups\", \"id\": 3 }\n          ]\n        }\n      },\n      \"links\": {\n        \"self\": \"\u002Fhosts\u002Fpc-ulrich\"\n      }\n    }\n  ]\n}\n",[184,34542,34543,34547,34554,34566,34578,34588,34592,34599,34603,34615,34627,34634,34641,34646,34658,34669,34679,34683,34688,34692,34699,34706,34713,34725,34735,34739,34746,34770,34790,34810,34830,34835,34839,34843,34850,34860,34864,34868,34872],{"__ignoreMap":1646},[2113,34544,34545],{"class":2115,"line":2116},[2113,34546,18060],{"class":2119},[2113,34548,34549,34552],{"class":2115,"line":1647},[2113,34550,34551],{"class":2330},"  \"links\"",[2113,34553,8091],{"class":2119},[2113,34555,34556,34559,34561,34564],{"class":2115,"line":1652},[2113,34557,34558],{"class":2330},"    \"self\"",[2113,34560,4429],{"class":2119},[2113,34562,34563],{"class":2149},"\"\u002Fapi\u002Fhosts\"",[2113,34565,4706],{"class":2119},[2113,34567,34568,34571,34573,34576],{"class":2115,"line":2185},[2113,34569,34570],{"class":2330},"    \"next\"",[2113,34572,4429],{"class":2119},[2113,34574,34575],{"class":2149},"\"\u002Fapi\u002Fhosts?page[offset]=2\"",[2113,34577,4706],{"class":2119},[2113,34579,34580,34583,34585],{"class":2115,"line":2230},[2113,34581,34582],{"class":2330},"    \"last\"",[2113,34584,4429],{"class":2119},[2113,34586,34587],{"class":2149},"\"\u002Fapi\u002Fhosts?page[offset]=10\"\n",[2113,34589,34590],{"class":2115,"line":2235},[2113,34591,8490],{"class":2119},[2113,34593,34594,34597],{"class":2115,"line":2241},[2113,34595,34596],{"class":2330},"  \"data\"",[2113,34598,18219],{"class":2119},[2113,34600,34601],{"class":2115,"line":2246},[2113,34602,18224],{"class":2119},[2113,34604,34605,34608,34610,34613],{"class":2115,"line":2464},[2113,34606,34607],{"class":2330},"      \"type\"",[2113,34609,4429],{"class":2119},[2113,34611,34612],{"class":2149},"\"hosts\"",[2113,34614,4706],{"class":2119},[2113,34616,34617,34620,34622,34625],{"class":2115,"line":2085},[2113,34618,34619],{"class":2330},"      \"id\"",[2113,34621,4429],{"class":2119},[2113,34623,34624],{"class":2149},"\"pc-ulrich\"",[2113,34626,4706],{"class":2119},[2113,34628,34629,34632],{"class":2115,"line":2514},[2113,34630,34631],{"class":2330},"      \"attributes\"",[2113,34633,8091],{"class":2119},[2113,34635,34636,34639],{"class":2115,"line":2533},[2113,34637,34638],{"class":2330},"        \"dhcp\"",[2113,34640,18219],{"class":2119},[2113,34642,34643],{"class":2115,"line":2556},[2113,34644,34645],{"class":2119},"          {\n",[2113,34647,34648,34651,34653,34656],{"class":2115,"line":2569},[2113,34649,34650],{"class":2330},"            \"address\"",[2113,34652,4429],{"class":2119},[2113,34654,34655],{"class":2149},"\"192.168.101\"",[2113,34657,4706],{"class":2119},[2113,34659,34660,34663,34665,34667],{"class":2115,"line":2575},[2113,34661,34662],{"class":2330},"            \"start\"",[2113,34664,4429],{"class":2119},[2113,34666,3095],{"class":2166},[2113,34668,4706],{"class":2119},[2113,34670,34671,34674,34676],{"class":2115,"line":2596},[2113,34672,34673],{"class":2330},"            \"end\"",[2113,34675,4429],{"class":2119},[2113,34677,34678],{"class":2166},"50\n",[2113,34680,34681],{"class":2115,"line":3192},[2113,34682,23088],{"class":2119},[2113,34684,34685],{"class":2115,"line":3213},[2113,34686,34687],{"class":2119},"        ]\n",[2113,34689,34690],{"class":2115,"line":3236},[2113,34691,12282],{"class":2119},[2113,34693,34694,34697],{"class":2115,"line":3248},[2113,34695,34696],{"class":2330},"      \"relationships\"",[2113,34698,8091],{"class":2119},[2113,34700,34701,34704],{"class":2115,"line":4899},[2113,34702,34703],{"class":2330},"        \"backups\"",[2113,34705,8091],{"class":2119},[2113,34707,34708,34711],{"class":2115,"line":1777},[2113,34709,34710],{"class":2330},"          \"links\"",[2113,34712,8091],{"class":2119},[2113,34714,34715,34718,34720,34723],{"class":2115,"line":4931},[2113,34716,34717],{"class":2330},"            \"self\"",[2113,34719,4429],{"class":2119},[2113,34721,34722],{"class":2149},"\"\u002Fapi\u002Fhosts\u002Fpc-ulrich\u002Frelationships\u002Fbackups\"",[2113,34724,4706],{"class":2119},[2113,34726,34727,34730,34732],{"class":2115,"line":4961},[2113,34728,34729],{"class":2330},"            \"related\"",[2113,34731,4429],{"class":2119},[2113,34733,34734],{"class":2149},"\"\u002Fapi\u002Fhosts\u002Fpc-ulrich\u002Fbackups\"\n",[2113,34736,34737],{"class":2115,"line":4976},[2113,34738,23722],{"class":2119},[2113,34740,34741,34744],{"class":2115,"line":4993},[2113,34742,34743],{"class":2330},"          \"data\"",[2113,34745,18219],{"class":2119},[2113,34747,34748,34751,34754,34756,34759,34761,34764,34766,34768],{"class":2115,"line":5013},[2113,34749,34750],{"class":2119},"            { ",[2113,34752,34753],{"class":2330},"\"type\"",[2113,34755,4429],{"class":2119},[2113,34757,34758],{"class":2149},"\"backups\"",[2113,34760,932],{"class":2119},[2113,34762,34763],{"class":2330},"\"id\"",[2113,34765,4429],{"class":2119},[2113,34767,3095],{"class":2166},[2113,34769,8129],{"class":2119},[2113,34771,34772,34774,34776,34778,34780,34782,34784,34786,34788],{"class":2115,"line":5018},[2113,34773,34750],{"class":2119},[2113,34775,34753],{"class":2330},[2113,34777,4429],{"class":2119},[2113,34779,34758],{"class":2149},[2113,34781,932],{"class":2119},[2113,34783,34763],{"class":2330},[2113,34785,4429],{"class":2119},[2113,34787,3686],{"class":2166},[2113,34789,8129],{"class":2119},[2113,34791,34792,34794,34796,34798,34800,34802,34804,34806,34808],{"class":2115,"line":5042},[2113,34793,34750],{"class":2119},[2113,34795,34753],{"class":2330},[2113,34797,4429],{"class":2119},[2113,34799,34758],{"class":2149},[2113,34801,932],{"class":2119},[2113,34803,34763],{"class":2330},[2113,34805,4429],{"class":2119},[2113,34807,2547],{"class":2166},[2113,34809,8129],{"class":2119},[2113,34811,34812,34814,34816,34818,34820,34822,34824,34826,34828],{"class":2115,"line":5057},[2113,34813,34750],{"class":2119},[2113,34815,34753],{"class":2330},[2113,34817,4429],{"class":2119},[2113,34819,34758],{"class":2149},[2113,34821,932],{"class":2119},[2113,34823,34763],{"class":2330},[2113,34825,4429],{"class":2119},[2113,34827,3696],{"class":2166},[2113,34829,26460],{"class":2119},[2113,34831,34832],{"class":2115,"line":5062},[2113,34833,34834],{"class":2119},"          ]\n",[2113,34836,34837],{"class":2115,"line":5098},[2113,34838,4578],{"class":2119},[2113,34840,34841],{"class":2115,"line":5117},[2113,34842,12282],{"class":2119},[2113,34844,34845,34848],{"class":2115,"line":5142},[2113,34846,34847],{"class":2330},"      \"links\"",[2113,34849,8091],{"class":2119},[2113,34851,34852,34855,34857],{"class":2115,"line":5148},[2113,34853,34854],{"class":2330},"        \"self\"",[2113,34856,4429],{"class":2119},[2113,34858,34859],{"class":2149},"\"\u002Fhosts\u002Fpc-ulrich\"\n",[2113,34861,34862],{"class":2115,"line":5163},[2113,34863,27855],{"class":2119},[2113,34865,34866],{"class":2115,"line":5169},[2113,34867,4665],{"class":2119},[2113,34869,34870],{"class":2115,"line":5175},[2113,34871,18491],{"class":2119},[2113,34873,34874],{"class":2115,"line":5180},[2113,34875,2599],{"class":2119},[12,34877,34878],{},"Comme vous pouvez voir, l'idée est de pouvoir à la lecture de la réponse comprendre automatiquement les relations\nentre les différents composants de l'API.",[27533,34880,34882],{"id":34881},"faut-il-faire-une-api-hateaos","Faut-il faire une API HATEAOS",[12,34884,34885],{},"Le but d'une telle API est de permettre au client de la découvrir automatiquement. Il serait envisageable alors de\ncréer un client capable, sur la base de l'API et des types de données qui y sont décrits, de pouvoir génerer une IHM\ndirectement.",[12,34887,34888],{},"Même si le principe d'une API HATEAOS est louable, le temps d'écriture d'une telle API est très consommatrice de temps.\nDe plus, généralement, un client est généralement créé pour des utilisateurs et non par une API.",[12,34890,34891],{},"Une bonne documentation peut amplement suffir pour développer une application, et comprendre les interactions entre\nles différentes APIs ?",[133,34893,34895],{"id":34894},"générér-sa-documentation-avec-swagger","Générér sa documentation avec Swagger",[12,34897,34898],{},"Il existe de très bon outils pour écrire sa documentation.",[12,34900,34901],{},"L'un d'entre eux s'appelle Swagger basé sur la spécification OpenAPI. Il y a alors plusieurs manières de faire pour\ngénérer sa documention qui dépendent également des capacités du language.",[506,34903,34904,34907,34913],{},[370,34905,34906],{},"Sur un language typé, on peut générer sa documentation à partir du code. Il est alors possible via des annotations\nou des commentaires du code de définir les informations.",[370,34908,34909,34910,34912],{},"Il est également possible d'écrire le Swagger directement, et de générer le code après coup. Cela à l'avantage de\npouvoir travailler l'API avant sans écrire une ligne de code.",[15567,34911],{},"L'important sur ces deux méthodes c'est que le code de l'application et le code du Swagger soient synchronisé.",[370,34914,34915],{},"Si l'API a déjà été développée, il est beaucoup plus dur de générer une API par dessus. Surtout si le language est\nnon typé, et que toute la description doit être synchronisée à la main avec les objets de l'application, il y aura\nalors un risque que la documentation ne soit pas à jour.",[12,34917,34918],{},[121,34919],{"alt":34920,"src":34921},"Swagger WoodstockBackup","\u002FProgrammation\u002Fcreation-api\u002Fdocumentation-swagger.png",[133,34923,34925],{"id":34924},"cacher-ses-requêtes","Cacher ses requêtes",[12,34927,34928],{},"Il est important côté client de cacher les requêtes afin de rester en dessous du Rate Limit, et aussi d'économiser les\nressources de bande passante de l'application.",[12,34930,34931],{},"Comme dit plus haut par contre, il ne faut pas que le serveur prenne comme hypothèse que le client va cacher les\nrequêtes au risque de se faire saturer par des requêtes. Côté serveur, il faut définir des limites d'accès de l'API,\net surtout définir également les headers de Cache qui vont bien afin d'avertir le client de la durée pendant laquelle\nil doit garder la requête.",[12,34933,34934],{},"En effet seul le serveur connaît fonctionnellement combien de temps une donnée peut être gardée.",[133,34936,34938],{"id":34937},"quelques-références","Quelques références",[506,34940,34941,34947,34953,34959,34965],{},[370,34942,34943],{},[49,34944,34945],{"href":34945,"rel":34946},"https:\u002F\u002Fzestedesavoir.com\u002Ftutoriels\u002F299\u002Fla-theorie-rest-restful-et-hateoas\u002F",[347],[370,34948,34949],{},[49,34950,34951],{"href":34951,"rel":34952},"https:\u002F\u002Fwww.vinaysahni.com\u002Fbest-practices-for-a-pragmatic-restful-api",[347],[370,34954,34955],{},[49,34956,34957],{"href":34957,"rel":34958},"https:\u002F\u002Fstackoverflow.blog\u002F2020\u002F03\u002F02\u002Fbest-practices-for-rest-api-design\u002F",[347],[370,34960,34961],{},[49,34962,34963],{"href":34963,"rel":34964},"https:\u002F\u002Frestfulapi.net\u002Fhttp-methods\u002F",[347],[370,34966,34967],{},[49,34968,34969],{"href":34969,"rel":34970},"https:\u002F\u002Fwilliamdurand.fr\u002F2014\u002F02\u002F14\u002Fplease-do-not-patch-like-an-idiot\u002F",[347],[23219,34972,34974,34977],{"className":34973,"dataFootnotes":1646},[23222],[128,34975,23227],{"className":34976,"id":17406},[23226],[367,34978,34979,34990,35003],{},[370,34980,34982,34983,34985,34986],{"id":34981},"user-content-fn-getter","Ce que j'ai déjà vu faire. Pour préciser les choses, un getter doit être ",[362,34984,33534],{},". Cela signifie que l'appel ",[49,34987,23240],{"href":34988,"ariaLabel":23237,"className":34989,"dataFootnoteBackref":1646},"#user-content-fnref-getter",[23239],[370,34991,34993,34998,34999],{"id":34992},"user-content-fn-idempotent",[49,34994,34997],{"href":34995,"rel":34996},"https:\u002F\u002Ffr.wikipedia.org\u002Fwiki\u002FIdempotence#:~:text=En%20math%C3%A9matiques%20et%20en%20informatique,applique%20une%20ou%20plusieurs%20fois.",[347],"En mathématiques et en informatique, l'idempotence signifie qu'une opération a le même effet qu'on l'applique une ou plusieurs fois.",": Dans notre cas cela signifie qu'elles n'ont pas d'effets de bord. ",[49,35000,23240],{"href":35001,"ariaLabel":23252,"className":35002,"dataFootnoteBackref":1646},"#user-content-fnref-idempotent",[23239],[370,35004,35006,35007],{"id":35005},"user-content-fn-atomic","Ce qui veux dire que tout est fait ou rien. ",[49,35008,23240],{"href":35009,"ariaLabel":35010,"className":35011,"dataFootnoteBackref":1646},"#user-content-fnref-atomic","Back to reference 3",[23239],[3358,35013,35014],{},"html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sLaUg, html code.shiki .sLaUg{--shiki-default:#FFFFFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}",{"title":1646,"searchDepth":1647,"depth":1647,"links":35016},[35017,35026],{"id":33223,"depth":1647,"text":33224,"children":35018},[35019,35020,35021,35022,35023,35024,35025],{"id":33240,"depth":1652,"text":33241},{"id":33454,"depth":1652,"text":33455},{"id":34154,"depth":1652,"text":34155},{"id":34507,"depth":1652,"text":34508},{"id":34894,"depth":1652,"text":34895},{"id":34924,"depth":1652,"text":34925},{"id":34937,"depth":1652,"text":34938},{"id":17406,"depth":1647,"text":23227},"2020-11-02",{"type":9,"value":35029},[35030,35032,35034,35048,35050,35052,35054],[12,35031,26097],{},[12,35033,26100],{},[506,35035,35036,35040,35044],{},[370,35037,35038],{},[49,35039,26108],{"href":26107},[370,35041,35042],{},[49,35043,26114],{"href":26113},[370,35045,35046],{},[49,35047,26120],{"href":26119},[128,35049,33224],{"id":33223},[12,35051,33227],{},[12,35053,33230],{},[12,35055,33233,35056,33237],{},[184,35057,33236],{},{"planet":1767},"\u002Fpost\u002Fcreation-api-2",{"title":33200,"description":26097},"creation-api-2","posts\u002FProgrammation\u002F2020-11-02_creation-api-2",[29161,26527,29162,3510,3511],"qRIJgww-9nhPwt3g0Lbn9M52urBRQ1PwFy4IGXjFHQI",{"id":35066,"title":35067,"author":7,"body":35068,"category":2057,"categorySlug":2058,"date":35925,"description":26097,"excerpt":35926,"extension":1764,"location":1765,"meta":35950,"navigation":1767,"path":35951,"published":1767,"seo":35952,"slug":35953,"stem":35954,"tags":35955,"timeToRead":2514,"__hash__":35956},"posts\u002Fposts\u002FProgrammation\u002F2020-10-11_creation-api-1.md","Comment créer une bonne API Web - Partie 1",{"type":9,"value":35069,"toc":35915},[35070,35072,35075,35089,35092,35095,35098,35101,35107,35124,35127,35144,35147,35151,35162,35165,35171,35174,35177,35180,35183,35186,35189,35193,35196,35199,35204,35207,35227,35230,35258,35264,35267,35271,35274,35277,35296,35299,35302,35396,35407,35411,35414,35427,35540,35546,35549,35654,35657,35663,35787,35791,35794,35844,35848,35851,35854,35857,35860,35863,35866,35888,35891,35893,35896,35912],[12,35071,26097],{},[12,35073,35074],{},"Je souhaite vous parler de l'écriture d'API. Je vais découper cet article en 3 parties:",[506,35076,35077,35081,35085],{},[370,35078,35079],{},[49,35080,26108],{"href":26107},[370,35082,35083],{},[49,35084,26114],{"href":26113},[370,35086,35087],{},[49,35088,26120],{"href":26119},[12,35090,35091],{},"Je me limiterai au WEB et aux normes REST et GraphQL même s'il y a d'autres normes\u002Fframeworks pour écrire des API.",[12,35093,35094],{},"Commençons donc par le début ! Qu'est-ce qu'une API ? API signifie Application Programming Interface. C'est une\ninterface de programmation prête à être consommée par un client.",[12,35096,35097],{},"Par exemple quand on développe une librairie (en C\u002FC++, voir un module NodeJS, ...), on définit une liste de méthodes\nque l'on rend publique et qui sont utilisées pour appeler cette librairie. Ces méthodes sont alors utilisées par des\nclients. L'API c'est ce contrat entre la librairie et le client.",[12,35099,35100],{},"Dans le cadre d'un site internet, l'API est le contrat entre un site internet et le client qui l'appelle. Comme tout\ncontrat il faut que celui-ci soit clairement défini si on veut que ça se passe bien entre les clients et les services.",[12,35102,35103,35104,15746],{},"Il est important pour une bonne API ",[155,35105,35106],{},"public",[506,35108,35109,35112,35115,35118,35121],{},[370,35110,35111],{},"d'être stable dans le temps, ceci afin qu'un client qui utilise une API puisse continuer à l'utiliser sans devoir\ntout réécrire entre chaque version,",[370,35113,35114],{},"d'être fiable, cela va sans dire mais une API qui retourne des erreurs une fois sur deux ou qui ne fait pas ce qu'on\nlui demande va vite être abandonnée,",[370,35116,35117],{},"d'être simple d'utilisation et bien documentée, afin que le développeur passe le moins de temps possible à apprendre à\nl'utiliser,",[370,35119,35120],{},"d'avoir une bonne gestion des erreurs,",[370,35122,35123],{},"d'être performante.",[12,35125,35126],{},"Dans le monde du web, il existe plusieurs protocole\u002Fnorme afin de faciliter l'écriture de ce contrat d'interface, on\npeut citer par exemple:",[506,35128,35129,35132,35135,35138,35141],{},[370,35130,35131],{},"SOAP (Simple Object Access Protocol)",[370,35133,35134],{},"REST (Representational State Transfer)",[370,35136,35137],{},"XML-RPC (XML Remote Procedure Call)",[370,35139,35140],{},"GraphQL (créé par Facebook)",[370,35142,35143],{},"Falcor (qui est un protocole basé sur une implémentation de référence ; créé par Netflix)",[12,35145,35146],{},"Il existe plein de manières d'écrire des API. On peut aussi en écrire à base de socket réseau. Dans cet article je ne\nvais pas détailler toutes les méthodes possibles mais je vais présenter uniquement une selection d'API Web (Rest et\nGraphQL).",[128,35148,35150],{"id":35149},"pourquoi-écrire-une-api-web","Pourquoi écrire une API Web",[12,35152,35153,35154,35161],{},"Pour commencer si votre application web est une SPA",[17400,35155,35156],{},[49,35157,3686],{"href":35158,"ariaDescribedBy":35159,"dataFootnoteRef":1646,"id":35160},"#user-content-fn-spa",[17406],"user-content-fnref-spa",", il sera nécessaire d'avoir un contrat d'interface entre le client (SPA)\net le serveur (sauf s'il n'y a pas de serveur...). Ce contrat est généralement implicite.",[12,35163,35164],{},"Même pour un site web statique, ou pour un site dont les pages HTML sont générées coté serveur, nous pouvons considérer\nque l'API (l'interface de programation exposée) de l'application sont ses pages HTML. En effet, il existe des web\nscrapper qui lisent le contenu HTML des pages afin d'en lire le contenu (voir même qui en expose une API plus compréhensible).",[12,35166,35167,35168,179],{},"Du coup, la question la plus importante est ",[362,35169,35170],{},"\"Faut-il que mon interface de programmation soit publique ou privée ? Faut-il qu'elle\nsoit simple d'utilisation\"",[12,35172,35173],{},"Pour un site, dont les pages sont générées côté serveur et dont le rendu est en HTML, l'API n'est ni simple\nd'utilisation, ni documentable. Pour une SPA (Single Page Application), l'écriture d'une API utilisable par un\nprogramme est la norme et elle sera alors consumée par Angular, Vue, React, jQuery ....",[12,35175,35176],{},"Souhaite-t-on alors que notre application soit utilisable publiquement ?",[12,35178,35179],{},"Souhaite-t-on que d'autres applications développées par des tiers puisse utiliser notre API, librement ? gratuitement ?",[12,35181,35182],{},"Si oui, dois-je mettre en place une politique sur le nombre de requêtes maximales par utilisateur (OUI) ? Et de combien ?\nVais-je pouvoir supporter la charge ?",[12,35184,35185],{},"Prenez en considération dans votre décision que de tout facon, les gens feront ce qu'ils veulent. Après tout, si un\nhumain peut visualiser la page, un programme le peu également (WebScrapper).",[12,35187,35188],{},"communique avec le serveur via ses API. C'est en quelque sorte une application lourde mais écrite en JavaScript. Au démarrage du\nsite l'interface (le client) est chargée en mémoire (par morceau si on prend le lazy loading) et l'interface est ensuite générée à la\nvolée. Les pages HTML sont donc générées côté client et non coté serveur.",[128,35190,35192],{"id":35191},"quest-quune-bonne-documentation","Qu'est qu'une bonne documentation",[12,35194,35195],{},"Si on veux que l'API soit utilisée, il faut qu'elle soit bien documentée et surtout à jour. Sans cela personne ne voudra\nl'utiliser.",[12,35197,35198],{},"Pour faciliter l'écriture de la documentation certains outils permettent de générer celle-ci à partir du code (des\ntypes de données, du nom des méthodes, d'annotation ...), ce qui permet de maintenir plus facilement sa documentation avec\nl'évolution du code.\nPar contre, l'utilisation de générateur ne dédouanne pas de l'écriture de la partie qui ne peux pas être documentée (comme\nles descriptions, les exemples, ...).",[12,35200,35201],{},[121,35202],{"alt":34920,"src":35203},"\u002FProgrammation\u002Fcreation-api\u002Fdocumentation-swagger-head.png",[12,35205,35206],{},"La documentation doit contenir :",[506,35208,35209,35212,35215,35218,35221,35224],{},[370,35210,35211],{},"des exemples d'appels",[370,35213,35214],{},"des descriptions détaillées de ce que fait chaque point d'entrée,",[370,35216,35217],{},"quels type de données sont présentes en entrée et comment les utiliser,",[370,35219,35220],{},"mais aussi le type des données que l'on a en sortie,",[370,35222,35223],{},"les erreurs que peut retourner le endpoint et dans quelles circonstances,",[370,35225,35226],{},"comment seront traitées les données en entrée (batch, immédiat).",[12,35228,35229],{},"Au début de la documentation, ne pas oublier de documenter:",[506,35231,35232,35235],{},[370,35233,35234],{},"le fonctionnement de l'authentification",[370,35236,35237,35238],{},"les règles d'utilisation\n",[506,35239,35240,35243,35246,35255],{},[370,35241,35242],{},"ce qu'on est autorisé à faire",[370,35244,35245],{},"ce qu'on n'est pas autorisé à faire",[370,35247,35248,35249,35254],{},"le nombre d'appel par seconde\u002Fminutes\u002Fheures\u002F...et quels sont les headers HTTP permettant de récuperer le résultat\n(par exemple dans l'",[49,35250,35253],{"href":35251,"rel":35252},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Frest\u002Foverview\u002Fresources-in-the-rest-api#rate-limiting",[347],"API Github",",\non peut retrouver les headers: X-RateLimit-Limit, X-RateLimit-Remaining)",[370,35256,35257],{},"le fonctionnement de la pagination dans l'API",[12,35259,35260,35261,35263],{},"Il peut être efficace d'avoir une page au démarrage de l'API (",[362,35262,16973],{},") donnant un première exemple complet d'un\npremier appel à l'API.",[12,35265,35266],{},"Dans le développement de l'API, l'écriture de la documentation est aussi important que l'écriture du code.",[128,35268,35270],{"id":35269},"gestion-des-erreurs","Gestion des erreurs",[12,35272,35273],{},"Pour la gestion des erreurs, l'API doit retourner le maximum d'informations pour que le développeur puisse comprendre\nl'erreur et effectuer une correction mais également suffisament d'informations pour que le développeur puisse les utiliser\ndans son programme pour retourner les problèmes fonctionnels à l'utilisateur final.",[12,35275,35276],{},"Par exemple dans une API Rest, il est important que les différents cas d'erreur soit explicités:",[506,35278,35279,35282,35288,35291],{},[370,35280,35281],{},"400 - BadRequest: The request is malformed.",[370,35283,35284,35285,35287],{},"404 - NotFound: The resource ",[362,35286,1773],{}," can't be found",[370,35289,35290],{},"401 - Unauthorized: The user is not authentified.",[370,35292,35293,35294,179],{},"403 - Forbidden: The user is not authorized to access to the resource ",[362,35295,1773],{},[12,35297,35298],{},"Ceci afin d'aider les développeurs à traiter tout les cas d'erreurs et d'afficher un message cohérent à l'utilisateur.",[12,35300,35301],{},"Lors d'une erreur, le retour de la requête doit toujours être le même. Voici un exemple de requête:",[2105,35303,35305],{"className":18051,"code":35304,"language":18053,"meta":1646,"style":1646},"GET http:\u002F\u002F192.168.101.205:3000\u002Fapi\u002Fhosts\u002Funknownhost\n\n{\n  \"statusCode\": 404,\n  \"message\": \"Can't find configuration for the host with name unknownhost\",\n  \"error\": \"Not Found\",\n  \"errorCode\": \"HOST_NOT_FOUND\",\n  \"params\": {\n      \"host\": \"unknownhost\"\n  }\n}\n",[184,35306,35307,35315,35319,35323,35335,35347,35359,35371,35378,35388,35392],{"__ignoreMap":1646},[2113,35308,35309,35312],{"class":2115,"line":2116},[2113,35310,35311],{"class":2119},"GET http:",[2113,35313,35314],{"class":2396},"\u002F\u002F192.168.101.205:3000\u002Fapi\u002Fhosts\u002Funknownhost\n",[2113,35316,35317],{"class":2115,"line":1647},[2113,35318,2125],{"emptyLinePlaceholder":1767},[2113,35320,35321],{"class":2115,"line":1652},[2113,35322,18060],{"class":2119},[2113,35324,35325,35328,35330,35333],{"class":2115,"line":2185},[2113,35326,35327],{"class":2330},"  \"statusCode\"",[2113,35329,4429],{"class":2119},[2113,35331,35332],{"class":2166},"404",[2113,35334,4706],{"class":2119},[2113,35336,35337,35340,35342,35345],{"class":2115,"line":2230},[2113,35338,35339],{"class":2330},"  \"message\"",[2113,35341,4429],{"class":2119},[2113,35343,35344],{"class":2149},"\"Can't find configuration for the host with name unknownhost\"",[2113,35346,4706],{"class":2119},[2113,35348,35349,35352,35354,35357],{"class":2115,"line":2235},[2113,35350,35351],{"class":2330},"  \"error\"",[2113,35353,4429],{"class":2119},[2113,35355,35356],{"class":2149},"\"Not Found\"",[2113,35358,4706],{"class":2119},[2113,35360,35361,35364,35366,35369],{"class":2115,"line":2241},[2113,35362,35363],{"class":2330},"  \"errorCode\"",[2113,35365,4429],{"class":2119},[2113,35367,35368],{"class":2149},"\"HOST_NOT_FOUND\"",[2113,35370,4706],{"class":2119},[2113,35372,35373,35376],{"class":2115,"line":2246},[2113,35374,35375],{"class":2330},"  \"params\"",[2113,35377,8091],{"class":2119},[2113,35379,35380,35383,35385],{"class":2115,"line":2464},[2113,35381,35382],{"class":2330},"      \"host\"",[2113,35384,4429],{"class":2119},[2113,35386,35387],{"class":2149},"\"unknownhost\"\n",[2113,35389,35390],{"class":2115,"line":2085},[2113,35391,2572],{"class":2119},[2113,35393,35394],{"class":2115,"line":2514},[2113,35395,2599],{"class":2119},[12,35397,35398,35399,35402,35403,35406],{},"Dans l'exemple ci-dessus, ",[184,35400,35401],{},"errorCode"," peut être utilisé pour indiquer à l'utilisateur, un champ obligatoire, (avec dans ",[184,35404,35405],{},"params"," le\nnom du champ) ou une règle de gestion mal utilisée.\nCe code erreur pourra alors être remontée à l'utilisateur final directement pour rendre l'interface plus réactive.",[128,35408,35410],{"id":35409},"cohérence","Cohérence",[12,35412,35413],{},"Pour faire une bonne documentation il faut également de bonnes bases. Pour cela il faut que l'API soit cohérente dans\nson fonctionnement. La cohérence est importante tant au niveau des paramètres que dans le contenu de la requête ou de la\nréponse.",[12,35415,35416,35417,35420,35421,792,35424,15746],{},"Par exemple, sur une API REST, imaginons que pour gérer la pagination un ",[362,35418,35419],{},"endpoint"," demande les paramètres ",[184,35422,35423],{},"skip",[184,35425,35426],{},"limit",[2105,35428,35430],{"className":18051,"code":35429,"language":18053,"meta":1646,"style":1646},"GET \u002Fhosts\u002Fpc-ulrich\u002Fbackups?skip=5&limit=10\n\n{\n    \"skip\": 5,\n    \"limit\": 10,\n    \"size\": 100,\n    \"result\": [\n        {\n            \"id\": 1\n        },\n        {\n            \"id\": 2\n        }\n    ]\n}\n",[184,35431,35432,35446,35450,35454,35465,35477,35488,35495,35500,35510,35514,35518,35527,35531,35536],{"__ignoreMap":1646},[2113,35433,35434,35437,35440,35443],{"class":2115,"line":2116},[2113,35435,35436],{"class":2119},"GET \u002Fhosts\u002Fpc-ulrich\u002Fbackups?skip=",[2113,35438,35439],{"class":2166},"5",[2113,35441,35442],{"class":2119},"&limit=",[2113,35444,35445],{"class":2166},"10\n",[2113,35447,35448],{"class":2115,"line":1647},[2113,35449,2125],{"emptyLinePlaceholder":1767},[2113,35451,35452],{"class":2115,"line":1652},[2113,35453,18060],{"class":2119},[2113,35455,35456,35459,35461,35463],{"class":2115,"line":2185},[2113,35457,35458],{"class":2330},"    \"skip\"",[2113,35460,4429],{"class":2119},[2113,35462,35439],{"class":2166},[2113,35464,4706],{"class":2119},[2113,35466,35467,35470,35472,35475],{"class":2115,"line":2230},[2113,35468,35469],{"class":2330},"    \"limit\"",[2113,35471,4429],{"class":2119},[2113,35473,35474],{"class":2166},"10",[2113,35476,4706],{"class":2119},[2113,35478,35479,35482,35484,35486],{"class":2115,"line":2235},[2113,35480,35481],{"class":2330},"    \"size\"",[2113,35483,4429],{"class":2119},[2113,35485,8081],{"class":2166},[2113,35487,4706],{"class":2119},[2113,35489,35490,35493],{"class":2115,"line":2241},[2113,35491,35492],{"class":2330},"    \"result\"",[2113,35494,18219],{"class":2119},[2113,35496,35497],{"class":2115,"line":2246},[2113,35498,35499],{"class":2119},"        {\n",[2113,35501,35502,35505,35507],{"class":2115,"line":2464},[2113,35503,35504],{"class":2330},"            \"id\"",[2113,35506,4429],{"class":2119},[2113,35508,35509],{"class":2166},"1\n",[2113,35511,35512],{"class":2115,"line":2085},[2113,35513,12744],{"class":2119},[2113,35515,35516],{"class":2115,"line":2514},[2113,35517,35499],{"class":2119},[2113,35519,35520,35522,35524],{"class":2115,"line":2533},[2113,35521,35504],{"class":2330},[2113,35523,4429],{"class":2119},[2113,35525,35526],{"class":2166},"2\n",[2113,35528,35529],{"class":2115,"line":2556},[2113,35530,4578],{"class":2119},[2113,35532,35533],{"class":2115,"line":2569},[2113,35534,35535],{"class":2119},"    ]\n",[2113,35537,35538],{"class":2115,"line":2575},[2113,35539,2599],{"class":2119},[12,35541,35542,35543,35545],{},"N'utilisez pas pour un autre ",[362,35544,35419],{}," de votre application des paramètres différents. De même, n'utilisez pas dans\nle résultat de la requête des noms différents de ceux utilisés dans les paramètres.",[12,35547,35548],{},"Enfin structurez le contenu de la réponse toujours de la même manière, pour que vos utilisateurs puissent développer\ndes méthodes génériques lors de l'utilisation de votre API (Par exemple une méthode de pagination générique).",[2105,35550,35552],{"className":18051,"code":35551,"language":18053,"meta":1646,"style":1646},"GET \u002Fhosts?start=5&size=10\n\n{\n    \"debut\": 5,\n    \"taille\": 10,\n    \"fin\": 100,\n    \"liste\": [\n        {\n            \"id\": 1\n        },\n        {\n            \"id\": 2\n        }\n    ]\n}\n",[184,35553,35554,35566,35570,35574,35585,35596,35607,35614,35618,35626,35630,35634,35642,35646,35650],{"__ignoreMap":1646},[2113,35555,35556,35559,35561,35564],{"class":2115,"line":2116},[2113,35557,35558],{"class":2119},"GET \u002Fhosts?start=",[2113,35560,35439],{"class":2166},[2113,35562,35563],{"class":2119},"&size=",[2113,35565,35445],{"class":2166},[2113,35567,35568],{"class":2115,"line":1647},[2113,35569,2125],{"emptyLinePlaceholder":1767},[2113,35571,35572],{"class":2115,"line":1652},[2113,35573,18060],{"class":2119},[2113,35575,35576,35579,35581,35583],{"class":2115,"line":2185},[2113,35577,35578],{"class":2330},"    \"debut\"",[2113,35580,4429],{"class":2119},[2113,35582,35439],{"class":2166},[2113,35584,4706],{"class":2119},[2113,35586,35587,35590,35592,35594],{"class":2115,"line":2230},[2113,35588,35589],{"class":2330},"    \"taille\"",[2113,35591,4429],{"class":2119},[2113,35593,35474],{"class":2166},[2113,35595,4706],{"class":2119},[2113,35597,35598,35601,35603,35605],{"class":2115,"line":2235},[2113,35599,35600],{"class":2330},"    \"fin\"",[2113,35602,4429],{"class":2119},[2113,35604,8081],{"class":2166},[2113,35606,4706],{"class":2119},[2113,35608,35609,35612],{"class":2115,"line":2241},[2113,35610,35611],{"class":2330},"    \"liste\"",[2113,35613,18219],{"class":2119},[2113,35615,35616],{"class":2115,"line":2246},[2113,35617,35499],{"class":2119},[2113,35619,35620,35622,35624],{"class":2115,"line":2464},[2113,35621,35504],{"class":2330},[2113,35623,4429],{"class":2119},[2113,35625,35509],{"class":2166},[2113,35627,35628],{"class":2115,"line":2085},[2113,35629,12744],{"class":2119},[2113,35631,35632],{"class":2115,"line":2514},[2113,35633,35499],{"class":2119},[2113,35635,35636,35638,35640],{"class":2115,"line":2533},[2113,35637,35504],{"class":2330},[2113,35639,4429],{"class":2119},[2113,35641,35526],{"class":2166},[2113,35643,35644],{"class":2115,"line":2556},[2113,35645,4578],{"class":2119},[2113,35647,35648],{"class":2115,"line":2569},[2113,35649,35535],{"class":2119},[2113,35651,35652],{"class":2115,"line":2575},[2113,35653,2599],{"class":2119},[12,35655,35656],{},"Cela aurait comme conséquence de perdre les utilisateurs (développeurs) qui utiliseront votre API.",[12,35658,35659,35660,35662],{},"Les structures, les champs utilisés sur les différents ",[362,35661,35419],{}," de votre API doivent toujours suivre la même\nlogique et par exemple:",[506,35664,35665,35671,35781,35784],{},[370,35666,35667,35668,26987],{},"nommer toujours les champs ayant la même fonction de la même manière: toujours nommer la date de création ",[184,35669,35670],{},"createdAt",[370,35672,35673,35674,35677,35733,35735,35736],{},"toujours retourner la resource demandé directement ",[362,35675,35676],{},"ou inversement toujours encapsuler la resource demandée dans un\nsous-objet",[2105,35678,35680],{"className":18051,"code":35679,"language":18053,"meta":1646,"style":1646},"{\n  \"result\": [\n    {\n      \"id\": 1\n    },\n    {\n      \"id\": 2\n    }\n  ]\n}\n",[184,35681,35682,35686,35693,35697,35705,35709,35713,35721,35725,35729],{"__ignoreMap":1646},[2113,35683,35684],{"class":2115,"line":2116},[2113,35685,18060],{"class":2119},[2113,35687,35688,35691],{"class":2115,"line":1647},[2113,35689,35690],{"class":2330},"  \"result\"",[2113,35692,18219],{"class":2119},[2113,35694,35695],{"class":2115,"line":1652},[2113,35696,18224],{"class":2119},[2113,35698,35699,35701,35703],{"class":2115,"line":2185},[2113,35700,34619],{"class":2330},[2113,35702,4429],{"class":2119},[2113,35704,35509],{"class":2166},[2113,35706,35707],{"class":2115,"line":2230},[2113,35708,8237],{"class":2119},[2113,35710,35711],{"class":2115,"line":2235},[2113,35712,18224],{"class":2119},[2113,35714,35715,35717,35719],{"class":2115,"line":2241},[2113,35716,34619],{"class":2330},[2113,35718,4429],{"class":2119},[2113,35720,35526],{"class":2166},[2113,35722,35723],{"class":2115,"line":2246},[2113,35724,4665],{"class":2119},[2113,35726,35727],{"class":2115,"line":2464},[2113,35728,18491],{"class":2119},[2113,35730,35731],{"class":2115,"line":2085},[2113,35732,2599],{"class":2119},[15567,35734],{},"vs",[2105,35737,35739],{"className":18051,"code":35738,"language":18053,"meta":1646,"style":1646},"[\n  {\n    \"id\": 1\n  },\n  {\n    \"id\": 2\n  }\n]\n",[184,35740,35741,35745,35749,35757,35761,35765,35773,35777],{"__ignoreMap":1646},[2113,35742,35743],{"class":2115,"line":2116},[2113,35744,10710],{"class":2119},[2113,35746,35747],{"class":2115,"line":1647},[2113,35748,33331],{"class":2119},[2113,35750,35751,35753,35755],{"class":2115,"line":1652},[2113,35752,33336],{"class":2330},[2113,35754,4429],{"class":2119},[2113,35756,35509],{"class":2166},[2113,35758,35759],{"class":2115,"line":2185},[2113,35760,8490],{"class":2119},[2113,35762,35763],{"class":2115,"line":2230},[2113,35764,33331],{"class":2119},[2113,35766,35767,35769,35771],{"class":2115,"line":2235},[2113,35768,33336],{"class":2330},[2113,35770,4429],{"class":2119},[2113,35772,35526],{"class":2166},[2113,35774,35775],{"class":2115,"line":2241},[2113,35776,2572],{"class":2119},[2113,35778,35779],{"class":2115,"line":2246},[2113,35780,26198],{"class":2119},[370,35782,35783],{},"toujours gérer la pagination de la même manière dans la réponse (par des entête http, ou directement dans le body,\nmais surtout avec les mêmes noms de champs).",[370,35785,35786],{},"toujours gérer les erreurs de la même manière.",[128,35788,35790],{"id":35789},"gérer-le-versionning-de-lapi","Gérer le versionning de l'API",[12,35792,35793],{},"Si vous souhaitez changer le fonctionnement de l'API, surtout ne cassez pas la cohérence de l'API, ni la version\nutilisée par tous. Pour cela vous avez plusieurs choix, et ils sont complémentaires:",[506,35795,35796,35816,35832],{},[370,35797,35798,35799,35801,35802,35805,35806,35809,35810,35812,35813,35815],{},"Vous pouvez créer une nouvelle version de l'API.",[15567,35800],{},"En créant une nouvelle version de l'API (passage de ",[184,35803,35804],{},"\u002Fapi\u002Fv1\u002F..."," à ",[184,35807,35808],{},"\u002Fapi\u002Fv2\u002F...","), vous vous assurez que l'ancienne\nversion fonctionne toujours et que les clients pourront migrer doucement de l'ancienne version vers la nouvelle.",[15567,35811],{},"Si vous souhaitez casser la cohérence de l'API (par exemple remonter les infos skip et limit du body dans des headers\nX-Pagination-Skip et X-Pagination-Limit), vous pouvez le faire une à une sur chaque API.",[15567,35814],{},"Pensez à un plan de décommissionnement pour ne pas maintenir 50 versions d'API différentes. Prévenez vos utilisateurs\nque les anciennes versions vont être décommissionées avec une date raisonable pour qu'ils puissent modifier leurs\napplications.",[370,35817,35818,35819,35822,35823,35825,35826,35828,35829,35831],{},"Lors de la création d'une nouvelle API, il peut être intéressant de ",[362,35820,35821],{},"déprécier"," les attributs de l'ancienne API, cela\nvous permet de prévenir l'utilisateur qu'il utilise un champ déprécié et qu'il sera décommisionné.",[15567,35824],{},"L'ajout d'attribut ne pose généralement pas de problème, mais cela peut vous permettre de supprimer des attributs\naprès avoir veillé à prévenir vos utilisateurs de leur obsolescence future sans forcément créer une nouvelle API.",[15567,35827],{},"Par contre cela nécessite de faire des modifications peu structurantes.",[15567,35830],{},"Pour des modifications plus structurantes vous devrez alors créer des nouveaux endpoint au coeur de l'API au risque\nde perdre l'utilisateur.",[370,35833,35834,35835,35837,35838,35840,35841,35843],{},"Monitorer votre API et son utilisation (analytics).",[15567,35836],{},"Cela vous permettra de savoir si vos API sont utilisées, leurs taux d'utilisation.",[15567,35839],{},"Etudier le taux d'utilisation vous permet ainsi de savoir le risque à décommissionner une API, mais aussi peut vous\nmotiver à améliorer les API les plus utilisées.",[15567,35842],{},"Avec certains framework (GraphQL) il est même possible de monitorer l'utilisation des attributs de votre API. Cela\nvous permet de plus facilement décommissionner les attributs de votre API au fur et à mesure de leur dépréciation.",[128,35845,35847],{"id":35846},"pour-le-client","Pour le client",[12,35849,35850],{},"Une bonne API doit être écrite pour le client qui va l'utiliser.",[12,35852,35853],{},"Il est important de ne pas créer son API en se basant sur son modèle interne mais en créant son API sur son usage.",[12,35855,35856],{},"Un appel d'API peut nécessiter plusieurs appels internes, et inversement plusieurs API peuvent faire appel aux mêmes\ndonnées internes. C'est au serveur derrière l'API ensuite de gérer un système de cache (avec invalidation de celui-ci)\npour limiter les appels à son back, ou sa base de données trop régulièrement.",[12,35858,35859],{},"De la même manière certains champs internes peuvent nécessiter d'être transformés pour être intégrés ou pour être exposés.",[12,35861,35862],{},"Certaines données internes n'ont parfois même pas besoin d'être exposées.",[12,35864,35865],{},"Du coup il faut se poser les bonnes questions lors de la création de son API :",[506,35867,35868,35871,35874],{},[370,35869,35870],{},"Qui sont les clients qui vont appeler mon API ?",[370,35872,35873],{},"Quelles cinématiques utilisateurs se cachent derrière ses clients ?",[370,35875,35876,35877],{},"Lors de chaque appel de quelles données auront besoin les clients ?\n",[506,35878,35879,35882,35885],{},[370,35880,35881],{},"Dois-je regrouper certains champs dans une même resource pour limiter le nombre d'appels ?",[370,35883,35884],{},"Dois-je déplacer certaines données dans des sous-resources pour limiter la quantité de données lors d'un appel ?",[370,35886,35887],{},"Dois-je ajouter de la pagination, de la projection, de la recherche pour limiter la quantité de données qui\nressortira de mon API ?",[12,35889,35890],{},"Il faut prendre en compte qu'en fonction de l'appelant le résultat peut être différent. Une application mobile ne\nprésentera pas forcément les données de la même manière qu'un site WEB. Est-ce que l'API doit savoir répondre aux deux ?\n(Certains framework savent mieux répondre à ce genre de questions que d'autres).",[128,35892,1621],{"id":1620},[12,35894,35895],{},"Si vous avez aimé cet article, je vous invite à lire la suite qui paraitera bientôt.",[23219,35897,35899,35902],{"className":35898,"dataFootnotes":1646},[23222],[128,35900,23227],{"className":35901,"id":17406},[23226],[367,35903,35904],{},[370,35905,35907,35908],{"id":35906},"user-content-fn-spa","une SPA (Single Page Application) est une application dont l'interface Web est entièrement écrite en javascript et qui ",[49,35909,23240],{"href":35910,"ariaLabel":23237,"className":35911,"dataFootnoteBackref":1646},"#user-content-fnref-spa",[23239],[3358,35913,35914],{},"html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":1646,"searchDepth":1647,"depth":1647,"links":35916},[35917,35918,35919,35920,35921,35922,35923,35924],{"id":35149,"depth":1647,"text":35150},{"id":35191,"depth":1647,"text":35192},{"id":35269,"depth":1647,"text":35270},{"id":35409,"depth":1647,"text":35410},{"id":35789,"depth":1647,"text":35790},{"id":35846,"depth":1647,"text":35847},{"id":1620,"depth":1647,"text":1621},{"id":17406,"depth":1647,"text":23227},"2020-10-11",{"type":9,"value":35927},[35928,35930,35932,35946,35948],[12,35929,26097],{},[12,35931,35074],{},[506,35933,35934,35938,35942],{},[370,35935,35936],{},[49,35937,26108],{"href":26107},[370,35939,35940],{},[49,35941,26114],{"href":26113},[370,35943,35944],{},[49,35945,26120],{"href":26119},[12,35947,35091],{},[12,35949,35094],{},{"planet":1767},"\u002Fpost\u002Fcreation-api-1",{"title":35067,"description":26097},"creation-api-1","posts\u002FProgrammation\u002F2020-10-11_creation-api-1",[29161,26527,29162,3510,3511],"TBg-AMlBnBvdkbTX1a-CGytRJUL2yk4FcVzMHR6lj6c",{"id":35958,"title":52,"author":7,"body":35959,"category":1682,"categorySlug":1683,"date":55,"description":14,"excerpt":36677,"extension":1764,"location":1765,"meta":36706,"navigation":1767,"path":51,"published":1767,"seo":36707,"slug":1683,"stem":36708,"tags":36709,"timeToRead":2533,"__hash__":36710},"posts\u002Fposts\u002FWoodstock\u002F2020-09-20_woodstock.md",{"type":9,"value":35960,"toc":36662},[35961,35963,35973,35978,35982,35991,35999,36002,36005,36010,36018,36023,36028,36066,36069,36073,36076,36102,36105,36111,36113,36119,36125,36128,36131,36134,36137,36143,36146,36157,36160,36163,36165,36172,36178,36181,36184,36189,36197,36200,36206,36210,36216,36221,36224,36233,36235,36238,36241,36244,36248,36251,36303,36307,36320,36324,36331,36334,36346,36354,36357,36368,36372,36375,36397,36400,36404,36449,36452,36456,36464,36473,36476,36537,36540,36633,36636,36639,36642],[12,35962,14],{},[12,35964,35965,35966,35970,35971,179],{},"Un projet ",[49,35967,35969],{"href":35968},"\u002Fpost\u002F2020-07-27-fin-passprotect\u002F","s'en va"," et un autre commence.\nJe suis heureux de vous présenter ce nouveau projet: ",[155,35972,25493],{},[35974,35975,35977],"h1",{"id":35976},"genèse-du-projet","Genèse du projet",[128,35979,35981],{"id":35980},"mes-problèmes","Mes problèmes",[12,35983,35984,35985,35990],{},"Pour faire des sauvegardes, j'utilisais jusqu'ici ",[155,35986,35987],{},[49,35988,1013],{"href":15391,"rel":35989},[347],". C'est un très bon logiciel pour effectuer des sauvegardes de plusieurs machines sur une instance centralisée.",[12,35992,35993,35995,35996,35998],{},[155,35994,1013],{}," est écrit en ",[362,35997,1063],{}," avec des partie en C. En effet il se base sur un fork de rsync qui permet d'enregistrer le résultat des sauvegardes dans un format qui lui est propre. Malgré qu'il fonctionne très bien j'ai eu plusieurs problèmes avec récemment.",[12,36000,36001],{},"Il permet de faire des sauvegardes lancées depuis un serveur centralisé. Ce dernier vérifie régulièrement si les différents PC à sauvegarder sont présents sur le réseau et s'y connecte pour faire les sauvegardes sur la base d'un calendrier.",[12,36003,36004],{},"Le serveur a besoin de pouvoir se connecter sur tous les clients (incovénient que je suis prêt à accepter) mais les clients n'ont du coup pas besoin de devoir se connecter au serveur (avec le risque qu'un client compromis, compromette le serveur).",[12,36006,36007,36009],{},[155,36008,1013],{}," est également capable de décider de l'heure de la sauvegarde en fonction de la date de la dernière sauvegarde ainsi que de la présence du PC sur le réseau. Ainsi si un PC est toujours présent, il peut être sauvegardé tout en préservant les heures où la personne a besoin de toute la puissance de son PC, sauf si la sauvegarde date de trop.",[12,36011,36012,36013,36017],{},"Je vous invite fortement à aller à l'adresse ",[49,36014,15391],{"href":36015,"rel":36016},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002Finfo.html",[347]," pour en savoir plus sur BackupPC.",[12,36019,36020],{},[121,36021],{"alt":1013,"src":36022},"\u002FWoodstock\u002Fbackuppc.png",[12,36024,36025,36026,2384],{},"Voici les quelques problèmes que j'ai eu récemment avec les dernières versions de ",[155,36027,1013],{},[506,36029,36030,36039],{},[370,36031,36032,36033,36035,36036,36038],{},"Lors du passage de la version v3 à la version v4, et alors que le pool de sauvegarde était stocké sur un partage NFS, je me suis mis à perdre des fichiers: Impossible de restaurer des fichiers.",[15567,36034],{},"Après quelques recherches j'ai découvert que BackupPC v4 n'était pas compatible avec NFS, et j'ai dû basculer toute mon installation sur un disque iSCSI sur mon NAS.",[15567,36037],{},"J'ai alors perdu mon historique de sauvegarde (je n'avais alors pas assez de place pour faire faire mes sauvegardes dans un coin et des expérimentations\u002Fmigrations de l'autre).",[370,36040,36041,36042,36044,36046,36047,36049,36050,36052,36053,36055,36056,36058,36059,36061,179],{},"Une fois les sauvegardes effectuées je souhaite pouvoir copier le résultat sur un disque dur USB (qui contient donc alors la dernière version). Cette archive est alors déposée régulièrement hors site afin de minimiser le risque de perte de données.",[15567,36043],{},[155,36045,1013],{}," permet de le faire nativement en exportant des archives au format TAR sur ce disque dur USB (moyennant une pré-étape pour monter le disque, et une post-étape pour le démonter).",[15567,36048],{},"Cela me pose problème, en effet je veux pouvoir facilement accéder au contenu de l'archive (donc sans ouvrir le tar), et cette dernière est également très longue à créer.\nDe plus il m'est déjà arrivé d'ouvrir un tar et de me rendre compte que l'archive était corrompue (tronquée) soit par manque de place soit car l'archivage n'avait pas fonctionné correctement mais sans avertissement.",[15567,36051],{},"Je m'emploie donc à créer un script qui à l'aide d'un connecteur Fuse BackupPC me permet de synchroniser les dernières versions des sauvegardes avec rsync vers le disque dur USB.",[15567,36054],{},"Malheureusement le script FUSE n'est pas très maintenu, et pose problème avec des fichiers de plusieurs centaines de gigaoctets, ou avec les permissions de fichiers sauvegardés avec des machines Windows. J'ai donc adapté le script Fuse à mes problèmes pour régler uniquement le problème de droits.",[15567,36057],{},"La modification n'est pas super propre: je squeeze les droits si l'utilisateur à un uid particulier.",[15567,36060],{},[49,36062,36065],{"href":36063,"rel":36064},"https:\u002F\u002Fgist.github.com\u002Fphoenix741\u002F99a5076569b01ba5a116cec24a798d5f",[347],"Vous pouvez retrouver ma modification ici",[12,36067,36068],{},"Le fait est que même si la solution continue de tourner actuellement et fonctionne bien, j'ai perdu une partie de la confiance que j'ai en ce produit.",[128,36070,36072],{"id":36071},"etudes-des-autres-solutions","Etudes des autres solutions",[12,36074,36075],{},"Quelques critères:",[506,36077,36078,36084,36087,36090,36093,36096,36099],{},[370,36079,36080,36081,179],{},"Déjà un premier critère est que la solution doit être ",[362,36082,36083],{},"Open Source",[370,36085,36086],{},"Je ne veux pas de client lourd pour visualiser mes sauvegardes (seul un client léger pour y accéder de n'importe où).",[370,36088,36089],{},"Je veux pouvoir facilement créer des archives sur des disques durs USB à plat (sans format spécial comme zip, tar, ...)",[370,36091,36092],{},"Je dois pouvoir sauvegarder des machines sous Windows ou sous Linux.",[370,36094,36095],{},"Je suis prêt à installer un client lourd sur le client qui est sauvegardé.",[370,36097,36098],{},"Je dois sauvegarder 6 machines (1 serveur dédié, 2 Vieux PC portable, 2 Vieux PC Fixe, 1 Vieux NAS) sans que les utilisateurs (ma femme, moi) n'aient à y penser.",[370,36100,36101],{},"Si le logiciel est facilement contrôlable par API et via une IHM simple d'utilisation, c'est un grand plus.",[12,36103,36104],{},"J'ai regardé plusieurs solutions et voici les deux solutions qui m'ont tapé dans l'oeil (et ça fait mal) en plus de BackupPC que j'utilse déjà.",[12,36106,36107,36108,36110],{},"Je précise que mes tests sont alors limités car je n'ai pas assez d'espace de stockage pour avoir à la fois le pool de ",[155,36109,1013],{}," de environ 2To et le pool d'un autre gestionnaire de sauvegarde en même temps.",[133,36112,15400],{"id":25401},[12,36114,36115,36116,179],{},"J'ai alors installé ",[49,36117,15400],{"href":15398,"rel":36118},[347],[12,36120,36121,36122,36124],{},"J'ai apprécié l'utiliser en utilisant le stockage ",[362,36123,26088],{},". Il permet aussi d'utiliser un système de stockage qui lui est propre.",[12,36126,36127],{},"Je n'ai pas trouvé de méthode pour effectuer de l'archivage sur disque dur USB des dernières sauvegardes uniquement (et pas l'ensemble du pool) pour stockage off-site. Ce point pourrait être resolvable en utilisant justement le stockage Btrfs et en écrivant mes propres scripts.",[12,36129,36130],{},"Sous Windows, il permet aussi de créer des Snapshots (équivalent LVM) pour permettre de faire des sauvegardes de fichier en lecture.",[12,36132,36133],{},"Il aussi capable de faire des images disque des machines Windows.",[12,36135,36136],{},"UrBackup utilise un client à installer sur chaque ordinateur à sauvegarder. Cela ne pose pas de problème et peut permettre d'optimiser la vitesse de sauvegarde par rapport à un simple rsync, et d'avoir un format de stockage propre pour optimiser la taille occupé.",[12,36138,36139,36140,36142],{},"Bref, sur le papier ",[155,36141,15400],{}," a tout pour plaire.",[12,36144,36145],{},"Pourquoi ne pas l'utiliser ? Bonne question :).",[12,36147,36148,36149,36152,36153,36156],{},"La migration de mon historique de sauvegarde de BackupPC vers UrBackup n'était pas possible facilement. Il aurait fallu pour cela qu'en plus de générer la structure ",[362,36150,36151],{},"Btrfs"," qui va bien créer la structure dans la base ",[362,36154,36155],{},"Sqlite",", ce que je n'ai pas forcément trouvé pratique.",[12,36158,36159],{},"Il me semble aussi que je ne trouvais pas l'interface très pratique (entre autres pour la restauration de fichiers).",[12,36161,36162],{},"Je n'ai pas plus de raisons que cela de ne pas l'utiliser, donc si vous cherchez un logiciel pour faire des sauvegardes n'hésitez pas à le tester.",[133,36164,15449],{"id":25369},[12,36166,36167,36168,179],{},"J'ai aimé utiliser également ",[49,36169,15449],{"href":36170,"rel":36171},"https:\u002F\u002Fborgbackup.readthedocs.io\u002F",[347],[12,36173,36174,36175,36177],{},"Surtout la possibilité de pouvoir chiffrer les sauvegardes, ainsi que les performances de ",[155,36176,15449],{}," mêmes.",[12,36179,36180],{},"Malheureusement, je souhaite pouvoir facilement déchiffrer le contenu depuis le serveur principal pour archiver plusieurs sauvegardes de plusieurs machines en même temps.",[12,36182,36183],{},"Je souhaite également pouvoir sauvegarder toute sortes d'ordinateurs. Certains appartenant au réseau local, d'autres se trouvant être des dédiés sur Internet. Des PC sous Linux mais aussi un PC sous Windows.",[12,36185,36186,36188],{},[155,36187,15449],{}," m'aurait alors posé quelques soucis sous Windows.",[12,36190,36191,36192],{},"Je ne souhaite pas que mes serveurs dédiés aient accès à mon réseau local pour faire les sauvegardes, ni aient accès aux serveurs de backup directement au risque de compromettre la sécurité. ",[49,36193,36196],{"href":36194,"rel":36195},"https:\u002F\u002Fborgbackup.readthedocs.io\u002Fen\u002Fstable\u002Ffaq.html#how-can-i-protect-against-a-hacked-backup-client",[347],"Il faut alors paramétrer le serveur de backup pour gérer cela au niveau de ssh",[12,36198,36199],{},"Il n'est pas possible non plus de mutualiser les sauvegardes dans un seul repo, ce qui au delà des problèmes de sécurité, pose également des problèmes de lock.",[12,36201,36202,36203,36205],{},"Je pense que ",[155,36204,15449],{}," est par contre un très bon logiciel pour faire une sauvegarde de son PC perso quand on ne s'occupe que de soi et qu'on a qu'un seul PC.",[133,36207,36209],{"id":36208},"autres-tests","Autres tests",[12,36211,36212,36213,36215],{},"J'ai également testé rapidement ",[155,36214,15463],{}," pour lequel je jette un oeil également régulièrement, mais l'IHM non intégré était alors très lente lors de mes tests, et la ligne de commande me rendait la restauration de fichiers complexe lors du peu de fois que je l'ai utilisé.",[12,36217,36218,36219,179],{},"Il est alors difficile de trouver un concurrent qui me convienne pour remplacer ",[155,36220,1013],{},[12,36222,36223],{},"Si vous pensez que je me suis trompé sur les tests que j'ai fait ci-dessus, ou que vous voyez un autre logiciel de sauvegardes qui pourrait me convenir, n'hésitez pas à m'envoyer un mail.",[12,36225,36226,36227,179],{},"A l'heure actuelle j'ai décidé d'écrire mon propre système de sauvegarde que j'ai nommé ",[155,36228,36229],{},[49,36230,25493],{"href":36231,"rel":36232},"https:\u002F\u002Fwoodstock.shadoware.org\u002F",[347],[35974,36234,25493],{"id":25492},[12,36236,36237],{},"A défaut de tourner en rond et de ne pas être complètement satisfait, j'ai décidé de développer ma propre solution. Ainsi si je n'ai pas toutes les fonctionnalités que je désire, je n'ai qu'à m'en prendre à moi-même et les développer.",[12,36239,36240],{},"Il n'est pas facile de choisir un nom pour un logiciel.",[12,36242,36243],{},"Lors du développement de cette application de sauvegarde, je regardais un épisode d'une série dont l'histoire se passait à Woodstock, d'où le nom :).",[128,36245,36247],{"id":36246},"les-fonctionnalités","Les fonctionnalités",[12,36249,36250],{},"Quels sont les fonctionnalités à l'heure actuelle :",[506,36252,36253,36256,36259,36262,36265,36275,36280,36292,36300],{},[370,36254,36255],{},"Faire des sauvegardes de façon régulière (par exemple une fois par jour) et lors de la dispo de l'ordinateur.",[370,36257,36258],{},"Pour chaque sauvegarde il est possible d'éxecuter des étapes en amont et en aval, voir de faire plusieurs sauvegardes de plusieurs dossiers.",[370,36260,36261],{},"On peut lister les sauvegardes pour chaque hôte.",[370,36263,36264],{},"On peut télécharger les fichiers un par un ou en téléchargeant un fichier Zip.",[370,36266,36267,36268,36270,36271,36274],{},"Les sauvegardes sont stockées sur un système de fichier ",[362,36269,36151],{}," ce qui permet de bénéficier du système de ",[362,36272,36273],{},"Snapshot"," pour la déduplication, et de pouvoir accéder (à des fins d'archivage) directement aux sauvegardes sans contrainte.",[370,36276,36277,36279],{},[362,36278,36151],{}," permet également de compresser les sauvegardes (avec les bonnes options au montage).",[370,36281,36282,36283,36286,36287,179],{},"Comme ",[362,36284,36285],{},"RSync"," est utilisé, il est installé sur tous les clients (même windows), ce qui facilite son utilisation",[17400,36288,36289],{},[49,36290,3686],{"href":17404,"ariaDescribedBy":36291,"dataFootnoteRef":1646,"id":17407},[17406],[370,36293,36294,36295],{},"L'application possède une interface moderne (à mon goût)",[17400,36296,36297],{},[49,36298,2547],{"href":17977,"ariaDescribedBy":36299,"dataFootnoteRef":1646,"id":17979},[17406],[370,36301,36302],{},"L'application est contrôlable via une API Rest et via une API GraphQL.",[128,36304,36306],{"id":36305},"quelques-captures-décran","Quelques captures d'écran",[12,36308,36309,36312,36316],{},[121,36310],{"alt":26666,"src":36311},"\u002FWoodstock\u002Fdashboard.png",[121,36313],{"alt":36314,"src":36315},"Hosts","\u002FWoodstock\u002Fhosts.png",[121,36317],{"alt":36318,"src":36319},"RunningTask","\u002FWoodstock\u002Frunning_tasks_0.png",[128,36321,36323],{"id":36322},"comment-linstaller","Comment l'installer ?",[12,36325,36326,36327,36330],{},"J'ai écrit un site Internet pour présenter Woodstock: ",[49,36328,36231],{"href":36231,"rel":36329},[347]," et porter la documentation.",[12,36332,36333],{},"Vous pouvez retrouver les liens de téléchargement depuis le repository de code sources :",[506,36335,36336,36341],{},[370,36337,36338],{},[49,36339,1633],{"href":1633,"rel":36340},[347],[370,36342,36343],{},[49,36344,26039],{"href":26039,"rel":36345},[347],[12,36347,36348,36349,36353],{},"L'installation se fait très simplement, j'ai écrit un peu de ",[49,36350,26138],{"href":36351,"rel":36352},"https:\u002F\u002Fwoodstock.shadoware.org\u002Fdoc\u002Finstallation.html",[347]," pour expliquer cela.",[12,36355,36356],{},"Il est possible d'effectuer une installation via:",[506,36358,36359,36362,36365],{},[370,36360,36361],{},"les sources",[370,36363,36364],{},"un paquet debian\u002Fubuntu",[370,36366,36367],{},"l'image docker (c'est l'installation que j'utilise moi-même)",[128,36369,36371],{"id":36370},"par-rapport-à-mon-besoin","Par rapport à mon besoin",[12,36373,36374],{},"Reprenons mes critères :",[506,36376,36377,36382,36385,36388,36391,36394],{},[370,36378,36379,36381],{},[362,36380,36083],{},": Check",[370,36383,36384],{},"Client léger: Check",[370,36386,36387],{},"Archivage sur disque dur USB à plat: Partiel (Btrfs me permet de le faire manuellement à l'aide d'un script maison)",[370,36389,36390],{},"Compatible Windows\u002FLinux: Check (via rsync)",[370,36392,36393],{},"Sauvegarde sans y penser (automatique): Check",[370,36395,36396],{},"API ou IHM simple d'utilisation: Milk Check. L'API est là. Il est simple d'utilisation pour moi, mais peut encore être amélioré",[12,36398,36399],{},"Donc pour l'instant il répond à mes besoins mais reste améliorable.",[128,36401,36403],{"id":36402},"roadmap","Roadmap",[506,36405,36406,36409,36412],{},[370,36407,36408],{},"Avoir un outil d'archivage automatique intégré.",[370,36410,36411],{},"Les suppressions automatiques de sauvegardes sont à prévoir également.",[370,36413,36414,36415],{},"Peut-être remplacer Btrfs par autre chose, car actuellement",[506,36416,36417,36420,36426,36429,36432],{},[370,36418,36419],{},"Sans Btrfs, le logiciel ne peut pas fonctionner, donc cela limite les OS du serveur de sauvegarde.",[370,36421,36422,36423,36425],{},"Il y aurait des problèmes de performances avec ",[362,36424,36151],{}," en cas de trop grand nombre de snapshots et de l'utilisation des qgroups. Pour l'instant je n'ai ce genre de problème que lors de la suppression de sauvegarde.",[370,36427,36428],{},"Il n'est pas possible de faire de la déduplication inter-machines simplement.",[370,36430,36431],{},"L'utilisation d'un système de fichier unique empêche l'utilisation de plusieurs serveurs de backups, synchronisés. (Je me dis que sur les grosses infrastructures, il peut-être intéressant de scaler horizontalement)",[370,36433,36434,36435],{},"Par contre, si je remplace Btrfs par autre chose, les questions sont alors",[506,36436,36437,36440,36443,36446],{},[370,36438,36439],{},"par quoi ?",[370,36441,36442],{},"Comment fais-je pour Rsync ?",[370,36444,36445],{},"Dois-je le remplacer aussi ?",[370,36447,36448],{},"Avoir mon propre client de sauvegarde (comme UrBackup) ?",[12,36450,36451],{},"Si vous avez des éléments de réponses, ou des idées là aussi, n'hésitez pas non plus à m'envoyer un mail.\nSi vous aussi n'êtes pas satisfaits de votre solution de sauvegardes, contactez-moi pour me dire ce qu'il vous faudrait, voir même pour contribuer.",[128,36453,36455],{"id":36454},"quelques-chiffres","Quelques chiffres",[12,36457,36458,36459,36461,36462,179],{},"À l'heure actuelle, je suis passé à ",[155,36460,25493],{}," pour mes sauvegardes en ayant migré toutes mes sauvegardes depuis ",[155,36463,1013],{},[12,36465,36466,36467,36469,36470,36472],{},"Je peux donc faire quelques comparaisons rapides (",[155,36468,25493],{}," méritant encore quelques évolutions pour atteindre le niveau de ",[155,36471,1013],{}," dans certains domaines).",[12,36474,36475],{},"Je possède 6 machines dont l'espace est répartit comme suite :",[22,36477,36478,36487],{},[25,36479,36480],{},[28,36481,36482,36484],{},[31,36483,567],{},[31,36485,36486],{},"Stockage",[41,36488,36489,36497,36505,36513,36521,36529],{},[28,36490,36491,36494],{},[46,36492,36493],{},"pc-windows",[46,36495,36496],{},"260.8 Gb ",[28,36498,36499,36502],{},[46,36500,36501],{},"pc-portable-1",[46,36503,36504],{},"148.1 Gb",[28,36506,36507,36510],{},[46,36508,36509],{},"pc-portable-2",[46,36511,36512],{},"41.0 Gb",[28,36514,36515,36518],{},[46,36516,36517],{},"pc-linux",[46,36519,36520],{},"153.3 Gb",[28,36522,36523,36526],{},[46,36524,36525],{},"nas",[46,36527,36528],{},"1.4 Tb",[28,36530,36531,36534],{},[46,36532,36533],{},"server-ovh",[46,36535,36536],{},"189.6 Gb",[12,36538,36539],{},"Certains fichiers (comme les photos de vacances, ...) peuvent être sur plusieurs machines (nas + pc fixe).",[22,36541,36542,36553],{},[25,36543,36544],{},[28,36545,36546,36549,36551],{},[31,36547,36548],{},"Comparaison",[31,36550,1013],{},[31,36552,1682],{},[41,36554,36555,36569,36580,36590,36601,36611,36622],{},[28,36556,36557,36560,36563],{},[46,36558,36559],{},"Pool de sauvegarde",[46,36561,36562],{},"le stockage optimisé 1,88 To",[46,36564,36565,36566,36568],{}," Le stockage ",[362,36567,26088],{}," avec compression prend 2,1 To",[28,36570,36571,36574,36577],{},[46,36572,36573],{},"Compression pc-windows",[46,36575,36576],{},"357 Go non compressé",[46,36578,36579],{},"326 Go non compressé",[28,36581,36582,36584,36587],{},[46,36583],{},[46,36585,36586],{},"246 Go compressé",[46,36588,36589],{},"245 Go compressé",[28,36591,36592,36595,36598],{},[46,36593,36594],{},"Compression pc-portable-1",[46,36596,36597],{},"74 Go non compressé",[46,36599,36600],{},"79 Go non compressé",[28,36602,36603,36605,36608],{},[46,36604],{},[46,36606,36607],{},"70 Go compressé",[46,36609,36610],{},"75 Go compressé",[28,36612,36613,36616,36619],{},[46,36614,36615],{},"Temps de sauvegarde incrémental NAS",[46,36617,36618],{}," 21 minutes en moyenne",[46,36620,36621],{},"15 minutes en moyenne",[28,36623,36624,36627,36630],{},[46,36625,36626],{},"Temps de sauvegarde incrémental pc-linux",[46,36628,36629],{},"30 minutes en moyenne",[46,36631,36632],{},"7 minutes en moyenne",[12,36634,36635],{},"J'ai l'impression que la notion de compress-force n'est pas prise en compte, et que la compression n'est pas des plus efficaces.",[12,36637,36638],{},"Pour la taille du pool de sauvegarde cela ne m'étonne pas vu que la déduplication n'est pas cross-machine.",[12,36640,36641],{},"Pour le temps de sauvegarde c'est une bonne surprise mais là aussi, je pense que c'est normal vu que rsync ne doit pas faire de déduplication à\nson niveau.",[23219,36643,36645,36648],{"className":36644,"dataFootnotes":1646},[23222],[128,36646,23227],{"className":36647,"id":17406},[23226],[367,36649,36650,36656],{},[370,36651,36652,36653],{"id":23232},"A voir si je conserve ce mode de fonctionnement pour le futur. J'envisage de peut-être prendre un client lourd. ",[49,36654,23240],{"href":23236,"ariaLabel":23237,"className":36655,"dataFootnoteBackref":1646},[23239],[370,36657,36658,36659],{"id":23243},"Mais si un UI\u002FUX souhaite améliorer l'interface, je suis preneur. ",[49,36660,23240],{"href":23251,"ariaLabel":23252,"className":36661,"dataFootnoteBackref":1646},[23239],{"title":1646,"searchDepth":1647,"depth":1647,"links":36663},[36664,36665,36670,36671,36672,36673,36674,36675,36676],{"id":35980,"depth":1647,"text":35981},{"id":36071,"depth":1647,"text":36072,"children":36666},[36667,36668,36669],{"id":25401,"depth":1652,"text":15400},{"id":25369,"depth":1652,"text":15449},{"id":36208,"depth":1652,"text":36209},{"id":36246,"depth":1647,"text":36247},{"id":36305,"depth":1647,"text":36306},{"id":36322,"depth":1647,"text":36323},{"id":36370,"depth":1647,"text":36371},{"id":36402,"depth":1647,"text":36403},{"id":36454,"depth":1647,"text":36455},{"id":17406,"depth":1647,"text":23227},{"type":9,"value":36678},[36679,36681,36687,36689,36691,36698,36704],[12,36680,14],{},[12,36682,35965,36683,35970,36685,179],{},[49,36684,35969],{"href":35968},[155,36686,25493],{},[35974,36688,35977],{"id":35976},[128,36690,35981],{"id":35980},[12,36692,35984,36693,35990],{},[155,36694,36695],{},[49,36696,1013],{"href":15391,"rel":36697},[347],[12,36699,36700,35995,36702,35998],{},[155,36701,1013],{},[362,36703,1063],{},[12,36705,36001],{},{"planet":1767},{"title":52,"description":14},"posts\u002FWoodstock\u002F2020-09-20_woodstock",[1773,1774,26088,983,3510,3511,1683],"3u7RFr1fMKcgbLywQbn4HX3gySdxxhd7EFlJL3Est34",{"id":36712,"title":36713,"author":7,"body":36714,"category":36726,"categorySlug":37269,"date":37270,"description":14,"excerpt":37271,"extension":1764,"location":1765,"meta":37291,"navigation":1767,"path":37292,"published":1767,"seo":37293,"slug":37294,"stem":37295,"tags":37296,"timeToRead":2230,"__hash__":37298},"posts\u002Fposts\u002FPassprotect\u002F2020-07-27-fin-passprotect.md","Fin de Passprotect",{"type":9,"value":36715,"toc":37265},[36716,36718,36721,36727,36735,36743,36747,36750,36756,36759,36762,36765,36780,36783,36787,36793,36796,36799,36802,36810,36813,36822,36825,36829,36832,36854,36857,36859,36873,36882,37230,37237,37240,37242,37245,37248,37251,37262],[12,36717,14],{},[12,36719,36720],{},"Cela fait longtemps que je n'ai pas écrit de billet de blog (3 ans et demi). Et il s'en est passé des choses. Je travaille actuellement sur un nouveau projet dont je reviendrai vous parler plus tard.",[12,36722,36723,36724,179],{},"A aujourd'hui je souhaiterais surtout vous parler de la fin de service de mon propre gestionnaire de mot de passe ",[184,36725,36726],{},"Passprotect",[12,36728,36729,36730,36734],{},"Ces dernières années, j'ai utilisé mon propre gestionnaire de mot de passe: ",[49,36731,36726],{"href":36732,"rel":36733},"https:\u002F\u002Fgogs.shadoware.org\u002Fphoenix\u002Fpassprotect-vue",[347]," développé par mes soins :).\nJe n'avais pas trouvé mon bonheur par ailleurs (OpenSource, Client WEB, ...) et avais décidé de développer le mien.",[12,36736,36737,36738,179],{},"Puis j'ai découvert ",[49,36739,36742],{"href":36740,"rel":36741},"https:\u002F\u002Fbitwarden.com\u002F",[347],"Bitwarden",[35974,36744,36746],{"id":36745},"la-fin","La Fin",[12,36748,36749],{},"L'avantage d'utiliser un logiciel fabriqué par soi-même est la fierté de l'utiliser et de voir d'autres personnes l'utiliser également.",[12,36751,36752,36753,36755],{},"Malheureusement ",[184,36754,36726],{}," ne possédait que deux utilisateurs actifs (et quelques Bot\u002FSPAM) et ne proposait pas les fonctionnalités que d'autres proposaient. J'aurais pu les développer mais n'ayant pas assez d'utilisateurs, je n'avais pas assez de motivation pour le faire.",[12,36757,36758],{},"J'ai développé mon gestionnaire de mot de passe, à moi, car je ne me retrouvais pas dans les gestionnaires de mot de passe existants. Puis un jour, par hasard, j'ai découvert bitwarden.",[12,36760,36761],{},"J'ai alors décidé de le tester, (et cela fait maintenant presque un an), et étant satisfait, j'ai décidé d'arrêter de développer mon gestionnaire de mot de passe pour me concenter sur d'autres logiciels.",[12,36763,36764],{},"Même si le nom de domaine servant l'application n'existe plus, les sources resteront néanmoins disponibles ici, si cela intéresse quelqu'un :",[506,36766,36767,36774],{},[370,36768,36769],{},[49,36770,36773],{"href":36771,"rel":36772},"https:\u002F\u002Fgogs.shadoware.org\u002Fphoenix\u002Fpassprotect-server",[347],"Le serveur",[370,36775,36776],{},[49,36777,36779],{"href":36732,"rel":36778},[347],"Le client",[12,36781,36782],{},"Si quelqu'un trouve que le projet vaut toujours le coup, qu'il n'hésite pas à me contacter ou à proposer des évolutions, ou à développer des nouvelles fonctionnalités.",[35974,36784,36786],{"id":36785},"le-début","Le début",[12,36788,36789,36790,179],{},"Parlons alors de ",[49,36791,36742],{"href":36740,"rel":36792},[347],[12,36794,36795],{},"D'un point de vue technique, c'est pas tout rose si on regarde la version officielle.",[12,36797,36798],{},"Ce qui est super, c'est qu'il existe un client pour Windows, Linux, MacOS, Android, Apple, les différents navigateurs et tout cela en OpenSource :).",[12,36800,36801],{},"Par contre la partie serveur (qui est également en OpenSource), nécessite :",[506,36803,36804,36807],{},[370,36805,36806],{},".NET Core 3.1 SDK",[370,36808,36809],{},"SQL Serveur 2017",[12,36811,36812],{},"Et je n'apprécie pas forcément ces deux technos. (Et je n'ai surtout pas envie d'installer une base SQL Serveur uniquement pour un gestionnaire de mot de passe).",[12,36814,36815,36816,36821],{},"Par contre pour la partie Serveur, il existe une alternative non officielle en RUST (un fork, quoi): ",[49,36817,36820],{"href":36818,"rel":36819},"https:\u002F\u002Fgithub.com\u002Fdani-garcia\u002Fbitwarden_rs",[347],"bitwarden_rs",". Le serveur peut alors utiliser une base MySQL, Sqlite, PostgresQL.",[12,36823,36824],{},"Je décide alors de l'installer et l'utiliser pour me faire un avis.",[128,36826,36828],{"id":36827},"quelles-sont-les-fonctionnalités-que-japprécie","Quelles sont les fonctionnalités que j'apprécie ?",[12,36830,36831],{},"Après quelques mois d'utilisation, voici ce que j'apprécie :",[506,36833,36834,36837,36840],{},[370,36835,36836],{},"Le partage de mot de passe entre compte",[370,36838,36839],{},"Les clients sous Android et dans les différents navigateurs.",[370,36841,36842,36843],{},"Les rapports d'utilisation des mots de passes :\n",[506,36844,36845,36848,36851],{},[370,36846,36847],{},"réutilisés",[370,36849,36850],{},"exposés",[370,36852,36853],{},"faibles",[12,36855,36856],{},"Je n'y ai pas trouvé de point que je n'apprécie pas (à part que ce n'est pas développé par moi même 😄).",[128,36858,36323],{"id":36322},[12,36860,36861,36862,792,36865,36868,36869,36872],{},"Il faudra que je vous parle de mon infra-structure et de comment j'héberge mes différents sites, mais en gros je me base entièrement sur ",[184,36863,36864],{},"Docker",[184,36866,36867],{},"Docker Swarm",". Du coup, l'hébergement de ",[184,36870,36871],{},"bitwarden"," également passe par là.",[12,36874,36875,36876,36879,36880,2384],{},"Voici mon fichier ",[184,36877,36878],{},"docker-compose.yml"," qui injecté à ",[184,36881,36867],{},[2105,36883,36885],{"className":19667,"code":36884,"language":19669,"meta":1646,"style":1646},"version: \"3.4\"\n\nnetworks:\n  traefik-swarm:\n    external:\n      name: traefik-swarm\n  sendmail:\n    external:\n      name: sendmail\n\nservices:\n  bitwarden:\n    image: bitwardenrs\u002Fserver\n    volumes:\n      - \u002Fbitwarden\u002Fdata:\u002Fdata\n    environment:\n      - WEBSOCKET_ENABLED=true\n      - SIGNUPS_ALLOWED=false\n      - DOMAIN=https:\u002F\u002Fbitwrd.shadoware.org\n      - SMTP_HOST=postfix_relay\n      - SMTP_FROM=bitwrd@shadoware.org\n      - SMTP_PORT=25\n      - SMTP_SSL=false\n    networks:\n      - traefik-swarm\n      - sendmail\n    deploy:\n      labels:\n        - traefik.enable=true\n        - traefik.http.routers.bitwarden_server_router.rule=Host(`bitwrd.{{ domains_default }}`)\n        - traefik.http.routers.bitwarden_server_router.entrypoints=web\n        - traefik.http.routers.bitwarden_server_router.middlewares=redirecthttps@file,bitwarden_headers\n        - traefik.http.routers.bitwarden_server_router_https.rule=Host(`bitwrd.{{ domains_default }}`)\n        - traefik.http.routers.bitwarden_server_router_https.middlewares=bitwarden_headers\n        - traefik.http.routers.bitwarden_server_router_https.tls\n        - traefik.http.routers.bitwarden_server_router_https.tls.certresolver=shadoware\n        - traefik.http.routers.bitwarden_server_router_https.tls.domains[0].main={{ domains_default }}\n        - traefik.http.routers.bitwarden_server_router_https.tls.domains[0].sans=*.{{ domains_default }}\n        - traefik.http.services.bitwarden_server.loadbalancer.server.port=80\n        - traefik.http.middlewares.bitwarden_headers.headers.sslRedirect=true\n        - traefik.http.middlewares.bitwarden_headers.headers.browserXssFilter=true\n        - traefik.http.middlewares.bitwarden_headers.headers.contentTypeNosniff=true\n        - traefik.http.middlewares.bitwarden_headers.headers.sslHost=bitwrd.{{ domains_default }}\n        - traefik.http.middlewares.bitwarden_headers.headers.frameDeny=true\n      restart_policy:\n        condition: any\n      update_config:\n        order: start-first\n",[184,36886,36887,36897,36901,36908,36915,36922,36932,36939,36945,36954,36958,36965,36972,36982,36989,36996,37002,37009,37016,37023,37030,37037,37044,37051,37058,37064,37070,37077,37084,37091,37098,37105,37112,37119,37126,37133,37140,37147,37154,37161,37168,37175,37182,37189,37196,37203,37213,37220],{"__ignoreMap":1646},[2113,36888,36889,36892,36894],{"class":2115,"line":2116},[2113,36890,36891],{"class":2330},"version",[2113,36893,4429],{"class":2119},[2113,36895,36896],{"class":2149},"\"3.4\"\n",[2113,36898,36899],{"class":2115,"line":1647},[2113,36900,2125],{"emptyLinePlaceholder":1767},[2113,36902,36903,36906],{"class":2115,"line":1652},[2113,36904,36905],{"class":2330},"networks",[2113,36907,19702],{"class":2119},[2113,36909,36910,36913],{"class":2115,"line":2185},[2113,36911,36912],{"class":2330},"  traefik-swarm",[2113,36914,19702],{"class":2119},[2113,36916,36917,36920],{"class":2115,"line":2230},[2113,36918,36919],{"class":2330},"    external",[2113,36921,19702],{"class":2119},[2113,36923,36924,36927,36929],{"class":2115,"line":2235},[2113,36925,36926],{"class":2330},"      name",[2113,36928,4429],{"class":2119},[2113,36930,36931],{"class":2149},"traefik-swarm\n",[2113,36933,36934,36937],{"class":2115,"line":2241},[2113,36935,36936],{"class":2330},"  sendmail",[2113,36938,19702],{"class":2119},[2113,36940,36941,36943],{"class":2115,"line":2246},[2113,36942,36919],{"class":2330},[2113,36944,19702],{"class":2119},[2113,36946,36947,36949,36951],{"class":2115,"line":2464},[2113,36948,36926],{"class":2330},[2113,36950,4429],{"class":2119},[2113,36952,36953],{"class":2149},"sendmail\n",[2113,36955,36956],{"class":2115,"line":2085},[2113,36957,2125],{"emptyLinePlaceholder":1767},[2113,36959,36960,36963],{"class":2115,"line":2514},[2113,36961,36962],{"class":2330},"services",[2113,36964,19702],{"class":2119},[2113,36966,36967,36970],{"class":2115,"line":2533},[2113,36968,36969],{"class":2330},"  bitwarden",[2113,36971,19702],{"class":2119},[2113,36973,36974,36977,36979],{"class":2115,"line":2556},[2113,36975,36976],{"class":2330},"    image",[2113,36978,4429],{"class":2119},[2113,36980,36981],{"class":2149},"bitwardenrs\u002Fserver\n",[2113,36983,36984,36987],{"class":2115,"line":2569},[2113,36985,36986],{"class":2330},"    volumes",[2113,36988,19702],{"class":2119},[2113,36990,36991,36993],{"class":2115,"line":2575},[2113,36992,20039],{"class":2119},[2113,36994,36995],{"class":2149},"\u002Fbitwarden\u002Fdata:\u002Fdata\n",[2113,36997,36998,37000],{"class":2115,"line":2596},[2113,36999,20016],{"class":2330},[2113,37001,19702],{"class":2119},[2113,37003,37004,37006],{"class":2115,"line":3192},[2113,37005,20039],{"class":2119},[2113,37007,37008],{"class":2149},"WEBSOCKET_ENABLED=true\n",[2113,37010,37011,37013],{"class":2115,"line":3213},[2113,37012,20039],{"class":2119},[2113,37014,37015],{"class":2149},"SIGNUPS_ALLOWED=false\n",[2113,37017,37018,37020],{"class":2115,"line":3236},[2113,37019,20039],{"class":2119},[2113,37021,37022],{"class":2149},"DOMAIN=https:\u002F\u002Fbitwrd.shadoware.org\n",[2113,37024,37025,37027],{"class":2115,"line":3248},[2113,37026,20039],{"class":2119},[2113,37028,37029],{"class":2149},"SMTP_HOST=postfix_relay\n",[2113,37031,37032,37034],{"class":2115,"line":4899},[2113,37033,20039],{"class":2119},[2113,37035,37036],{"class":2149},"SMTP_FROM=bitwrd@shadoware.org\n",[2113,37038,37039,37041],{"class":2115,"line":1777},[2113,37040,20039],{"class":2119},[2113,37042,37043],{"class":2149},"SMTP_PORT=25\n",[2113,37045,37046,37048],{"class":2115,"line":4931},[2113,37047,20039],{"class":2119},[2113,37049,37050],{"class":2149},"SMTP_SSL=false\n",[2113,37052,37053,37056],{"class":2115,"line":4961},[2113,37054,37055],{"class":2330},"    networks",[2113,37057,19702],{"class":2119},[2113,37059,37060,37062],{"class":2115,"line":4976},[2113,37061,20039],{"class":2119},[2113,37063,36931],{"class":2149},[2113,37065,37066,37068],{"class":2115,"line":4993},[2113,37067,20039],{"class":2119},[2113,37069,36953],{"class":2149},[2113,37071,37072,37075],{"class":2115,"line":5013},[2113,37073,37074],{"class":2330},"    deploy",[2113,37076,19702],{"class":2119},[2113,37078,37079,37082],{"class":2115,"line":5018},[2113,37080,37081],{"class":2330},"      labels",[2113,37083,19702],{"class":2119},[2113,37085,37086,37088],{"class":2115,"line":5042},[2113,37087,22595],{"class":2119},[2113,37089,37090],{"class":2149},"traefik.enable=true\n",[2113,37092,37093,37095],{"class":2115,"line":5057},[2113,37094,22595],{"class":2119},[2113,37096,37097],{"class":2149},"traefik.http.routers.bitwarden_server_router.rule=Host(`bitwrd.{{ domains_default }}`)\n",[2113,37099,37100,37102],{"class":2115,"line":5062},[2113,37101,22595],{"class":2119},[2113,37103,37104],{"class":2149},"traefik.http.routers.bitwarden_server_router.entrypoints=web\n",[2113,37106,37107,37109],{"class":2115,"line":5098},[2113,37108,22595],{"class":2119},[2113,37110,37111],{"class":2149},"traefik.http.routers.bitwarden_server_router.middlewares=redirecthttps@file,bitwarden_headers\n",[2113,37113,37114,37116],{"class":2115,"line":5117},[2113,37115,22595],{"class":2119},[2113,37117,37118],{"class":2149},"traefik.http.routers.bitwarden_server_router_https.rule=Host(`bitwrd.{{ domains_default }}`)\n",[2113,37120,37121,37123],{"class":2115,"line":5142},[2113,37122,22595],{"class":2119},[2113,37124,37125],{"class":2149},"traefik.http.routers.bitwarden_server_router_https.middlewares=bitwarden_headers\n",[2113,37127,37128,37130],{"class":2115,"line":5148},[2113,37129,22595],{"class":2119},[2113,37131,37132],{"class":2149},"traefik.http.routers.bitwarden_server_router_https.tls\n",[2113,37134,37135,37137],{"class":2115,"line":5163},[2113,37136,22595],{"class":2119},[2113,37138,37139],{"class":2149},"traefik.http.routers.bitwarden_server_router_https.tls.certresolver=shadoware\n",[2113,37141,37142,37144],{"class":2115,"line":5169},[2113,37143,22595],{"class":2119},[2113,37145,37146],{"class":2149},"traefik.http.routers.bitwarden_server_router_https.tls.domains[0].main={{ domains_default }}\n",[2113,37148,37149,37151],{"class":2115,"line":5175},[2113,37150,22595],{"class":2119},[2113,37152,37153],{"class":2149},"traefik.http.routers.bitwarden_server_router_https.tls.domains[0].sans=*.{{ domains_default }}\n",[2113,37155,37156,37158],{"class":2115,"line":5180},[2113,37157,22595],{"class":2119},[2113,37159,37160],{"class":2149},"traefik.http.services.bitwarden_server.loadbalancer.server.port=80\n",[2113,37162,37163,37165],{"class":2115,"line":5199},[2113,37164,22595],{"class":2119},[2113,37166,37167],{"class":2149},"traefik.http.middlewares.bitwarden_headers.headers.sslRedirect=true\n",[2113,37169,37170,37172],{"class":2115,"line":5204},[2113,37171,22595],{"class":2119},[2113,37173,37174],{"class":2149},"traefik.http.middlewares.bitwarden_headers.headers.browserXssFilter=true\n",[2113,37176,37177,37179],{"class":2115,"line":5209},[2113,37178,22595],{"class":2119},[2113,37180,37181],{"class":2149},"traefik.http.middlewares.bitwarden_headers.headers.contentTypeNosniff=true\n",[2113,37183,37184,37186],{"class":2115,"line":5232},[2113,37185,22595],{"class":2119},[2113,37187,37188],{"class":2149},"traefik.http.middlewares.bitwarden_headers.headers.sslHost=bitwrd.{{ domains_default }}\n",[2113,37190,37191,37193],{"class":2115,"line":5237},[2113,37192,22595],{"class":2119},[2113,37194,37195],{"class":2149},"traefik.http.middlewares.bitwarden_headers.headers.frameDeny=true\n",[2113,37197,37198,37201],{"class":2115,"line":5242},[2113,37199,37200],{"class":2330},"      restart_policy",[2113,37202,19702],{"class":2119},[2113,37204,37205,37208,37210],{"class":2115,"line":5267},[2113,37206,37207],{"class":2330},"        condition",[2113,37209,4429],{"class":2119},[2113,37211,37212],{"class":2149},"any\n",[2113,37214,37215,37218],{"class":2115,"line":5282},[2113,37216,37217],{"class":2330},"      update_config",[2113,37219,19702],{"class":2119},[2113,37221,37222,37225,37227],{"class":2115,"line":5295},[2113,37223,37224],{"class":2330},"        order",[2113,37226,4429],{"class":2119},[2113,37228,37229],{"class":2149},"start-first\n",[12,37231,37232,37233,37236],{},"Comme vous pouvez le voir, j'utilise ",[184,37234,37235],{},"traefik"," pour router les noms de domaines vers les bonnes images.\nDans mon infrastructure j'essaye d'isoler au mieux chaque application et utilise ensuite les différents réseaux pour les faire communiquer.",[12,37238,37239],{},"Par exemple, cette application communique avec le réseau d'envoi de mail et aussi avec celui de traefik pour sortir sur Internet.",[35974,37241,33086],{"id":33085},[12,37243,37244],{},"Ce que j'ai appris avec tout cela : j'ai apprécié faire mon propre gestionnaire de mot de passe. En développant ce dernier, j'ai appris quelques trucs sur comment bien gérer le chiffrage des mots de passes, de la bonne utilisation du sel, par exemple.",[12,37246,37247],{},"Ce que je savais déjà par contre, c'est qu'il est très difficile de concurrencer d'autres structures (comme Lastpass, BitWarden, ...) que beaucoup de monde utilise déjà.",[12,37249,37250],{},"Et je rappelle également que quelque soit la solution que vous choisissez pour stocker vos mots de passes, il est important :",[506,37252,37253,37256,37259],{},[370,37254,37255],{},"d'avoir un mot de passe différent par site internet, ainsi si un site se fait pirater, ce ne sont pas tous vos mots de passe qui sont à remplacer,",[370,37257,37258],{},"avoir un gestionnaire de mot de passe sécurisé est important, car si c'est ce dernier qui se fait véroler, vous devrez renouveller tous vos mots de passes,",[370,37260,37261],{},"utiliser un gestionnaire de mot de passe permet aussi de savoir tous les sites où on laisse une trace. En effet il est souvent très facile de créer un compte pour un site puis de ne jamais y retourner avant plusieurs années. Avoir une trace de ces sites peut permettre de les supprimer régulièrement et de savoir où on a laissé une trace numérique.",[3358,37263,37264],{},"html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":1646,"searchDepth":1647,"depth":1647,"links":37266},[37267,37268],{"id":36827,"depth":1647,"text":36828},{"id":36322,"depth":1647,"text":36323},"passprotect","2020-07-27",{"type":9,"value":37272},[37273,37275,37277,37281,37286],[12,37274,14],{},[12,37276,36720],{},[12,37278,36723,37279,179],{},[184,37280,36726],{},[12,37282,36729,37283,36734],{},[49,37284,36726],{"href":36732,"rel":37285},[347],[12,37287,36737,37288,179],{},[49,37289,36742],{"href":36740,"rel":37290},[347],{"planet":1767},"\u002Fpost\u002Ffin-passprotect",{"title":36713,"description":14},"fin-passprotect","posts\u002FPassprotect\u002F2020-07-27-fin-passprotect",[37269,3510,3511,37297,36871],"password","aBn055QA5xKVE20jUEfM0UAavpQjMuzZ-j1dg5xODUg",{"id":37300,"title":37301,"author":7,"body":37302,"category":2057,"categorySlug":2058,"date":38733,"description":1646,"excerpt":38734,"extension":1764,"location":1765,"meta":38755,"navigation":1767,"path":38756,"published":1767,"seo":38757,"slug":38758,"stem":38759,"tags":38760,"timeToRead":2533,"__hash__":38765},"posts\u002Fposts\u002FProgrammation\u002F2016-12-10-findsimilarity.md","FindSimilarity - Trouver les différences entre plusieurs vidéos",{"type":9,"value":37303,"toc":38721},[37304,37306,37308,37311,37314,37317,37322,37329,37333,37336,37339,37368,37371,37422,37426,37429,37443,37449,37457,37460,37469,37472,37475,37478,37861,37863,37866,37875,37878,37881,37907,37910,38032,38035,38041,38056,38059,38062,38593,38603,38607,38610,38614,38617,38620,38626,38629,38633,38639,38649,38652,38669,38686,38690,38693,38699,38702,38708,38713,38715,38718],[133,37305,7760],{"id":7759},[12,37307,26097],{},[12,37309,37310],{},"Je souhaite vous présenter une petite expérience que je viens d'écrire.",[12,37312,37313],{},"Cela fait plusieurs années que je souhaitais m'amuser sur la librairie OpenCV mais sans jamais en avoir eu l'utilité. J'ai profité d'avoir un peu de temps libre, pour écrire un petit\nprogramme dont le but est de comparer un ensemble de vidéos.",[12,37315,37316],{},"Le but est ensuite de dire si dans cet ensemble de vidéos, deux vidéos sont identiques, ou se ressemblent, ou sont trop éloignées.",[12,37318,37319],{},[362,37320,37321],{},"J'ai souhaité faire cette expérience par amusement, je n'ai donc pas passé beaucoup de temps sur la qualité du code écrit. Ce dernier aurait pu être mieux découpé, posséder des\ncommentaires, des tests unitaires, ... . Si vous voulez utiliser ce code pour un véritable usage production, n'hésitez pas à améliorer celui ci.",[12,37323,37324,37325,179],{},"Vous pouvez trouver le code source de cette expérience à l'adresse suivante : ",[49,37326,37327],{"href":37327,"rel":37328},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002Ffind-similarity",[347],[133,37330,37332],{"id":37331},"le-jeux-de-données","Le jeux de données",[12,37334,37335],{},"J'ai pris plusieurs films en DVD que je possède. Possédant un NAS, et une chromecast, j'encode ces DVD au format vidéo et je les y dépose. Malheureusement la qualité est dégradée\npar rapport au DVD.",[12,37337,37338],{},"Pour constituer le jeu de données, je prends ces films encodés, que je dépose dans un dossier. Je copie certains d'entre eux telquel",[2105,37340,37342],{"className":2317,"code":37341,"language":2319,"meta":1646,"style":1646},"mkdir example\ncd example\ncp ..\u002Ffilm1.avi film1_copy.avi\n",[184,37343,37344,37351,37357],{"__ignoreMap":1646},[2113,37345,37346,37348],{"class":2115,"line":2116},[2113,37347,17072],{"class":2133},[2113,37349,37350],{"class":2149}," example\n",[2113,37352,37353,37355],{"class":2115,"line":1647},[2113,37354,17064],{"class":2334},[2113,37356,37350],{"class":2149},[2113,37358,37359,37362,37365],{"class":2115,"line":1652},[2113,37360,37361],{"class":2133},"cp",[2113,37363,37364],{"class":2149}," ..\u002Ffilm1.avi",[2113,37366,37367],{"class":2149}," film1_copy.avi\n",[12,37369,37370],{},"J'encode certains de ces films avec une résolution différente :",[2105,37372,37374],{"className":2317,"code":37373,"language":2319,"meta":1646,"style":1646},"avconv -i film2.m4v  -preset veryslow -s 320x240 film2.320x240.m4v\navconv -i film2.m4v  -preset veryslow -s 640x480 film2.640x480.m4v\n",[184,37375,37376,37402],{"__ignoreMap":1646},[2113,37377,37378,37381,37384,37387,37390,37393,37396,37399],{"class":2115,"line":2116},[2113,37379,37380],{"class":2133},"avconv",[2113,37382,37383],{"class":2166}," -i",[2113,37385,37386],{"class":2149}," film2.m4v",[2113,37388,37389],{"class":2166},"  -preset",[2113,37391,37392],{"class":2149}," veryslow",[2113,37394,37395],{"class":2166}," -s",[2113,37397,37398],{"class":2149}," 320x240",[2113,37400,37401],{"class":2149}," film2.320x240.m4v\n",[2113,37403,37404,37406,37408,37410,37412,37414,37416,37419],{"class":2115,"line":1647},[2113,37405,37380],{"class":2133},[2113,37407,37383],{"class":2166},[2113,37409,37386],{"class":2149},[2113,37411,37389],{"class":2166},[2113,37413,37392],{"class":2149},[2113,37415,37395],{"class":2166},[2113,37417,37418],{"class":2149}," 640x480",[2113,37420,37421],{"class":2149}," film2.640x480.m4v\n",[133,37423,37425],{"id":37424},"comment-fonctionne-la-comparaison","Comment fonctionne la comparaison",[12,37427,37428],{},"Avant de parler de la comparaison, parlons des fichiers que nous allons comparer. Le programme va se constituer une liste des fichiers à comparer et pour chaque fichier va lire\nles informations suivantes :",[506,37430,37431,37434,37437,37440],{},[370,37432,37433],{},"la durée du film",[370,37435,37436],{},"la largeur",[370,37438,37439],{},"la hauteur",[370,37441,37442],{},"une miniature du film (utilisée pour comparer à l'oeil les vidéos)",[12,37444,37445,37446,179],{},"Ensuite, une fois les méta-données récupérées, le programe se constitue une liste de paires de fichiers à comparer en sélectionnant les fichiers qui ont une durée identique à +\u002F- 5\nsecondes. Ce paramètre est modifiable au niveau de la constante ",[184,37447,37448],{},"DELTA_SEC",[12,37450,37451,37452,179],{},"Enfin vient la comparaison pour laquelle je me suis simplement basé sur les exemples du site OpenCV que vous pouvez trouver dans la rubrique\n",[49,37453,37456],{"href":37454,"rel":37455},"http:\u002F\u002Fdocs.opencv.org\u002F2.4\u002Fdoc\u002Ftutorials\u002Fhighgui\u002Fvideo-input-psnr-ssim\u002Fvideo-input-psnr-ssim.html",[347],"Video Input with OpenCV and similarity measurement",[12,37458,37459],{},"J'ai utilisé l'algorithme PSNR (Peak signal-to-noise ratio) pour déterminer si les deux images de la vidéos sont plutôt proches ou éloignées. Cet algorithme calcul la distorsion\nentre deux images. Il est principalement utilisé pour quantifier la performance réalisée par un encodeur lors de la compression d'une vidéo. Une valeur entre 30 et 50 signifie que\nles images sont relativement proches. Plus la valeur est haute, et plus la qualité d'image est conservée entre les deux images. Si la valeur est inférieure à 30 on peut estimer qu'il\ny a une forte chance pour que les images soit différentes.",[12,37461,37462,37463,37468],{},"Vous pouvez retrouver les formules utilisées par le calcul sur le site d'OpenCV ou sur la page ",[49,37464,37467],{"href":37465,"rel":37466},"https:\u002F\u002Ffr.wikipedia.org\u002Fwiki\u002FPeak_Signal_to_Noise_Ratio",[347],"Wikipedia",". Est-ce que ce\ncalcul est le meilleur pour trouver les images similaires ? Je ne sais pas. Si vous avez d'autres propositions, on peut les tester.",[12,37470,37471],{},"Sur une vidéo on a une multitude d'images (sur un film d'une heure et demie à 25 images secondes, nous en avons 135 000), on pourrait comparer chaque image de la vidéo pour se faire\nune moyenne, de mon coté j'ai préféré comparer une image au milieu de la vidéo afin de parcourir plus vite les vidéos.",[12,37473,37474],{},"De la même manière pour m'abstraire de la taille de la vidéo qui peut avoir été modififée, je redimensionne, à tort ou à raison, les deux images à une taille identique\n(arbitrairement: 160x120).",[12,37476,37477],{},"Je vous présente donc ci-dessous l'algorithme que vous pouvez retrouver sur le site d'OpenCV. J'ai légèrement modifié l'algorithme pour redimensionner les images ainsi que pour\nretourner une valeur de PSNR infiniment grande quand deux vidéos sont identiques.",[2105,37479,37481],{"className":2387,"code":37480,"language":2389,"meta":1646,"style":1646},"double getPSNR(const cv::Mat& F1, const cv::Mat& F2) {\n    cv::Mat I1, I2;\n\n    cv::resize(F1, I1, cv::Size(160, 120));\n    cv::resize(F2, I2, cv::Size(160, 120));\n\n    cv::Mat s1;\n    cv::absdiff(I1, I2, s1);   \u002F\u002F |I1 - I2|\n    s1.convertTo(s1, CV_32F);  \u002F\u002F cannot make a square on 8 bits\n    s1 = s1.mul(s1);           \u002F\u002F |I1 - I2|^2\n\n    cv::Scalar s = sum(s1);    \u002F\u002F sum elements per channel\n\n    double sse = s.val[0] + s.val[1] + s.val[2]; \u002F\u002F sum channels\n\n    if( sse \u003C= 1e-10) {        \u002F\u002F for small values return zero\n        return std::numeric_limits\u003Cdouble>::infinity();\n    } else {\n        double mse  = sse \u002F (double)(I1.channels() * I1.total());\n        double psnr = 10.0 * log10((255 * 255) \u002F mse);\n        return psnr;\n    }\n",[184,37482,37483,37522,37527,37531,37557,37578,37582,37587,37600,37616,37637,37641,37656,37660,37716,37720,37744,37765,37773,37816,37850,37857],{"__ignoreMap":1646},[2113,37484,37485,37488,37491,37493,37495,37498,37501,37504,37507,37509,37511,37513,37515,37517,37520],{"class":2115,"line":2116},[2113,37486,37487],{"class":2326},"double",[2113,37489,37490],{"class":2133}," getPSNR",[2113,37492,2423],{"class":2119},[2113,37494,7785],{"class":2326},[2113,37496,37497],{"class":2119}," cv::",[2113,37499,37500],{"class":2414},"Mat",[2113,37502,37503],{"class":2326},"&",[2113,37505,37506],{"class":2429}," F1",[2113,37508,932],{"class":2119},[2113,37510,7785],{"class":2326},[2113,37512,37497],{"class":2119},[2113,37514,37500],{"class":2414},[2113,37516,37503],{"class":2326},[2113,37518,37519],{"class":2429}," F2",[2113,37521,2433],{"class":2119},[2113,37523,37524],{"class":2115,"line":1647},[2113,37525,37526],{"class":2119},"    cv::Mat I1, I2;\n",[2113,37528,37529],{"class":2115,"line":1652},[2113,37530,2125],{"emptyLinePlaceholder":1767},[2113,37532,37533,37536,37539,37542,37545,37547,37550,37552,37555],{"class":2115,"line":2185},[2113,37534,37535],{"class":2119},"    cv::",[2113,37537,37538],{"class":2133},"resize",[2113,37540,37541],{"class":2119},"(F1, I1, cv::",[2113,37543,37544],{"class":2133},"Size",[2113,37546,2423],{"class":2119},[2113,37548,37549],{"class":2166},"160",[2113,37551,932],{"class":2119},[2113,37553,37554],{"class":2166},"120",[2113,37556,5818],{"class":2119},[2113,37558,37559,37561,37563,37566,37568,37570,37572,37574,37576],{"class":2115,"line":2230},[2113,37560,37535],{"class":2119},[2113,37562,37538],{"class":2133},[2113,37564,37565],{"class":2119},"(F2, I2, cv::",[2113,37567,37544],{"class":2133},[2113,37569,2423],{"class":2119},[2113,37571,37549],{"class":2166},[2113,37573,932],{"class":2119},[2113,37575,37554],{"class":2166},[2113,37577,5818],{"class":2119},[2113,37579,37580],{"class":2115,"line":2235},[2113,37581,2125],{"emptyLinePlaceholder":1767},[2113,37583,37584],{"class":2115,"line":2241},[2113,37585,37586],{"class":2119},"    cv::Mat s1;\n",[2113,37588,37589,37591,37594,37597],{"class":2115,"line":2246},[2113,37590,37535],{"class":2119},[2113,37592,37593],{"class":2133},"absdiff",[2113,37595,37596],{"class":2119},"(I1, I2, s1);",[2113,37598,37599],{"class":2396},"   \u002F\u002F |I1 - I2|\n",[2113,37601,37602,37605,37607,37610,37613],{"class":2115,"line":2464},[2113,37603,37604],{"class":2414},"    s1",[2113,37606,179],{"class":2119},[2113,37608,37609],{"class":2133},"convertTo",[2113,37611,37612],{"class":2119},"(s1, CV_32F);",[2113,37614,37615],{"class":2396},"  \u002F\u002F cannot make a square on 8 bits\n",[2113,37617,37618,37621,37623,37626,37628,37631,37634],{"class":2115,"line":2085},[2113,37619,37620],{"class":2119},"    s1 ",[2113,37622,2335],{"class":2326},[2113,37624,37625],{"class":2414}," s1",[2113,37627,179],{"class":2119},[2113,37629,37630],{"class":2133},"mul",[2113,37632,37633],{"class":2119},"(s1);",[2113,37635,37636],{"class":2396},"           \u002F\u002F |I1 - I2|^2\n",[2113,37638,37639],{"class":2115,"line":2514},[2113,37640,2125],{"emptyLinePlaceholder":1767},[2113,37642,37643,37646,37648,37651,37653],{"class":2115,"line":2533},[2113,37644,37645],{"class":2119},"    cv::Scalar s ",[2113,37647,2335],{"class":2326},[2113,37649,37650],{"class":2133}," sum",[2113,37652,37633],{"class":2119},[2113,37654,37655],{"class":2396},"    \u002F\u002F sum elements per channel\n",[2113,37657,37658],{"class":2115,"line":2556},[2113,37659,2125],{"emptyLinePlaceholder":1767},[2113,37661,37662,37665,37668,37670,37673,37675,37678,37680,37682,37684,37686,37688,37690,37692,37694,37696,37698,37700,37702,37704,37706,37708,37710,37713],{"class":2115,"line":2569},[2113,37663,37664],{"class":2326},"    double",[2113,37666,37667],{"class":2119}," sse ",[2113,37669,2335],{"class":2326},[2113,37671,37672],{"class":2414}," s",[2113,37674,179],{"class":2119},[2113,37676,37677],{"class":2330},"val",[2113,37679,4124],{"class":2119},[2113,37681,3095],{"class":2166},[2113,37683,4129],{"class":2119},[2113,37685,3061],{"class":2326},[2113,37687,37672],{"class":2414},[2113,37689,179],{"class":2119},[2113,37691,37677],{"class":2330},[2113,37693,4124],{"class":2119},[2113,37695,3686],{"class":2166},[2113,37697,4129],{"class":2119},[2113,37699,3061],{"class":2326},[2113,37701,37672],{"class":2414},[2113,37703,179],{"class":2119},[2113,37705,37677],{"class":2330},[2113,37707,4124],{"class":2119},[2113,37709,2547],{"class":2166},[2113,37711,37712],{"class":2119},"];",[2113,37714,37715],{"class":2396}," \u002F\u002F sum channels\n",[2113,37717,37718],{"class":2115,"line":2575},[2113,37719,2125],{"emptyLinePlaceholder":1767},[2113,37721,37722,37724,37727,37730,37732,37734,37736,37738,37741],{"class":2115,"line":2596},[2113,37723,7394],{"class":2326},[2113,37725,37726],{"class":2119},"( sse ",[2113,37728,37729],{"class":2326},"\u003C=",[2113,37731,2850],{"class":2166},[2113,37733,5601],{"class":2330},[2113,37735,3242],{"class":2119},[2113,37737,35474],{"class":2166},[2113,37739,37740],{"class":2119},") {",[2113,37742,37743],{"class":2396},"        \u002F\u002F for small values return zero\n",[2113,37745,37746,37748,37750,37753,37755,37757,37760,37763],{"class":2115,"line":3192},[2113,37747,28968],{"class":2326},[2113,37749,2581],{"class":2119},[2113,37751,37752],{"class":2414},"numeric_limits",[2113,37754,3109],{"class":2119},[2113,37756,37487],{"class":2326},[2113,37758,37759],{"class":2119},">::",[2113,37761,37762],{"class":2133},"infinity",[2113,37764,3944],{"class":2119},[2113,37766,37767,37769,37771],{"class":2115,"line":3213},[2113,37768,20671],{"class":2119},[2113,37770,4227],{"class":2326},[2113,37772,2647],{"class":2119},[2113,37774,37775,37778,37781,37783,37785,37787,37789,37791,37794,37797,37799,37802,37804,37806,37809,37811,37814],{"class":2115,"line":3236},[2113,37776,37777],{"class":2326},"        double",[2113,37779,37780],{"class":2119}," mse  ",[2113,37782,2335],{"class":2326},[2113,37784,37667],{"class":2119},[2113,37786,2520],{"class":2326},[2113,37788,2495],{"class":2119},[2113,37790,37487],{"class":2326},[2113,37792,37793],{"class":2119},")(",[2113,37795,37796],{"class":2414},"I1",[2113,37798,179],{"class":2119},[2113,37800,37801],{"class":2133},"channels",[2113,37803,4543],{"class":2119},[2113,37805,2676],{"class":2326},[2113,37807,37808],{"class":2414}," I1",[2113,37810,179],{"class":2119},[2113,37812,37813],{"class":2133},"total",[2113,37815,2593],{"class":2119},[2113,37817,37818,37820,37823,37825,37828,37830,37833,37835,37838,37840,37843,37845,37847],{"class":2115,"line":3248},[2113,37819,37777],{"class":2326},[2113,37821,37822],{"class":2119}," psnr ",[2113,37824,2335],{"class":2326},[2113,37826,37827],{"class":2166}," 10.0",[2113,37829,2671],{"class":2326},[2113,37831,37832],{"class":2133}," log10",[2113,37834,8879],{"class":2119},[2113,37836,37837],{"class":2166},"255",[2113,37839,2671],{"class":2326},[2113,37841,37842],{"class":2166}," 255",[2113,37844,5709],{"class":2119},[2113,37846,2520],{"class":2326},[2113,37848,37849],{"class":2119}," mse);\n",[2113,37851,37852,37854],{"class":2115,"line":4899},[2113,37853,28968],{"class":2326},[2113,37855,37856],{"class":2119}," psnr;\n",[2113,37858,37859],{"class":2115,"line":1777},[2113,37860,4665],{"class":2119},[133,37862,22696],{"id":22695},[12,37864,37865],{},"La raison qui fait que je voulais m'amuser avec OpenCV c'est qu'il permet de faire ces calculs à l'aide du GPU au lieu du CPU.",[12,37867,37868,37869,37874],{},"L'utilisation du GPU permet d'améliorer la vitesse de calcul pour tout ce qui est traitement d'image, ce pour quoi un GPU est prévu pour. Pour plus d'informations sur\nl'utilisation du GPU dans OpenCV peut être trouvé sur la page ",[49,37870,37873],{"href":37871,"rel":37872},"http:\u002F\u002Fopencv.org\u002Fplatforms\u002Fcuda.html",[347],"CUDA"," d'OpenCV.",[12,37876,37877],{},"Le problème est que sur la version de Debian jessie que j'utilise, OpenCV n'est pas compilé avec CUDA, et ne permet donc pas d'utiliser le GPU. J'ai donc dû compiler ma propre\nversion d'OpenCV.",[12,37879,37880],{},"Pour cela la première étape consiste à récupérer le code source et à se positionner sur la branche que l'on souhaite compiler. Pour ma part je préfère compiler sur la branche 2.4,\nplus proche de la version de Debian.",[2105,37882,37884],{"className":2317,"code":37883,"language":2319,"meta":1646,"style":1646},"git clone https:\u002F\u002Fgithub.com\u002Fopencv\u002Fopencv.git\ngit checkout 2.4\n",[184,37885,37886,37897],{"__ignoreMap":1646},[2113,37887,37888,37891,37894],{"class":2115,"line":2116},[2113,37889,37890],{"class":2133},"git",[2113,37892,37893],{"class":2149}," clone",[2113,37895,37896],{"class":2149}," https:\u002F\u002Fgithub.com\u002Fopencv\u002Fopencv.git\n",[2113,37898,37899,37901,37904],{"class":2115,"line":1647},[2113,37900,37890],{"class":2133},[2113,37902,37903],{"class":2149}," checkout",[2113,37905,37906],{"class":2166}," 2.4\n",[12,37908,37909],{},"Viens ensuite la compilation :",[2105,37911,37913],{"className":2317,"code":37912,"language":2319,"meta":1646,"style":1646},"mkdir build\ncd build\ncmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=\u002Fhome\u002Fphoenix\u002Fusr\u002Flocal  -DENABLE_SSE=ON -DENABLE_SSE2=ON -DENABLE_SSE3=ON -DWITH_TBB=ON -DWITH_1394=ON -DWITH_V4L=ON -DWITH_OPENGL=ON  -DWITH_GTK=ON -DWITH_JASPER=ON -DWITH_JPEG=ON -DWITH_PNG=ON -DWITH_TIFF=ON  -DWITH_OPENEXR=ON -DWITH_PVAPI=ON   -DWITH_EIGEN=ON -DCMAKE_SKIP_RPATH=ON -D WITH_CUDA=ON -D ENABLE_FAST_MATH=1 -D CUDA_FAST_MATH=1 -D WITH_CUBLAS=1 -DWITH_IPP=ON -D CUDA_GENERATION=Auto -D WITH_FFMPEG=ON  ..\u002F\n",[184,37914,37915,37921,37927],{"__ignoreMap":1646},[2113,37916,37917,37919],{"class":2115,"line":2116},[2113,37918,17072],{"class":2133},[2113,37920,17075],{"class":2149},[2113,37922,37923,37925],{"class":2115,"line":1647},[2113,37924,17064],{"class":2334},[2113,37926,17075],{"class":2149},[2113,37928,37929,37931,37934,37937,37939,37942,37945,37948,37951,37954,37957,37960,37963,37966,37969,37972,37975,37978,37981,37984,37987,37990,37992,37995,37997,38000,38002,38004,38007,38009,38011,38014,38016,38019,38021,38024,38026,38029],{"class":2115,"line":1652},[2113,37930,17086],{"class":2133},[2113,37932,37933],{"class":2166}," -D",[2113,37935,37936],{"class":2149}," CMAKE_BUILD_TYPE=RELEASE",[2113,37938,37933],{"class":2166},[2113,37940,37941],{"class":2149}," CMAKE_INSTALL_PREFIX=\u002Fhome\u002Fphoenix\u002Fusr\u002Flocal",[2113,37943,37944],{"class":2166},"  -DENABLE_SSE=ON",[2113,37946,37947],{"class":2166}," -DENABLE_SSE2=ON",[2113,37949,37950],{"class":2166}," -DENABLE_SSE3=ON",[2113,37952,37953],{"class":2166}," -DWITH_TBB=ON",[2113,37955,37956],{"class":2166}," -DWITH_1394=ON",[2113,37958,37959],{"class":2166}," -DWITH_V4L=ON",[2113,37961,37962],{"class":2166}," -DWITH_OPENGL=ON",[2113,37964,37965],{"class":2166},"  -DWITH_GTK=ON",[2113,37967,37968],{"class":2166}," -DWITH_JASPER=ON",[2113,37970,37971],{"class":2166}," -DWITH_JPEG=ON",[2113,37973,37974],{"class":2166}," -DWITH_PNG=ON",[2113,37976,37977],{"class":2166}," -DWITH_TIFF=ON",[2113,37979,37980],{"class":2166},"  -DWITH_OPENEXR=ON",[2113,37982,37983],{"class":2166}," -DWITH_PVAPI=ON",[2113,37985,37986],{"class":2166},"   -DWITH_EIGEN=ON",[2113,37988,37989],{"class":2166}," -DCMAKE_SKIP_RPATH=ON",[2113,37991,37933],{"class":2166},[2113,37993,37994],{"class":2149}," WITH_CUDA=ON",[2113,37996,37933],{"class":2166},[2113,37998,37999],{"class":2149}," ENABLE_FAST_MATH=",[2113,38001,3686],{"class":2166},[2113,38003,37933],{"class":2166},[2113,38005,38006],{"class":2149}," CUDA_FAST_MATH=",[2113,38008,3686],{"class":2166},[2113,38010,37933],{"class":2166},[2113,38012,38013],{"class":2149}," WITH_CUBLAS=",[2113,38015,3686],{"class":2166},[2113,38017,38018],{"class":2166}," -DWITH_IPP=ON",[2113,38020,37933],{"class":2166},[2113,38022,38023],{"class":2149}," CUDA_GENERATION=Auto",[2113,38025,37933],{"class":2166},[2113,38027,38028],{"class":2149}," WITH_FFMPEG=ON",[2113,38030,38031],{"class":2149},"  ..\u002F\n",[12,38033,38034],{},"J'active lors de la compilation le maximum d'optimisation dont CUDA. J'active également FFMPEG sans lequel le nombre de fichier reconnu baisse énormément sur ma machine. Après\navoir lancé cmake j'obtiens le résultat suivant :",[2105,38036,38039],{"className":38037,"code":38038,"language":26023},[26021],"-- General configuration for OpenCV 2.4.13.1 =====================================\n--   Version control:               2.4.13.1-48-gac118ae\n--\n--   Platform:\n--     Host:                        Linux 3.16.0-4-amd64 x86_64\n--     CMake:                       3.6.2\n--     CMake generator:             Unix Makefiles\n--     CMake build tool:            \u002Fusr\u002Fbin\u002Fmake\n--     Configuration:               RELEASE\n--\n--   C\u002FC++:\n--     Built as dynamic libs?:      YES\n--     C++ Compiler:                \u002Fusr\u002Fbin\u002Fc++  (ver 4.9.2)\n--     C++ flags (Release):         -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wno-narrowing -Wno-delete-non-virtual-dtor -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -O3 -DNDEBUG  -DNDEBUG\n--     C++ flags (Debug):           -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wno-narrowing -Wno-delete-non-virtual-dtor -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -g  -O0 -DDEBUG -D_DEBUG\n--     C Compiler:                  \u002Fusr\u002Fbin\u002Fcc\n--     C flags (Release):           -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wno-narrowing -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -O3 -DNDEBUG  -DNDEBUG\n--     C flags (Debug):             -fsigned-char -W -Wall -Werror=return-type -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wno-narrowing -Wno-comment -Wno-array-bounds -Wno-aggressive-loop-optimizations -fdiagnostics-show-option -Wno-long-long -pthread -fomit-frame-pointer -ffast-math -msse -msse2 -msse3 -ffunction-sections -g  -O0 -DDEBUG -D_DEBUG\n--     Linker flags (Release):\n--     Linker flags (Debug):\n--     ccache:                      NO\n--     Precompiled headers:         YES\n--\n--   OpenCV modules:\n--     To be built:                 core flann imgproc highgui features2d calib3d ml video legacy objdetect photo gpu ocl nonfree contrib java python stitching superres ts videostab\n--     Disabled:                    world\n--     Disabled by dependency:      -\n--     Unavailable:                 androidcamera dynamicuda viz\n--\n--   GUI:\n--     QT:                          NO\n--     GTK+ 2.x:                    YES (ver 2.24.25)\n--     GThread :                    YES (ver 2.42.1)\n--     GtkGlExt:                    NO\n--     OpenGL support:              NO\n--     VTK support:                 NO\n--\n--   Media I\u002FO:\n--     ZLib:                        \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibz.so (ver 1.2.8)\n--     JPEG:                        \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibjpeg.so (ver )\n--     PNG:                         \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibpng.so (ver 1.2.50)\n--     TIFF:                        \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibtiff.so (ver 42 - 4.0.3)\n--     JPEG 2000:                   \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibjasper.so (ver 1.900.1)\n--     OpenEXR:                     \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002FlibImath.so \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002FlibIlmImf.so \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002FlibIex.so \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002FlibHalf.so \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002FlibIlmThread.so (ver 1.6.1)\n--\n--   Video I\u002FO:\n--     DC1394 1.x:                  NO\n--     DC1394 2.x:                  YES (ver 2.2.3)\n--     FFMPEG:                      YES\n--       codec:                     YES (ver 56.1.0)\n--       format:                    YES (ver 56.1.0)\n--       util:                      YES (ver 54.3.0)\n--       swscale:                   YES (ver 3.0.0)\n--       resample:                  YES (ver 2.1.0)\n--       gentoo-style:              YES\n--     GStreamer:                   NO\n--     OpenNI:                      NO\n--     OpenNI PrimeSensor Modules:  NO\n--     PvAPI:                       NO\n--     GigEVisionSDK:               NO\n--     UniCap:                      NO\n--     UniCap ucil:                 NO\n--     V4L\u002FV4L2:                    NO\u002FYES\n--     XIMEA:                       NO\n--     Xine:                        NO\n--\n--   Other third-party libraries:\n--     Use IPP:                     IPP not found\n--     Use Eigen:                   NO\n--     Use TBB:                     NO\n--     Use OpenMP:                  NO\n--     Use GCD                      NO\n--     Use Concurrency              NO\n--     Use C=:                      NO\n--     Use Cuda:                    YES (ver 7.5)\n--     Use OpenCL:                  YES\n--\n--   NVIDIA CUDA\n--     Use CUFFT:                   YES\n--     Use CUBLAS:                  YES\n--     USE NVCUVID:                 NO\n--     NVIDIA GPU arch:             21\n--     NVIDIA PTX archs:\n--     Use fast math:               YES\n--     Tiny gpu module:             NO\n--\n--   OpenCL:\n--     Version:                     dynamic\n--     Include path:                \u002Fhome\u002Fphoenix\u002FDeveloppement\u002FExternalSoftware\u002Fopencv\u002F3rdparty\u002Finclude\u002Fopencl\u002F1.2\n--     Use AMD FFT:                 NO\n--     Use AMD BLAS:                NO\n--\n--   Python:\n--     Interpreter:                 \u002Fusr\u002Fbin\u002Fpython2 (ver 2.7.10)\n--     Libraries:                   \u002Fusr\u002Flib\u002Fx86_64-linux-gnu\u002Flibpython2.7.so (ver 2.7.10rc1)\n--     numpy:                       \u002Fusr\u002Flib\u002Fpython2.7\u002Fdist-packages\u002Fnumpy\u002Fcore\u002Finclude (ver 1.8.2)\n--     packages path:               lib\u002Fpython2.7\u002Fdist-packages\n--\n--   Java:\n--     ant:                         \u002Fusr\u002Fbin\u002Fant (ver 1.9.4)\n--     JNI:                         \u002Fusr\u002Flib\u002Fjvm\u002Fjava-7-openjdk-amd64\u002Finclude \u002Fusr\u002Flib\u002Fjvm\u002Fjava-7-openjdk-amd64\u002Finclude \u002Fusr\u002Flib\u002Fjvm\u002Fjava-7-openjdk-amd64\u002Finclude\n--     Java tests:                  YES\n--\n--   Documentation:\n--     Build Documentation:         NO\n--     Sphinx:                      NO\n--     PdfLaTeX compiler:           \u002Fusr\u002Fbin\u002Fpdflatex\n--     Doxygen:                     YES (\u002Fusr\u002Fbin\u002Fdoxygen)\n--\n--   Tests and samples:\n--     Tests:                       YES\n--     Performance tests:           YES\n--     C\u002FC++ Examples:              NO\n--\n--   Install path:                  \u002Fhome\u002Fphoenix\u002Fusr\u002Flocal\n--\n--   cvconfig.h is in:              \u002Fhome\u002Fphoenix\u002FDeveloppement\u002FExternalSoftware\u002Fopencv\u002Fbuild\n-- -----------------------------------------------------------------\n",[184,38040,38038],{"__ignoreMap":1646},[12,38042,38043,38044,38047,38048,38051,38052,38055],{},"Pour que la compilation se déroule sans problème, il vous faudra installer certains paquets sur votre distribution. Sur Debian Jessie, j'ai installé ",[184,38045,38046],{},"nvidia-cuda-toolkit"," en\nversion ",[184,38049,38050],{},"7.5.18-4~bpo8+1",". Comme vous pouvez les voir c'est une version qui provient du repository de backports. La version ",[184,38053,38054],{},"6.0.37-5"," ne me permettait pas d'activer CUDA. J'ai\ndonc du monter l'ensemble du driver propriétaire sur mon poste de développement.",[12,38057,38058],{},"Rasssurez-vous, si vous ne voulez pas utiliser les backports ou ne pas utiliser de driver propriétaire, vous pouvez tester le programme dans sa version CPU. :)",[12,38060,38061],{},"Voici comment le code a été ré-écrit pour utiliser le GPU à la place du CPU:",[2105,38063,38065],{"className":2387,"code":38064,"language":2389,"meta":1646,"style":1646},"struct BufferPSNR {                                    \u002F\u002F Optimized GPU versions\n    \u002F\u002F Data allocations are very expensive on GPU. Use a buffer to solve: allocate once reuse later.\n    cv::gpu::GpuMat gF1, gF2, gI1, gI2, gs, t1,t2;\n\n    cv::gpu::GpuMat buf;\n};\n\ndouble getPSNR_GPU_optimized(const cv::Mat& F1, const cv::Mat& F2, BufferPSNR& b) {\n    b.gF1.upload(F1);\n    b.gF2.upload(F2);\n\n    cv::gpu::resize(b.gF1, b.gI1, cv::Size(160, 120));\n    cv::gpu::resize(b.gF2, b.gI2, cv::Size(160, 120));\n\n    b.gI1.convertTo(b.t1, CV_32F);\n    b.gI2.convertTo(b.t2, CV_32F);\n\n    cv::gpu::absdiff(b.t1.reshape(1), b.t2.reshape(1), b.gs);\n    cv::gpu::multiply(b.gs, b.gs, b.gs);\n\n    double sse = cv::gpu::sum(b.gs, b.buf)[0];\n\n    if( sse \u003C= 1e-10) \u002F\u002F for small values return zero\n        return std::numeric_limits\u003Cdouble>::infinity();\n    else {\n        double mse = sse \u002F(double)(F1.channels() * F1.total());\n        double psnr = 10.0*log10((255*255)\u002Fmse);\n        return psnr;\n    }\n}\n",[184,38066,38067,38080,38085,38090,38094,38099,38103,38107,38149,38167,38183,38187,38230,38271,38275,38299,38322,38326,38380,38417,38421,38461,38465,38486,38504,38511,38549,38579,38585,38589],{"__ignoreMap":1646},[2113,38068,38069,38071,38074,38077],{"class":2115,"line":2116},[2113,38070,2641],{"class":2326},[2113,38072,38073],{"class":2414}," BufferPSNR",[2113,38075,38076],{"class":2119}," {",[2113,38078,38079],{"class":2396},"                                    \u002F\u002F Optimized GPU versions\n",[2113,38081,38082],{"class":2115,"line":1647},[2113,38083,38084],{"class":2396},"    \u002F\u002F Data allocations are very expensive on GPU. Use a buffer to solve: allocate once reuse later.\n",[2113,38086,38087],{"class":2115,"line":1652},[2113,38088,38089],{"class":2119},"    cv::gpu::GpuMat gF1, gF2, gI1, gI2, gs, t1,t2;\n",[2113,38091,38092],{"class":2115,"line":2185},[2113,38093,2125],{"emptyLinePlaceholder":1767},[2113,38095,38096],{"class":2115,"line":2230},[2113,38097,38098],{"class":2119},"    cv::gpu::GpuMat buf;\n",[2113,38100,38101],{"class":2115,"line":2235},[2113,38102,2787],{"class":2119},[2113,38104,38105],{"class":2115,"line":2241},[2113,38106,2125],{"emptyLinePlaceholder":1767},[2113,38108,38109,38111,38114,38116,38118,38120,38122,38124,38126,38128,38130,38132,38134,38136,38138,38140,38143,38145,38147],{"class":2115,"line":2246},[2113,38110,37487],{"class":2326},[2113,38112,38113],{"class":2133}," getPSNR_GPU_optimized",[2113,38115,2423],{"class":2119},[2113,38117,7785],{"class":2326},[2113,38119,37497],{"class":2119},[2113,38121,37500],{"class":2414},[2113,38123,37503],{"class":2326},[2113,38125,37506],{"class":2429},[2113,38127,932],{"class":2119},[2113,38129,7785],{"class":2326},[2113,38131,37497],{"class":2119},[2113,38133,37500],{"class":2414},[2113,38135,37503],{"class":2326},[2113,38137,37519],{"class":2429},[2113,38139,932],{"class":2119},[2113,38141,38142],{"class":2414},"BufferPSNR",[2113,38144,37503],{"class":2326},[2113,38146,3951],{"class":2429},[2113,38148,2433],{"class":2119},[2113,38150,38151,38154,38156,38159,38161,38164],{"class":2115,"line":2464},[2113,38152,38153],{"class":2414},"    b",[2113,38155,179],{"class":2119},[2113,38157,38158],{"class":2414},"gF1",[2113,38160,179],{"class":2119},[2113,38162,38163],{"class":2133},"upload",[2113,38165,38166],{"class":2119},"(F1);\n",[2113,38168,38169,38171,38173,38176,38178,38180],{"class":2115,"line":2085},[2113,38170,38153],{"class":2414},[2113,38172,179],{"class":2119},[2113,38174,38175],{"class":2414},"gF2",[2113,38177,179],{"class":2119},[2113,38179,38163],{"class":2133},[2113,38181,38182],{"class":2119},"(F2);\n",[2113,38184,38185],{"class":2115,"line":2514},[2113,38186,2125],{"emptyLinePlaceholder":1767},[2113,38188,38189,38191,38194,38196,38198,38200,38202,38204,38206,38208,38210,38212,38215,38218,38220,38222,38224,38226,38228],{"class":2115,"line":2533},[2113,38190,37535],{"class":2119},[2113,38192,38193],{"class":2414},"gpu",[2113,38195,2418],{"class":2119},[2113,38197,37538],{"class":2133},[2113,38199,2423],{"class":2119},[2113,38201,3992],{"class":2414},[2113,38203,179],{"class":2119},[2113,38205,38158],{"class":2330},[2113,38207,932],{"class":2119},[2113,38209,3992],{"class":2414},[2113,38211,179],{"class":2119},[2113,38213,38214],{"class":2330},"gI1",[2113,38216,38217],{"class":2119},", cv::",[2113,38219,37544],{"class":2133},[2113,38221,2423],{"class":2119},[2113,38223,37549],{"class":2166},[2113,38225,932],{"class":2119},[2113,38227,37554],{"class":2166},[2113,38229,5818],{"class":2119},[2113,38231,38232,38234,38236,38238,38240,38242,38244,38246,38248,38250,38252,38254,38257,38259,38261,38263,38265,38267,38269],{"class":2115,"line":2556},[2113,38233,37535],{"class":2119},[2113,38235,38193],{"class":2414},[2113,38237,2418],{"class":2119},[2113,38239,37538],{"class":2133},[2113,38241,2423],{"class":2119},[2113,38243,3992],{"class":2414},[2113,38245,179],{"class":2119},[2113,38247,38175],{"class":2330},[2113,38249,932],{"class":2119},[2113,38251,3992],{"class":2414},[2113,38253,179],{"class":2119},[2113,38255,38256],{"class":2330},"gI2",[2113,38258,38217],{"class":2119},[2113,38260,37544],{"class":2133},[2113,38262,2423],{"class":2119},[2113,38264,37549],{"class":2166},[2113,38266,932],{"class":2119},[2113,38268,37554],{"class":2166},[2113,38270,5818],{"class":2119},[2113,38272,38273],{"class":2115,"line":2569},[2113,38274,2125],{"emptyLinePlaceholder":1767},[2113,38276,38277,38279,38281,38283,38285,38287,38289,38291,38293,38296],{"class":2115,"line":2575},[2113,38278,38153],{"class":2414},[2113,38280,179],{"class":2119},[2113,38282,38214],{"class":2414},[2113,38284,179],{"class":2119},[2113,38286,37609],{"class":2133},[2113,38288,2423],{"class":2119},[2113,38290,3992],{"class":2414},[2113,38292,179],{"class":2119},[2113,38294,38295],{"class":2330},"t1",[2113,38297,38298],{"class":2119},", CV_32F);\n",[2113,38300,38301,38303,38305,38307,38309,38311,38313,38315,38317,38320],{"class":2115,"line":2596},[2113,38302,38153],{"class":2414},[2113,38304,179],{"class":2119},[2113,38306,38256],{"class":2414},[2113,38308,179],{"class":2119},[2113,38310,37609],{"class":2133},[2113,38312,2423],{"class":2119},[2113,38314,3992],{"class":2414},[2113,38316,179],{"class":2119},[2113,38318,38319],{"class":2330},"t2",[2113,38321,38298],{"class":2119},[2113,38323,38324],{"class":2115,"line":3192},[2113,38325,2125],{"emptyLinePlaceholder":1767},[2113,38327,38328,38330,38332,38334,38336,38338,38340,38342,38344,38346,38349,38351,38353,38355,38357,38359,38361,38363,38365,38367,38369,38371,38373,38375,38378],{"class":2115,"line":3213},[2113,38329,37535],{"class":2119},[2113,38331,38193],{"class":2414},[2113,38333,2418],{"class":2119},[2113,38335,37593],{"class":2133},[2113,38337,2423],{"class":2119},[2113,38339,3992],{"class":2414},[2113,38341,179],{"class":2119},[2113,38343,38295],{"class":2414},[2113,38345,179],{"class":2119},[2113,38347,38348],{"class":2133},"reshape",[2113,38350,2423],{"class":2119},[2113,38352,3686],{"class":2166},[2113,38354,8507],{"class":2119},[2113,38356,3992],{"class":2414},[2113,38358,179],{"class":2119},[2113,38360,38319],{"class":2414},[2113,38362,179],{"class":2119},[2113,38364,38348],{"class":2133},[2113,38366,2423],{"class":2119},[2113,38368,3686],{"class":2166},[2113,38370,8507],{"class":2119},[2113,38372,3992],{"class":2414},[2113,38374,179],{"class":2119},[2113,38376,38377],{"class":2330},"gs",[2113,38379,2553],{"class":2119},[2113,38381,38382,38384,38386,38388,38391,38393,38395,38397,38399,38401,38403,38405,38407,38409,38411,38413,38415],{"class":2115,"line":3236},[2113,38383,37535],{"class":2119},[2113,38385,38193],{"class":2414},[2113,38387,2418],{"class":2119},[2113,38389,38390],{"class":2133},"multiply",[2113,38392,2423],{"class":2119},[2113,38394,3992],{"class":2414},[2113,38396,179],{"class":2119},[2113,38398,38377],{"class":2330},[2113,38400,932],{"class":2119},[2113,38402,3992],{"class":2414},[2113,38404,179],{"class":2119},[2113,38406,38377],{"class":2330},[2113,38408,932],{"class":2119},[2113,38410,3992],{"class":2414},[2113,38412,179],{"class":2119},[2113,38414,38377],{"class":2330},[2113,38416,2553],{"class":2119},[2113,38418,38419],{"class":2115,"line":3248},[2113,38420,2125],{"emptyLinePlaceholder":1767},[2113,38422,38423,38425,38427,38429,38431,38433,38435,38438,38440,38442,38444,38446,38448,38450,38452,38454,38457,38459],{"class":2115,"line":4899},[2113,38424,37664],{"class":2326},[2113,38426,37667],{"class":2119},[2113,38428,2335],{"class":2326},[2113,38430,37497],{"class":2119},[2113,38432,38193],{"class":2414},[2113,38434,2418],{"class":2119},[2113,38436,38437],{"class":2133},"sum",[2113,38439,2423],{"class":2119},[2113,38441,3992],{"class":2414},[2113,38443,179],{"class":2119},[2113,38445,38377],{"class":2330},[2113,38447,932],{"class":2119},[2113,38449,3992],{"class":2414},[2113,38451,179],{"class":2119},[2113,38453,4469],{"class":2330},[2113,38455,38456],{"class":2119},")[",[2113,38458,3095],{"class":2166},[2113,38460,6179],{"class":2119},[2113,38462,38463],{"class":2115,"line":1777},[2113,38464,2125],{"emptyLinePlaceholder":1767},[2113,38466,38467,38469,38471,38473,38475,38477,38479,38481,38483],{"class":2115,"line":4931},[2113,38468,7394],{"class":2326},[2113,38470,37726],{"class":2119},[2113,38472,37729],{"class":2326},[2113,38474,2850],{"class":2166},[2113,38476,5601],{"class":2330},[2113,38478,3242],{"class":2119},[2113,38480,35474],{"class":2166},[2113,38482,17832],{"class":2119},[2113,38484,38485],{"class":2396}," \u002F\u002F for small values return zero\n",[2113,38487,38488,38490,38492,38494,38496,38498,38500,38502],{"class":2115,"line":4961},[2113,38489,28968],{"class":2326},[2113,38491,2581],{"class":2119},[2113,38493,37752],{"class":2414},[2113,38495,3109],{"class":2119},[2113,38497,37487],{"class":2326},[2113,38499,37759],{"class":2119},[2113,38501,37762],{"class":2133},[2113,38503,3944],{"class":2119},[2113,38505,38506,38509],{"class":2115,"line":4976},[2113,38507,38508],{"class":2326},"    else",[2113,38510,2647],{"class":2119},[2113,38512,38513,38515,38518,38520,38522,38524,38526,38528,38530,38533,38535,38537,38539,38541,38543,38545,38547],{"class":2115,"line":4993},[2113,38514,37777],{"class":2326},[2113,38516,38517],{"class":2119}," mse ",[2113,38519,2335],{"class":2326},[2113,38521,37667],{"class":2119},[2113,38523,2520],{"class":2326},[2113,38525,2423],{"class":2119},[2113,38527,37487],{"class":2326},[2113,38529,37793],{"class":2119},[2113,38531,38532],{"class":2414},"F1",[2113,38534,179],{"class":2119},[2113,38536,37801],{"class":2133},[2113,38538,4543],{"class":2119},[2113,38540,2676],{"class":2326},[2113,38542,37506],{"class":2414},[2113,38544,179],{"class":2119},[2113,38546,37813],{"class":2133},[2113,38548,2593],{"class":2119},[2113,38550,38551,38553,38555,38557,38559,38561,38564,38566,38568,38570,38572,38574,38576],{"class":2115,"line":5013},[2113,38552,37777],{"class":2326},[2113,38554,37822],{"class":2119},[2113,38556,2335],{"class":2326},[2113,38558,37827],{"class":2166},[2113,38560,2676],{"class":2326},[2113,38562,38563],{"class":2133},"log10",[2113,38565,8879],{"class":2119},[2113,38567,37837],{"class":2166},[2113,38569,2676],{"class":2326},[2113,38571,37837],{"class":2166},[2113,38573,17832],{"class":2119},[2113,38575,2520],{"class":2326},[2113,38577,38578],{"class":2119},"mse);\n",[2113,38580,38581,38583],{"class":2115,"line":5018},[2113,38582,28968],{"class":2326},[2113,38584,37856],{"class":2119},[2113,38586,38587],{"class":2115,"line":5042},[2113,38588,4665],{"class":2119},[2113,38590,38591],{"class":2115,"line":5057},[2113,38592,2599],{"class":2119},[12,38594,38595,38596,38598,38599,38602],{},"L'utilisation de la structure ",[184,38597,38142],{}," permet de ne pas perdre de performance lors de l'initialisation relativement lourde des objets ",[184,38600,38601],{},"GpuMat",". Sans cela, l'utilisation du Gpu\nserait moins performant que la version Cpu.",[133,38604,38606],{"id":38605},"lexpérience","L'expérience",[12,38608,38609],{},"Maintenant place à l'expérience. Nous allons lancer notre programme sur notre jeu d'essai comprenant les vidéos issue des DVD, ainsi que les vidéos recompressées pour l'expérience.\nSi l'expérence se déroule bien l'algorithme devrait nous detecter les fichiers dupliqués, ainsi que les fichiers recompressés.",[128,38611,38613],{"id":38612},"lancement-et-selection-des-dossiers","Lancement et selection des dossiers",[12,38615,38616],{},"La première étape est la sélection des dossiers que l'on souhaite comparer. Le programme ira lire récursivement l'ensemble des dossiers pour y trouver l'ensemble des fichiers\nvidéos.",[12,38618,38619],{},"La sélection d'un projet provient de mon envie de départ de pouvoir enregistrer l'avancement du projet au fur et à mesure. Cette étape n'a pas été réalisée mais l'existance du mode\nprojet existe toujours.",[12,38621,38622],{},[121,38623],{"alt":38624,"src":38625},"Selection du projet","\u002FProgrammation\u002Ffind-similarity\u002Ffind-similarity-1.png",[12,38627,38628],{},"Une fois le dossier projet choisi, il faut sélectionner la liste des dossiers contenant les vidéos et lancer le programme ...",[128,38630,38632],{"id":38631},"comparaison-des-vidéos","Comparaison des vidéos",[12,38634,38635,38636,179],{},"Dans cette étape le programme compare l'ensemble des vidéos présentes dans les dossiers. L'ensemble du processus tourne dans des threads afin de ne pas figer l'IHM, grâce à l'API\n",[184,38637,38638],{},"QtConcurrent",[12,38640,38641,38645],{},[121,38642],{"alt":38643,"src":38644},"Recherche","\u002FProgrammation\u002Ffind-similarity\u002Ffind-similarity-2.png",[121,38646],{"alt":38647,"src":38648},"Recherche encore","\u002FProgrammation\u002Ffind-similarity\u002Ffind-similarity-3.png",[12,38650,38651],{},"Les étapes de la recherche sont donc :",[506,38653,38654,38657,38660,38663,38666],{},[370,38655,38656],{},"Constitution de la liste des fichiers",[370,38658,38659],{},"Récupération des méta-données",[370,38661,38662],{},"Création de la liste des paires de fichiers (en filtrant sur la durée)",[370,38664,38665],{},"Calcul du PSNR pour chaque paire de fichiers",[370,38667,38668],{},"Filtrage pour ne garder que les paires de fichiers dont le PSNR est supérieur à 30 db.",[12,38670,38671,38672,38674,38675,38677,38678,38681,38682,38685],{},"Lors de mon développement je me suis basé sur l'API ",[184,38673,38638],{}," pour faire les différentes étapes. Faisant beaucoup de développement NodeJS ces derniers temps je suis habitué à\nl'utilisation des promesses et de leur enchainement pour faire des processus complexes. J'ai trouvé dommage de ne pas retrouver la même chose dans l'API ",[184,38676,38638],{},". Pour\nreproduire un équivalent, lorsqu'un ",[184,38679,38680],{},"QFuture","se termine, le signal émis par ",[184,38683,38684],{},"QFutureWatcher"," est récupérer par un SLOT du moteur qui s'occupe de lancer l'étape suivante.",[128,38687,38689],{"id":38688},"la-page-de-résultat","La page de résultat",[12,38691,38692],{},"La page de résultat liste les vidéos considérées identiques suite à l'étude d'une des images. Un coup d'oeil visuel permet alors de se faire un avis sur la question, et de\nsupprimer la vidéo que l'on souhaite.",[12,38694,38695],{},[121,38696],{"alt":38697,"src":38698},"Résultat 1","\u002FProgrammation\u002Ffind-similarity\u002Ffind-similarity-4.png",[12,38700,38701],{},"Comme on peut le voir le programme retrouve les vidéos dont l'image est identique, ainsi que les films qui ont été redimensionnés sans trop de soucis.\nLe problème se situe alors au niveau du bruit qui est généré. Plusieurs films sont considérés comme proches alors que complètement différents. Pour régler ce problème, comparer\nplusieurs images d'une même vidéo à des timestamps différents pourrait peut-être régler le problème.",[12,38703,38704],{},[121,38705],{"alt":38706,"src":38707},"Résultat 2","\u002FProgrammation\u002Ffind-similarity\u002Ffind-similarity-5.png",[12,38709,38710],{},[155,38711,38712],{},"Je vous conseille de vérifier manuellement la qualité et la similarité de chaque vidéo manuellement avant toute suppression.",[133,38714,1621],{"id":1620},[12,38716,38717],{},"En conclusion, j'ai trouvé l'expérience intéressante, et maintenant qu'elle est terminée, je vais pouvoir en tenter une autre ;). Est-ce que le programme continuera d'évoluer ?\nPourquoi pas ? Cela dépendera des PR (Pull Request) et des demandes faites par les utilisateurs, ainsi que du temps que j'ai envie de passer dessus.",[3358,38719,38720],{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}",{"title":1646,"searchDepth":1647,"depth":1647,"links":38722},[38723,38724,38725,38726,38727,38728,38729,38730],{"id":7759,"depth":1652,"text":7760},{"id":37331,"depth":1652,"text":37332},{"id":37424,"depth":1652,"text":37425},{"id":22695,"depth":1652,"text":22696},{"id":38605,"depth":1652,"text":38606},{"id":38612,"depth":1647,"text":38613},{"id":38631,"depth":1647,"text":38632},{"id":38688,"depth":1647,"text":38689,"children":38731},[38732],{"id":1620,"depth":1652,"text":1621},"2016-12-10",{"type":9,"value":38735},[38736,38738,38740,38742,38744,38746,38750],[133,38737,7760],{"id":7759},[12,38739,26097],{},[12,38741,37310],{},[12,38743,37313],{},[12,38745,37316],{},[12,38747,38748],{},[362,38749,37321],{},[12,38751,37324,38752,179],{},[49,38753,37327],{"href":37327,"rel":38754},[347],{"planet":1767},"\u002Fpost\u002Ffindsimilarity",{"title":37301,"description":1646},"findsimilarity","posts\u002FProgrammation\u002F2016-12-10-findsimilarity",[38761,38762,38763,38764],"qt","opencv","video","debian","LR-Ko6CoDmyd4rJj7msRVs_pkiaov-yO2_RYmY_C6cE",{"id":38767,"title":38768,"author":7,"body":38769,"category":36726,"categorySlug":37269,"date":39239,"description":14,"excerpt":39240,"extension":1764,"location":1765,"meta":39276,"navigation":1767,"path":39277,"published":1767,"seo":39278,"slug":39279,"stem":39280,"tags":39281,"timeToRead":2185,"__hash__":39282},"posts\u002Fposts\u002FPassprotect\u002F2016-10-02-presentation-passprotect.md","Passprotect 1.0.0",{"type":9,"value":38770,"toc":39230},[38771,38773,38776,38780,38787,38821,38824,38828,38835,38838,38848,38851,38857,38860,38874,38878,38881,38889,38892,38895,38899,38903,38906,38910,38918,38922,38962,38966,38973,39196,39199,39213,39216,39220,39227],[12,38772,14],{},[12,38774,38775],{},"Passprotect est un nouvel outil permettant d'enregistrer et de gérer vos mots de passe. L'idée derrière Passprotect est de pouvoir enregistrer et lire les mots de passe de vos\nsites Internet, de vos cartes de crédit, ou de toute autre forme de texte que vous voudriez garder en sécurité.",[35974,38777,38779],{"id":38778},"la-concurence","La concurence",[12,38781,38782,38783,38786],{},"La première question que l'on peut se poser est ",[362,38784,38785],{},"Pourquoi ne pas avoir choisi une solution existante ?"," ? Alors passons en revue les différents outils que j'utilise actuellement\nou dont j'ai étudié la possibilité d'utiliser :",[506,38788,38789,38797,38805,38813],{},[370,38790,38791,38796],{},[49,38792,38795],{"href":38793,"rel":38794},"http:\u002F\u002Fwww.awallet.org\u002F",[347],"aWallet",": Un outil pour android que j'utilise actuellement (sans la partie synchro). Même si l'utilise jusqu'à présent régulièrement, le logiciel ne\nme convient pas. Il est propriétaire. Je n'ai pas confiance dans la synchro cloud. Je ne peux pas l'héberger moi même.",[370,38798,38799,38804],{},[49,38800,38803],{"href":38801,"rel":38802},"https:\u002F\u002Flastpass.com",[347],"LastPass",": propriétaire, trop compliqué,",[370,38806,38807,38812],{},[49,38808,38811],{"href":38809,"rel":38810},"http:\u002F\u002Fkeepass.info\u002F",[347],"KeePass",": Client lourd, je souhaitais un client léger avec potentiellement un futur client lourd sur android, et une extension chrome.",[370,38814,38815,38820],{},[49,38816,38819],{"href":38817,"rel":38818},"https:\u002F\u002Fspideroak.com\u002Fsolutions\u002Fencryptr",[347],"Encryptr",": Libre :) Belle interface :) Mais basée sur un serveur crypton distant. J'aurais bien sûr pu forker le projet et le faire\npointer sur un de mes serveurs, installé manuellement, mais non.",[12,38822,38823],{},"Une autre raison de vouloir faire ma version, est tout simplement pour m'amuser 😄.",[35974,38825,38827],{"id":38826},"linterface","L'interface",[12,38829,38830,38831,179],{},"Passprotect est un logiciel que vous pouvez installer sur votre propre serveur ou utiliser la version en ligne se trouvant à l'adresse\n",[49,38832,38833],{"href":38833,"rel":38834},"https:\u002F\u002Fpassprotect.shadoware.org",[347],[12,38836,38837],{},"Pour commencer nous avons la page de login qui vous permet de vous connecter, et si bien sûr vous n'avez pas de login, vous pouvez en enregistrer un. C'est à ce moment là que\nl'application va générer la clé et le sel utilisé pour protéger toutes vos données.",[12,38839,38840,38844],{},[121,38841],{"alt":38842,"src":38843},"Login","\u002FPassprotect\u002Flogin.png",[121,38845],{"alt":38846,"src":38847},"Register","\u002FPassprotect\u002Fregister.png",[12,38849,38850],{},"Une fois connecté, vous pouvez visualiser la liste de vos données cryptées. A ce moment les données ne sont pas cryptées.",[12,38852,38853],{},[121,38854],{"alt":38855,"src":38856},"Ma liste","\u002FPassprotect\u002Fliste.png",[12,38858,38859],{},"Et enfin vous pouvez visualiser le détail de vos données sauvegardées.",[12,38861,38862,38866,38870],{},[121,38863],{"alt":38864,"src":38865},"Détail du texte","\u002FPassprotect\u002Fdetail_text.png",[121,38867],{"alt":38868,"src":38869},"Détail du mot de passe","\u002FPassprotect\u002Fdetail_password.png",[121,38871],{"alt":38872,"src":38873},"Détail de la carte de paiement","\u002FPassprotect\u002Fdetail_card.png",[35974,38875,38877],{"id":38876},"comment-les-données-sont-cryptées","Comment les données sont cryptées",[12,38879,38880],{},"Lors de la création de l'utilisateur, l'application crée alors 2 clés :",[506,38882,38883,38886],{},[370,38884,38885],{},"La clé maître, utilisée pour chiffrer les mots de passe, cartes de crédit, et textes (à l'aide de la méthode AES-256-CTR) en tous genre. Cette clé est elle-même chiffrée avec\nle mot de passe de l'utilisateur et le sel (16 octets aléatoires). La clé maître est générée à partir de 32 octets choisis aléatoirement,",[370,38887,38888],{},"La clé de session utilisé pour chiffrer le jeton JWT contenant la clé maître lors de la session: Cette clé générée à partir de 32 octets choisis aléatoirement.",[12,38890,38891],{},"Lors de la sauvegarde d'une ligne, la clé maître est utilisée pour chiffrer les données, et inversement quand la clé est lue, la clé maître est utilisée pour les déchiffrer.",[12,38893,38894],{},"Seul l'utilisateur connecté a accès à ses propres données (chiffrées). Le chiffrage est fait côté serveur par le nodejs. Du coup les données transitant en clair vers le client, il\nfaut que le site soit accédé à partir d'un serveur en HTTPS.",[128,38896,38898],{"id":38897},"installation","Installation",[133,38900,38902],{"id":38901},"attention","Attention",[12,38904,38905],{},"Attention, j'essaie de rendre passprotect le plus sécurisé possible mais je ne suis pas un expert en cryptographie. Sauf si vous savez estimer le niveau de sécurité de l'application,\nl'utilisation de l'application pour protéger des données importantes se fait à vos risques et périls.",[133,38907,38909],{"id":38908},"les-pré-requis","Les pré-requis",[506,38911,38912,38915],{},[370,38913,38914],{},"NodeJS > 6.2.1",[370,38916,38917],{},"MongoDB > 3.2.7",[133,38919,38921],{"id":38920},"depuis-les-sources","Depuis les sources",[2105,38923,38925],{"className":2317,"code":38924,"language":2319,"meta":1646,"style":1646},"    hg clone https:\u002F\u002Fgithub.com\u002Fphoenix741\u002Fpassprotect-server\n    npm install\n    MODE=prod npm run build\n",[184,38926,38927,38937,38945],{"__ignoreMap":1646},[2113,38928,38929,38932,38934],{"class":2115,"line":2116},[2113,38930,38931],{"class":2133},"    hg",[2113,38933,37893],{"class":2149},[2113,38935,38936],{"class":2149}," https:\u002F\u002Fgithub.com\u002Fphoenix741\u002Fpassprotect-server\n",[2113,38938,38939,38942],{"class":2115,"line":1647},[2113,38940,38941],{"class":2133},"    npm",[2113,38943,38944],{"class":2149}," install\n",[2113,38946,38947,38950,38952,38955,38958,38960],{"class":2115,"line":1652},[2113,38948,38949],{"class":2330},"    MODE",[2113,38951,2335],{"class":2334},[2113,38953,38954],{"class":2149},"prod",[2113,38956,38957],{"class":2133}," npm",[2113,38959,29680],{"class":2149},[2113,38961,17075],{"class":2149},[133,38963,38965],{"id":38964},"depuis-docker","Depuis docker",[12,38967,38968,38969,38972],{},"En utilisant ",[184,38970,38971],{},"docker-compose"," vous pouvez instancier les différentes images avec le fichier de configuration suivant. Pensez à modifier les clés servers.",[2105,38974,38976],{"className":19667,"code":38975,"language":19669,"meta":1646,"style":1646},"version: \"2\"\nservices:\n  nodejs:\n    image: phoenix741\u002Fpassprotect-server:1.0.0\n    expose:\n      - 3000\n    links:\n      - mongodb\n    environment:\n      - MONGODB_HOST=mongodb:\u002F\u002Fmongodb:27017\u002Fpassprotect\n      - NODE_ENV=production\n      - DEBUG=App:*\n      - JWT_SECRET=dnLUMtULQsNmNbmGV3Lx8SxrxEtaxTc8aPdRh8YMemj515Faip7wQYueSaBFYm5r\n      - CRYPTO_SESSIONKEY=xtipKI38GUCvE5cNGtTJxa1wQFvCicF5GDLTWyaBAb5RQqQ8rRBR1yVEq7Jg10cu\n  nginx:\n    image: phoenix741\u002Fpassprotect-client:1.0.0\n    links:\n      - nodejs\n    environment:\n      - UPSTREAM_SERVER=nodejs\n      - UPSTREAM_PORT=3000\n      - PIWIK_SITE_URL=\u002F\u002Fstats-demo.shadoware.org\u002F\n      - PIWIK_SITE_ID=3\n    ports:\n      - \"8080:80\"\n  mongodb:\n    image: mongo:3.3.9\n    expose:\n      - 27017\n    volumes:\n      - \".\u002Fmongodb:\u002Fdata\"\n",[184,38977,38978,38986,38992,38999,39008,39015,39022,39029,39036,39042,39049,39056,39063,39070,39077,39084,39093,39099,39106,39112,39119,39126,39133,39140,39147,39154,39161,39170,39176,39183,39189],{"__ignoreMap":1646},[2113,38979,38980,38982,38984],{"class":2115,"line":2116},[2113,38981,36891],{"class":2330},[2113,38983,4429],{"class":2119},[2113,38985,19690],{"class":2149},[2113,38987,38988,38990],{"class":2115,"line":1647},[2113,38989,36962],{"class":2330},[2113,38991,19702],{"class":2119},[2113,38993,38994,38997],{"class":2115,"line":1652},[2113,38995,38996],{"class":2330},"  nodejs",[2113,38998,19702],{"class":2119},[2113,39000,39001,39003,39005],{"class":2115,"line":2185},[2113,39002,36976],{"class":2330},[2113,39004,4429],{"class":2119},[2113,39006,39007],{"class":2149},"phoenix741\u002Fpassprotect-server:1.0.0\n",[2113,39009,39010,39013],{"class":2115,"line":2230},[2113,39011,39012],{"class":2330},"    expose",[2113,39014,19702],{"class":2119},[2113,39016,39017,39019],{"class":2115,"line":2235},[2113,39018,20039],{"class":2119},[2113,39020,39021],{"class":2166},"3000\n",[2113,39023,39024,39027],{"class":2115,"line":2241},[2113,39025,39026],{"class":2330},"    links",[2113,39028,19702],{"class":2119},[2113,39030,39031,39033],{"class":2115,"line":2246},[2113,39032,20039],{"class":2119},[2113,39034,39035],{"class":2149},"mongodb\n",[2113,39037,39038,39040],{"class":2115,"line":2464},[2113,39039,20016],{"class":2330},[2113,39041,19702],{"class":2119},[2113,39043,39044,39046],{"class":2115,"line":2085},[2113,39045,20039],{"class":2119},[2113,39047,39048],{"class":2149},"MONGODB_HOST=mongodb:\u002F\u002Fmongodb:27017\u002Fpassprotect\n",[2113,39050,39051,39053],{"class":2115,"line":2514},[2113,39052,20039],{"class":2119},[2113,39054,39055],{"class":2149},"NODE_ENV=production\n",[2113,39057,39058,39060],{"class":2115,"line":2533},[2113,39059,20039],{"class":2119},[2113,39061,39062],{"class":2149},"DEBUG=App:*\n",[2113,39064,39065,39067],{"class":2115,"line":2556},[2113,39066,20039],{"class":2119},[2113,39068,39069],{"class":2149},"JWT_SECRET=dnLUMtULQsNmNbmGV3Lx8SxrxEtaxTc8aPdRh8YMemj515Faip7wQYueSaBFYm5r\n",[2113,39071,39072,39074],{"class":2115,"line":2569},[2113,39073,20039],{"class":2119},[2113,39075,39076],{"class":2149},"CRYPTO_SESSIONKEY=xtipKI38GUCvE5cNGtTJxa1wQFvCicF5GDLTWyaBAb5RQqQ8rRBR1yVEq7Jg10cu\n",[2113,39078,39079,39082],{"class":2115,"line":2575},[2113,39080,39081],{"class":2330},"  nginx",[2113,39083,19702],{"class":2119},[2113,39085,39086,39088,39090],{"class":2115,"line":2596},[2113,39087,36976],{"class":2330},[2113,39089,4429],{"class":2119},[2113,39091,39092],{"class":2149},"phoenix741\u002Fpassprotect-client:1.0.0\n",[2113,39094,39095,39097],{"class":2115,"line":3192},[2113,39096,39026],{"class":2330},[2113,39098,19702],{"class":2119},[2113,39100,39101,39103],{"class":2115,"line":3213},[2113,39102,20039],{"class":2119},[2113,39104,39105],{"class":2149},"nodejs\n",[2113,39107,39108,39110],{"class":2115,"line":3236},[2113,39109,20016],{"class":2330},[2113,39111,19702],{"class":2119},[2113,39113,39114,39116],{"class":2115,"line":3248},[2113,39115,20039],{"class":2119},[2113,39117,39118],{"class":2149},"UPSTREAM_SERVER=nodejs\n",[2113,39120,39121,39123],{"class":2115,"line":4899},[2113,39122,20039],{"class":2119},[2113,39124,39125],{"class":2149},"UPSTREAM_PORT=3000\n",[2113,39127,39128,39130],{"class":2115,"line":1777},[2113,39129,20039],{"class":2119},[2113,39131,39132],{"class":2149},"PIWIK_SITE_URL=\u002F\u002Fstats-demo.shadoware.org\u002F\n",[2113,39134,39135,39137],{"class":2115,"line":4931},[2113,39136,20039],{"class":2119},[2113,39138,39139],{"class":2149},"PIWIK_SITE_ID=3\n",[2113,39141,39142,39145],{"class":2115,"line":4961},[2113,39143,39144],{"class":2330},"    ports",[2113,39146,19702],{"class":2119},[2113,39148,39149,39151],{"class":2115,"line":4976},[2113,39150,20039],{"class":2119},[2113,39152,39153],{"class":2149},"\"8080:80\"\n",[2113,39155,39156,39159],{"class":2115,"line":4993},[2113,39157,39158],{"class":2330},"  mongodb",[2113,39160,19702],{"class":2119},[2113,39162,39163,39165,39167],{"class":2115,"line":5013},[2113,39164,36976],{"class":2330},[2113,39166,4429],{"class":2119},[2113,39168,39169],{"class":2149},"mongo:3.3.9\n",[2113,39171,39172,39174],{"class":2115,"line":5018},[2113,39173,39012],{"class":2330},[2113,39175,19702],{"class":2119},[2113,39177,39178,39180],{"class":2115,"line":5042},[2113,39179,20039],{"class":2119},[2113,39181,39182],{"class":2166},"27017\n",[2113,39184,39185,39187],{"class":2115,"line":5057},[2113,39186,36986],{"class":2330},[2113,39188,19702],{"class":2119},[2113,39190,39191,39193],{"class":2115,"line":5062},[2113,39192,20039],{"class":2119},[2113,39194,39195],{"class":2149},"\".\u002Fmongodb:\u002Fdata\"\n",[12,39197,39198],{},"L'application est composée de deux images docker :",[506,39200,39201,39207],{},[370,39202,39203,39206],{},[184,39204,39205],{},"phoenix741\u002Fpassprotect-server:1.0.0",": contenant la partie serveur nodejs",[370,39208,39209,39212],{},[184,39210,39211],{},"phoenix741\u002Fpassprotect-client:1.0.0",": servant les fichiers static et redirigeant les appels à l'API vers le serveur.",[12,39214,39215],{},"La troisième image contient la base de données mongodb utilisée par le projet.",[128,39217,39219],{"id":39218},"utilisation","Utilisation",[12,39221,39222,39223,39226],{},"Pour accéder au serveur, utiliser l'adresse ",[184,39224,39225],{},"http:\u002F\u002Flocalhost:8080"," dans votre navigateur.",[3358,39228,39229],{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}",{"title":1646,"searchDepth":1647,"depth":1647,"links":39231},[39232,39238],{"id":38897,"depth":1647,"text":38898,"children":39233},[39234,39235,39236,39237],{"id":38901,"depth":1652,"text":38902},{"id":38908,"depth":1652,"text":38909},{"id":38920,"depth":1652,"text":38921},{"id":38964,"depth":1652,"text":38965},{"id":39218,"depth":1647,"text":39219},"2016-10-02",{"type":9,"value":39241},[39242,39244,39246,39248,39252,39274],[12,39243,14],{},[12,39245,38775],{},[35974,39247,38779],{"id":38778},[12,39249,38782,39250,38786],{},[362,39251,38785],{},[506,39253,39254,39259,39264,39269],{},[370,39255,39256,38796],{},[49,39257,38795],{"href":38793,"rel":39258},[347],[370,39260,39261,38804],{},[49,39262,38803],{"href":38801,"rel":39263},[347],[370,39265,39266,38812],{},[49,39267,38811],{"href":38809,"rel":39268},[347],[370,39270,39271,38820],{},[49,39272,38819],{"href":38817,"rel":39273},[347],[12,39275,38823],{},{"planet":1767},"\u002Fpost\u002Fpresentation-passprotect",{"title":38768,"description":14},"presentation-passprotect","posts\u002FPassprotect\u002F2016-10-02-presentation-passprotect",[37269,3510,3511,37297],"CCmSZITdHfrTxlAN2-s6g_tkrpyHzhmvPrXpWcOyZzA",{"id":39284,"title":39285,"author":7,"body":39286,"category":24866,"categorySlug":24867,"date":40764,"description":1646,"excerpt":40765,"extension":1764,"location":1765,"meta":40783,"navigation":1767,"path":40784,"published":1767,"seo":40785,"slug":40786,"stem":40787,"tags":40788,"timeToRead":2556,"__hash__":40792},"posts\u002Fposts\u002FLogiciels\u002F2014-03-19-monlivretdemesse-fr.md","Mon livret de messe - Site de génération de livrets au format PDF",{"type":9,"value":39287,"toc":40744},[39288,39292,39294,39303,39311,39314,39352,39367,39371,39374,39377,39380,39387,39390,39393,39396,39399,39402,39405,39411,39414,39425,39428,39437,39441,39447,39451,39454,39464,39467,39470,39473,39476,39484,39487,39724,39727,40166,40169,40175,40179,40182,40185,40189,40192,40195,40199,40206,40210,40219,40223,40226,40230,40242,40245,40249,40252,40255,40259,40266,40269,40377,40380,40383,40391,40394,40398,40404,40407,40410,40421,40425,40428,40435,40438,40452,40456,40463,40471,40479,40482,40486,40492,40495,40498,40573,40576,40579,40582,40660,40663,40733,40735,40738,40741],[133,39289,39291],{"id":39290},"mon-livret-de-messe-générateur-de-pdf","Mon livret de messe - Générateur de PDF",[12,39293,26097],{},[12,39295,39296,39297,39302],{},"Ce petit billet pour vous parler d'un site que j'ai développé pour ma femme et dont l'adresse est\n",[49,39298,39301],{"href":39299,"rel":39300},"http:\u002F\u002Fmonlivretdemesse.fr\u002F",[347],"http:\u002F\u002Fmonlivretdemesse.fr",". Ce projet que je développe depuis\nplusieurs années, a été mis à jours récément. Je profite de cette mise à jours pour vous parler de\nce projet fonctionnellement mais aussi techniquement.",[12,39304,39305,39310],{},[49,39306,39309],{"href":39307,"rel":39308},"http:\u002F\u002Fmonlivretdemessse.fr",[347],"Monlivretdemesse.FR"," est un site permettant aux utilisateurs allant\nse marier de générer leur livret de messe au format PDF afin de l'imprimer directement chez eux.\nLes pages du livret ainsi générées sont alors ordonnées de telle manière qu'il suffit de faire une\nimpression recto\u002Fverso puis de plier les feuilles pour avoir son livret. Je me suis chargé\ndu développement de ce site, pendant que ma femme se charge du contenu (donc le contenu des textes,\ndes images, mais aussi et surtout le thème de chaque produit, leur format).",[12,39312,39313],{},"La nouvelle version sortie le 11 mars 2014 permet également la création d'autres types de produits\nafin de générer des",[506,39315,39316,39323,39335,39342,39349],{},[370,39317,39318],{},[49,39319,39322],{"href":39320,"rel":39321},"http:\u002F\u002Fmonlivretdemesse.fr\u002Ffr\u002Fcategory\u002Fbapteme",[347],"livrets de messe de baptêmes",[370,39324,39325,4706,39330],{},[49,39326,39329],{"href":39327,"rel":39328},"http:\u002F\u002Fmonlivretdemesse.fr\u002Ffr\u002Fcategory\u002Ffaire-part-mariage",[347],"faire-parts de mariage",[49,39331,39334],{"href":39332,"rel":39333},"http:\u002F\u002Fmonlivretdemesse.fr\u002Ffr\u002Fcategory\u002Ffaire-part-bapteme",[347],"de baptêmes",[370,39336,39337],{},[49,39338,39341],{"href":39339,"rel":39340},"http:\u002F\u002Fmonlivretdemesse.fr\u002Ffr\u002Fcategory\u002Fmenus-mariage",[347],"menus pour la soirée",[370,39343,39344],{},[49,39345,39348],{"href":39346,"rel":39347},"http:\u002F\u002Fmonlivretdemesse.fr\u002Ffr\u002Fcategory\u002Fmarque-places",[347],"des marques places",[370,39350,39351],{},"et bien d'autres...",[12,39353,39354,39355,39360,39361,39366],{},"Le site a été écrit à l'aide du framework Symfony2 et utilise la bibliothèque\n",[49,39356,39359],{"href":39357,"rel":39358},"http:\u002F\u002Fwww.tcpdf.org\u002F",[347],"TCPDF"," afin de générer les fichiers PDF. Les données sont stockées dans une\nbase de données ",[49,39362,39365],{"href":39363,"rel":39364},"http:\u002F\u002Fwww.mongodb.org\u002F",[347],"MongoDB",". Parmi les données on peut compter les données\nproduits, utilisateurs, mais aussi le cache des PDF générés enregistrés en tant que fichier dans\nGridFS (et ceci afin qu'un livret qui n'a pas été modifié ne soit pas re-généré).",[133,39368,39370],{"id":39369},"symfony2","Symfony2",[12,39372,39373],{},"Symfony 2 est un framework PHP permettant le développement de sites Internet. Il est livré par\ndéfaut avec un ORM: Doctrine qui permet de faire correspondre à une structure de base de données des\nclasses PHP qui seront automatiquement hydratées.",[12,39375,39376],{},"Le développement PHP s'en retrouve presque agréable (je préfère les langages compilés en règle\ngénérale). Le framework est de la même trempe que le Framework Python Django.",[12,39378,39379],{},"Ce dernier m'a d'ailleurs tenté (bien qu'interpreté aussi), mais une lecture rapide de la\ndocumentation m'a donné l'impression d'être un peu moins pratique à utiliser que Symfony2. Peut-être\ncar je ne fais pas de Python.",[12,39381,39382,39383,39386],{},"Pour mon prochain projet j'étudierai l'utilisation de symfony2 vs ruby on rails vs django vs\nnode.js. Mais je ne suis pas sûr qu'au final l'utilisation d'une des technos précédentes m'apporte\nbeaucoup plus par rapport à ce que sait déjà faire un framework comme symfony2. L'avantage d'un tel\nframework réside aussi dans le nombre de ",[362,39384,39385],{},"bundle"," et de librairie utilisables à l'extérieur.",[12,39388,39389],{},"Par rapport à la distribution de base de symfony2 j'ai remplacé la version Doctrine ORM par Doctrine\nODM. Ce qui me permet de me connecter à une base de données MongoDB.",[133,39391,39365],{"id":39392},"mongodb",[12,39394,39395],{},"Mon projet d'abord basé sur une base MySQL a été basculé sur une base de données NoSQL nommée\nMongoDB.",[12,39397,39398],{},"La raison n'est pas technique (je n'ai pas besoin de replication, de sharding, ...., pas assez de\nvisiteurs). J'avais juste envie de tester cette base de données sur mon projet. De plus l'aspect\norienté document est agréable au développement.",[12,39400,39401],{},"En effet, au lieu de stocker les informations dans différentes tables et de tenter d'y accéder à\nl'aide de jointure ou de requête multiple, dans on MongoDB on stocke un document dans un format\nbinaire du JSON (le BSON).",[12,39403,39404],{},"Un panier stocké en base pourra avoir la forme :",[2105,39406,39409],{"className":39407,"code":39408,"language":26023},[26021],"{\n  \"_id\": ObjectId(\"......................\"),\n  \"expiration_updated_at\": ISODate(\"2014-03-17T21:00:34.0Z\"),\n  \"lines\": [\n    {\n      \"product\": {\n        \"product\": \"marque-place-baroque-dore\",\n        \"product_name\": \"Marque-place baroque doré\",\n        \"variant\": \"dore-gris\",\n        \"variant_name\": \"Doré\\\u002FGris\",\n        \"amount\": 7.9\n      },\n      \"custom\": ObjectId(\"....................\")\n    }\n  ],\n  \"lines_count\": NumberInt(1)\n}\n",[184,39410,39408],{"__ignoreMap":1646},[12,39412,39413],{},"On retrouve dans un seul document les informations liées au panier et les informations concernant\nchaque ligne. La dénormalisation n'étant pas un problème, on rappelera alors ici le nom du produit,\net le nombre de lignes que l'on pourrait retrouver autrement mais qui permettra un affichage plus\nrapide de cette manière.",[12,39415,39416,39417,39420,39421,39424],{},"Par exemple, sur la page d'acceuil où on affiche le nombre de produit dans le panier, il nous\nsuffira de requêter ",[362,39418,39419],{},"lines_count"," sans toucher à l'attribute ",[362,39422,39423],{},"lines",". Ensuite lors de l'affichage\ndu panier, on récupérera les lignes et les informations du panier en une seule requête et sans\njointure.",[12,39426,39427],{},"Ainsi pour des entités avec forte relation, qu'on ne récupère jamais les unes sans les autres, une\nseule requête permet de récupérer toutes les informations. Par exemple, dans une base relationnelle,\non aura l'habitude de stocker l'entête du panier dans une table, et les lignes dans une autre table.\nAvec Mongo, si je veux récupérer le panier en une requête, je récupère aussi les lignes. Cela\nimplique par contre de ne pouvoir requêter facilement sur les lignes du panier indépendamment de\nleur entête.",[12,39429,39430,39431,39436],{},"Le seul problème que j'ai actuellement avec l'ODM Doctrine est que la mise à jour du panier se fait\nen plusieurs requêtes alors qu'il serait préférable de le faire en une seule pour des questions\nd'atomicité. Ce point fait d'ailleurs l'objet du ticket\n",[49,39432,39435],{"href":39433,"rel":39434},"https:\u002F\u002Fgithub.com\u002Fdoctrine\u002Fmongodb-odm\u002Fissues\u002F437",[347],"437"," du Github du projet.",[133,39438,39440],{"id":39439},"les-différents-modules-du-site","Les différents modules du site",[12,39442,39443,39444,39446],{},"Dans cette section je vais vous parler rapidement des différents ",[362,39445,39385],{}," que j'ai créé pour le site.",[128,39448,39450],{"id":39449},"le-cms","Le CMS",[12,39452,39453],{},"Afin de faciliter l'édition des pages de contenu (Mention légales, Documentation, FAQ) sans toucher\nau code. J'ai écrit un mini CMS. Le but est de stocker dans la base de données les différentes pages\ndu projet, ainsi que les images associées aux pages.",[12,39455,39456,39457,39459,39460,39463],{},"Pour cela j'ai un ",[362,39458,39385],{}," nommé CMS qui utilise la notion d'édition inline de CKEditor. J'ai écris\nmon propre ",[155,39461,39462],{},"Explorer de media"," qui permet de récupérer et ajouter dans cette même base des\nfichiers à attacher aux différentes pages.",[12,39465,39466],{},"L'adresse de la page est alors décomposée pour récupérer la clé de la page. Une page dont l'adresse\nsera \u002Fpage\u002Fprout aura comme clé en base \u002Fprout. Pour un utilisateur non administrateur (ou anonyme),\nsi la page est trouvée, elle est affichée telle quelle et si elle n'est pas trouvée, une page 404\nest affichée.",[12,39468,39469],{},"En mode administrateur si la page existe, elle est ouverte en mode édition inline pour CKEditor. Si\nla page n'existe pas, elle est ouverte en mode création et en mode inline avec CKEditor. Il est\nainsi super facile pour un administrateur de créer de nouvelles pages.",[12,39471,39472],{},"La partie media permet d'ajouter dans un GridFS dedié les images, et fichiers attachés.",[12,39474,39475],{},"Le module est du coup assez simple, deux documents",[506,39477,39478,39481],{},[370,39479,39480],{},"Page",[370,39482,39483],{},"Media",[12,39485,39486],{},"Le document Page ressemble à ceci:",[2105,39488,39492],{"className":39489,"code":39490,"language":39491,"meta":1646,"style":1646},"language-php shiki shiki-themes one-dark-pro","\u002F**\n  * Page\n  *\n  * @MongoDB\\Document(collection=\"cms_page\")\n  *\u002F\nclass Page {\n    \u002F**\n      * @var string\n      *\n      * @MongoDB\\Id(strategy=\"CUSTOM\", options={\"class\"=\"\\Shadoware\\CMSBundle\\Generator\\PageSlugGenerator\"})\n      *\u002F\n    private $slug;\n\n    \u002F**\n      * @var string\n      *\n      * @MongoDB\\String\n      *\u002F\n    private $name;\n\n    \u002F**\n      * @var string\n      *\n      * @MongoDB\\String\n      *\u002F\n    private $title;\n\n    \u002F**\n      * @var string\n      *\n      * @MongoDB\\String\n      *\u002F\n    private $content;\n\n    \u002F**\n      * @var string $lang\n      *\n      * @MongoDB\\String\n      *\u002F\n    private $lang;\n}\n","php",[184,39493,39494,39499,39504,39509,39514,39519,39528,39533,39544,39549,39554,39559,39568,39572,39576,39584,39588,39593,39597,39606,39610,39614,39622,39626,39630,39634,39643,39647,39651,39659,39663,39667,39671,39680,39684,39688,39699,39703,39707,39711,39720],{"__ignoreMap":1646},[2113,39495,39496],{"class":2115,"line":2116},[2113,39497,39498],{"class":2396},"\u002F**\n",[2113,39500,39501],{"class":2115,"line":1647},[2113,39502,39503],{"class":2396},"  * Page\n",[2113,39505,39506],{"class":2115,"line":1652},[2113,39507,39508],{"class":2396},"  *\n",[2113,39510,39511],{"class":2115,"line":2185},[2113,39512,39513],{"class":2396},"  * @MongoDB\\Document(collection=\"cms_page\")\n",[2113,39515,39516],{"class":2115,"line":2230},[2113,39517,39518],{"class":2396},"  *\u002F\n",[2113,39520,39521,39523,39526],{"class":2115,"line":2235},[2113,39522,20538],{"class":2326},[2113,39524,39525],{"class":2414}," Page",[2113,39527,2647],{"class":2119},[2113,39529,39530],{"class":2115,"line":2241},[2113,39531,39532],{"class":2396},"    \u002F**\n",[2113,39534,39535,39538,39541],{"class":2115,"line":2246},[2113,39536,39537],{"class":2396},"      * ",[2113,39539,39540],{"class":20182},"@var",[2113,39542,39543],{"class":20186}," string\n",[2113,39545,39546],{"class":2115,"line":2464},[2113,39547,39548],{"class":2396},"      *\n",[2113,39550,39551],{"class":2115,"line":2085},[2113,39552,39553],{"class":2396},"      * @MongoDB\\Id(strategy=\"CUSTOM\", options={\"class\"=\"\\Shadoware\\CMSBundle\\Generator\\PageSlugGenerator\"})\n",[2113,39555,39556],{"class":2115,"line":2514},[2113,39557,39558],{"class":2396},"      *\u002F\n",[2113,39560,39561,39563,39566],{"class":2115,"line":2533},[2113,39562,21028],{"class":2326},[2113,39564,39565],{"class":2330}," $slug",[2113,39567,2487],{"class":2119},[2113,39569,39570],{"class":2115,"line":2556},[2113,39571,2125],{"emptyLinePlaceholder":1767},[2113,39573,39574],{"class":2115,"line":2569},[2113,39575,39532],{"class":2396},[2113,39577,39578,39580,39582],{"class":2115,"line":2575},[2113,39579,39537],{"class":2396},[2113,39581,39540],{"class":20182},[2113,39583,39543],{"class":20186},[2113,39585,39586],{"class":2115,"line":2596},[2113,39587,39548],{"class":2396},[2113,39589,39590],{"class":2115,"line":3192},[2113,39591,39592],{"class":2396},"      * @MongoDB\\String\n",[2113,39594,39595],{"class":2115,"line":3213},[2113,39596,39558],{"class":2396},[2113,39598,39599,39601,39604],{"class":2115,"line":3236},[2113,39600,21028],{"class":2326},[2113,39602,39603],{"class":2330}," $name",[2113,39605,2487],{"class":2119},[2113,39607,39608],{"class":2115,"line":3248},[2113,39609,2125],{"emptyLinePlaceholder":1767},[2113,39611,39612],{"class":2115,"line":4899},[2113,39613,39532],{"class":2396},[2113,39615,39616,39618,39620],{"class":2115,"line":1777},[2113,39617,39537],{"class":2396},[2113,39619,39540],{"class":20182},[2113,39621,39543],{"class":20186},[2113,39623,39624],{"class":2115,"line":4931},[2113,39625,39548],{"class":2396},[2113,39627,39628],{"class":2115,"line":4961},[2113,39629,39592],{"class":2396},[2113,39631,39632],{"class":2115,"line":4976},[2113,39633,39558],{"class":2396},[2113,39635,39636,39638,39641],{"class":2115,"line":4993},[2113,39637,21028],{"class":2326},[2113,39639,39640],{"class":2330}," $title",[2113,39642,2487],{"class":2119},[2113,39644,39645],{"class":2115,"line":5013},[2113,39646,2125],{"emptyLinePlaceholder":1767},[2113,39648,39649],{"class":2115,"line":5018},[2113,39650,39532],{"class":2396},[2113,39652,39653,39655,39657],{"class":2115,"line":5042},[2113,39654,39537],{"class":2396},[2113,39656,39540],{"class":20182},[2113,39658,39543],{"class":20186},[2113,39660,39661],{"class":2115,"line":5057},[2113,39662,39548],{"class":2396},[2113,39664,39665],{"class":2115,"line":5062},[2113,39666,39592],{"class":2396},[2113,39668,39669],{"class":2115,"line":5098},[2113,39670,39558],{"class":2396},[2113,39672,39673,39675,39678],{"class":2115,"line":5117},[2113,39674,21028],{"class":2326},[2113,39676,39677],{"class":2330}," $content",[2113,39679,2487],{"class":2119},[2113,39681,39682],{"class":2115,"line":5142},[2113,39683,2125],{"emptyLinePlaceholder":1767},[2113,39685,39686],{"class":2115,"line":5148},[2113,39687,39532],{"class":2396},[2113,39689,39690,39692,39694,39696],{"class":2115,"line":5163},[2113,39691,39537],{"class":2396},[2113,39693,39540],{"class":20182},[2113,39695,16397],{"class":20186},[2113,39697,39698],{"class":2396}," $lang\n",[2113,39700,39701],{"class":2115,"line":5169},[2113,39702,39548],{"class":2396},[2113,39704,39705],{"class":2115,"line":5175},[2113,39706,39592],{"class":2396},[2113,39708,39709],{"class":2115,"line":5180},[2113,39710,39558],{"class":2396},[2113,39712,39713,39715,39718],{"class":2115,"line":5199},[2113,39714,21028],{"class":2326},[2113,39716,39717],{"class":2330}," $lang",[2113,39719,2487],{"class":2119},[2113,39721,39722],{"class":2115,"line":5204},[2113,39723,2599],{"class":2119},[12,39725,39726],{},"Le controlleur est assez simple :",[2105,39728,39730],{"className":39489,"code":39729,"language":39491,"meta":1646,"style":1646},"\u002F**\n  * @Route(\"\u002Fpage\u002F{slug}\", requirements={\"slug\" = \".+\"})\n  * @Template()\n  *\u002F\npublic function indexAction($slug) {\n    $dm = $this->get('doctrine_mongodb')->getManager();\n\n    $page = $dm->getRepository(\"CMSBundle:Page\")->find($slug);\n    if ($page == null) {\n        if ($this->get('security.context')->isGranted('ROLE_ADMIN')) {\n            $page = new Page();\n            $page->setTitle(\"Titre de la page\");\n            $page->setSlug($slug);\n            $page->setName(\"Nom\");\n            $page->setContent(\"Contenue de la page\");\n        } else {\n            throw $this->createNotFoundException(\"Page not found\");\n        }\n    }\n\n    $request = $this->getRequest();\n    if ($request->getMethod() == \"POST\" && $page != null) {\n        $page->setName($request->get('name'));\n        $page->setTitle($request->get('title'));\n        $page->setContent($request->get('content'));\n\n        $dm->persist($page);\n        $dm->flush();\n    }\n\n    return array('page' => $page);\n}\n",[184,39731,39732,39736,39741,39746,39750,39766,39793,39797,39827,39842,39872,39885,39901,39916,39932,39948,39956,39975,39979,39983,39987,40003,40035,40059,40082,40105,40109,40125,40136,40140,40144,40162],{"__ignoreMap":1646},[2113,39733,39734],{"class":2115,"line":2116},[2113,39735,39498],{"class":2396},[2113,39737,39738],{"class":2115,"line":1647},[2113,39739,39740],{"class":2396},"  * @Route(\"\u002Fpage\u002F{slug}\", requirements={\"slug\" = \".+\"})\n",[2113,39742,39743],{"class":2115,"line":1652},[2113,39744,39745],{"class":2396},"  * @Template()\n",[2113,39747,39748],{"class":2115,"line":2185},[2113,39749,39518],{"class":2396},[2113,39751,39752,39754,39756,39759,39761,39764],{"class":2115,"line":2230},[2113,39753,35106],{"class":2326},[2113,39755,17728],{"class":2326},[2113,39757,39758],{"class":2133}," indexAction",[2113,39760,2423],{"class":2119},[2113,39762,39763],{"class":2330},"$slug",[2113,39765,2433],{"class":2119},[2113,39767,39768,39771,39773,39776,39778,39780,39782,39785,39788,39791],{"class":2115,"line":2235},[2113,39769,39770],{"class":2330},"    $dm",[2113,39772,2153],{"class":2334},[2113,39774,39775],{"class":2414}," $this",[2113,39777,4115],{"class":2119},[2113,39779,19283],{"class":2133},[2113,39781,2423],{"class":2119},[2113,39783,39784],{"class":2149},"'doctrine_mongodb'",[2113,39786,39787],{"class":2119},")->",[2113,39789,39790],{"class":2133},"getManager",[2113,39792,3944],{"class":2119},[2113,39794,39795],{"class":2115,"line":2241},[2113,39796,2125],{"emptyLinePlaceholder":1767},[2113,39798,39799,39802,39804,39807,39809,39812,39814,39817,39819,39821,39823,39825],{"class":2115,"line":2246},[2113,39800,39801],{"class":2330},"    $page",[2113,39803,2153],{"class":2334},[2113,39805,39806],{"class":2330}," $dm",[2113,39808,4115],{"class":2119},[2113,39810,39811],{"class":2133},"getRepository",[2113,39813,2423],{"class":2119},[2113,39815,39816],{"class":2149},"\"CMSBundle:Page\"",[2113,39818,39787],{"class":2119},[2113,39820,7343],{"class":2133},[2113,39822,2423],{"class":2119},[2113,39824,39763],{"class":2330},[2113,39826,2553],{"class":2119},[2113,39828,39829,39831,39833,39836,39838,39840],{"class":2115,"line":2464},[2113,39830,7394],{"class":2326},[2113,39832,2495],{"class":2119},[2113,39834,39835],{"class":2330},"$page",[2113,39837,5671],{"class":2334},[2113,39839,20727],{"class":2166},[2113,39841,2433],{"class":2119},[2113,39843,39844,39846,39848,39851,39853,39855,39857,39860,39862,39865,39867,39870],{"class":2115,"line":2085},[2113,39845,4518],{"class":2326},[2113,39847,2495],{"class":2119},[2113,39849,39850],{"class":2414},"$this",[2113,39852,4115],{"class":2119},[2113,39854,19283],{"class":2133},[2113,39856,2423],{"class":2119},[2113,39858,39859],{"class":2149},"'security.context'",[2113,39861,39787],{"class":2119},[2113,39863,39864],{"class":2133},"isGranted",[2113,39866,2423],{"class":2119},[2113,39868,39869],{"class":2149},"'ROLE_ADMIN'",[2113,39871,23034],{"class":2119},[2113,39873,39874,39877,39879,39881,39883],{"class":2115,"line":2514},[2113,39875,39876],{"class":2330},"            $page",[2113,39878,2153],{"class":2334},[2113,39880,4778],{"class":2326},[2113,39882,39525],{"class":2414},[2113,39884,3944],{"class":2119},[2113,39886,39887,39889,39891,39894,39896,39899],{"class":2115,"line":2533},[2113,39888,39876],{"class":2330},[2113,39890,4115],{"class":2119},[2113,39892,39893],{"class":2133},"setTitle",[2113,39895,2423],{"class":2119},[2113,39897,39898],{"class":2149},"\"Titre de la page\"",[2113,39900,2553],{"class":2119},[2113,39902,39903,39905,39907,39910,39912,39914],{"class":2115,"line":2556},[2113,39904,39876],{"class":2330},[2113,39906,4115],{"class":2119},[2113,39908,39909],{"class":2133},"setSlug",[2113,39911,2423],{"class":2119},[2113,39913,39763],{"class":2330},[2113,39915,2553],{"class":2119},[2113,39917,39918,39920,39922,39925,39927,39930],{"class":2115,"line":2569},[2113,39919,39876],{"class":2330},[2113,39921,4115],{"class":2119},[2113,39923,39924],{"class":2133},"setName",[2113,39926,2423],{"class":2119},[2113,39928,39929],{"class":2149},"\"Nom\"",[2113,39931,2553],{"class":2119},[2113,39933,39934,39936,39938,39941,39943,39946],{"class":2115,"line":2575},[2113,39935,39876],{"class":2330},[2113,39937,4115],{"class":2119},[2113,39939,39940],{"class":2133},"setContent",[2113,39942,2423],{"class":2119},[2113,39944,39945],{"class":2149},"\"Contenue de la page\"",[2113,39947,2553],{"class":2119},[2113,39949,39950,39952,39954],{"class":2115,"line":2596},[2113,39951,29879],{"class":2119},[2113,39953,4227],{"class":2326},[2113,39955,2647],{"class":2119},[2113,39957,39958,39961,39963,39965,39968,39970,39973],{"class":2115,"line":3192},[2113,39959,39960],{"class":2326},"            throw",[2113,39962,39775],{"class":2414},[2113,39964,4115],{"class":2119},[2113,39966,39967],{"class":2133},"createNotFoundException",[2113,39969,2423],{"class":2119},[2113,39971,39972],{"class":2149},"\"Page not found\"",[2113,39974,2553],{"class":2119},[2113,39976,39977],{"class":2115,"line":3213},[2113,39978,4578],{"class":2119},[2113,39980,39981],{"class":2115,"line":3236},[2113,39982,4665],{"class":2119},[2113,39984,39985],{"class":2115,"line":3248},[2113,39986,2125],{"emptyLinePlaceholder":1767},[2113,39988,39989,39992,39994,39996,39998,40001],{"class":2115,"line":4899},[2113,39990,39991],{"class":2330},"    $request",[2113,39993,2153],{"class":2334},[2113,39995,39775],{"class":2414},[2113,39997,4115],{"class":2119},[2113,39999,40000],{"class":2133},"getRequest",[2113,40002,3944],{"class":2119},[2113,40004,40005,40007,40009,40012,40014,40017,40019,40021,40024,40026,40029,40031,40033],{"class":2115,"line":1777},[2113,40006,7394],{"class":2326},[2113,40008,2495],{"class":2119},[2113,40010,40011],{"class":2330},"$request",[2113,40013,4115],{"class":2119},[2113,40015,40016],{"class":2133},"getMethod",[2113,40018,4543],{"class":2119},[2113,40020,4132],{"class":2334},[2113,40022,40023],{"class":2149}," \"POST\"",[2113,40025,2506],{"class":2334},[2113,40027,40028],{"class":2330}," $page",[2113,40030,5638],{"class":2334},[2113,40032,20727],{"class":2166},[2113,40034,2433],{"class":2119},[2113,40036,40037,40040,40042,40044,40046,40048,40050,40052,40054,40057],{"class":2115,"line":4931},[2113,40038,40039],{"class":2330},"        $page",[2113,40041,4115],{"class":2119},[2113,40043,39924],{"class":2133},[2113,40045,2423],{"class":2119},[2113,40047,40011],{"class":2330},[2113,40049,4115],{"class":2119},[2113,40051,19283],{"class":2133},[2113,40053,2423],{"class":2119},[2113,40055,40056],{"class":2149},"'name'",[2113,40058,5818],{"class":2119},[2113,40060,40061,40063,40065,40067,40069,40071,40073,40075,40077,40080],{"class":2115,"line":4961},[2113,40062,40039],{"class":2330},[2113,40064,4115],{"class":2119},[2113,40066,39893],{"class":2133},[2113,40068,2423],{"class":2119},[2113,40070,40011],{"class":2330},[2113,40072,4115],{"class":2119},[2113,40074,19283],{"class":2133},[2113,40076,2423],{"class":2119},[2113,40078,40079],{"class":2149},"'title'",[2113,40081,5818],{"class":2119},[2113,40083,40084,40086,40088,40090,40092,40094,40096,40098,40100,40103],{"class":2115,"line":4976},[2113,40085,40039],{"class":2330},[2113,40087,4115],{"class":2119},[2113,40089,39940],{"class":2133},[2113,40091,2423],{"class":2119},[2113,40093,40011],{"class":2330},[2113,40095,4115],{"class":2119},[2113,40097,19283],{"class":2133},[2113,40099,2423],{"class":2119},[2113,40101,40102],{"class":2149},"'content'",[2113,40104,5818],{"class":2119},[2113,40106,40107],{"class":2115,"line":4993},[2113,40108,2125],{"emptyLinePlaceholder":1767},[2113,40110,40111,40114,40116,40119,40121,40123],{"class":2115,"line":5013},[2113,40112,40113],{"class":2330},"        $dm",[2113,40115,4115],{"class":2119},[2113,40117,40118],{"class":2133},"persist",[2113,40120,2423],{"class":2119},[2113,40122,39835],{"class":2330},[2113,40124,2553],{"class":2119},[2113,40126,40127,40129,40131,40134],{"class":2115,"line":5018},[2113,40128,40113],{"class":2330},[2113,40130,4115],{"class":2119},[2113,40132,40133],{"class":2133},"flush",[2113,40135,3944],{"class":2119},[2113,40137,40138],{"class":2115,"line":5042},[2113,40139,4665],{"class":2119},[2113,40141,40142],{"class":2115,"line":5057},[2113,40143,2125],{"emptyLinePlaceholder":1767},[2113,40145,40146,40148,40151,40153,40156,40158,40160],{"class":2115,"line":5062},[2113,40147,3137],{"class":2326},[2113,40149,40150],{"class":2334}," array",[2113,40152,2423],{"class":2119},[2113,40154,40155],{"class":2149},"'page'",[2113,40157,7133],{"class":2119},[2113,40159,39835],{"class":2330},[2113,40161,2553],{"class":2119},[2113,40163,40164],{"class":2115,"line":5098},[2113,40165,2599],{"class":2119},[12,40167,40168],{},"Et enfin la vue Twig contient les références à CKEditor et l'activation du contenu modifiable inliné\npour les projets. Pour l'instant le module est fortement lié à l'application existante. J'ai dans\nl'espoir d'avoir assez de temps un jour pour externaliser ce module afin de pouvoir le partager à\nplus de monde.",[12,40170,40171,40172,40174],{},"Si vous avez envie d'avoir plus d'information sur ce ",[362,40173,39385],{}," n'hesitez pas à me contacter et je\nvous réponderez avec joie.",[128,40176,40178],{"id":40177},"les-produits","Les produits",[12,40180,40181],{},"La partie produits et beaucoup plus liée à l'activité du site. Les produits contiennent pour chaque\ncolori, un modèle dans lequel sont définis la position des différentes informations qui seront par\nla suite saisies par l'utilisateur. Ma femme peut ainsi, après avoir préparé son modèle le\npersonnaliser sur le site.",[12,40183,40184],{},"Un document de type produit personnalisé permet ensuite de spécialiser le produit en y ajoutant les\ninformations saisies par l'utilisateur. C'est ce produit final qui est ensuite acheté par\nl'utilisateur.",[128,40186,40188],{"id":40187},"les-paiements","Les paiements",[12,40190,40191],{},"La partie paiement contient la gestion du panier, de la facturation et le lien avec le site Paypal.\nEn effet on utilise l'API de paypal pour effectuer les paiements car nous n'avions pas l'envie de\ngérer pour ce site un système de paiement onéreux et complexe. Paypal prélève un pourcentage de la\ntransaction, et permet le paiement par carte bancaire des utilisateurs anonymes, ce qui nous\nconvient.",[12,40193,40194],{},"Au niveau du panier, certaines informations sont enregistrées, comme le nombre de lignes dans le\npanier, ainsi que le nom du produit et le nom du coloris choisis afin de faciliter le requêtage et\nl'affichage.",[128,40196,40198],{"id":40197},"la-gestion-des-utilisateurs","La gestion des utilisateurs",[12,40200,40201,40202,40205],{},"La gestion des utilisateurs passent par ",[362,40203,40204],{},"FOS\u002FUserBundle",". Quelques personnalisations ont été\najoutées à ce module afin de correspondre au thème du site et aussi pour ajouter quelques\ninformations.",[128,40207,40209],{"id":40208},"le-site","Le site",[12,40211,40212,40213,792,40216,40218],{},"Ajoute les ",[362,40214,40215],{},"CSS",[362,40217,3510],{}," personnalisés du site. Le theme a été acheté à une époque sur un\nsite proposant des templates, mais à depuis été customizé et adapté pour notre utilisation.",[128,40220,40222],{"id":40221},"ladmin","L'admin",[12,40224,40225],{},"L'interface d'administration permet à femme et à moi d'accéder aux différents produit, d'en créer de\nnouveau, de les modifiers. L'interface se base sur un thème bootstrape et reste assez simple. Seul\nl'utilisateur avec le role d'administrateur peut accéder à cette page.",[128,40227,40229],{"id":40228},"dans-lavenir","Dans l'avenir",[12,40231,40232,40233,40236,40237,40241],{},"Le découpage actuel ne me plaît pas forcément. En effet, je charge des ",[362,40234,40235],{},"bundles"," propres à l'admin\nsur le site alors que ces derniers pourraient être chargés uniquement dans le cadre de l'admin et\ninversement. Je pense qu'appliquer une architecture comme celle décrite ici:\n",[49,40238,40239],{"href":40239,"rel":40240},"http:\u002F\u002Fjolicode.com\u002Fblog\u002Fmultiple-applications-with-symfony2",[347],",\npourrait être une bonne idée pour mieux découper l'application.",[12,40243,40244],{},"Dans le même style, la partie CMS actuelle est liée au site, et j'aimerais la découpler du site pour\npouvoir l'utiliser assez facilement dans d'autres projets.",[133,40246,40248],{"id":40247},"mes-contributions","Mes contributions",[12,40250,40251],{},"Lors du développement de mon projet, j'ai eu besoin de certaines fonctionnalités que je n'ai pas\ntrouvées dans les bundles existants ou qui ne me convenaient pas. Je vous présente ici différents\nprojets que j'ai développé pour pallier à ces manques, sachant que pour l'instant ceux-ci ne sont\npas parfaits et voir même, la documentation peut laisser à désirer (quand aux tests unitaires ils\nsont dans le néant).",[12,40253,40254],{},"Si vous souhaiter aider ou contribuer, n'hésitez pas.",[128,40256,40258],{"id":40257},"collectionbundle","CollectionBundle",[12,40260,40261,40262],{},"Lien: ",[49,40263,40258],{"href":40264,"rel":40265},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002FCollectionBundle",[347],[12,40267,40268],{},"Dans symfony, il est possible d'ajouter dans un formulaire un type collection pour permettre à un\nutilisateur de saisir une collection de sous-éléments (jointure de type OneToMany):",[2105,40270,40272],{"className":39489,"code":40271,"language":39491,"meta":1646,"style":1646},"$builder->add('emails', 'collection', array(\n    \u002F\u002F chaque item du tableau sera un champ « email »\n    'type'   => 'email',\n    \u002F\u002F ces options sont passées à chaque type « email »\n    'options'  => array(\n        'required'  => false,\n        'attr'      => array('class' => 'email-box')\n    ),\n));\n",[184,40273,40274,40300,40305,40318,40323,40335,40346,40368,40373],{"__ignoreMap":1646},[2113,40275,40276,40279,40281,40283,40285,40288,40290,40293,40295,40298],{"class":2115,"line":2116},[2113,40277,40278],{"class":2330},"$builder",[2113,40280,4115],{"class":2119},[2113,40282,29588],{"class":2133},[2113,40284,2423],{"class":2119},[2113,40286,40287],{"class":2149},"'emails'",[2113,40289,932],{"class":2119},[2113,40291,40292],{"class":2149},"'collection'",[2113,40294,932],{"class":2119},[2113,40296,40297],{"class":2334},"array",[2113,40299,3146],{"class":2119},[2113,40301,40302],{"class":2115,"line":1647},[2113,40303,40304],{"class":2396},"    \u002F\u002F chaque item du tableau sera un champ « email »\n",[2113,40306,40307,40310,40313,40316],{"class":2115,"line":1652},[2113,40308,40309],{"class":2149},"    'type'",[2113,40311,40312],{"class":2119},"   => ",[2113,40314,40315],{"class":2149},"'email'",[2113,40317,4706],{"class":2119},[2113,40319,40320],{"class":2115,"line":2185},[2113,40321,40322],{"class":2396},"    \u002F\u002F ces options sont passées à chaque type « email »\n",[2113,40324,40325,40328,40331,40333],{"class":2115,"line":2230},[2113,40326,40327],{"class":2149},"    'options'",[2113,40329,40330],{"class":2119},"  => ",[2113,40332,40297],{"class":2334},[2113,40334,3146],{"class":2119},[2113,40336,40337,40340,40342,40344],{"class":2115,"line":2235},[2113,40338,40339],{"class":2149},"        'required'",[2113,40341,40330],{"class":2119},[2113,40343,18557],{"class":2166},[2113,40345,4706],{"class":2119},[2113,40347,40348,40351,40354,40356,40358,40361,40363,40366],{"class":2115,"line":2241},[2113,40349,40350],{"class":2149},"        'attr'",[2113,40352,40353],{"class":2119},"      => ",[2113,40355,40297],{"class":2334},[2113,40357,2423],{"class":2119},[2113,40359,40360],{"class":2149},"'class'",[2113,40362,7133],{"class":2119},[2113,40364,40365],{"class":2149},"'email-box'",[2113,40367,4660],{"class":2119},[2113,40369,40370],{"class":2115,"line":2246},[2113,40371,40372],{"class":2119},"    ),\n",[2113,40374,40375],{"class":2115,"line":2464},[2113,40376,5818],{"class":2119},[12,40378,40379],{},"Le problème c'est que dans les formulaires symfony2 il n'est pas possible de gérer des formulaires\ndifférents suivant le sous-type de l'objet (gestion de l'héritage dans l'ORM).",[12,40381,40382],{},"CollectionBundle propose deux nouveaux types :",[506,40384,40385,40388],{},[370,40386,40387],{},"Un type permettant de gérer pour chaque classe fille, un formulaire différent.",[370,40389,40390],{},"Un type permettant de gérer des collections de taille fixe : Exemple toujours 5 éléments, quel\nque soit le nombre d'éléments rééls en base.",[12,40392,40393],{},"Ce bundle est actuellement utilisé uniquement dans la partie admin du site.",[128,40395,40397],{"id":40396},"doctrinemigrationodmbundle","DoctrineMigrationODMBundle",[12,40399,40261,40400],{},[49,40401,40397],{"href":40402,"rel":40403},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002Fdoctrine-migrations-bundle",[347],[12,40405,40406],{},"Pour l'ORM Doctrine, il existe DoctrineMigrationBundle qui permet de faire des migrations de schéma,\nmais il n'existait pas d'équivalent pour l'ODM gérant MongoDB.",[12,40408,40409],{},"Même si MongoDB est schemaless, et que les données peuvent être migrées à l'execution, je ressens le\nbesoin d'avoir la possiblité d'exécuter des scripts lors des changements de version, pour :",[506,40411,40412,40415,40418],{},[370,40413,40414],{},"ajouter de nouvelles données (nécessaires) dans des tables (car on requête sur ces données).",[370,40416,40417],{},"renommage de collection, suite à gros refactoring.",[370,40419,40420],{},"voir autre",[128,40422,40424],{"id":40423},"fpdi-et-fpdf_tpl","FPDI et FPDF_TPL",[12,40426,40427],{},"Pour la génération des PDF, je génère une première version où chaque page est un élément différent,\npuis je me sers de FPDI pour associer les différentes pages sur une même page (avec la mention\nSPECIMEN ou pas).",[12,40429,40430,40431,40434],{},"Afin d'avoir accès à FPDI je me suis créé les dépôts suivants qui fonctionnent avec ceux de\n",[362,40432,40433],{},"tecnick.com\u002Ftcpdf"," pour les utilisateurs de TCPDF.",[12,40436,40437],{},"Liens:",[506,40439,40440,40446],{},[370,40441,40442],{},[49,40443,40444],{"href":40444,"rel":40445},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002Ffpdi",[347],[370,40447,40448],{},[49,40449,40450],{"href":40450,"rel":40451},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002Ffpdf_tpl",[347],[128,40453,40455],{"id":40454},"imageresizerbundle","ImageResizerBundle",[12,40457,40458,40459],{},"Lien : ",[49,40460,40455],{"href":40461,"rel":40462},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002FImageResizerBundle",[347],[12,40464,40465,40466,40470],{},"Dérivé de\n",[49,40467,40468],{"href":40468,"rel":40469},"https:\u002F\u002Fgithub.com\u002Fnresni\u002FImageResizerBundle",[347],", ce\nbundle ajoute",[506,40472,40473,40476],{},[370,40474,40475],{},"des caches supplémentaires",[370,40477,40478],{},"fournit une URL sur la valeur du cache directement (et de générer le cache lors de l'appel de la\ncommande twig). Cette dernière permet de cacher un peu l'URL utilisée pour accéder à l'image\nd'origine. On ne peut accéder alors qu'à l'URL finale. Comme les ids des images sont des ObjectId,\nil n'est pas possible de retrouver par le nom l'URL de l'image d'origine.",[12,40480,40481],{},"Cette extension est utilisée par toute l'application pour l'affichage de toutes les miniatures.",[128,40483,40485],{"id":40484},"piwikbundle","PiwikBundle",[12,40487,40458,40488],{},[49,40489,40485],{"href":40490,"rel":40491},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FSymfony2Bundle\u002FPiwikBundle",[347],[12,40493,40494],{},"Ce plugin a été créé afin de pouvoir ajouter la gestion de Piwik dans Symfony2. Les commandes PIWIK\npeuvent être passées au travers d'un service ou au travers de commandes TWIG. Ce plugin gère\négalement la notion d'e-commerce de PIWIK.",[12,40496,40497],{},"Une fois le plugin installé (via composer), il faut l'activer à l'aide de la configuration suivante:",[2105,40499,40501],{"className":19667,"code":40500,"language":19669,"meta":1646,"style":1646},"shadoware_piwik:\n  base_url: http:\u002F\u002Fmonpiwik.monsite # URL de base du serveur PIWIK\n  id_site: 1 # N° du site dans PIWIK\n  hidePiwik: false # Indique s'il faut cacher le tracker par un controlleur interne.\n  tokenId: abcedfghijkmn123456789 # Le token id de l'utilisateur (pour le cas où on cache piwik)\n  heartbeat: ~ # Permet de définir quelques attributs activant la fonctionnalité de heatbeat de piwik.\n",[184,40502,40503,40510,40523,40535,40547,40560],{"__ignoreMap":1646},[2113,40504,40505,40508],{"class":2115,"line":2116},[2113,40506,40507],{"class":2330},"shadoware_piwik",[2113,40509,19702],{"class":2119},[2113,40511,40512,40515,40517,40520],{"class":2115,"line":1647},[2113,40513,40514],{"class":2330},"  base_url",[2113,40516,4429],{"class":2119},[2113,40518,40519],{"class":2149},"http:\u002F\u002Fmonpiwik.monsite",[2113,40521,40522],{"class":2396}," # URL de base du serveur PIWIK\n",[2113,40524,40525,40528,40530,40532],{"class":2115,"line":1652},[2113,40526,40527],{"class":2330},"  id_site",[2113,40529,4429],{"class":2119},[2113,40531,3686],{"class":2166},[2113,40533,40534],{"class":2396}," # N° du site dans PIWIK\n",[2113,40536,40537,40540,40542,40544],{"class":2115,"line":2185},[2113,40538,40539],{"class":2330},"  hidePiwik",[2113,40541,4429],{"class":2119},[2113,40543,18557],{"class":2166},[2113,40545,40546],{"class":2396}," # Indique s'il faut cacher le tracker par un controlleur interne.\n",[2113,40548,40549,40552,40554,40557],{"class":2115,"line":2230},[2113,40550,40551],{"class":2330},"  tokenId",[2113,40553,4429],{"class":2119},[2113,40555,40556],{"class":2149},"abcedfghijkmn123456789",[2113,40558,40559],{"class":2396}," # Le token id de l'utilisateur (pour le cas où on cache piwik)\n",[2113,40561,40562,40565,40567,40570],{"class":2115,"line":2235},[2113,40563,40564],{"class":2330},"  heartbeat",[2113,40566,4429],{"class":2119},[2113,40568,40569],{"class":2166},"~",[2113,40571,40572],{"class":2396}," # Permet de définir quelques attributs activant la fonctionnalité de heatbeat de piwik.\n",[12,40574,40575],{},"Une fois la configuration faite, le plugin ajoutera juste avant chaque balise  l'appel à\npiwik (en utilisant la méthode asynchrone). Si la page ne contient pas de balise  ou si elle\nconstitue une page de redirection, les informations seront déportées à l'affichage suivant.",[12,40577,40578],{},"Cette dernière fonctionnalité permet par exemple d'ajouter des éléménts au panier e-commerce piwik\nlors des pages de redirection, et de traiter son affichage dès que possible. Cela a par contre comme\nlimitation de ne pas gérer les conflits.",[12,40580,40581],{},"L'utilisation depuis un controlleur se fait grâce à l'utilisation du service:",[2105,40583,40585],{"className":39489,"code":40584,"language":39491,"meta":1646,"style":1646},"$this->container->get('shadoware_piwik.service')->addEcommerceItem($productId, $productName, $category, $amount);\n$this->container->get('shadoware_piwik.service')->trackEcommerceCartUpdate($totalAmount);\n",[184,40586,40587,40632],{"__ignoreMap":1646},[2113,40588,40589,40591,40593,40596,40598,40600,40602,40605,40607,40610,40612,40615,40617,40620,40622,40625,40627,40630],{"class":2115,"line":2116},[2113,40590,39850],{"class":2414},[2113,40592,4115],{"class":2119},[2113,40594,40595],{"class":2330},"container",[2113,40597,4115],{"class":2119},[2113,40599,19283],{"class":2133},[2113,40601,2423],{"class":2119},[2113,40603,40604],{"class":2149},"'shadoware_piwik.service'",[2113,40606,39787],{"class":2119},[2113,40608,40609],{"class":2133},"addEcommerceItem",[2113,40611,2423],{"class":2119},[2113,40613,40614],{"class":2330},"$productId",[2113,40616,932],{"class":2119},[2113,40618,40619],{"class":2330},"$productName",[2113,40621,932],{"class":2119},[2113,40623,40624],{"class":2330},"$category",[2113,40626,932],{"class":2119},[2113,40628,40629],{"class":2330},"$amount",[2113,40631,2553],{"class":2119},[2113,40633,40634,40636,40638,40640,40642,40644,40646,40648,40650,40653,40655,40658],{"class":2115,"line":1647},[2113,40635,39850],{"class":2414},[2113,40637,4115],{"class":2119},[2113,40639,40595],{"class":2330},[2113,40641,4115],{"class":2119},[2113,40643,19283],{"class":2133},[2113,40645,2423],{"class":2119},[2113,40647,40604],{"class":2149},[2113,40649,39787],{"class":2119},[2113,40651,40652],{"class":2133},"trackEcommerceCartUpdate",[2113,40654,2423],{"class":2119},[2113,40656,40657],{"class":2330},"$totalAmount",[2113,40659,2553],{"class":2119},[12,40661,40662],{},"L'utilisation depuis une page twig se fait à l'aide des commandes twig. Par exemple dans la page\ntwig de base:",[2105,40664,40668],{"className":40665,"code":40666,"language":40667,"meta":1646,"style":1646},"language-twig shiki shiki-themes one-dark-pro","\u003Ctitle>{{ 'title' | trans }} - {% block title %}{{ 'menu.home' | trans }}{% endblock %}\u003C\u002Ftitle>\n{{ setPiwikPageName(block('title')) }}\n","twig",[184,40669,40670,40719],{"__ignoreMap":1646},[2113,40671,40672,40674,40677,40680,40682,40684,40687,40690,40693,40696,40699,40702,40704,40706,40709,40712,40715,40717],{"class":2115,"line":2116},[2113,40673,3109],{"class":2119},[2113,40675,40676],{"class":2330},"title",[2113,40678,40679],{"class":2119},">{{ ",[2113,40681,40079],{"class":2149},[2113,40683,7357],{"class":2119},[2113,40685,40686],{"class":2330},"trans",[2113,40688,40689],{"class":2119}," }} - {% ",[2113,40691,40692],{"class":2326},"block",[2113,40694,40695],{"class":2330}," title",[2113,40697,40698],{"class":2119}," %}{{ ",[2113,40700,40701],{"class":2149},"'menu.home'",[2113,40703,7357],{"class":2119},[2113,40705,40686],{"class":2330},[2113,40707,40708],{"class":2119}," }}{% ",[2113,40710,40711],{"class":2326},"endblock",[2113,40713,40714],{"class":2119}," %}\u003C\u002F",[2113,40716,40676],{"class":2330},[2113,40718,18185],{"class":2119},[2113,40720,40721,40724,40726,40728,40730],{"class":2115,"line":1647},[2113,40722,40723],{"class":2119},"{{ setPiwikPageName(",[2113,40725,40692],{"class":2334},[2113,40727,2423],{"class":2119},[2113,40729,40079],{"class":2149},[2113,40731,40732],{"class":2119},")) }}\n",[35974,40734,1621],{"id":1620},[12,40736,40737],{},"Bon voilà j'espère vous avoir fait découvrir le site ainsi que quelques nouveaux plugins\nintéressant.",[12,40739,40740],{},"A bientôt,",[3358,40742,40743],{},"html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .shdRp, html code.shiki .shdRp{--shiki-default:#C678DD;--shiki-default-font-style:italic}html pre.shiki code .sKU4T, html code.shiki .sKU4T{--shiki-default:#E5C07B;--shiki-default-font-style:italic}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}",{"title":1646,"searchDepth":1647,"depth":1647,"links":40745},[40746,40747,40748,40749,40750,40751,40752,40753,40754,40755,40756,40759,40760,40761,40762,40763],{"id":39290,"depth":1652,"text":39291},{"id":39369,"depth":1652,"text":39370},{"id":39392,"depth":1652,"text":39365},{"id":39439,"depth":1652,"text":39440},{"id":39449,"depth":1647,"text":39450},{"id":40177,"depth":1647,"text":40178},{"id":40187,"depth":1647,"text":40188},{"id":40197,"depth":1647,"text":40198},{"id":40208,"depth":1647,"text":40209},{"id":40221,"depth":1647,"text":40222},{"id":40228,"depth":1647,"text":40229,"children":40757},[40758],{"id":40247,"depth":1652,"text":40248},{"id":40257,"depth":1647,"text":40258},{"id":40396,"depth":1647,"text":40397},{"id":40423,"depth":1647,"text":40424},{"id":40454,"depth":1647,"text":40455},{"id":40484,"depth":1647,"text":40485},"2014-03-19",{"type":9,"value":40766},[40767,40769,40771,40776,40781],[133,40768,39291],{"id":39290},[12,40770,26097],{},[12,40772,39296,40773,39302],{},[49,40774,39301],{"href":39299,"rel":40775},[347],[12,40777,40778,39310],{},[49,40779,39309],{"href":39307,"rel":40780},[347],[12,40782,39313],{},{},"\u002Fpost\u002Fmonlivretdemesse-fr",{"title":39285,"description":1646},"monlivretdemesse-fr","posts\u002FLogiciels\u002F2014-03-19-monlivretdemesse-fr",[24897,40789,40790,40791],"livrets","mariage","bapteme","CLzxUTrMi7D7_9aacVnIiqCI2iUc0MClvm1Kv0yZXCA",{"id":40794,"title":40795,"author":7,"body":40796,"category":24866,"categorySlug":24867,"date":41765,"description":14,"excerpt":41766,"extension":1764,"location":1765,"meta":41776,"navigation":1767,"path":41777,"published":1767,"seo":41778,"slug":41779,"stem":41780,"tags":41781,"timeToRead":2246,"__hash__":41782},"posts\u002Fposts\u002FLogiciels\u002F2013-01-12-migration-to-pelican.md","Passage du site sous Pelican",{"type":9,"value":40797,"toc":41751},[40798,40800,40803,40806,40809,40813,40816,40819,40822,40825,40828,40831,40842,40845,40861,40864,40868,40871,40874,40877,40881,40884,40887,40891,40901,40907,40910,40913,40917,40924,40931,40935,40944,40947,40978,40981,40984,41001,41004,41007,41011,41014,41024,41027,41136,41139,41199,41202,41306,41323,41327,41334,41337,41355,41361,41367,41371,41374,41381,41538,41541,41544,41550,41554,41557,41560,41563,41566,41611,41621,41625,41628,41651,41660,41730,41733,41748],[12,40799,14],{},[12,40801,40802],{},"Cela fait bien longtemps que je n'ai rien écrit sur ce site. Et pour\ncause, je suis bien occupé 😄.",[12,40804,40805],{},"J'ai quand même pris un peu de temps récemment pour passer entièrement mon\nsite sous Pelican.",[12,40807,40808],{},"C'est mon cadeau de Noël de Geek ;).",[128,40810,40812],{"id":40811},"quest-ce-que-pelican","Qu'est-ce que Pelican ?",[12,40814,40815],{},"Pelican est un gestionnaire de blog statique.",[12,40817,40818],{},"Qu'est-ce que ça veut dire ?",[12,40820,40821],{},"Cela signifie que je vais écrire mes billets avec mon éditeur de texte\npréféré (kwrite), au format Markdown (ou restructuredText), et que je\ngénère mon blog au format HTML avant publication. (De la même manière que\nl'on compilerait un programme).",[12,40823,40824],{},"Le serveur n'a alors besoin de servir que des fichiers statiques, il n'y\na donc pas de surplus de mémoire, ou de délai dû à la nécessité de\ngénérer les pages.",[12,40826,40827],{},"Les pages n'étant pas modifiées tous les jours, il n'y a de toute façon\npas d'intérêt de générer les pages à chaque accès.",[12,40829,40830],{},"Les avantages indéniables sont les suivants :",[506,40832,40833,40836,40839],{},[370,40834,40835],{},"Pas de base de données, pas de page générée à la volée : donc pas de\ntemps de latence. La page est servie dés qu'elle est demandée. De plus\nil existe un plugin permettant de générer les pages au format pré-zippé,\nce qui fait un traitement de moins sur le serveur.",[370,40837,40838],{},"Pas de faille de sécurité dû à un script mal paramétré.",[370,40840,40841],{},"Je peux gérer mes billets sous un gestionnaire de version (exemple\nMercurial).",[12,40843,40844],{},"Par contre les inconvénients sont :",[506,40846,40847,40855,40858],{},[370,40848,40849,40850,17832],{},"Plus de commentaire direct sur le blog (sauf à utiliser des plateformes\ntel que ",[49,40851,40854],{"href":40852,"rel":40853},"http:\u002F\u002Fdisqus.com\u002F",[347],"disqus",[370,40856,40857],{},"Je dois installer le générateur de blog sur la\u002Fles machines que je\nsouhaite utiliser pour générer le blog.",[370,40859,40860],{},"Je dois ensuite utiliser SCP pour transférer mon blog.",[12,40862,40863],{},"Pour les deux derniers points, ils sont de mon choix. D'autres installent\nces outils sur le serveur, ils peuvent donc générer leur page directement\nà partir du serveur.",[128,40865,40867],{"id":40866},"les-implications","Les implications",[12,40869,40870],{},"Les conséquences pour ce blog sont donc : plus de commentaire (jusqu'à\nnouvel ordre). Vous pouvez toujours m'envoyer un mail, pour poser une\nquestion, je l'ajouterai manuellement en tant que commentaire.",[12,40872,40873],{},"Je garde tout de même les anciens commentaires, qui peuvent contenir\nparfois quelques informations utiles.",[12,40875,40876],{},"J'ai également profité de ce petit changement pour faire un petit\nnettoyage et j'ai supprimé tous les billets d'humeur, pour ne garder que\nles articles réellement intéressant.",[128,40878,40880],{"id":40879},"comment-sest-déroulé-la-conversion","Comment s'est déroulé la conversion ?",[12,40882,40883],{},"Dans cette partie, je vais décrire le déroulement de la conversion du\nblog.",[12,40885,40886],{},"Cette conversion comprend les articles, les commentaires et le thème.",[133,40888,40890],{"id":40889},"exportation-des-billets-provenant-de-dotclear","Exportation des billets provenant de dotclear",[12,40892,40893,40894,40896,40897,40900],{},"Il a d'abord fallu exporter les billets depuis le site sous\n",[362,40895,23327],{},". Sous Dotclear, dans la partie administration, il est possible\nd'utiliser le plugin ",[184,40898,40899],{},"Import\u002FExport"," pour exporter le site dans un fichier\ntexte.",[12,40902,40903],{},[121,40904],{"alt":40905,"src":40906},"Export","\u002FLogiciels\u002Fmigration\u002Fdotclear_export.png",[12,40908,40909],{},"Le fichier texte se présente alors comme un mixte entre un fichier INI et\nun ensemble de fichier CSV. Il contient, les catégories, les articles, et\nles commentaires.",[12,40911,40912],{},"Ce fichier peut alors être utilisé avec l'outil d'import de pelican pour\nrécupérer les différents billets.",[133,40914,40916],{"id":40915},"création-dun-environnement-pour-pelican","Création d'un environnement pour pelican",[12,40918,40919,40920,40923],{},"La création de l'environnement se fait comme décrit sur le ",[2113,40921,40922],{},"site de\npelican",". Je ne vais donc pas le décrire une fois de plus.",[12,40925,40926,40927,40930],{},"Une fois installé, les billets sont à placer dans le dossier ",[184,40928,40929],{},"content",".\nLes fichiers générés par l'import seront donc placés dans ce dossier.",[133,40932,40934],{"id":40933},"importation-des-billets-au-format-markdown","Importation des billets au format Markdown",[12,40936,40937,40938,40943],{},"L'import est décrit dans la ",[49,40939,40942],{"href":40940,"rel":40941},"http:\u002F\u002Fdocs.getpelican.com\u002Fen\u002Flatest\u002Fimporter.html",[347],"documentation de pelican",". Après différents\ntests, l'import au format Markdown a été celui qui a donné de meilleurs\nrésultats.",[12,40945,40946],{},"Voici donc la commande qui m'a permis de réaliser cet import :",[2105,40948,40950],{"className":2317,"code":40949,"language":2319,"meta":1646,"style":1646},"pelican-import --dotclear -m markdown --dir-cat 2012-12-11-shadoware-backup.txt -o content\n",[184,40951,40952],{"__ignoreMap":1646},[2113,40953,40954,40957,40960,40963,40966,40969,40972,40975],{"class":2115,"line":2116},[2113,40955,40956],{"class":2133},"pelican-import",[2113,40958,40959],{"class":2166}," --dotclear",[2113,40961,40962],{"class":2166}," -m",[2113,40964,40965],{"class":2149}," markdown",[2113,40967,40968],{"class":2166}," --dir-cat",[2113,40970,40971],{"class":2149}," 2012-12-11-shadoware-backup.txt",[2113,40973,40974],{"class":2166}," -o",[2113,40976,40977],{"class":2149}," content\n",[12,40979,40980],{},"Cela m'a généré une liste de 65 fichiers : 1 par billet. Parmi ces\nbillets, j'ai fait le tri, et j'ai supprimé les billets d'humeur. J'ai\najouté également un tag indiquant l'état de draft sur quelques billets qui\nsont en cours d'écriture et que je n'ai pas encore eu le temps de\nterminer et de publier.",[12,40982,40983],{},"Puis j'ai effectué une passe sur chaque billet, pour",[506,40985,40986,40989,40992,40995,40998],{},[370,40987,40988],{},"reprendre les parties de code (coloration syntaxique),",[370,40990,40991],{},"reprendre les tableaux,",[370,40993,40994],{},"reprendre les tags (qui n'ont pas été importés),",[370,40996,40997],{},"corriger les dates (la date était celle de création du poste, et non\ncelle de publication),",[370,40999,41000],{},"modification des catégories.",[12,41002,41003],{},"Sous dotclear, j'avais fait plusieurs niveaux de catégories, alors que\nsous pelican, il n'y a qu'un seul niveau de catégorie. J'ai donc dû mettre\nà plat les différentes catégories.",[12,41005,41006],{},"Pour les tableaux, ces derniers étaient repris directement au format HTML,\nils étaient donc exploitables directement. J'ai préféré refaire les\ntableaux au format Markdown.",[133,41008,41010],{"id":41009},"ajustement-du-fichier-de-configuration","Ajustement du fichier de configuration",[12,41012,41013],{},"Viennent ensuite les ajustements faits dans le fichier de configuration,\npour que les URL des articles sous dotclear soient les plus proches\npossibles des nouvelles URL.",[12,41015,41016,41017,41020,41021,179],{},"Pour cela, j'ai choisi d'avoir les mêmes adresses que sur le site dotclear\nsuivies de ",[184,41018,41019],{},".html"," ou de ",[184,41022,41023],{},".xml",[12,41025,41026],{},"Dans le fichier, j'ai positionné les variables pelicanconf.py",[2105,41028,41032],{"className":41029,"code":41030,"language":41031,"meta":1646,"style":1646},"language-python shiki shiki-themes one-dark-pro","ARTICLE_URL = 'post\u002F{slug}.html'\nARTICLE_SAVE_AS = 'post\u002F{slug}.html'\nAUTHOR_SAVE_AS = False\n\nFEED_DOMAIN = SITEURL\nFEED_ALL_ATOM = 'feed\u002Fatom.xml'\nCATEGORY_FEED_ATOM = 'feed\u002Fcategory\u002F%s\u002Fatom.xml'\nTAG_FEED_ATOM = 'feed\u002Ftags\u002F%s\u002Fatom.xml'\nTRANSLATION_FEED_ATOM = False\n","python",[184,41033,41034,41050,41063,41073,41077,41087,41097,41113,41127],{"__ignoreMap":1646},[2113,41035,41036,41039,41041,41044,41047],{"class":2115,"line":2116},[2113,41037,41038],{"class":2166},"ARTICLE_URL",[2113,41040,2153],{"class":2334},[2113,41042,41043],{"class":2149}," 'post\u002F",[2113,41045,41046],{"class":2166},"{slug}",[2113,41048,41049],{"class":2149},".html'\n",[2113,41051,41052,41055,41057,41059,41061],{"class":2115,"line":1647},[2113,41053,41054],{"class":2166},"ARTICLE_SAVE_AS",[2113,41056,2153],{"class":2334},[2113,41058,41043],{"class":2149},[2113,41060,41046],{"class":2166},[2113,41062,41049],{"class":2149},[2113,41064,41065,41068,41070],{"class":2115,"line":1652},[2113,41066,41067],{"class":2166},"AUTHOR_SAVE_AS",[2113,41069,2153],{"class":2334},[2113,41071,41072],{"class":2166}," False\n",[2113,41074,41075],{"class":2115,"line":2185},[2113,41076,2125],{"emptyLinePlaceholder":1767},[2113,41078,41079,41082,41084],{"class":2115,"line":2230},[2113,41080,41081],{"class":2166},"FEED_DOMAIN",[2113,41083,2153],{"class":2334},[2113,41085,41086],{"class":2166}," SITEURL\n",[2113,41088,41089,41092,41094],{"class":2115,"line":2235},[2113,41090,41091],{"class":2166},"FEED_ALL_ATOM",[2113,41093,2153],{"class":2334},[2113,41095,41096],{"class":2149}," 'feed\u002Fatom.xml'\n",[2113,41098,41099,41102,41104,41107,41110],{"class":2115,"line":2241},[2113,41100,41101],{"class":2166},"CATEGORY_FEED_ATOM",[2113,41103,2153],{"class":2334},[2113,41105,41106],{"class":2149}," 'feed\u002Fcategory\u002F",[2113,41108,41109],{"class":2166},"%s",[2113,41111,41112],{"class":2149},"\u002Fatom.xml'\n",[2113,41114,41115,41118,41120,41123,41125],{"class":2115,"line":2246},[2113,41116,41117],{"class":2166},"TAG_FEED_ATOM",[2113,41119,2153],{"class":2334},[2113,41121,41122],{"class":2149}," 'feed\u002Ftags\u002F",[2113,41124,41109],{"class":2166},[2113,41126,41112],{"class":2149},[2113,41128,41129,41132,41134],{"class":2115,"line":2464},[2113,41130,41131],{"class":2166},"TRANSLATION_FEED_ATOM",[2113,41133,2153],{"class":2334},[2113,41135,41072],{"class":2166},[12,41137,41138],{},"Dans le fichier, j'ai positionné les variables publishconf.py",[2105,41140,41142],{"className":41029,"code":41141,"language":41031,"meta":1646,"style":1646},"ARTICLE_URL = 'post\u002F{slug}'\nPAGE_URL = 'pages\u002F{slug}'\nCATEGORY_URL = 'category\u002F{slug}'\nTAG_URL = 'tag\u002F{slug}'\n",[184,41143,41144,41157,41171,41185],{"__ignoreMap":1646},[2113,41145,41146,41148,41150,41152,41154],{"class":2115,"line":2116},[2113,41147,41038],{"class":2166},[2113,41149,2153],{"class":2334},[2113,41151,41043],{"class":2149},[2113,41153,41046],{"class":2166},[2113,41155,41156],{"class":2149},"'\n",[2113,41158,41159,41162,41164,41167,41169],{"class":2115,"line":1647},[2113,41160,41161],{"class":2166},"PAGE_URL",[2113,41163,2153],{"class":2334},[2113,41165,41166],{"class":2149}," 'pages\u002F",[2113,41168,41046],{"class":2166},[2113,41170,41156],{"class":2149},[2113,41172,41173,41176,41178,41181,41183],{"class":2115,"line":1652},[2113,41174,41175],{"class":2166},"CATEGORY_URL",[2113,41177,2153],{"class":2334},[2113,41179,41180],{"class":2149}," 'category\u002F",[2113,41182,41046],{"class":2166},[2113,41184,41156],{"class":2149},[2113,41186,41187,41190,41192,41195,41197],{"class":2115,"line":2185},[2113,41188,41189],{"class":2166},"TAG_URL",[2113,41191,2153],{"class":2334},[2113,41193,41194],{"class":2149}," 'tag\u002F",[2113,41196,41046],{"class":2166},[2113,41198,41156],{"class":2149},[12,41200,41201],{},"Du coté de nginx, le serveur d'application, j'ai ajouté les lignes\nsuivantes :",[2105,41203,41207],{"className":41204,"code":41205,"language":41206,"meta":1646,"style":1646},"language-nginx shiki shiki-themes one-dark-pro","rewrite ^\u002Fdotclear\u002Findex.php\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F\\d+-(.*)$ \u002Fpost\u002F$1 permanent;\nrewrite ^\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F\\d+-(.*)$ \u002Fpost\u002F$1 permanent;\nrewrite ^\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F(.*)$ \u002Fpost\u002F$1 permanent;\n\nlocation \u002F {\n    try_files $uri.html $uri.xml $uri $uri\u002F =404;\n}\n","nginx",[184,41208,41209,41227,41242,41257,41261,41268,41302],{"__ignoreMap":1646},[2113,41210,41211,41214,41217,41220,41222,41225],{"class":2115,"line":2116},[2113,41212,41213],{"class":2326},"rewrite",[2113,41215,41216],{"class":2330}," ^\u002Fdotclear\u002Findex.php\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F\\d+-(.*)$",[2113,41218,41219],{"class":2119}," \u002Fpost\u002F$",[2113,41221,3686],{"class":2330},[2113,41223,41224],{"class":2326}," permanent",[2113,41226,2487],{"class":2119},[2113,41228,41229,41231,41234,41236,41238,41240],{"class":2115,"line":1647},[2113,41230,41213],{"class":2326},[2113,41232,41233],{"class":2330}," ^\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F\\d+-(.*)$",[2113,41235,41219],{"class":2119},[2113,41237,3686],{"class":2330},[2113,41239,41224],{"class":2326},[2113,41241,2487],{"class":2119},[2113,41243,41244,41246,41249,41251,41253,41255],{"class":2115,"line":1652},[2113,41245,41213],{"class":2326},[2113,41247,41248],{"class":2330}," ^\u002Fpost\u002F\\d+\u002F\\d+\u002F\\d+\u002F(.*)$",[2113,41250,41219],{"class":2119},[2113,41252,3686],{"class":2330},[2113,41254,41224],{"class":2326},[2113,41256,2487],{"class":2119},[2113,41258,41259],{"class":2115,"line":2185},[2113,41260,2125],{"emptyLinePlaceholder":1767},[2113,41262,41263,41265],{"class":2115,"line":2230},[2113,41264,30726],{"class":2326},[2113,41266,41267],{"class":2119}," \u002F {\n",[2113,41269,41270,41273,41276,41279,41282,41284,41287,41289,41292,41294,41297,41300],{"class":2115,"line":2235},[2113,41271,41272],{"class":2326},"    try_files ",[2113,41274,41275],{"class":2119},"$",[2113,41277,41278],{"class":2330},"uri",[2113,41280,41281],{"class":2119},".html $",[2113,41283,41278],{"class":2330},[2113,41285,41286],{"class":2119},".xml $",[2113,41288,41278],{"class":2330},[2113,41290,41291],{"class":2119}," $",[2113,41293,41278],{"class":2330},[2113,41295,41296],{"class":2119},"\u002F ",[2113,41298,41299],{"class":2166},"=404",[2113,41301,2487],{"class":2119},[2113,41303,41304],{"class":2115,"line":2241},[2113,41305,2599],{"class":2119},[12,41307,41308,41309,41312,41313,41316,41317,41319,41320,41322],{},"Les premières lignes permettent de reprendre les anciennes adresses\n",[184,41310,41311],{},"dotclear",", et la commande ",[184,41314,41315],{},"try_files"," permet de reprendre les adresses\nexistantes et de les compléter par ",[184,41318,41019],{}," et par ",[184,41321,41023],{}," selon le besoin.",[133,41324,41326],{"id":41325},"modification-du-thème","Modification du thème",[12,41328,41329,41330,41333],{},"Enfin j'ai écrit un nouveau thème dans le dossier ",[184,41331,41332],{},"themes\u002Fshadoware",". Ce\nthème est fait pour coller le plus possible à celui que j'avais sur le\nsite dotclear.",[12,41335,41336],{},"Il génère donc la page de la même manière que dotclear le faisait (à\nquelques différences prés) pour éviter de devoir ré-écrire la feuille de\nstyle CSS.",[12,41338,41339,41340,41345,41346,33746,41349,41352,41353,179],{},"L'écriture du thème n'est en soit pas très compliqué, car ",[49,41341,41344],{"href":41342,"rel":41343},"http:\u002F\u002Fjinja.pocoo.org\u002Fdocs\u002F",[347],"jinja2"," est\nassez simple d'utilisation. Je me suis inspiré des thèmes ",[184,41347,41348],{},"simple",[184,41350,41351],{},"notmyidea"," pour écrire le thème, basé sur celui que j'avais sous\n",[184,41354,41311],{},[12,41356,41357,41358,179],{},"Vous pouvez trouver le thème au ",[2113,41359,41360],{},"lien suivant",[12,41362,41363],{},[121,41364],{"alt":41365,"src":41366},"Theme","\u002FLogiciels\u002Fmigration\u002Fsite.png",[133,41368,41370],{"id":41369},"importation-des-commentaires","Importation des commentaires",[12,41372,41373],{},"Les commentaires existants sur mon blog actuel peuvent parfois contenir\ndes informations complémentaires. Les ignorer lors de la migration est\ntout a fait possible, mais j'ai préféré les récupérer.",[12,41375,41376,41377,41380],{},"Pour cela j'ai installé le ",[2113,41378,41379],{},"plugin comments",". Ensuite j'ai modifié le\nthème pour ajouter l'affichage des commentaires :",[2105,41382,41384],{"className":18156,"code":41383,"language":18158,"meta":1646,"style":1646},"{% if article.comments %}\n\u003Cdiv id=\"comments\">\n  \u003Ch3>Commentaires\u003C\u002Fh3>\n  \u003Cdl>\n    {% for comment in article.comments %}\n    \u003Cdt>\n      \u003Ca class=\"comment-number\" href=\"#\">{{ loop.index }}.\u003C\u002Fa>\n      Le {{ comment.date }} par {{ comment.author }}\n    \u003C\u002Fdt>\n    \u003Cdd>\n      \u003Cp>{{ comment.content }}\u003C\u002Fp>\n    \u003C\u002Fdd>\n    {% endfor %}\n  \u003C\u002Fdl>\n\u003C\u002Fdiv>\n{% endif %}\n",[184,41385,41386,41391,41405,41418,41427,41432,41441,41469,41474,41482,41491,41504,41512,41517,41525,41533],{"__ignoreMap":1646},[2113,41387,41388],{"class":2115,"line":2116},[2113,41389,41390],{"class":2119},"{% if article.comments %}\n",[2113,41392,41393,41395,41397,41399,41401,41403],{"class":2115,"line":1647},[2113,41394,3109],{"class":2119},[2113,41396,24628],{"class":2330},[2113,41398,18170],{"class":2166},[2113,41400,2335],{"class":2119},[2113,41402,24651],{"class":2149},[2113,41404,18185],{"class":2119},[2113,41406,41407,41409,41411,41414,41416],{"class":2115,"line":1652},[2113,41408,24615],{"class":2119},[2113,41410,133],{"class":2330},[2113,41412,41413],{"class":2119},">Commentaires\u003C\u002F",[2113,41415,133],{"class":2330},[2113,41417,18185],{"class":2119},[2113,41419,41420,41422,41425],{"class":2115,"line":2185},[2113,41421,24615],{"class":2119},[2113,41423,41424],{"class":2330},"dl",[2113,41426,18185],{"class":2119},[2113,41428,41429],{"class":2115,"line":2230},[2113,41430,41431],{"class":2119},"    {% for comment in article.comments %}\n",[2113,41433,41434,41436,41439],{"class":2115,"line":2235},[2113,41435,24625],{"class":2119},[2113,41437,41438],{"class":2330},"dt",[2113,41440,18185],{"class":2119},[2113,41442,41443,41445,41447,41449,41451,41454,41457,41459,41462,41465,41467],{"class":2115,"line":2241},[2113,41444,24642],{"class":2119},[2113,41446,49],{"class":2330},[2113,41448,19179],{"class":2166},[2113,41450,2335],{"class":2119},[2113,41452,41453],{"class":2149},"\"comment-number\"",[2113,41455,41456],{"class":2166}," href",[2113,41458,2335],{"class":2119},[2113,41460,41461],{"class":2149},"\"#\"",[2113,41463,41464],{"class":2119},">{{ loop.index }}.\u003C\u002F",[2113,41466,49],{"class":2330},[2113,41468,18185],{"class":2119},[2113,41470,41471],{"class":2115,"line":2246},[2113,41472,41473],{"class":2119},"      Le {{ comment.date }} par {{ comment.author }}\n",[2113,41475,41476,41478,41480],{"class":2115,"line":2464},[2113,41477,24744],{"class":2119},[2113,41479,41438],{"class":2330},[2113,41481,18185],{"class":2119},[2113,41483,41484,41486,41489],{"class":2115,"line":2085},[2113,41485,24625],{"class":2119},[2113,41487,41488],{"class":2330},"dd",[2113,41490,18185],{"class":2119},[2113,41492,41493,41495,41497,41500,41502],{"class":2115,"line":2514},[2113,41494,24642],{"class":2119},[2113,41496,12],{"class":2330},[2113,41498,41499],{"class":2119},">{{ comment.content }}\u003C\u002F",[2113,41501,12],{"class":2330},[2113,41503,18185],{"class":2119},[2113,41505,41506,41508,41510],{"class":2115,"line":2533},[2113,41507,24744],{"class":2119},[2113,41509,41488],{"class":2330},[2113,41511,18185],{"class":2119},[2113,41513,41514],{"class":2115,"line":2556},[2113,41515,41516],{"class":2119},"    {% endfor %}\n",[2113,41518,41519,41521,41523],{"class":2115,"line":2569},[2113,41520,24753],{"class":2119},[2113,41522,41424],{"class":2330},[2113,41524,18185],{"class":2119},[2113,41526,41527,41529,41531],{"class":2115,"line":2575},[2113,41528,18195],{"class":2119},[2113,41530,24628],{"class":2330},[2113,41532,18185],{"class":2119},[2113,41534,41535],{"class":2115,"line":2596},[2113,41536,41537],{"class":2119},"{% endif %}\n",[12,41539,41540],{},"Cela permet d'afficher les commentaires à l'aide du plugin. Par contre le\nproblème est que les commentaires provenant de dotclear ne sont pas\nimportés.",[12,41542,41543],{},"Du coup j'ai modifié le script d'import, pour en faire un qui importe les\ncommentaires. Il doit être lancé séparément du script d'import\ntraditionnel.",[12,41545,41546,41549],{},[2113,41547,41548],{},"Voici donc le script"," permettant de récupérer les commentaires.",[133,41551,41553],{"id":41552},"gestion-du-tag-planet-libre","Gestion du tag planet libre",[12,41555,41556],{},"Afin de pouvoir publier des articles sur le planet libre, je proposais\njusqu'ici des articles sur le planet libre à l'aide d'un tag de type\nplanet.",[12,41558,41559],{},"L'utilisation d'un tag de type planet permet de filtrer les articles\nque je souhaite mettre sur le planet libre. Par contre avec pelican,\nl'utilisation de ce tag fausse le calcul des billets liés.",[12,41561,41562],{},"Du coup j'ai décidé que la génération du flux ATOM donné au planet sera\ngénérée avec les articles qui posséderont un metatag déposé au début de\nchaque billet. Sous la présence du tag avec la valeur true, le billet est\najouté dans le flux ATOM des planets.",[12,41564,41565],{},"Pour ajouter le tag, il faut donc au début de l'article écrire le texte\nsuivant :",[2105,41567,41571],{"className":41568,"code":41569,"language":41570,"meta":1646,"style":1646},"language-markdown shiki shiki-themes one-dark-pro","Title: Passage du site sous Pelican\nTags: dedie\nPlanet: true\n\nBonjour à tous,\n\nCela fait bien longtemps que je n'ai rien écrit sur ce site. Et pour\ncause, je suis bien occupé 😄.\n","markdown",[184,41572,41573,41578,41583,41588,41592,41597,41601,41606],{"__ignoreMap":1646},[2113,41574,41575],{"class":2115,"line":2116},[2113,41576,41577],{"class":2119},"Title: Passage du site sous Pelican\n",[2113,41579,41580],{"class":2115,"line":1647},[2113,41581,41582],{"class":2119},"Tags: dedie\n",[2113,41584,41585],{"class":2115,"line":1652},[2113,41586,41587],{"class":2119},"Planet: true\n",[2113,41589,41590],{"class":2115,"line":2185},[2113,41591,2125],{"emptyLinePlaceholder":1767},[2113,41593,41594],{"class":2115,"line":2230},[2113,41595,41596],{"class":2119},"Bonjour à tous,\n",[2113,41598,41599],{"class":2115,"line":2235},[2113,41600,2125],{"emptyLinePlaceholder":1767},[2113,41602,41603],{"class":2115,"line":2241},[2113,41604,41605],{"class":2119},"Cela fait bien longtemps que je n'ai rien écrit sur ce site. Et pour\n",[2113,41607,41608],{"class":2115,"line":2246},[2113,41609,41610],{"class":2119},"cause, je suis bien occupé 😄.\n",[12,41612,41613,41614,41617,41618,179],{},"Pour ce faire j'ai écrit une extension à pelican qui génére un flux nommé\n",[184,41615,41616],{},"{OUTPUT_PATH}\u002F{TAG_FEED_ATOM}"," (avec %s remplacé par planet). Vous pouvez\ntélécharger l'",[2113,41619,41620],{},"extension au lien suivant",[133,41622,41624],{"id":41623},"création-dune-page-404","Création d'une page 404",[12,41626,41627],{},"Afin d'avoir une belle page 404 si la page n'est pas correctement trouvée\n(par exemple si certaines URLs ne correspondent plus à celle de dotclear),\nj'ai défini au niveau de pelican, la création d'une belle page 404 de la\nmanière suivante :",[2105,41629,41631],{"className":41029,"code":41630,"language":41031,"meta":1646,"style":1646},"TEMPLATE_PAGES = {'error404.html': 'error404.html'}\n",[184,41632,41633],{"__ignoreMap":1646},[2113,41634,41635,41638,41640,41642,41645,41647,41649],{"class":2115,"line":2116},[2113,41636,41637],{"class":2166},"TEMPLATE_PAGES",[2113,41639,2153],{"class":2334},[2113,41641,38076],{"class":2119},[2113,41643,41644],{"class":2149},"'error404.html'",[2113,41646,4429],{"class":2119},[2113,41648,41644],{"class":2149},[2113,41650,2599],{"class":2119},[12,41652,41653,41654,21856,41656,41659],{},"J'ai alors créé dans le dossier ",[184,41655,40929],{},[184,41657,41658],{},"error404.html"," avec\nle contenu suivant :",[2105,41661,41664],{"className":41662,"code":41663,"language":41344,"meta":1646,"style":1646},"language-jinja2 shiki shiki-themes one-dark-pro","{% extends \"base.html\" %}\n{% block title %}Page non trouvée{% endblock %}\n{% block piwik_title %}404{% endblock %}\n{% block content %}\n\u003Csection id=\"content\">\n    \u003Cdiv id=\"content-info\">\n        \u003Ch2>Document non trouvé\u003C\u002Fh2>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"content-inner\">\n        \u003Cp>Le document que vous cherchez n'existe pas.\u003C\u002Fp>\n    \u003C\u002Fdiv>\n\u003C\u002Fsection>\n{% endblock %}\n",[184,41665,41666,41671,41676,41681,41686,41691,41696,41701,41706,41711,41716,41720,41725],{"__ignoreMap":1646},[2113,41667,41668],{"class":2115,"line":2116},[2113,41669,41670],{},"{% extends \"base.html\" %}\n",[2113,41672,41673],{"class":2115,"line":1647},[2113,41674,41675],{},"{% block title %}Page non trouvée{% endblock %}\n",[2113,41677,41678],{"class":2115,"line":1652},[2113,41679,41680],{},"{% block piwik_title %}404{% endblock %}\n",[2113,41682,41683],{"class":2115,"line":2185},[2113,41684,41685],{},"{% block content %}\n",[2113,41687,41688],{"class":2115,"line":2230},[2113,41689,41690],{},"\u003Csection id=\"content\">\n",[2113,41692,41693],{"class":2115,"line":2235},[2113,41694,41695],{},"    \u003Cdiv id=\"content-info\">\n",[2113,41697,41698],{"class":2115,"line":2241},[2113,41699,41700],{},"        \u003Ch2>Document non trouvé\u003C\u002Fh2>\n",[2113,41702,41703],{"class":2115,"line":2246},[2113,41704,41705],{},"    \u003C\u002Fdiv>\n",[2113,41707,41708],{"class":2115,"line":2464},[2113,41709,41710],{},"    \u003Cdiv class=\"content-inner\">\n",[2113,41712,41713],{"class":2115,"line":2085},[2113,41714,41715],{},"        \u003Cp>Le document que vous cherchez n'existe pas.\u003C\u002Fp>\n",[2113,41717,41718],{"class":2115,"line":2514},[2113,41719,41705],{},[2113,41721,41722],{"class":2115,"line":2533},[2113,41723,41724],{},"\u003C\u002Fsection>\n",[2113,41726,41727],{"class":2115,"line":2556},[2113,41728,41729],{},"{% endblock %}\n",[12,41731,41732],{},"Enfin, comme j'utilise le serveur nginx pour servir mes pages j'ai ajouté\nla règle (toute simple) suivante pour définir ma page 404 :",[2105,41734,41736],{"className":41204,"code":41735,"language":41206,"meta":1646,"style":1646},"error_page 404 \u002Ferror404.html;\n",[184,41737,41738],{"__ignoreMap":1646},[2113,41739,41740,41743,41745],{"class":2115,"line":2116},[2113,41741,41742],{"class":2326},"error_page ",[2113,41744,35332],{"class":2166},[2113,41746,41747],{"class":2119}," \u002Ferror404.html;\n",[3358,41749,41750],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}",{"title":1646,"searchDepth":1647,"depth":1647,"links":41752},[41753,41754,41755],{"id":40811,"depth":1647,"text":40812},{"id":40866,"depth":1647,"text":40867},{"id":40879,"depth":1647,"text":40880,"children":41756},[41757,41758,41759,41760,41761,41762,41763,41764],{"id":40889,"depth":1652,"text":40890},{"id":40915,"depth":1652,"text":40916},{"id":40933,"depth":1652,"text":40934},{"id":41009,"depth":1652,"text":41010},{"id":41325,"depth":1652,"text":41326},{"id":41369,"depth":1652,"text":41370},{"id":41552,"depth":1652,"text":41553},{"id":41623,"depth":1652,"text":41624},"2013-01-12",{"type":9,"value":41767},[41768,41770,41772,41774],[12,41769,14],{},[12,41771,40802],{},[12,41773,40805],{},[12,41775,40808],{},{"planet":1767},"\u002Fpost\u002Fmigration-to-pelican",{"title":40795,"description":14},"migration-to-pelican","posts\u002FLogiciels\u002F2013-01-12-migration-to-pelican",[24897],"RjPEE1XKQzoFIQDsH16W_XStvfsUCDU0KyWZDWKPIXs",{"id":41784,"title":41785,"author":7,"body":41786,"category":2057,"categorySlug":2058,"date":43568,"description":1646,"excerpt":43569,"extension":1764,"location":1765,"meta":43610,"navigation":1767,"path":43611,"published":1767,"seo":43612,"slug":43613,"stem":43614,"tags":43615,"timeToRead":2464,"__hash__":43616},"posts\u002Fposts\u002FProgrammation\u002F2012-07-01-cross-compilation-compiler-un-programme-pour-ms-windows-sous-gnu-linux.md","Cross-Compilation - Compiler un programme pour MS\u002FWindows sous Gnu\u002FLinux",{"type":9,"value":41787,"toc":43558},[41788,41798,41801,41804,41807,41833,41836,41842,41846,41861,41864,41889,41892,41903,41906,41925,41934,41937,42042,42045,42048,42119,42122,42178,42181,42196,42199,42230,42233,42582,42585,42880,42883,42887,42890,42893,43009,43016,43070,43077,43083,43086,43094,43100,43103,43109,43112,43123,43126,43158,43170,43440,43443,43462,43468,43470,43473,43555],[133,41789,41791,41792,41797],{"id":41790},"quest-que-la-cross-compilation1","Qu'est que la cross-compilation",[17400,41793,41794],{},[49,41795,3686],{"href":17404,"ariaDescribedBy":41796,"dataFootnoteRef":1646,"id":17407},[17406]," ?",[12,41799,41800],{},"La cross compilation est la possibilité sur une machine avec un matériel\nspécifique (architecture) et avec un système d'exploitation donné, de\ncompiler des programmes pour une autre architecture, ou pour un autre\nsystème d'exploitation.",[12,41802,41803],{},"Cela peut être utilisé par exemple pour compiler un programme sur votre\nordinateur de tous les jours (sous Gnu\u002FLinux, avec une architecture\ni386) à destination de votre téléphone mobile, qui lui est sous Symbian\navec un processeur ARM.",[12,41805,41806],{},"Les raisons de faire de la compilation croisée peuvent donc être\nmultiples :",[506,41808,41809,41812,41820,41830],{},[370,41810,41811],{},"Éviter de redémarrer votre machine pour compiler vos binaires.",[370,41813,41814,41815,3938],{},"Disponibilité des outils sur votre machine \u002F Indisponibilité des\noutils de compilation sur la machine de destination (on trouve\nrarement des outils de compilation sur des téléphones\nportables",[17400,41816,41817],{},[49,41818,2547],{"href":17977,"ariaDescribedBy":41819,"dataFootnoteRef":1646,"id":17979},[17406],[370,41821,41822,41823,3938],{},"Puissances des calculs (la compilation prendra moins de temps sur\nvotre PC de bureau que sur votre appareil mobile",[17400,41824,41825],{},[49,41826,3696],{"href":41827,"ariaDescribedBy":41828,"dataFootnoteRef":1646,"id":41829},"#user-content-fn-3",[17406],"user-content-fnref-3",[370,41831,41832],{},"Licence : Vous voulez compiler à destination d'un système\nd'exploitation que vous ne possédez pas",[12,41834,41835],{},"Attention: La compilation croisée ne garantie pas que programme\nfonctionnera, vous devrez toujours faire quelques tests à partir d'un\némulateur ou à partir du système d'exploitation final.",[12,41837,41838,41839,179],{},"Bref, à partir du moment où vous avez besoin de compiler un programme\npour une autre architecture, ou pour un autre système d'exploitation que\nvotre machine actuelle, vous avez besoin de faire de la ",[362,41840,41841],{},"compilation\ncroisée",[133,41843,41845],{"id":41844},"de-quoi-va-parler-ce-billet","De quoi va parler ce billet ?",[12,41847,41848,41849,41852,41853,41860],{},"Ce billet ne va pas parler de la ",[362,41850,41851],{},"compilation croisée"," entre deux\narchitectures différentes, mais uniquement de la compilation croisée à\ndestination d'une machine Windows à partir d'une machine Linux. La\ncompilation croisée entre architectures pourra être vue dans un\nfutur",[17400,41854,41855],{},[49,41856,706],{"href":41857,"ariaDescribedBy":41858,"dataFootnoteRef":1646,"id":41859},"#user-content-fn-4",[17406],"user-content-fnref-4"," article, ou sur d'autres sites.",[12,41862,41863],{},"Afin de pouvoir faire de la compilation croisée, il vous faudra\ninstaller les outils suivants :",[506,41865,41866,41877],{},[370,41867,41868,41869,41876],{},"MinGW",[17400,41870,41871],{},[49,41872,35439],{"href":41873,"ariaDescribedBy":41874,"dataFootnoteRef":1646,"id":41875},"#user-content-fn-5",[17406],"user-content-fnref-5"," : utilisé en tant que cross-compilateur, il nous\ngénèrera un exécutable Windows.",[370,41878,41879,41880,41888],{},"Wine",[17400,41881,41882],{},[49,41883,41887],{"href":41884,"ariaDescribedBy":41885,"dataFootnoteRef":1646,"id":41886},"#user-content-fn-6",[17406],"user-content-fnref-6","6"," : Qui nous servira à vérifier l'exécutable créé.",[12,41890,41891],{},"Les différentes étapes de la constitution de ce billet seront :",[506,41893,41894,41897,41900],{},[370,41895,41896],{},"Installation des outils",[370,41898,41899],{},"Compilation d'un programme simple",[370,41901,41902],{},"Compilation d'un programme Qt simple",[133,41904,41896],{"id":41905},"installation-des-outils",[12,41907,41908,41909,41912,41913,41916,41924],{},"Nous y sommes :). Nous allons commencer par installer les outils qui\nnous permettront de faire de la compilation croisée. Sous la\ndistribution de votre choix, il vous faudra donc installer ",[184,41910,41911],{},"mingw"," ainsi\nque ",[184,41914,41915],{},"wine",[17400,41917,41918],{},[49,41919,41923],{"href":41920,"ariaDescribedBy":41921,"dataFootnoteRef":1646,"id":41922},"#user-content-fn-7",[17406],"user-content-fnref-7","7",". Sous une Gnu\u002FDebian, on pourra par exemple faire :",[2105,41926,41928],{"className":2317,"code":41927,"language":2319,"meta":1646,"style":1646},"> sudo aptitude install mingw32-runtime wine\n",[184,41929,41930],{"__ignoreMap":1646},[2113,41931,41932],{"class":2115,"line":2116},[2113,41933,41927],{"class":2119},[12,41935,41936],{},"Pour vérifier la version actuelle de mingw vous pouvez faire :",[2105,41938,41940],{"className":2317,"code":41939,"language":2319,"meta":1646,"style":1646},"> i586-mingw32msvc-gcc --version\ni586-mingw32msvc-gcc (GCC) 4.2.1-sjlj (mingw32-2)\nCopyright (C) 2007 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions.  There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",[184,41941,41942,41947,41960,41968,42007],{"__ignoreMap":1646},[2113,41943,41944],{"class":2115,"line":2116},[2113,41945,41946],{"class":2119},"> i586-mingw32msvc-gcc --version\n",[2113,41948,41949,41952,41955,41958],{"class":2115,"line":1647},[2113,41950,41951],{"class":2133},"i586-mingw32msvc-gcc",[2113,41953,41954],{"class":2119}," (GCC) 4.2.1-sjlj (",[2113,41956,41957],{"class":2133},"mingw32-2",[2113,41959,4660],{"class":2119},[2113,41961,41962,41965],{"class":2115,"line":1652},[2113,41963,41964],{"class":2133},"Copyright",[2113,41966,41967],{"class":2119}," (C) 2007 Free Software Foundation, Inc.\n",[2113,41969,41970,41973,41976,41979,41982,41984,41986,41988,41991,41993,41996,41999,42002,42004],{"class":2115,"line":2185},[2113,41971,41972],{"class":2133},"This",[2113,41974,41975],{"class":2149}," is",[2113,41977,41978],{"class":2149}," free",[2113,41980,41981],{"class":2149}," software",[2113,41983,6160],{"class":2119},[2113,41985,17826],{"class":2133},[2113,41987,17783],{"class":2149},[2113,41989,41990],{"class":2149}," source",[2113,41992,4440],{"class":2149},[2113,41994,41995],{"class":2149}," copying",[2113,41997,41998],{"class":2149}," conditions.",[2113,42000,42001],{"class":2149},"  There",[2113,42003,41975],{"class":2149},[2113,42005,42006],{"class":2149}," NO\n",[2113,42008,42009,42012,42014,42017,42020,42022,42025,42027,42030,42033,42036,42039],{"class":2115,"line":2230},[2113,42010,42011],{"class":2133},"warranty",[2113,42013,6160],{"class":2119},[2113,42015,42016],{"class":2133},"not",[2113,42018,42019],{"class":2149}," even",[2113,42021,4440],{"class":2149},[2113,42023,42024],{"class":2149}," MERCHANTABILITY",[2113,42026,17743],{"class":2149},[2113,42028,42029],{"class":2149}," FITNESS",[2113,42031,42032],{"class":2149}," FOR",[2113,42034,42035],{"class":2149}," A",[2113,42037,42038],{"class":2149}," PARTICULAR",[2113,42040,42041],{"class":2149}," PURPOSE.\n",[133,42043,41899],{"id":42044},"compilation-dun-programme-simple",[12,42046,42047],{},"Commençons par le programme le plus simple du monde:",[2105,42049,42051],{"className":2387,"code":42050,"language":2389,"meta":1646,"style":1646},"#include \u003Ciostream>\n\nint main(int argc, char** argv)\n{\n    std::cout \u003C\u003C \"Hello\" \u003C\u003C std::endl;\n    return 0;\n}\n",[184,42052,42053,42061,42065,42088,42092,42107,42115],{"__ignoreMap":1646},[2113,42054,42055,42058],{"class":2115,"line":2116},[2113,42056,42057],{"class":2326},"#include",[2113,42059,42060],{"class":2149}," \u003Ciostream>\n",[2113,42062,42063],{"class":2115,"line":1647},[2113,42064,2125],{"emptyLinePlaceholder":1767},[2113,42066,42067,42069,42071,42073,42075,42078,42080,42083,42086],{"class":2115,"line":1652},[2113,42068,30984],{"class":2326},[2113,42070,3909],{"class":2133},[2113,42072,2423],{"class":2119},[2113,42074,30984],{"class":2326},[2113,42076,42077],{"class":2429}," argc",[2113,42079,932],{"class":2119},[2113,42081,42082],{"class":2326},"char**",[2113,42084,42085],{"class":2429}," argv",[2113,42087,4660],{"class":2119},[2113,42089,42090],{"class":2115,"line":2185},[2113,42091,18060],{"class":2119},[2113,42093,42094,42097,42099,42102,42104],{"class":2115,"line":2230},[2113,42095,42096],{"class":2119},"    std::cout ",[2113,42098,9427],{"class":2326},[2113,42100,42101],{"class":2149}," \"Hello\"",[2113,42103,6265],{"class":2326},[2113,42105,42106],{"class":2119}," std::endl;\n",[2113,42108,42109,42111,42113],{"class":2115,"line":2235},[2113,42110,3137],{"class":2326},[2113,42112,3208],{"class":2166},[2113,42114,2487],{"class":2119},[2113,42116,42117],{"class":2115,"line":2241},[2113,42118,2599],{"class":2119},[12,42120,42121],{},"Puis compilons :",[2105,42123,42125],{"className":2317,"code":42124,"language":2319,"meta":1646,"style":1646},"> i586-mingw32msvc-g++ -o test.exe test.cpp\n> ls\ntest.c\ntest.exe\n> file test.exe\ntest.exe: PE32 executable (console) Intel 80386, for MS Windows\n",[184,42126,42127,42132,42137,42145,42152,42157],{"__ignoreMap":1646},[2113,42128,42129],{"class":2115,"line":2116},[2113,42130,42131],{"class":2119},"> i586-mingw32msvc-g++ -o test.exe test.cpp\n",[2113,42133,42134],{"class":2115,"line":1647},[2113,42135,42136],{"class":2119},"> ls\n",[2113,42138,42139,42142],{"class":2115,"line":1652},[2113,42140,42141],{"class":2334},"test",[2113,42143,42144],{"class":2149},".c\n",[2113,42146,42147,42149],{"class":2115,"line":2185},[2113,42148,42141],{"class":2334},[2113,42150,42151],{"class":2149},".exe\n",[2113,42153,42154],{"class":2115,"line":2230},[2113,42155,42156],{"class":2119},"> file test.exe\n",[2113,42158,42159,42161,42164,42167,42170,42173,42175],{"class":2115,"line":2235},[2113,42160,42141],{"class":2334},[2113,42162,42163],{"class":2149},".exe:",[2113,42165,42166],{"class":2149}," PE32",[2113,42168,42169],{"class":2149}," executable",[2113,42171,42172],{"class":2119}," (console) Intel 80386, ",[2113,42174,8677],{"class":2326},[2113,42176,42177],{"class":2119}," MS Windows\n",[12,42179,42180],{},"Voilà nous avons donc un programme à destination de Windows. Il ne nous\nreste plus qu'à le tester :",[2105,42182,42184],{"className":2317,"code":42183,"language":2319,"meta":1646,"style":1646},"> wine .\u002Ftest.exe\nHello\n",[184,42185,42186,42191],{"__ignoreMap":1646},[2113,42187,42188],{"class":2115,"line":2116},[2113,42189,42190],{"class":2119},"> wine .\u002Ftest.exe\n",[2113,42192,42193],{"class":2115,"line":1647},[2113,42194,42195],{"class":2133},"Hello\n",[12,42197,42198],{},"Et voilà, nous avons écrit un petit programme Windows, et nous l'avons\ntesté à l'aide de Wine. Généralement, on utilise l'utilisation de\nMakefile, voir même des générateurs de Makefile. Nous allons compléter\nl'exemple avec CMake. Voici donc un exemple de fichier CMake :",[2105,42200,42203],{"className":42201,"code":42202,"language":17086,"meta":1646,"style":1646},"language-cmake shiki shiki-themes one-dark-pro","project(test)\nadd_executable(test test.cpp)\n",[184,42204,42205,42216],{"__ignoreMap":1646},[2113,42206,42207,42210,42212,42214],{"class":2115,"line":2116},[2113,42208,42209],{"class":2326},"project",[2113,42211,2423],{"class":2119},[2113,42213,42141],{"class":2326},[2113,42215,4660],{"class":2119},[2113,42217,42218,42221,42223,42225,42227],{"class":2115,"line":1647},[2113,42219,42220],{"class":2326},"add_executable",[2113,42222,2423],{"class":2119},[2113,42224,42141],{"class":2326},[2113,42226,13135],{"class":2326},[2113,42228,42229],{"class":2119},".cpp)\n",[12,42231,42232],{},"Nous allons donc lancer la compilation, sous Linux :",[2105,42234,42236],{"className":2317,"code":42235,"language":2319,"meta":1646,"style":1646},"> mkdir build\n> cd build\n> cmake ..\u002F\n-- The C compiler identification is GNU\n-- The CXX compiler identification is GNU\n-- Check for working C compiler: \u002Fusr\u002Flib\u002Fccache\u002Fgcc\n-- Check for working C compiler: \u002Fusr\u002Flib\u002Fccache\u002Fgcc -- works\n-- Detecting C compiler ABI info\n-- Detecting C compiler ABI info - done\n-- Check for working CXX compiler: \u002Fusr\u002Flib\u002Fccache\u002Fc++\n-- Check for working CXX compiler: \u002Fusr\u002Flib\u002Fccache\u002Fc++ -- works\n-- Detecting CXX compiler ABI info\n-- Detecting CXX compiler ABI info - done\n-- Configuring done\n-- Generating done\n-- Build files have been written to: \u002Ftmp\u002Fbuild\n> make\nScanning dependencies of target test\n[100%] Building CXX object CMakeFiles\u002Ftest.dir\u002Ftest.cpp.o\nLinking CXX executable test\n\n[100%] Built target test\n> file test\ntest: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU\u002FLinux 2.6.26, BuildID[sha1]=0xf17337fcecb8f3b6ed589d8dce8978be08f2caca, not stripped\n",[184,42237,42238,42243,42248,42253,42275,42292,42312,42335,42352,42372,42389,42410,42424,42442,42451,42460,42484,42489,42505,42510,42521,42525,42530,42535],{"__ignoreMap":1646},[2113,42239,42240],{"class":2115,"line":2116},[2113,42241,42242],{"class":2119},"> mkdir build\n",[2113,42244,42245],{"class":2115,"line":1647},[2113,42246,42247],{"class":2119},"> cd build\n",[2113,42249,42250],{"class":2115,"line":1652},[2113,42251,42252],{"class":2119},"> cmake ..\u002F\n",[2113,42254,42255,42258,42261,42264,42267,42270,42272],{"class":2115,"line":2185},[2113,42256,42257],{"class":2133},"--",[2113,42259,42260],{"class":2149}," The",[2113,42262,42263],{"class":2149}," C",[2113,42265,42266],{"class":2149}," compiler",[2113,42268,42269],{"class":2149}," identification",[2113,42271,41975],{"class":2149},[2113,42273,42274],{"class":2149}," GNU\n",[2113,42276,42277,42279,42281,42284,42286,42288,42290],{"class":2115,"line":2230},[2113,42278,42257],{"class":2133},[2113,42280,42260],{"class":2149},[2113,42282,42283],{"class":2149}," CXX",[2113,42285,42266],{"class":2149},[2113,42287,42269],{"class":2149},[2113,42289,41975],{"class":2149},[2113,42291,42274],{"class":2149},[2113,42293,42294,42296,42299,42301,42304,42306,42309],{"class":2115,"line":2235},[2113,42295,42257],{"class":2133},[2113,42297,42298],{"class":2149}," Check",[2113,42300,4440],{"class":2149},[2113,42302,42303],{"class":2149}," working",[2113,42305,42263],{"class":2149},[2113,42307,42308],{"class":2149}," compiler:",[2113,42310,42311],{"class":2149}," \u002Fusr\u002Flib\u002Fccache\u002Fgcc\n",[2113,42313,42314,42316,42318,42320,42322,42324,42326,42329,42332],{"class":2115,"line":2241},[2113,42315,42257],{"class":2133},[2113,42317,42298],{"class":2149},[2113,42319,4440],{"class":2149},[2113,42321,42303],{"class":2149},[2113,42323,42263],{"class":2149},[2113,42325,42308],{"class":2149},[2113,42327,42328],{"class":2149}," \u002Fusr\u002Flib\u002Fccache\u002Fgcc",[2113,42330,42331],{"class":2166}," --",[2113,42333,42334],{"class":2149}," works\n",[2113,42336,42337,42339,42342,42344,42346,42349],{"class":2115,"line":2246},[2113,42338,42257],{"class":2133},[2113,42340,42341],{"class":2149}," Detecting",[2113,42343,42263],{"class":2149},[2113,42345,42266],{"class":2149},[2113,42347,42348],{"class":2149}," ABI",[2113,42350,42351],{"class":2149}," info\n",[2113,42353,42354,42356,42358,42360,42362,42364,42367,42369],{"class":2115,"line":2464},[2113,42355,42257],{"class":2133},[2113,42357,42341],{"class":2149},[2113,42359,42263],{"class":2149},[2113,42361,42266],{"class":2149},[2113,42363,42348],{"class":2149},[2113,42365,42366],{"class":2149}," info",[2113,42368,2270],{"class":2149},[2113,42370,42371],{"class":2149}," done\n",[2113,42373,42374,42376,42378,42380,42382,42384,42386],{"class":2115,"line":2085},[2113,42375,42257],{"class":2133},[2113,42377,42298],{"class":2149},[2113,42379,4440],{"class":2149},[2113,42381,42303],{"class":2149},[2113,42383,42283],{"class":2149},[2113,42385,42308],{"class":2149},[2113,42387,42388],{"class":2149}," \u002Fusr\u002Flib\u002Fccache\u002Fc++\n",[2113,42390,42391,42393,42395,42397,42399,42401,42403,42406,42408],{"class":2115,"line":2514},[2113,42392,42257],{"class":2133},[2113,42394,42298],{"class":2149},[2113,42396,4440],{"class":2149},[2113,42398,42303],{"class":2149},[2113,42400,42283],{"class":2149},[2113,42402,42308],{"class":2149},[2113,42404,42405],{"class":2149}," \u002Fusr\u002Flib\u002Fccache\u002Fc++",[2113,42407,42331],{"class":2166},[2113,42409,42334],{"class":2149},[2113,42411,42412,42414,42416,42418,42420,42422],{"class":2115,"line":2533},[2113,42413,42257],{"class":2133},[2113,42415,42341],{"class":2149},[2113,42417,42283],{"class":2149},[2113,42419,42266],{"class":2149},[2113,42421,42348],{"class":2149},[2113,42423,42351],{"class":2149},[2113,42425,42426,42428,42430,42432,42434,42436,42438,42440],{"class":2115,"line":2556},[2113,42427,42257],{"class":2133},[2113,42429,42341],{"class":2149},[2113,42431,42283],{"class":2149},[2113,42433,42266],{"class":2149},[2113,42435,42348],{"class":2149},[2113,42437,42366],{"class":2149},[2113,42439,2270],{"class":2149},[2113,42441,42371],{"class":2149},[2113,42443,42444,42446,42449],{"class":2115,"line":2569},[2113,42445,42257],{"class":2133},[2113,42447,42448],{"class":2149}," Configuring",[2113,42450,42371],{"class":2149},[2113,42452,42453,42455,42458],{"class":2115,"line":2575},[2113,42454,42257],{"class":2133},[2113,42456,42457],{"class":2149}," Generating",[2113,42459,42371],{"class":2149},[2113,42461,42462,42464,42466,42469,42472,42475,42478,42481],{"class":2115,"line":2596},[2113,42463,42257],{"class":2133},[2113,42465,32419],{"class":2149},[2113,42467,42468],{"class":2149}," files",[2113,42470,42471],{"class":2149}," have",[2113,42473,42474],{"class":2149}," been",[2113,42476,42477],{"class":2149}," written",[2113,42479,42480],{"class":2149}," to:",[2113,42482,42483],{"class":2149}," \u002Ftmp\u002Fbuild\n",[2113,42485,42486],{"class":2115,"line":3192},[2113,42487,42488],{"class":2119},"> make\n",[2113,42490,42491,42494,42497,42499,42502],{"class":2115,"line":3213},[2113,42492,42493],{"class":2133},"Scanning",[2113,42495,42496],{"class":2149}," dependencies",[2113,42498,2281],{"class":2149},[2113,42500,42501],{"class":2149}," target",[2113,42503,42504],{"class":2149}," test\n",[2113,42506,42507],{"class":2115,"line":3236},[2113,42508,42509],{"class":2119},"[100%] Building CXX object CMakeFiles\u002Ftest.dir\u002Ftest.cpp.o\n",[2113,42511,42512,42515,42517,42519],{"class":2115,"line":3248},[2113,42513,42514],{"class":2133},"Linking",[2113,42516,42283],{"class":2149},[2113,42518,42169],{"class":2149},[2113,42520,42504],{"class":2149},[2113,42522,42523],{"class":2115,"line":4899},[2113,42524,2125],{"emptyLinePlaceholder":1767},[2113,42526,42527],{"class":2115,"line":1777},[2113,42528,42529],{"class":2119},"[100%] Built target test\n",[2113,42531,42532],{"class":2115,"line":4931},[2113,42533,42534],{"class":2119},"> file test\n",[2113,42536,42537,42539,42541,42544,42547,42550,42553,42556,42559,42561,42563,42566,42569,42572,42575,42577,42579],{"class":2115,"line":4961},[2113,42538,42141],{"class":2334},[2113,42540,15746],{"class":2149},[2113,42542,42543],{"class":2149}," ELF",[2113,42545,42546],{"class":2149}," 32-bit",[2113,42548,42549],{"class":2149}," LSB",[2113,42551,42552],{"class":2149}," executable,",[2113,42554,42555],{"class":2149}," Intel",[2113,42557,42558],{"class":2149}," 80386,",[2113,42560,17462],{"class":2149},[2113,42562,2850],{"class":2166},[2113,42564,42565],{"class":2119}," (SYSV), dynamically linked (",[2113,42567,42568],{"class":2133},"uses",[2113,42570,42571],{"class":2149}," shared",[2113,42573,42574],{"class":2149}," libs",[2113,42576,8507],{"class":2119},[2113,42578,8677],{"class":2326},[2113,42580,42581],{"class":2119}," GNU\u002FLinux 2.6.26, BuildID[sha1]=0xf17337fcecb8f3b6ed589d8dce8978be08f2caca, not stripped\n",[12,42583,42584],{},"Nous avons donc un binaire pour Gnu\u002FLinux. Recommençons donc mais avec\nWindows :",[2105,42586,42588],{"className":2317,"code":42587,"language":2319,"meta":1646,"style":1646},"> cmake -DCMAKE_C_COMPILER=i586-mingw32msvc-gcc -DCMAKE_CXX_COMPILER=i586-mingw32msvc-g++ ..\u002F\n-- The C compiler identification is GNU\n-- The CXX compiler identification is GNU\n-- Check for working C compiler: \u002Fusr\u002Flib\u002Fccache\u002Fi586-mingw32msvc-gcc\n-- Check for working C compiler: \u002Fusr\u002Flib\u002Fccache\u002Fi586-mingw32msvc-gcc -- works\n-- Detecting C compiler ABI info\n-- Detecting C compiler ABI info - done\n-- Check for working CXX compiler: \u002Fusr\u002Flib\u002Fccache\u002Fi586-mingw32msvc-g++\n-- Check for working CXX compiler: \u002Fusr\u002Flib\u002Fccache\u002Fi586-mingw32msvc-g++ -- works\n-- Detecting CXX compiler ABI info\n-- Detecting CXX compiler ABI info - done\n-- Configuring done\n-- Generating done\n-- Build files have been written to: \u002Ftmp\u002Fbuild-windows\n> make\nScanning dependencies of target test\n[100%] Building CXX object CMakeFiles\u002Ftest.dir\u002Ftest.cpp.o\n\nLinking CXX executable test\n[100%] Built target test\n> file test\ntest: PE32 executable (console) Intel 80386, for MS Windows\n",[184,42589,42590,42615,42631,42647,42664,42685,42699,42717,42734,42755,42769,42787,42795,42803,42822,42826,42838,42842,42846,42856,42860,42864],{"__ignoreMap":1646},[2113,42591,42592,42595,42598,42600,42602,42605,42607,42610,42612],{"class":2115,"line":2116},[2113,42593,42594],{"class":2119},"> cmake ",[2113,42596,42597],{"class":2330},"-DCMAKE_C_COMPILER",[2113,42599,2335],{"class":2334},[2113,42601,41951],{"class":2149},[2113,42603,42604],{"class":2330}," -DCMAKE_CXX_COMPILER",[2113,42606,2335],{"class":2334},[2113,42608,42609],{"class":2149},"i586-mingw32msvc-g++",[2113,42611,24994],{"class":2334},[2113,42613,42614],{"class":2149},".\u002F\n",[2113,42616,42617,42619,42621,42623,42625,42627,42629],{"class":2115,"line":1647},[2113,42618,42257],{"class":2133},[2113,42620,42260],{"class":2149},[2113,42622,42263],{"class":2149},[2113,42624,42266],{"class":2149},[2113,42626,42269],{"class":2149},[2113,42628,41975],{"class":2149},[2113,42630,42274],{"class":2149},[2113,42632,42633,42635,42637,42639,42641,42643,42645],{"class":2115,"line":1652},[2113,42634,42257],{"class":2133},[2113,42636,42260],{"class":2149},[2113,42638,42283],{"class":2149},[2113,42640,42266],{"class":2149},[2113,42642,42269],{"class":2149},[2113,42644,41975],{"class":2149},[2113,42646,42274],{"class":2149},[2113,42648,42649,42651,42653,42655,42657,42659,42661],{"class":2115,"line":2185},[2113,42650,42257],{"class":2133},[2113,42652,42298],{"class":2149},[2113,42654,4440],{"class":2149},[2113,42656,42303],{"class":2149},[2113,42658,42263],{"class":2149},[2113,42660,42308],{"class":2149},[2113,42662,42663],{"class":2149}," \u002Fusr\u002Flib\u002Fccache\u002Fi586-mingw32msvc-gcc\n",[2113,42665,42666,42668,42670,42672,42674,42676,42678,42681,42683],{"class":2115,"line":2230},[2113,42667,42257],{"class":2133},[2113,42669,42298],{"class":2149},[2113,42671,4440],{"class":2149},[2113,42673,42303],{"class":2149},[2113,42675,42263],{"class":2149},[2113,42677,42308],{"class":2149},[2113,42679,42680],{"class":2149}," \u002Fusr\u002Flib\u002Fccache\u002Fi586-mingw32msvc-gcc",[2113,42682,42331],{"class":2166},[2113,42684,42334],{"class":2149},[2113,42686,42687,42689,42691,42693,42695,42697],{"class":2115,"line":2235},[2113,42688,42257],{"class":2133},[2113,42690,42341],{"class":2149},[2113,42692,42263],{"class":2149},[2113,42694,42266],{"class":2149},[2113,42696,42348],{"class":2149},[2113,42698,42351],{"class":2149},[2113,42700,42701,42703,42705,42707,42709,42711,42713,42715],{"class":2115,"line":2241},[2113,42702,42257],{"class":2133},[2113,42704,42341],{"class":2149},[2113,42706,42263],{"class":2149},[2113,42708,42266],{"class":2149},[2113,42710,42348],{"class":2149},[2113,42712,42366],{"class":2149},[2113,42714,2270],{"class":2149},[2113,42716,42371],{"class":2149},[2113,42718,42719,42721,42723,42725,42727,42729,42731],{"class":2115,"line":2246},[2113,42720,42257],{"class":2133},[2113,42722,42298],{"class":2149},[2113,42724,4440],{"class":2149},[2113,42726,42303],{"class":2149},[2113,42728,42283],{"class":2149},[2113,42730,42308],{"class":2149},[2113,42732,42733],{"class":2149}," \u002Fusr\u002Flib\u002Fccache\u002Fi586-mingw32msvc-g++\n",[2113,42735,42736,42738,42740,42742,42744,42746,42748,42751,42753],{"class":2115,"line":2464},[2113,42737,42257],{"class":2133},[2113,42739,42298],{"class":2149},[2113,42741,4440],{"class":2149},[2113,42743,42303],{"class":2149},[2113,42745,42283],{"class":2149},[2113,42747,42308],{"class":2149},[2113,42749,42750],{"class":2149}," \u002Fusr\u002Flib\u002Fccache\u002Fi586-mingw32msvc-g++",[2113,42752,42331],{"class":2166},[2113,42754,42334],{"class":2149},[2113,42756,42757,42759,42761,42763,42765,42767],{"class":2115,"line":2085},[2113,42758,42257],{"class":2133},[2113,42760,42341],{"class":2149},[2113,42762,42283],{"class":2149},[2113,42764,42266],{"class":2149},[2113,42766,42348],{"class":2149},[2113,42768,42351],{"class":2149},[2113,42770,42771,42773,42775,42777,42779,42781,42783,42785],{"class":2115,"line":2514},[2113,42772,42257],{"class":2133},[2113,42774,42341],{"class":2149},[2113,42776,42283],{"class":2149},[2113,42778,42266],{"class":2149},[2113,42780,42348],{"class":2149},[2113,42782,42366],{"class":2149},[2113,42784,2270],{"class":2149},[2113,42786,42371],{"class":2149},[2113,42788,42789,42791,42793],{"class":2115,"line":2533},[2113,42790,42257],{"class":2133},[2113,42792,42448],{"class":2149},[2113,42794,42371],{"class":2149},[2113,42796,42797,42799,42801],{"class":2115,"line":2556},[2113,42798,42257],{"class":2133},[2113,42800,42457],{"class":2149},[2113,42802,42371],{"class":2149},[2113,42804,42805,42807,42809,42811,42813,42815,42817,42819],{"class":2115,"line":2569},[2113,42806,42257],{"class":2133},[2113,42808,32419],{"class":2149},[2113,42810,42468],{"class":2149},[2113,42812,42471],{"class":2149},[2113,42814,42474],{"class":2149},[2113,42816,42477],{"class":2149},[2113,42818,42480],{"class":2149},[2113,42820,42821],{"class":2149}," \u002Ftmp\u002Fbuild-windows\n",[2113,42823,42824],{"class":2115,"line":2575},[2113,42825,42488],{"class":2119},[2113,42827,42828,42830,42832,42834,42836],{"class":2115,"line":2596},[2113,42829,42493],{"class":2133},[2113,42831,42496],{"class":2149},[2113,42833,2281],{"class":2149},[2113,42835,42501],{"class":2149},[2113,42837,42504],{"class":2149},[2113,42839,42840],{"class":2115,"line":3192},[2113,42841,42509],{"class":2119},[2113,42843,42844],{"class":2115,"line":3213},[2113,42845,2125],{"emptyLinePlaceholder":1767},[2113,42847,42848,42850,42852,42854],{"class":2115,"line":3236},[2113,42849,42514],{"class":2133},[2113,42851,42283],{"class":2149},[2113,42853,42169],{"class":2149},[2113,42855,42504],{"class":2149},[2113,42857,42858],{"class":2115,"line":3248},[2113,42859,42529],{"class":2119},[2113,42861,42862],{"class":2115,"line":4899},[2113,42863,42534],{"class":2119},[2113,42865,42866,42868,42870,42872,42874,42876,42878],{"class":2115,"line":1777},[2113,42867,42141],{"class":2334},[2113,42869,15746],{"class":2149},[2113,42871,42166],{"class":2149},[2113,42873,42169],{"class":2149},[2113,42875,42172],{"class":2119},[2113,42877,8677],{"class":2326},[2113,42879,42177],{"class":2119},[12,42881,42882],{},"Nous avons donc maintenant la possibilité de compiler notre application\nmulti-platformes depuis Linux pour les systèmes Linux, mais aussi pour\nles systèmes Windows.",[133,42884,42886],{"id":42885},"compilation-dun-programme-écrit-avec-qt","Compilation d'un programme écrit avec Qt",[12,42888,42889],{},"Nous allons maintenant nous compliquer un peu la tâche en compilant un\nprogramme ayant une dépendance avec une librairie externe : Qt. Qt est\nun framework proposant une boîte à outil de classe permettant de faire\ndes interfaces graphiques mais aussi de faire des applications consoles\nrapidement.",[12,42891,42892],{},"Nous allons donc écrire le petit programme suivant, qui affiche une\nboîte de dialogue inutile, avec un bouton inutile :",[2105,42894,42896],{"className":2387,"code":42895,"language":2389,"meta":1646,"style":1646},"#include \u003CQApplication>\n#include \u003CQPushButton>\n\nint main(int argc, char** argv)\n{\n    QApplication app(argc, argv);\n    QPushButton * btn = new QPushButton(\"Do nothing\");\n    btn->show();\n\n    return app.exec();\n}\n",[184,42897,42898,42905,42912,42916,42936,42940,42951,42975,42987,42991,43005],{"__ignoreMap":1646},[2113,42899,42900,42902],{"class":2115,"line":2116},[2113,42901,42057],{"class":2326},[2113,42903,42904],{"class":2149}," \u003CQApplication>\n",[2113,42906,42907,42909],{"class":2115,"line":1647},[2113,42908,42057],{"class":2326},[2113,42910,42911],{"class":2149}," \u003CQPushButton>\n",[2113,42913,42914],{"class":2115,"line":1652},[2113,42915,2125],{"emptyLinePlaceholder":1767},[2113,42917,42918,42920,42922,42924,42926,42928,42930,42932,42934],{"class":2115,"line":2185},[2113,42919,30984],{"class":2326},[2113,42921,3909],{"class":2133},[2113,42923,2423],{"class":2119},[2113,42925,30984],{"class":2326},[2113,42927,42077],{"class":2429},[2113,42929,932],{"class":2119},[2113,42931,42082],{"class":2326},[2113,42933,42085],{"class":2429},[2113,42935,4660],{"class":2119},[2113,42937,42938],{"class":2115,"line":2230},[2113,42939,18060],{"class":2119},[2113,42941,42942,42945,42948],{"class":2115,"line":2235},[2113,42943,42944],{"class":2119},"    QApplication ",[2113,42946,42947],{"class":2133},"app",[2113,42949,42950],{"class":2119},"(argc, argv);\n",[2113,42952,42953,42956,42958,42961,42963,42965,42968,42970,42973],{"class":2115,"line":2241},[2113,42954,42955],{"class":2119},"    QPushButton ",[2113,42957,2676],{"class":2326},[2113,42959,42960],{"class":2119}," btn ",[2113,42962,2335],{"class":2326},[2113,42964,4778],{"class":2326},[2113,42966,42967],{"class":2133}," QPushButton",[2113,42969,2423],{"class":2119},[2113,42971,42972],{"class":2149},"\"Do nothing\"",[2113,42974,2553],{"class":2119},[2113,42976,42977,42980,42982,42985],{"class":2115,"line":2246},[2113,42978,42979],{"class":2414},"    btn",[2113,42981,4115],{"class":2119},[2113,42983,42984],{"class":2133},"show",[2113,42986,3944],{"class":2119},[2113,42988,42989],{"class":2115,"line":2464},[2113,42990,2125],{"emptyLinePlaceholder":1767},[2113,42992,42993,42995,42998,43000,43003],{"class":2115,"line":2085},[2113,42994,3137],{"class":2326},[2113,42996,42997],{"class":2414}," app",[2113,42999,179],{"class":2119},[2113,43001,43002],{"class":2133},"exec",[2113,43004,3944],{"class":2119},[2113,43006,43007],{"class":2115,"line":2514},[2113,43008,2599],{"class":2119},[12,43010,43011,43012,43015],{},"Avec le fichier ",[184,43013,43014],{},"qmake"," associé tout simple :",[2105,43017,43021],{"className":43018,"code":43019,"language":43020,"meta":1646,"style":1646},"language-ini shiki shiki-themes one-dark-pro","[qmake]\nTEMPLATE = app\nTARGET =\nDEPENDPATH += .\nINCLUDEPATH += .\n\n# Input\nSOURCES += test.cpp\n","ini",[184,43022,43023,43028,43038,43046,43051,43056,43060,43065],{"__ignoreMap":1646},[2113,43024,43025],{"class":2115,"line":2116},[2113,43026,43027],{"class":2133},"[qmake]\n",[2113,43029,43030,43033,43035],{"class":2115,"line":1647},[2113,43031,43032],{"class":2326},"TEMPLATE",[2113,43034,2153],{"class":2119},[2113,43036,43037],{"class":2149}," app\n",[2113,43039,43040,43043],{"class":2115,"line":1652},[2113,43041,43042],{"class":2326},"TARGET",[2113,43044,43045],{"class":2119}," =\n",[2113,43047,43048],{"class":2115,"line":2185},[2113,43049,43050],{"class":2149},"DEPENDPATH += .\n",[2113,43052,43053],{"class":2115,"line":2230},[2113,43054,43055],{"class":2149},"INCLUDEPATH += .\n",[2113,43057,43058],{"class":2115,"line":2235},[2113,43059,2125],{"emptyLinePlaceholder":1767},[2113,43061,43062],{"class":2115,"line":2241},[2113,43063,43064],{"class":2396},"# Input\n",[2113,43066,43067],{"class":2115,"line":2246},[2113,43068,43069],{"class":2149},"SOURCES += test.cpp\n",[12,43071,43072,43073,43076],{},"On test la compilation à l'aide de ",[184,43074,43075],{},"qmake ; make"," et on lance le\nprogramme :",[12,43078,43079],{},[121,43080],{"alt":43081,"src":43082},"cross-compil1","\u002FProgrammation\u002Fcross-compilation-compiler-un-programme-pour-ms-windows-sous-gnu-linux\u002Fcross-compil1.png",[12,43084,43085],{},"Maintenant que notre programme compile et fonctionne sous Gnu\u002FLinux,\nnous allons pouvoir faire le même test mais en compilant une version\nWindows. Pour cela, il va nous falloir la version Windows de Qt (nous\nn'allons pas compiler Qt, alors que la librairie existe déjà).",[12,43087,43088,43089,43093],{},"Vous pouvez commencer par télécharger la dernière version de Qt (ou\ncelle qui vous convient) à l'adresse suivante\n",[49,43090,43091],{"href":43091,"rel":43092},"http:\u002F\u002Fqt.nokia.com\u002Fproducts\u002F",[347]," et l'installer. Vous n'avez pas besoin\nde MinGW, ni de QtCreator. Vous pouvez donc télécharger directement la\nversion qui ne contient que la librairie.",[12,43095,43096],{},[121,43097],{"alt":43098,"src":43099},"cross-compil3","\u002FProgrammation\u002Fcross-compilation-compiler-un-programme-pour-ms-windows-sous-gnu-linux\u002Fcross-compil3.png",[12,43101,43102],{},"Si à l'installation, l'application demande l'installation ou\nl'emplacement de MinGW, vous n'avez pas besoin de le renseigner, nous\nutiliserons la version Linux de MinGW.",[12,43104,43105],{},[121,43106],{"alt":43107,"src":43108},"cross-compil2","\u002FProgrammation\u002Fcross-compilation-compiler-un-programme-pour-ms-windows-sous-gnu-linux\u002Fcross-compil2.png",[12,43110,43111],{},"Enfin nous allons faire un peu de paramétrage. Nous allons récupérer le\ndossier de specs Qt pour windows et l'adapter pour MinGW sous Linux. Les\nadaptations à faire sont :",[506,43113,43114,43117,43120],{},[370,43115,43116],{},"Utilisation de MinGW",[370,43118,43119],{},"Définition des dossiers de Qt Windows et MinGW",[370,43121,43122],{},"Suppression de l'extension .exe",[12,43124,43125],{},"Commençons par créer le fichier qmake.conf :",[2105,43127,43129],{"className":2317,"code":43128,"language":2319,"meta":1646,"style":1646},"sudo cp -Rf \u002Fusr\u002Fshare\u002Fqt4\u002Fmkspecs\u002Fwin32-g++ \u002Fusr\u002Fshare\u002Fqt4\u002Fmkspecs\u002Fwin32-x-g++\nsudo nano \u002Fusr\u002Fshare\u002Fqt4\u002Fmkspecs\u002Fwin32-x-g++\u002Fqmake.conf\n",[184,43130,43131,43148],{"__ignoreMap":1646},[2113,43132,43133,43136,43139,43142,43145],{"class":2115,"line":2116},[2113,43134,43135],{"class":2133},"sudo",[2113,43137,43138],{"class":2149}," cp",[2113,43140,43141],{"class":2166}," -Rf",[2113,43143,43144],{"class":2149}," \u002Fusr\u002Fshare\u002Fqt4\u002Fmkspecs\u002Fwin32-g++",[2113,43146,43147],{"class":2149}," \u002Fusr\u002Fshare\u002Fqt4\u002Fmkspecs\u002Fwin32-x-g++\n",[2113,43149,43150,43152,43155],{"class":2115,"line":1647},[2113,43151,43135],{"class":2133},[2113,43153,43154],{"class":2149}," nano",[2113,43156,43157],{"class":2149}," \u002Fusr\u002Fshare\u002Fqt4\u002Fmkspecs\u002Fwin32-x-g++\u002Fqmake.conf\n",[12,43159,43160,43161,43169],{},"Voici le fichier de diff",[17400,43162,43163],{},[49,43164,43168],{"href":43165,"ariaDescribedBy":43166,"dataFootnoteRef":1646,"id":43167},"#user-content-fn-8",[17406],"user-content-fnref-8","8"," qui contient les choses à modifier, vous pouvez\nle récupérer et utiliser la commande patch pour reporter les\nmodifications, ou faire les modifications à la mains :",[2105,43171,43175],{"className":43172,"code":43173,"language":43174,"meta":1646,"style":1646},"language-diff shiki shiki-themes one-dark-pro","17c17\n\u003C QMAKE_CC              = gcc\n---\n> QMAKE_CC              = i586-mingw32msvc-gcc\n30c30\n\u003C QMAKE_CXX             = g++\n---\n> QMAKE_CXX             = i586-mingw32msvc-g++\n44,46c44,46\n\u003C QMAKE_INCDIR          =\n\u003C QMAKE_INCDIR_QT               = $$[QT_INSTALL_HEADERS]\n\u003C QMAKE_LIBDIR_QT               = $$[QT_INSTALL_LIBS]\n---\n> QMAKE_INCDIR          = \u002Fusr\u002Fi586-mingw32msvc\u002Finclude\n> QMAKE_INCDIR_QT               = \u002Fhome\u002Fphoenix\u002F.wine\u002Fdrive_c\u002FQt\u002F4.8.2\u002Finclude\n> QMAKE_LIBDIR_QT               = \u002Fhome\u002Fphoenix\u002F.wine\u002Fdrive_c\u002FQt\u002F4.8.2\u002Flib\n53,54c53,54\n\u003C QMAKE_LINK            = g++\n\u003C QMAKE_LINK_C          = gcc\n---\n> QMAKE_LINK            = i586-mingw32msvc-g++\n> QMAKE_LINK_C          = i586-mingw32msvc-gcc\n77c77\n\u003C !isEmpty(QMAKE_SH) {\n---\n> #!isEmpty(QMAKE_SH) {\n88,100c88,100\n\u003C } else {\n\u003C       QMAKE_COPY              = copy \u002Fy\n\u003C       QMAKE_COPY_DIR          = xcopy \u002Fs \u002Fq \u002Fy \u002Fi\n\u003C       QMAKE_MOVE              = move\n\u003C       QMAKE_DEL_FILE          = del\n\u003C       QMAKE_MKDIR             = mkdir\n\u003C       QMAKE_DEL_DIR           = rmdir\n\u003C     QMAKE_CHK_DIR_EXISTS      = if not exist\n\u003C }\n\u003C\n\u003C QMAKE_MOC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}moc.exe\n\u003C QMAKE_UIC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}uic.exe\n\u003C QMAKE_IDC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}idc.exe\n---\n> #} else {\n> #     QMAKE_COPY              = copy \u002Fy\n> #     QMAKE_COPY_DIR          = xcopy \u002Fs \u002Fq \u002Fy \u002Fi\n> #     QMAKE_MOVE              = move\n> #     QMAKE_DEL_FILE          = del\n> #     QMAKE_MKDIR             = mkdir\n> #     QMAKE_DEL_DIR           = rmdir\n> #    QMAKE_CHK_DIR_EXISTS     = if not exist\n> #}\n>\n> QMAKE_MOC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}moc-qt4\n> QMAKE_UIC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}uic-qt4\n> QMAKE_IDC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}idc-qt4\n","diff",[184,43176,43177,43182,43187,43191,43196,43201,43206,43210,43215,43220,43225,43230,43235,43239,43244,43249,43254,43259,43264,43269,43273,43278,43283,43288,43293,43297,43302,43307,43312,43317,43322,43327,43332,43337,43342,43347,43352,43357,43362,43367,43372,43376,43381,43386,43391,43396,43401,43406,43411,43416,43421,43425,43430,43435],{"__ignoreMap":1646},[2113,43178,43179],{"class":2115,"line":2116},[2113,43180,43181],{"class":2119},"17c17\n",[2113,43183,43184],{"class":2115,"line":1647},[2113,43185,43186],{"class":2330},"\u003C QMAKE_CC              = gcc\n",[2113,43188,43189],{"class":2115,"line":1652},[2113,43190,23516],{"class":2119},[2113,43192,43193],{"class":2115,"line":2185},[2113,43194,43195],{"class":2149},"> QMAKE_CC              = i586-mingw32msvc-gcc\n",[2113,43197,43198],{"class":2115,"line":2230},[2113,43199,43200],{"class":2119},"30c30\n",[2113,43202,43203],{"class":2115,"line":2235},[2113,43204,43205],{"class":2330},"\u003C QMAKE_CXX             = g++\n",[2113,43207,43208],{"class":2115,"line":2241},[2113,43209,23516],{"class":2119},[2113,43211,43212],{"class":2115,"line":2246},[2113,43213,43214],{"class":2149},"> QMAKE_CXX             = i586-mingw32msvc-g++\n",[2113,43216,43217],{"class":2115,"line":2464},[2113,43218,43219],{"class":2119},"44,46c44,46\n",[2113,43221,43222],{"class":2115,"line":2085},[2113,43223,43224],{"class":2330},"\u003C QMAKE_INCDIR          =\n",[2113,43226,43227],{"class":2115,"line":2514},[2113,43228,43229],{"class":2330},"\u003C QMAKE_INCDIR_QT               = $$[QT_INSTALL_HEADERS]\n",[2113,43231,43232],{"class":2115,"line":2533},[2113,43233,43234],{"class":2330},"\u003C QMAKE_LIBDIR_QT               = $$[QT_INSTALL_LIBS]\n",[2113,43236,43237],{"class":2115,"line":2556},[2113,43238,23516],{"class":2119},[2113,43240,43241],{"class":2115,"line":2569},[2113,43242,43243],{"class":2149},"> QMAKE_INCDIR          = \u002Fusr\u002Fi586-mingw32msvc\u002Finclude\n",[2113,43245,43246],{"class":2115,"line":2575},[2113,43247,43248],{"class":2149},"> QMAKE_INCDIR_QT               = \u002Fhome\u002Fphoenix\u002F.wine\u002Fdrive_c\u002FQt\u002F4.8.2\u002Finclude\n",[2113,43250,43251],{"class":2115,"line":2596},[2113,43252,43253],{"class":2149},"> QMAKE_LIBDIR_QT               = \u002Fhome\u002Fphoenix\u002F.wine\u002Fdrive_c\u002FQt\u002F4.8.2\u002Flib\n",[2113,43255,43256],{"class":2115,"line":3192},[2113,43257,43258],{"class":2119},"53,54c53,54\n",[2113,43260,43261],{"class":2115,"line":3213},[2113,43262,43263],{"class":2330},"\u003C QMAKE_LINK            = g++\n",[2113,43265,43266],{"class":2115,"line":3236},[2113,43267,43268],{"class":2330},"\u003C QMAKE_LINK_C          = gcc\n",[2113,43270,43271],{"class":2115,"line":3248},[2113,43272,23516],{"class":2119},[2113,43274,43275],{"class":2115,"line":4899},[2113,43276,43277],{"class":2149},"> QMAKE_LINK            = i586-mingw32msvc-g++\n",[2113,43279,43280],{"class":2115,"line":1777},[2113,43281,43282],{"class":2149},"> QMAKE_LINK_C          = i586-mingw32msvc-gcc\n",[2113,43284,43285],{"class":2115,"line":4931},[2113,43286,43287],{"class":2119},"77c77\n",[2113,43289,43290],{"class":2115,"line":4961},[2113,43291,43292],{"class":2330},"\u003C !isEmpty(QMAKE_SH) {\n",[2113,43294,43295],{"class":2115,"line":4976},[2113,43296,23516],{"class":2119},[2113,43298,43299],{"class":2115,"line":4993},[2113,43300,43301],{"class":2149},"> #!isEmpty(QMAKE_SH) {\n",[2113,43303,43304],{"class":2115,"line":5013},[2113,43305,43306],{"class":2119},"88,100c88,100\n",[2113,43308,43309],{"class":2115,"line":5018},[2113,43310,43311],{"class":2330},"\u003C } else {\n",[2113,43313,43314],{"class":2115,"line":5042},[2113,43315,43316],{"class":2330},"\u003C       QMAKE_COPY              = copy \u002Fy\n",[2113,43318,43319],{"class":2115,"line":5057},[2113,43320,43321],{"class":2330},"\u003C       QMAKE_COPY_DIR          = xcopy \u002Fs \u002Fq \u002Fy \u002Fi\n",[2113,43323,43324],{"class":2115,"line":5062},[2113,43325,43326],{"class":2330},"\u003C       QMAKE_MOVE              = move\n",[2113,43328,43329],{"class":2115,"line":5098},[2113,43330,43331],{"class":2330},"\u003C       QMAKE_DEL_FILE          = del\n",[2113,43333,43334],{"class":2115,"line":5117},[2113,43335,43336],{"class":2330},"\u003C       QMAKE_MKDIR             = mkdir\n",[2113,43338,43339],{"class":2115,"line":5142},[2113,43340,43341],{"class":2330},"\u003C       QMAKE_DEL_DIR           = rmdir\n",[2113,43343,43344],{"class":2115,"line":5148},[2113,43345,43346],{"class":2330},"\u003C     QMAKE_CHK_DIR_EXISTS      = if not exist\n",[2113,43348,43349],{"class":2115,"line":5163},[2113,43350,43351],{"class":2330},"\u003C }\n",[2113,43353,43354],{"class":2115,"line":5169},[2113,43355,43356],{"class":2330},"\u003C\n",[2113,43358,43359],{"class":2115,"line":5175},[2113,43360,43361],{"class":2330},"\u003C QMAKE_MOC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}moc.exe\n",[2113,43363,43364],{"class":2115,"line":5180},[2113,43365,43366],{"class":2330},"\u003C QMAKE_UIC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}uic.exe\n",[2113,43368,43369],{"class":2115,"line":5199},[2113,43370,43371],{"class":2330},"\u003C QMAKE_IDC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}idc.exe\n",[2113,43373,43374],{"class":2115,"line":5204},[2113,43375,23516],{"class":2119},[2113,43377,43378],{"class":2115,"line":5209},[2113,43379,43380],{"class":2149},"> #} else {\n",[2113,43382,43383],{"class":2115,"line":5232},[2113,43384,43385],{"class":2149},"> #     QMAKE_COPY              = copy \u002Fy\n",[2113,43387,43388],{"class":2115,"line":5237},[2113,43389,43390],{"class":2149},"> #     QMAKE_COPY_DIR          = xcopy \u002Fs \u002Fq \u002Fy \u002Fi\n",[2113,43392,43393],{"class":2115,"line":5242},[2113,43394,43395],{"class":2149},"> #     QMAKE_MOVE              = move\n",[2113,43397,43398],{"class":2115,"line":5267},[2113,43399,43400],{"class":2149},"> #     QMAKE_DEL_FILE          = del\n",[2113,43402,43403],{"class":2115,"line":5282},[2113,43404,43405],{"class":2149},"> #     QMAKE_MKDIR             = mkdir\n",[2113,43407,43408],{"class":2115,"line":5295},[2113,43409,43410],{"class":2149},"> #     QMAKE_DEL_DIR           = rmdir\n",[2113,43412,43413],{"class":2115,"line":5310},[2113,43414,43415],{"class":2149},"> #    QMAKE_CHK_DIR_EXISTS     = if not exist\n",[2113,43417,43418],{"class":2115,"line":5315},[2113,43419,43420],{"class":2149},"> #}\n",[2113,43422,43423],{"class":2115,"line":5320},[2113,43424,18185],{"class":2149},[2113,43426,43427],{"class":2115,"line":8625},[2113,43428,43429],{"class":2149},"> QMAKE_MOC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}moc-qt4\n",[2113,43431,43432],{"class":2115,"line":8631},[2113,43433,43434],{"class":2149},"> QMAKE_UIC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}uic-qt4\n",[2113,43436,43437],{"class":2115,"line":8637},[2113,43438,43439],{"class":2149},"> QMAKE_IDC             = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}idc-qt4\n",[12,43441,43442],{},"Une fois terminé on peut compiler et lancer l'application de la manière\nsuivante :",[2105,43444,43446],{"className":2317,"code":43445,"language":2319,"meta":1646,"style":1646},"> qmake-qt4 -spec win32-x-g++\n> make\n> wine .\u002Frelease\u002Ftest.exe\n",[184,43447,43448,43453,43457],{"__ignoreMap":1646},[2113,43449,43450],{"class":2115,"line":2116},[2113,43451,43452],{"class":2119},"> qmake-qt4 -spec win32-x-g++\n",[2113,43454,43455],{"class":2115,"line":1647},[2113,43456,42488],{"class":2119},[2113,43458,43459],{"class":2115,"line":1652},[2113,43460,43461],{"class":2119},"> wine .\u002Frelease\u002Ftest.exe\n",[12,43463,43464],{},[121,43465],{"alt":43466,"src":43467},"cross-compil4","\u002FProgrammation\u002Fcross-compilation-compiler-un-programme-pour-ms-windows-sous-gnu-linux\u002Fcross-compil4.png",[128,43469,1621],{"id":1620},[12,43471,43472],{},"Et voilà, vous êtes maintenant capable de faire de la compilation\ncroisée pour des programmes aussi simples que complexes :). Quand vos\nprogrammes ont des dépendances, et si vous le pouvez, préférez la\nversion binaire. Sinon vous devrez compiler les librairies vous-même au\nformat Windows de la même manière avant de compiler votre programme.\nCela peut vous obliger à modifier les scriptes de build.",[23219,43474,43476,43479],{"className":43475,"dataFootnotes":1646},[23222],[128,43477,23227],{"className":43478,"id":17406},[23226],[367,43480,43481,43487,43493,43501,43510,43523,43532,43541],{},[370,43482,43483,43484],{"id":23232},"En français cela donne compilation croisée ",[49,43485,23240],{"href":23236,"ariaLabel":23237,"className":43486,"dataFootnoteBackref":1646},[23239],[370,43488,43489,43490],{"id":23243},"bien que ... ",[49,43491,23240],{"href":23251,"ariaLabel":23252,"className":43492,"dataFootnoteBackref":1646},[23239],[370,43494,43496,43497],{"id":43495},"user-content-fn-3","là aussi avec les smartphones actuels, on peut en douter ",[49,43498,23240],{"href":43499,"ariaLabel":35010,"className":43500,"dataFootnoteBackref":1646},"#user-content-fnref-3",[23239],[370,43502,43504,43505],{"id":43503},"user-content-fn-4","très lointain ",[49,43506,23240],{"href":43507,"ariaLabel":43508,"className":43509,"dataFootnoteBackref":1646},"#user-content-fnref-4","Back to reference 4",[23239],[370,43511,43513,43514,43517,43518],{"id":43512},"user-content-fn-5","MinGW est un ",[362,43515,43516],{},"portage"," de gcc pour Window. Il nous permettra donc de générer un executable Windows à partir de notre Gnu\u002FLinux ",[49,43519,23240],{"href":43520,"ariaLabel":43521,"className":43522,"dataFootnoteBackref":1646},"#user-content-fnref-5","Back to reference 5",[23239],[370,43524,43526,43527],{"id":43525},"user-content-fn-6","Un émulateur pour démarrer des programmes Windows sous Linux ",[49,43528,23240],{"href":43529,"ariaLabel":43530,"className":43531,"dataFootnoteBackref":1646},"#user-content-fnref-6","Back to reference 6",[23239],[370,43533,43535,43536],{"id":43534},"user-content-fn-7","L'installation de wine sous une distribution 64-bit peut-être un peu plus compliqué que prévu, mais reste néanmoins faisable. Référez vous à la documentation de votre distribution. ",[49,43537,23240],{"href":43538,"ariaLabel":43539,"className":43540,"dataFootnoteBackref":1646},"#user-content-fnref-7","Back to reference 7",[23239],[370,43542,43544,43545,43549,43550],{"id":43543},"user-content-fn-8","Vous pouvez aussi le télécharger ",[49,43546,43548],{"href":43547},"\u002FProgrammation\u002Fcross-compilation-compiler-un-programme-pour-ms-windows-sous-gnu-linux\u002Fqmake.conf.diff","ici",". ",[49,43551,23240],{"href":43552,"ariaLabel":43553,"className":43554,"dataFootnoteBackref":1646},"#user-content-fnref-8","Back to reference 8",[23239],[3358,43556,43557],{},"html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}",{"title":1646,"searchDepth":1647,"depth":1647,"links":43559},[43560,43562,43563,43564,43565,43566,43567],{"id":41790,"depth":1652,"text":43561},"Qu'est que la cross-compilation1 ?",{"id":41844,"depth":1652,"text":41845},{"id":41905,"depth":1652,"text":41896},{"id":42044,"depth":1652,"text":41899},{"id":42885,"depth":1652,"text":42886},{"id":1620,"depth":1647,"text":1621},{"id":17406,"depth":1647,"text":23227},"2012-07-01",{"type":9,"value":43570},[43571,43578,43580,43582,43584,43604,43606],[133,43572,41791,43573,41797],{"id":41790},[17400,43574,43575],{},[49,43576,3686],{"href":17404,"ariaDescribedBy":43577,"dataFootnoteRef":1646,"id":17407},[17406],[12,43579,41800],{},[12,43581,41803],{},[12,43583,41806],{},[506,43585,43586,43588,43595,43602],{},[370,43587,41811],{},[370,43589,41814,43590,3938],{},[17400,43591,43592],{},[49,43593,2547],{"href":17977,"ariaDescribedBy":43594,"dataFootnoteRef":1646,"id":17979},[17406],[370,43596,41822,43597,3938],{},[17400,43598,43599],{},[49,43600,3696],{"href":41827,"ariaDescribedBy":43601,"dataFootnoteRef":1646,"id":41829},[17406],[370,43603,41832],{},[12,43605,41835],{},[12,43607,41838,43608,179],{},[362,43609,41841],{},{},"\u002Fpost\u002Fcross-compilation-compiler-un-programme-pour-ms-windows-sous-gnu-linux",{"title":41785,"description":1646},"cross-compilation-compiler-un-programme-pour-ms-windows-sous-gnu-linux","posts\u002FProgrammation\u002F2012-07-01-cross-compilation-compiler-un-programme-pour-ms-windows-sous-gnu-linux",[38761],"xyp3lTmc_CVoPo48zy-LSh68_42nO_FD4l7WdmhTvoA",{"id":43618,"title":43619,"author":7,"body":43620,"category":43667,"categorySlug":43863,"date":43864,"description":1646,"excerpt":43865,"extension":1764,"location":1765,"meta":43889,"navigation":1767,"path":43890,"published":1767,"seo":43891,"slug":43892,"stem":43893,"tags":43894,"timeToRead":1652,"__hash__":43897},"posts\u002Fposts\u002FKMDAlert\u002F2011-08-14-kmdalert-0.2.md","KMDAlert - Logiciel de surveillance de périphérique RAID - Version pour KDE 4.0",{"type":9,"value":43621,"toc":43855},[43622,43626,43634,43641,43652,43655,43658,43662,43687,43696,43703,43723,43728,43732,43738,43806,43809,43812,43820,43824,43845,43849,43852],[128,43623,43625],{"id":43624},"présentation","Présentation",[12,43627,43628,43629,43633],{},"Possédant chez moi des disques durs en RAID logiciels sous Gnu\u002FLinux, en\n2007 (cela fait déjà 4 ans), ",[49,43630,43632],{"href":43631},"\u002Fpost\u002F2007-08-17-kmdalert-logiciel-de-surveillance-raid\u002F","j'avais écrit un petit logiciel"," dont le\nbut était de faire du monitoring RAID.",[12,43635,43636,43637,43640],{},"En cas de reconstruction du RAID ou de disque défectueux, le logiciel\naffiche une notification à l'utilisateur, et change le statut affiché\ndans la barre de notification (",[362,43638,43639],{},"systray","). L'icône change bien sur en\nfonction de l'état du RAID :",[506,43642,43643,43646,43649],{},[370,43644,43645],{},"dégradé (avec un point d'exclamation)",[370,43647,43648],{},"en cours de synchronisation (avec une barre de progression)",[370,43650,43651],{},"disque RAID dans un état normal",[12,43653,43654],{},"Ce logiciel ne tournait que sous KDE 3.X.",[12,43656,43657],{},"Voici donc une nouvelle version (qui a mis le temps de sortir) qui est\nun portage de l'application sous KDE 4.X. Elle utilise donc le nouveau\nsystème de notification de KDE (pour l'instant en passant uniquement par\nles notifications standard). Quelques améliorations y seront apportées\nplus tard.",[128,43659,43661],{"id":43660},"requis","Requis",[12,43663,43664,43665,43668,43669,43672,43673,43676,43677,932,43680,43683,43684,43686],{},"Pour fonctionner ",[362,43666,43667],{},"KMDAlert"," nécessite que le dossier ",[184,43670,43671],{},"\u002Fsys"," soit monté\net que ",[184,43674,43675],{},"\u002Fsys\u002Fblock\u002F"," soit renseigné pour chaque volume RAID (exemple\n",[184,43678,43679],{},"\u002Fsys\u002Fblock\u002Fmd0",[184,43681,43682],{},"\u002Fsys\u002Fblock\u002Fmd1",", ...). ",[184,43685,43667],{}," observe ensuite les\nmodifications faites sur les fichiers pour informer l'utilisateur des\nmodifications faites sur le volume RAID ou sur les disques le composant.",[12,43688,43689,43690,792,43692,43695],{},"De nos jours le dossier ",[184,43691,43671],{},[184,43693,43694],{},"\u002Fsys\u002Fblock"," devraient être toujours\nprésents.",[12,43697,43698,43699,43702],{},"L'application a été développée sur ",[362,43700,43701],{},"Gnu\u002FDebian Squeeze",", sur un KDE\n4.4.5 avec un noyau 2.6.32.",[12,43704,43705,43707,43708,43549,43711,43713,43714,43716,43717,43719,43720,43722],{},[362,43706,43667],{}," ne nécessite pas de ",[362,43709,43710],{},"Inotify",[362,43712,43667],{}," lit le contenu de\nplusieurs fichiers dans le dossier ",[184,43715,43671],{}," toutes les 6 secondes pour\nêtre informé de la modification de l'état du périphérique. Sur\nl'application d'origine, développé sous un noyau 2.6.18, ",[362,43718,43710],{}," n'est\npas averti des modifications faites sur le système de fichier ",[184,43721,43671],{},".\nLes dates de ces fichiers ne sont pas non plus modifiées. Si quelqu'un a\nune autre méthode à me proposer, je reste ouvert à toutes propositions.",[12,43724,43725,43727],{},[362,43726,43667],{}," est une application écrite en utilisant les librairies de\nKDE, mais devrait tout de même fonctionner sous Gnome. Les messages de\nnotification utilisent donc le système de notification de KDE (bien que\nje n'utilise pas toutes ses possibilités).",[128,43729,43731],{"id":43730},"compilation-installation","Compilation \u002F Installation",[12,43733,43734,43735,43737],{},"La compilation et l'installation de ",[362,43736,43667],{}," se fait à l'aide du trio\nhabituel :",[2105,43739,43741],{"className":2317,"code":43740,"language":2319,"meta":1646,"style":1646},"# Décompression de KMDAlert\ntar xjfv kmdalert-0.2-77c0d6b4d49f.tar.gz\ncd kmdalert-77c0d6b4d49f\n# Préparation de la compilation\nmkdir build\ncd build\n# Comilation\ncmake ..\u002Fmake\n# Installation\nmake install\n",[184,43742,43743,43748,43759,43766,43771,43777,43783,43788,43795,43800],{"__ignoreMap":1646},[2113,43744,43745],{"class":2115,"line":2116},[2113,43746,43747],{"class":2396},"# Décompression de KMDAlert\n",[2113,43749,43750,43753,43756],{"class":2115,"line":1647},[2113,43751,43752],{"class":2133},"tar",[2113,43754,43755],{"class":2149}," xjfv",[2113,43757,43758],{"class":2149}," kmdalert-0.2-77c0d6b4d49f.tar.gz\n",[2113,43760,43761,43763],{"class":2115,"line":1652},[2113,43762,17064],{"class":2334},[2113,43764,43765],{"class":2149}," kmdalert-77c0d6b4d49f\n",[2113,43767,43768],{"class":2115,"line":2185},[2113,43769,43770],{"class":2396},"# Préparation de la compilation\n",[2113,43772,43773,43775],{"class":2115,"line":2230},[2113,43774,17072],{"class":2133},[2113,43776,17075],{"class":2149},[2113,43778,43779,43781],{"class":2115,"line":2235},[2113,43780,17064],{"class":2334},[2113,43782,17075],{"class":2149},[2113,43784,43785],{"class":2115,"line":2241},[2113,43786,43787],{"class":2396},"# Comilation\n",[2113,43789,43790,43792],{"class":2115,"line":2246},[2113,43791,17086],{"class":2133},[2113,43793,43794],{"class":2149}," ..\u002Fmake\n",[2113,43796,43797],{"class":2115,"line":2464},[2113,43798,43799],{"class":2396},"# Installation\n",[2113,43801,43802,43804],{"class":2115,"line":2085},[2113,43803,17103],{"class":2133},[2113,43805,38944],{"class":2149},[12,43807,43808],{},"La compilation nécessite les librairies de développement KDE et Qt3,\nainsi que le compilateur GCC.",[128,43810,1075],{"id":43811},"licence",[12,43813,43814,43815],{},"La licence choisie est la ",[49,43816,43819],{"href":43817,"rel":43818},"http:\u002F\u002Fwww.gnu.org\u002Flicenses\u002Fgpl-2.0.txt",[347],"GNU GENERAL PUBLIC LICENSE",[128,43821,43823],{"id":43822},"téléchargement","Téléchargement",[506,43825,43826,43834,43842],{},[370,43827,43828,43829,179],{},"Vous pouvez télécharger une archive déjà construite de l'application\nsur ",[49,43830,43833],{"href":43831,"rel":43832},"http:\u002F\u002Fkde-apps.org\u002Fcontent\u002Fshow.php\u002FKMDAlert?content=73373",[347],"KDE-Apps.org",[370,43835,43836,43837,179],{},"Le dépôt officiel pour télécharger les sources et participer :\n",[49,43838,43841],{"href":43839,"rel":43840},"https:\u002F\u002Fgogs.shadoware.org\u002Fphoenix\u002Fkmdalert?pk_campaign=shadoware",[347],"https:\u002F\u002Fgogs.shadoware.org\u002Fphoenix\u002Fkmdalert",[370,43843,43844],{},"Vous pouvez également télécharger la version attachée au billet.",[128,43846,43848],{"id":43847},"système-de-suivi","Système de suivi",[12,43850,43851],{},"Il n'y a actuellement plus de système de suivi pour ce logiciel. En cas\nde problème, vous pouvez laisser un message dans les commentaires ou\nm'envoyer un mail.",[3358,43853,43854],{},"html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":1646,"searchDepth":1647,"depth":1647,"links":43856},[43857,43858,43859,43860,43861,43862],{"id":43624,"depth":1647,"text":43625},{"id":43660,"depth":1647,"text":43661},{"id":43730,"depth":1647,"text":43731},{"id":43811,"depth":1647,"text":1075},{"id":43822,"depth":1647,"text":43823},{"id":43847,"depth":1647,"text":43848},"kmdalert","2011-08-14",{"type":9,"value":43866},[43867,43869,43873,43877,43885,43887],[128,43868,43625],{"id":43624},[12,43870,43628,43871,43633],{},[49,43872,43632],{"href":43631},[12,43874,43636,43875,43640],{},[362,43876,43639],{},[506,43878,43879,43881,43883],{},[370,43880,43645],{},[370,43882,43648],{},[370,43884,43651],{},[12,43886,43654],{},[12,43888,43657],{},{},"\u002Fpost\u002Fkmdalert-0.2",{"title":43619,"description":1646},"kmdalert-0.2","posts\u002FKMDAlert\u002F2011-08-14-kmdalert-0.2",[43895,38761,43863,43896],"kde","raid","4FaKIyQl43RcZ_dgavc_Yj95PpItKkc48C5B55Yud_8",{"id":43899,"title":43900,"author":7,"body":43901,"category":44171,"categorySlug":44172,"date":44173,"description":1646,"excerpt":44174,"extension":1764,"location":1765,"meta":44380,"navigation":1767,"path":44381,"published":1767,"seo":44382,"slug":44383,"stem":44384,"tags":44385,"timeToRead":1647,"__hash__":44386},"posts\u002Fposts\u002FQGenConfig\u002F2011-08-06-qgenconfig-generateur-de-classe.md","qGenConfig - Générateur de classe",{"type":9,"value":43902,"toc":44166},[43903,43905,43916,43922,44028,44031,44130,44133,44139,44145,44147,44153,44155,44163],[128,43904,43625],{"id":43624},[12,43906,43907,43908,43911,43912,43915],{},"Ce logiciel permet de générer des objets ",[184,43909,43910],{},"Qt"," héritant de ",[184,43913,43914],{},"QConfig"," à\npartir d'un fichier écrit au format XML.",[12,43917,43918,43919,43921],{},"Lors de la sauvegarde de paramètres avec ",[184,43920,43910],{},", il faut généralement\nécrire des choses ainsi :",[2105,43923,43925],{"className":2387,"code":43924,"language":2389,"meta":1646,"style":1646},"QSettings settings(\"MaBoite\", \"MonLogiciel\");\n\nsettings.beginGroup(\"Ma fenêtre\");\nsettings.setValue(\"Taille\", win->size());\nsettings.setValue(\"Pleine écran\", win->isFullScreen());\nsettings.endGroup();\n",[184,43926,43927,43947,43951,43968,43993,44017],{"__ignoreMap":1646},[2113,43928,43929,43932,43935,43937,43940,43942,43945],{"class":2115,"line":2116},[2113,43930,43931],{"class":2414},"QSettings",[2113,43933,43934],{"class":2133}," settings",[2113,43936,2423],{"class":2119},[2113,43938,43939],{"class":2149},"\"MaBoite\"",[2113,43941,932],{"class":2119},[2113,43943,43944],{"class":2149},"\"MonLogiciel\"",[2113,43946,2553],{"class":2119},[2113,43948,43949],{"class":2115,"line":1647},[2113,43950,2125],{"emptyLinePlaceholder":1767},[2113,43952,43953,43956,43958,43961,43963,43966],{"class":2115,"line":1652},[2113,43954,43955],{"class":2414},"settings",[2113,43957,179],{"class":2119},[2113,43959,43960],{"class":2133},"beginGroup",[2113,43962,2423],{"class":2119},[2113,43964,43965],{"class":2149},"\"Ma fenêtre\"",[2113,43967,2553],{"class":2119},[2113,43969,43970,43972,43974,43977,43979,43982,43984,43987,43989,43991],{"class":2115,"line":2185},[2113,43971,43955],{"class":2414},[2113,43973,179],{"class":2119},[2113,43975,43976],{"class":2133},"setValue",[2113,43978,2423],{"class":2119},[2113,43980,43981],{"class":2149},"\"Taille\"",[2113,43983,932],{"class":2119},[2113,43985,43986],{"class":2414},"win",[2113,43988,4115],{"class":2119},[2113,43990,7873],{"class":2133},[2113,43992,2593],{"class":2119},[2113,43994,43995,43997,43999,44001,44003,44006,44008,44010,44012,44015],{"class":2115,"line":2230},[2113,43996,43955],{"class":2414},[2113,43998,179],{"class":2119},[2113,44000,43976],{"class":2133},[2113,44002,2423],{"class":2119},[2113,44004,44005],{"class":2149},"\"Pleine écran\"",[2113,44007,932],{"class":2119},[2113,44009,43986],{"class":2414},[2113,44011,4115],{"class":2119},[2113,44013,44014],{"class":2133},"isFullScreen",[2113,44016,2593],{"class":2119},[2113,44018,44019,44021,44023,44026],{"class":2115,"line":2235},[2113,44020,43955],{"class":2414},[2113,44022,179],{"class":2119},[2113,44024,44025],{"class":2133},"endGroup",[2113,44027,3944],{"class":2119},[12,44029,44030],{},"Puis pour récupérer les mêmes paramètres, il faut écrire :",[2105,44032,44034],{"className":2387,"code":44033,"language":2389,"meta":1646,"style":1646},"QSettings settings(\"MaBoite\", \"MonLogiciel\");\n\nsettings.beginGroup(\"Ma fenêtre\");\nQSize size = settings.value(\"Taille\").toSize();\nbool fullscreen = settings.value(\"Pleine écran\").toBool();\nsettings.endGroup();\n",[184,44035,44036,44052,44056,44070,44094,44120],{"__ignoreMap":1646},[2113,44037,44038,44040,44042,44044,44046,44048,44050],{"class":2115,"line":2116},[2113,44039,43931],{"class":2414},[2113,44041,43934],{"class":2133},[2113,44043,2423],{"class":2119},[2113,44045,43939],{"class":2149},[2113,44047,932],{"class":2119},[2113,44049,43944],{"class":2149},[2113,44051,2553],{"class":2119},[2113,44053,44054],{"class":2115,"line":1647},[2113,44055,2125],{"emptyLinePlaceholder":1767},[2113,44057,44058,44060,44062,44064,44066,44068],{"class":2115,"line":1652},[2113,44059,43955],{"class":2414},[2113,44061,179],{"class":2119},[2113,44063,43960],{"class":2133},[2113,44065,2423],{"class":2119},[2113,44067,43965],{"class":2149},[2113,44069,2553],{"class":2119},[2113,44071,44072,44075,44077,44079,44081,44083,44085,44087,44089,44092],{"class":2115,"line":2185},[2113,44073,44074],{"class":2119},"QSize size ",[2113,44076,2335],{"class":2326},[2113,44078,43934],{"class":2414},[2113,44080,179],{"class":2119},[2113,44082,21478],{"class":2133},[2113,44084,2423],{"class":2119},[2113,44086,43981],{"class":2149},[2113,44088,3938],{"class":2119},[2113,44090,44091],{"class":2133},"toSize",[2113,44093,3944],{"class":2119},[2113,44095,44096,44098,44101,44103,44105,44107,44109,44111,44113,44115,44118],{"class":2115,"line":2230},[2113,44097,4716],{"class":2326},[2113,44099,44100],{"class":2119}," fullscreen ",[2113,44102,2335],{"class":2326},[2113,44104,43934],{"class":2414},[2113,44106,179],{"class":2119},[2113,44108,21478],{"class":2133},[2113,44110,2423],{"class":2119},[2113,44112,44005],{"class":2149},[2113,44114,3938],{"class":2119},[2113,44116,44117],{"class":2133},"toBool",[2113,44119,3944],{"class":2119},[2113,44121,44122,44124,44126,44128],{"class":2115,"line":2235},[2113,44123,43955],{"class":2414},[2113,44125,179],{"class":2119},[2113,44127,44025],{"class":2133},[2113,44129,3944],{"class":2119},[12,44131,44132],{},"Si les valeurs doivent être récupérées dans toute l'application et qu'un\njour l'un des paramètres change de nom, de type, ou de groupe, il ne\nsera pas possible d'être sûr que tous les endroits de l'application sont\nbien modifiés lors de la compilation.",[12,44134,44135,44136,179],{},"C'est là qu'intervient ",[184,44137,44138],{},"qGenConfig",[12,44140,44141,44142,44144],{},"Le but est d'écrire un fichier XML représentant la configuration du\nlogiciel et possédant une certaine structure (des groupes, et des\nvaleurs). Le programme parcourt ensuite ce fichier et génère une classe\npermettant de lire et d'enregistrer la configuration à l'aide de\n",[184,44143,44138],{},". Le contenu des données est alors stocké dans une structure\nC, et permet l'accès facile aux données de configuration. Si une donnée\nchange de type, de nom, ou de groupe, le programme ne compilera plus.",[128,44146,1075],{"id":43811},[12,44148,43814,44149,179],{},[49,44150,43819],{"href":44151,"rel":44152},"http:\u002F\u002Fwww.gnu.org\u002Fcopyleft\u002Fgpl.txt",[347],[128,44154,43823],{"id":43822},[12,44156,44157,44158],{},"Il est possible de télécharger l'application sur le dépôt officiel à\nl'adresse : ",[49,44159,44162],{"href":44160,"rel":44161},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002Fqgenconfig?pk_campaign=shadoware",[347],"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002Fqgenconfig",[3358,44164,44165],{},"html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}",{"title":1646,"searchDepth":1647,"depth":1647,"links":44167},[44168,44169,44170],{"id":43624,"depth":1647,"text":43625},{"id":43811,"depth":1647,"text":1075},{"id":43822,"depth":1647,"text":43823},"QGenConfig","qgenconfig","2011-08-06",{"type":9,"value":44175},[44176,44178,44184,44188,44280,44282,44376,44378],[128,44177,43625],{"id":43624},[12,44179,43907,44180,43911,44182,43915],{},[184,44181,43910],{},[184,44183,43914],{},[12,44185,43918,44186,43921],{},[184,44187,43910],{},[2105,44189,44190],{"className":2387,"code":43924,"language":2389,"meta":1646,"style":1646},[184,44191,44192,44208,44212,44226,44248,44270],{"__ignoreMap":1646},[2113,44193,44194,44196,44198,44200,44202,44204,44206],{"class":2115,"line":2116},[2113,44195,43931],{"class":2414},[2113,44197,43934],{"class":2133},[2113,44199,2423],{"class":2119},[2113,44201,43939],{"class":2149},[2113,44203,932],{"class":2119},[2113,44205,43944],{"class":2149},[2113,44207,2553],{"class":2119},[2113,44209,44210],{"class":2115,"line":1647},[2113,44211,2125],{"emptyLinePlaceholder":1767},[2113,44213,44214,44216,44218,44220,44222,44224],{"class":2115,"line":1652},[2113,44215,43955],{"class":2414},[2113,44217,179],{"class":2119},[2113,44219,43960],{"class":2133},[2113,44221,2423],{"class":2119},[2113,44223,43965],{"class":2149},[2113,44225,2553],{"class":2119},[2113,44227,44228,44230,44232,44234,44236,44238,44240,44242,44244,44246],{"class":2115,"line":2185},[2113,44229,43955],{"class":2414},[2113,44231,179],{"class":2119},[2113,44233,43976],{"class":2133},[2113,44235,2423],{"class":2119},[2113,44237,43981],{"class":2149},[2113,44239,932],{"class":2119},[2113,44241,43986],{"class":2414},[2113,44243,4115],{"class":2119},[2113,44245,7873],{"class":2133},[2113,44247,2593],{"class":2119},[2113,44249,44250,44252,44254,44256,44258,44260,44262,44264,44266,44268],{"class":2115,"line":2230},[2113,44251,43955],{"class":2414},[2113,44253,179],{"class":2119},[2113,44255,43976],{"class":2133},[2113,44257,2423],{"class":2119},[2113,44259,44005],{"class":2149},[2113,44261,932],{"class":2119},[2113,44263,43986],{"class":2414},[2113,44265,4115],{"class":2119},[2113,44267,44014],{"class":2133},[2113,44269,2593],{"class":2119},[2113,44271,44272,44274,44276,44278],{"class":2115,"line":2235},[2113,44273,43955],{"class":2414},[2113,44275,179],{"class":2119},[2113,44277,44025],{"class":2133},[2113,44279,3944],{"class":2119},[12,44281,44030],{},[2105,44283,44284],{"className":2387,"code":44033,"language":2389,"meta":1646,"style":1646},[184,44285,44286,44302,44306,44320,44342,44366],{"__ignoreMap":1646},[2113,44287,44288,44290,44292,44294,44296,44298,44300],{"class":2115,"line":2116},[2113,44289,43931],{"class":2414},[2113,44291,43934],{"class":2133},[2113,44293,2423],{"class":2119},[2113,44295,43939],{"class":2149},[2113,44297,932],{"class":2119},[2113,44299,43944],{"class":2149},[2113,44301,2553],{"class":2119},[2113,44303,44304],{"class":2115,"line":1647},[2113,44305,2125],{"emptyLinePlaceholder":1767},[2113,44307,44308,44310,44312,44314,44316,44318],{"class":2115,"line":1652},[2113,44309,43955],{"class":2414},[2113,44311,179],{"class":2119},[2113,44313,43960],{"class":2133},[2113,44315,2423],{"class":2119},[2113,44317,43965],{"class":2149},[2113,44319,2553],{"class":2119},[2113,44321,44322,44324,44326,44328,44330,44332,44334,44336,44338,44340],{"class":2115,"line":2185},[2113,44323,44074],{"class":2119},[2113,44325,2335],{"class":2326},[2113,44327,43934],{"class":2414},[2113,44329,179],{"class":2119},[2113,44331,21478],{"class":2133},[2113,44333,2423],{"class":2119},[2113,44335,43981],{"class":2149},[2113,44337,3938],{"class":2119},[2113,44339,44091],{"class":2133},[2113,44341,3944],{"class":2119},[2113,44343,44344,44346,44348,44350,44352,44354,44356,44358,44360,44362,44364],{"class":2115,"line":2230},[2113,44345,4716],{"class":2326},[2113,44347,44100],{"class":2119},[2113,44349,2335],{"class":2326},[2113,44351,43934],{"class":2414},[2113,44353,179],{"class":2119},[2113,44355,21478],{"class":2133},[2113,44357,2423],{"class":2119},[2113,44359,44005],{"class":2149},[2113,44361,3938],{"class":2119},[2113,44363,44117],{"class":2133},[2113,44365,3944],{"class":2119},[2113,44367,44368,44370,44372,44374],{"class":2115,"line":2235},[2113,44369,43955],{"class":2414},[2113,44371,179],{"class":2119},[2113,44373,44025],{"class":2133},[2113,44375,3944],{"class":2119},[12,44377,44132],{},[3358,44379,44165],{},{},"\u002Fpost\u002Fqgenconfig-generateur-de-classe",{"title":43900,"description":1646},"qgenconfig-generateur-de-classe","posts\u002FQGenConfig\u002F2011-08-06-qgenconfig-generateur-de-classe",[44172,38761],"zKVGw5y0H6WBfipekCU-jDYgCl9Lo12soIhuD9beiyM",{"id":44388,"title":44389,"author":7,"body":44390,"category":2057,"categorySlug":2058,"date":45642,"description":45643,"excerpt":45644,"extension":1764,"location":1765,"meta":45702,"navigation":1767,"path":45703,"published":1767,"seo":45704,"slug":45705,"stem":45706,"tags":45707,"timeToRead":2235,"__hash__":45708},"posts\u002Fposts\u002FProgrammation\u002F2011-06-26-qt-cmake-et-qt.md","C++\u002FQt - CMake et Qt",{"type":9,"value":44391,"toc":45637},[44392,44423,44443,44451,44458,44475,44479,44490,44494,44507,44535,44539,44545,44815,44818,44853,44857,44869,44952,44959,44963,44976,44992,44998,45007,45014,45026,45030,45046,45246,45249,45264,45268,45275,45278,45287,45291,45297,45306,45310,45320,45329,45333,45336,45343,45347,45382,45386,45445,45449,45452,45455,45458,45504,45510,45514,45540,45544,45547,45582,45593,45596,45608,45634],[12,44393,44394,44395,44399,44400,44404,44405,44410,44411,44416,44417,44420,44421,179],{},"Suite à un ",[49,44396,44398],{"href":44397},"\u002Fpost\u002F2008-01-21-memo-qt\u002F","billet"," datant de 2008, je reviens vers vous pour ajouter\nquelques précisions sur la compilation de programme ",[49,44401,43910],{"href":44402,"rel":44403},"http:\u002F\u002Fqt.nokia.com\u002F",[347]," avec\n",[49,44406,44409],{"href":44407,"rel":44408},"http:\u002F\u002Fwww.cmake.org\u002F",[347],"CMake",". En effet, pour mon programme ",[49,44412,44415],{"href":44413,"rel":44414},"http:\u002F\u002Fxinx.shadoware.org?piwik_campaign=Shadoware&piwik_keyword=Billet",[347],"XINX",", j'ai modifié la chaîne\nde compilation actuelle utilisant ",[362,44418,44419],{},"QMake"," par une chaîne de compilation\n",[362,44422,44409],{},[12,44424,44425,44427,44428,44431,44432,43549,44437,44439,44440,44442],{},[362,44426,44409],{}," est un puissant générateur de ",[184,44429,44430],{},"Makefile",", il permet de remplacer\nles anciens (mais pas complètement révolus) ",[49,44433,44436],{"href":44434,"rel":44435},"http:\u002F\u002Fwww.gnu.org\u002Fsoftware\u002Fautoconf\u002F",[347],"autotools",[362,44438,44409],{}," ne\nremplace donc pas le programme ",[184,44441,17103],{}," mais vient se placer en amont.",[12,44444,44445,44447,44448,179],{},[362,44446,44409],{}," permet de compiler un programme à différents endroits du dossier\ndes sources, ce qui permet de garder le répertoire des sources ",[362,44449,44450],{},"propre",[12,44452,44453,44454,44457],{},"Nous allons considérer dans la suite le dossier ",[184,44455,44456],{},"projet"," suivant :",[506,44459,44460],{},[370,44461,44462,44464],{},[184,44463,44456],{},[506,44465,44466,44471],{},[370,44467,44468],{},[184,44469,44470],{},"source",[370,44472,44473],{},[184,44474,32993],{},[133,44476,44478],{"id":44477},"compilation","Compilation",[12,44480,44481,44482,44484,44485,44489],{},"Nous passerons sous silence la compilation d'un programme non ",[362,44483,43910],{}," qui\npeut être retrouvé dans la ",[49,44486,26138],{"href":44487,"rel":44488},"http:\u002F\u002Fwww.cmake.org\u002Fcmake\u002Fhelp\u002Fcmake_tutorial.html",[347]," et nous nous limiterons aux\nexplications liées aux programmes écrits en ''Qt' (qui peuvent également\nêtre retrouvés dans d'autres tutoriels sur Internet).",[27533,44491,44493],{"id":44492},"package-à-utiliser","Package à utiliser",[12,44495,44496,44497,44500,44501,44503,44504,44506],{},"Pour utiliser ",[362,44498,44499],{},"Qt4"," avec ",[362,44502,44409],{},", il faut inclure le package ",[184,44505,44499],{}," :",[2105,44508,44510],{"className":42201,"code":44509,"language":17086,"meta":1646,"style":1646},"project(lenomdemonprojet)\ncmake_minimum_required(VERSION 2.8.0)\nfind_package(Qt4 REQUIRED)\n",[184,44511,44512,44519,44527],{"__ignoreMap":1646},[2113,44513,44514,44516],{"class":2115,"line":2116},[2113,44515,42209],{"class":2326},[2113,44517,44518],{"class":2119},"(lenomdemonprojet)\n",[2113,44520,44521,44524],{"class":2115,"line":1647},[2113,44522,44523],{"class":2326},"cmake_minimum_required",[2113,44525,44526],{"class":2119},"(VERSION 2.8.0)\n",[2113,44528,44529,44532],{"class":2115,"line":1652},[2113,44530,44531],{"class":2326},"find_package",[2113,44533,44534],{"class":2119},"(Qt4 REQUIRED)\n",[27533,44536,44538],{"id":44537},"définir-les-modules-qt-à-utiliser","Définir les modules Qt à utiliser",[12,44540,44541,44542,44544],{},"Il est ensuite possible d'activer ou de désactiver les différents\nmodules de ",[362,44543,43910],{}," à utiliser suivant le programme que vous écrivez :",[2105,44546,44548],{"className":42201,"code":44547,"language":17086,"meta":1646,"style":1646},"set(QT_DONT_USE_QTCORE TRUE)\nset(QT_DONT_USE_QTGUI TRUE)\nset(QT_USE_QT3SUPPORT TRUE)\nset(QT_USE_QTASSISTANT TRUE)\nset(QT_USE_QAXCONTAINER TRUE)\nset(QT_USE_QAXSERVER TRUE)\nset(QT_USE_QTDESIGNER TRUE)\nset(QT_USE_QTMOTIF TRUE)\nset(QT_USE_QTMAIN TRUE)\nset(QT_USE_QTNETWORK TRUE)\nset(QT_USE_QTNSPLUGIN TRUE)\nset(QT_USE_QTOPENGL TRUE)\nset(QT_USE_QTSQL TRUE)\nset(QT_USE_QTXML TRUE)\nset(QT_USE_QTXMLPATTERNS TRUE)\nset(QT_USE_QTWEBKIT TRUE)\nset(QT_USE_QTSVG TRUE)\nset(QT_USE_QTTEST TRUE)\nset(QT_USE_QTUITOOLS TRUE)\nset(QT_USE_QTDBUS TRUE)\nset(QT_USE_QTSCRIPT TRUE)\nset(QT_USE_QTASSISTANTCLIENT TRUE)\nset(QT_USE_QTHELP TRUE)\nset(QT_USE_PHONON TRUE)\n",[184,44549,44550,44562,44573,44584,44595,44606,44617,44628,44639,44650,44661,44672,44683,44694,44705,44716,44727,44738,44749,44760,44771,44782,44793,44804],{"__ignoreMap":1646},[2113,44551,44552,44554,44557,44560],{"class":2115,"line":2116},[2113,44553,30242],{"class":2326},[2113,44555,44556],{"class":2119},"(QT_DONT_USE_QTCORE ",[2113,44558,44559],{"class":2326},"TRUE",[2113,44561,4660],{"class":2119},[2113,44563,44564,44566,44569,44571],{"class":2115,"line":1647},[2113,44565,30242],{"class":2326},[2113,44567,44568],{"class":2119},"(QT_DONT_USE_QTGUI ",[2113,44570,44559],{"class":2326},[2113,44572,4660],{"class":2119},[2113,44574,44575,44577,44580,44582],{"class":2115,"line":1652},[2113,44576,30242],{"class":2326},[2113,44578,44579],{"class":2119},"(QT_USE_QT3SUPPORT ",[2113,44581,44559],{"class":2326},[2113,44583,4660],{"class":2119},[2113,44585,44586,44588,44591,44593],{"class":2115,"line":2185},[2113,44587,30242],{"class":2326},[2113,44589,44590],{"class":2119},"(QT_USE_QTASSISTANT ",[2113,44592,44559],{"class":2326},[2113,44594,4660],{"class":2119},[2113,44596,44597,44599,44602,44604],{"class":2115,"line":2230},[2113,44598,30242],{"class":2326},[2113,44600,44601],{"class":2119},"(QT_USE_QAXCONTAINER ",[2113,44603,44559],{"class":2326},[2113,44605,4660],{"class":2119},[2113,44607,44608,44610,44613,44615],{"class":2115,"line":2235},[2113,44609,30242],{"class":2326},[2113,44611,44612],{"class":2119},"(QT_USE_QAXSERVER ",[2113,44614,44559],{"class":2326},[2113,44616,4660],{"class":2119},[2113,44618,44619,44621,44624,44626],{"class":2115,"line":2241},[2113,44620,30242],{"class":2326},[2113,44622,44623],{"class":2119},"(QT_USE_QTDESIGNER ",[2113,44625,44559],{"class":2326},[2113,44627,4660],{"class":2119},[2113,44629,44630,44632,44635,44637],{"class":2115,"line":2246},[2113,44631,30242],{"class":2326},[2113,44633,44634],{"class":2119},"(QT_USE_QTMOTIF ",[2113,44636,44559],{"class":2326},[2113,44638,4660],{"class":2119},[2113,44640,44641,44643,44646,44648],{"class":2115,"line":2464},[2113,44642,30242],{"class":2326},[2113,44644,44645],{"class":2119},"(QT_USE_QTMAIN ",[2113,44647,44559],{"class":2326},[2113,44649,4660],{"class":2119},[2113,44651,44652,44654,44657,44659],{"class":2115,"line":2085},[2113,44653,30242],{"class":2326},[2113,44655,44656],{"class":2119},"(QT_USE_QTNETWORK ",[2113,44658,44559],{"class":2326},[2113,44660,4660],{"class":2119},[2113,44662,44663,44665,44668,44670],{"class":2115,"line":2514},[2113,44664,30242],{"class":2326},[2113,44666,44667],{"class":2119},"(QT_USE_QTNSPLUGIN ",[2113,44669,44559],{"class":2326},[2113,44671,4660],{"class":2119},[2113,44673,44674,44676,44679,44681],{"class":2115,"line":2533},[2113,44675,30242],{"class":2326},[2113,44677,44678],{"class":2119},"(QT_USE_QTOPENGL ",[2113,44680,44559],{"class":2326},[2113,44682,4660],{"class":2119},[2113,44684,44685,44687,44690,44692],{"class":2115,"line":2556},[2113,44686,30242],{"class":2326},[2113,44688,44689],{"class":2119},"(QT_USE_QTSQL ",[2113,44691,44559],{"class":2326},[2113,44693,4660],{"class":2119},[2113,44695,44696,44698,44701,44703],{"class":2115,"line":2569},[2113,44697,30242],{"class":2326},[2113,44699,44700],{"class":2119},"(QT_USE_QTXML ",[2113,44702,44559],{"class":2326},[2113,44704,4660],{"class":2119},[2113,44706,44707,44709,44712,44714],{"class":2115,"line":2575},[2113,44708,30242],{"class":2326},[2113,44710,44711],{"class":2119},"(QT_USE_QTXMLPATTERNS ",[2113,44713,44559],{"class":2326},[2113,44715,4660],{"class":2119},[2113,44717,44718,44720,44723,44725],{"class":2115,"line":2596},[2113,44719,30242],{"class":2326},[2113,44721,44722],{"class":2119},"(QT_USE_QTWEBKIT ",[2113,44724,44559],{"class":2326},[2113,44726,4660],{"class":2119},[2113,44728,44729,44731,44734,44736],{"class":2115,"line":3192},[2113,44730,30242],{"class":2326},[2113,44732,44733],{"class":2119},"(QT_USE_QTSVG ",[2113,44735,44559],{"class":2326},[2113,44737,4660],{"class":2119},[2113,44739,44740,44742,44745,44747],{"class":2115,"line":3213},[2113,44741,30242],{"class":2326},[2113,44743,44744],{"class":2119},"(QT_USE_QTTEST ",[2113,44746,44559],{"class":2326},[2113,44748,4660],{"class":2119},[2113,44750,44751,44753,44756,44758],{"class":2115,"line":3236},[2113,44752,30242],{"class":2326},[2113,44754,44755],{"class":2119},"(QT_USE_QTUITOOLS ",[2113,44757,44559],{"class":2326},[2113,44759,4660],{"class":2119},[2113,44761,44762,44764,44767,44769],{"class":2115,"line":3248},[2113,44763,30242],{"class":2326},[2113,44765,44766],{"class":2119},"(QT_USE_QTDBUS ",[2113,44768,44559],{"class":2326},[2113,44770,4660],{"class":2119},[2113,44772,44773,44775,44778,44780],{"class":2115,"line":4899},[2113,44774,30242],{"class":2326},[2113,44776,44777],{"class":2119},"(QT_USE_QTSCRIPT ",[2113,44779,44559],{"class":2326},[2113,44781,4660],{"class":2119},[2113,44783,44784,44786,44789,44791],{"class":2115,"line":1777},[2113,44785,30242],{"class":2326},[2113,44787,44788],{"class":2119},"(QT_USE_QTASSISTANTCLIENT ",[2113,44790,44559],{"class":2326},[2113,44792,4660],{"class":2119},[2113,44794,44795,44797,44800,44802],{"class":2115,"line":4931},[2113,44796,30242],{"class":2326},[2113,44798,44799],{"class":2119},"(QT_USE_QTHELP ",[2113,44801,44559],{"class":2326},[2113,44803,4660],{"class":2119},[2113,44805,44806,44808,44811,44813],{"class":2115,"line":4961},[2113,44807,30242],{"class":2326},[2113,44809,44810],{"class":2119},"(QT_USE_PHONON ",[2113,44812,44559],{"class":2326},[2113,44814,4660],{"class":2119},[12,44816,44817],{},"Par exemple pour faire un programme utilisant le module Xml,\nXmlPatterns, et Webkit il faut ajouter :",[2105,44819,44821],{"className":42201,"code":44820,"language":17086,"meta":1646,"style":1646},"set(QT_USE_QTXML TRUE)\nset(QT_USE_QTXMLPATTERNS TRUE)\nset(QT_USE_QTWEBKIT TRUE)\n",[184,44822,44823,44833,44843],{"__ignoreMap":1646},[2113,44824,44825,44827,44829,44831],{"class":2115,"line":2116},[2113,44826,30242],{"class":2326},[2113,44828,44700],{"class":2119},[2113,44830,44559],{"class":2326},[2113,44832,4660],{"class":2119},[2113,44834,44835,44837,44839,44841],{"class":2115,"line":1647},[2113,44836,30242],{"class":2326},[2113,44838,44711],{"class":2119},[2113,44840,44559],{"class":2326},[2113,44842,4660],{"class":2119},[2113,44844,44845,44847,44849,44851],{"class":2115,"line":1652},[2113,44846,30242],{"class":2326},[2113,44848,44722],{"class":2119},[2113,44850,44559],{"class":2326},[2113,44852,4660],{"class":2119},[27533,44854,44856],{"id":44855},"quelques-déclarations-supplémentaires","Quelques déclarations supplémentaires",[12,44858,44859,44860,44862,44863,44865,44866,179],{},"Après avoir compilé les différentes parties du programme avec le\ngénérateur de ",[184,44861,44430],{}," de ",[362,44864,43910],{}," (QMake), j'ai relevé les différentes\ndéfinitions à ajouter lors de la compilation. De plus une fois les\nmodules définis, il faut inclure également le module ",[362,44867,44868],{},"UseQt4",[2105,44870,44872],{"className":42201,"code":44871,"language":17086,"meta":1646,"style":1646},"add_definitions(-DUNICODE)\n# Les Q_ASSERT, Q_ASSERT_X, ... sont désactivés en mode Release\nif(CMAKE_BUILD_TYPE STREQUAL \"Release\")\n    add_definitions(-DQT_NO_DEBUG)\nendif()\n# Include utilisé par Qt (du style include (UseQt4))\ninclude(${QT_USE_FILE})\n# Ajoute les définitions propres à Qt (suivant les modules ajoutés)\nadd_definitions(${QT_DEFINITIONS})\n",[184,44873,44874,44882,44887,44904,44912,44919,44924,44936,44941],{"__ignoreMap":1646},[2113,44875,44876,44879],{"class":2115,"line":2116},[2113,44877,44878],{"class":2326},"add_definitions",[2113,44880,44881],{"class":2119},"(-DUNICODE)\n",[2113,44883,44884],{"class":2115,"line":1647},[2113,44885,44886],{"class":2396},"# Les Q_ASSERT, Q_ASSERT_X, ... sont désactivés en mode Release\n",[2113,44888,44889,44891,44893,44896,44899,44902],{"class":2115,"line":1652},[2113,44890,4109],{"class":2326},[2113,44892,2423],{"class":2119},[2113,44894,44895],{"class":2330},"CMAKE_BUILD_TYPE",[2113,44897,44898],{"class":2326}," STREQUAL",[2113,44900,44901],{"class":2149}," \"Release\"",[2113,44903,4660],{"class":2119},[2113,44905,44906,44909],{"class":2115,"line":2185},[2113,44907,44908],{"class":2326},"    add_definitions",[2113,44910,44911],{"class":2119},"(-DQT_NO_DEBUG)\n",[2113,44913,44914,44917],{"class":2115,"line":2230},[2113,44915,44916],{"class":2326},"endif",[2113,44918,6745],{"class":2119},[2113,44920,44921],{"class":2115,"line":2235},[2113,44922,44923],{"class":2396},"# Include utilisé par Qt (du style include (UseQt4))\n",[2113,44925,44926,44929,44931,44934],{"class":2115,"line":2241},[2113,44927,44928],{"class":2326},"include",[2113,44930,2423],{"class":2119},[2113,44932,44933],{"class":2326},"${QT_USE_FILE}",[2113,44935,4660],{"class":2119},[2113,44937,44938],{"class":2115,"line":2246},[2113,44939,44940],{"class":2396},"# Ajoute les définitions propres à Qt (suivant les modules ajoutés)\n",[2113,44942,44943,44945,44947,44950],{"class":2115,"line":2464},[2113,44944,44878],{"class":2326},[2113,44946,2423],{"class":2119},[2113,44948,44949],{"class":2326},"${QT_DEFINITIONS}",[2113,44951,4660],{"class":2119},[27533,44953,44955,44956],{"id":44954},"macros-pour-générer-les-fichiers-moc","Macros pour générer les fichiers ",[184,44957,44958],{},"moc",[34199,44960,44962],{"id":44961},"macro-par-défaut","Macro par défaut",[12,44964,44965,44966,44968,44969,44971,179],{},"La facilité de programmation en ",[362,44967,43910],{}," est due à l'ajout par le framework\nde la notion de méta-objet. Cette notion se fait à l'aide d'un\ngénérateur propre à ce framework : ",[184,44970,44958],{},[17400,44972,44973],{},[49,44974,3686],{"href":17404,"ariaDescribedBy":44975,"dataFootnoteRef":1646,"id":17407},[17406],[12,44977,44978,44979,44982,44983,44986,44987,179],{},"Pour générer les méta-objets, il faut définir sur les objets descendants\nde ",[184,44980,44981],{},"QObject",", une macro ",[184,44984,44985],{},"Q_OBJECT",". Cette macro ajoute, côté interface,\nla déclaration de méthodes supplémentaires pour la gestion des\nméta-informations, et coté implémentation, ajoute l'implémentation de\nces méta-informations ainsi que la déclaration des signaux et de slots\nde l'objet",[17400,44988,44989],{},[49,44990,2547],{"href":17977,"ariaDescribedBy":44991,"dataFootnoteRef":1646,"id":17979},[17406],[12,44993,44994,44995,44997],{},"Pour générer les fichiers d'implémentation générés par le compilateur\n",[184,44996,44958],{},", il est possible d'utiliser la macro suivante pour une liste de\nfichier sources (extension .cpp, ou .cxx, ...) :",[2105,44999,45001],{"className":42201,"code":45000,"language":17086,"meta":1646,"style":1646},"qt4_wrap_cpp(outfiles inputfile ... OPTIONS ...)\n",[184,45002,45003],{"__ignoreMap":1646},[2113,45004,45005],{"class":2115,"line":2116},[2113,45006,45000],{"class":2119},[12,45008,45009,45010,45013],{},"Cette macro générera un fichier de type ",[184,45011,45012],{},"moc_nomfichier.cxx"," qu'il\nfaudra inclure dans le ficher CPP (car il n'est pas inclus\nautomatiquement dans la liste des fichiers à compiler.",[2105,45015,45017],{"className":2387,"code":45016,"language":2389,"meta":1646,"style":1646},"#include \"moc_nomfichier.cxx\"\n",[184,45018,45019],{"__ignoreMap":1646},[2113,45020,45021,45023],{"class":2115,"line":2116},[2113,45022,42057],{"class":2326},[2113,45024,45025],{"class":2149}," \"moc_nomfichier.cxx\"\n",[34199,45027,45029],{"id":45028},"une-autre-macro","Une autre macro",[12,45031,45032,45033,44862,45036,45038,45039,45042,45043,45045],{},"Autre possibilité, faire comme avec les fichiers ",[184,45034,45035],{},".pro",[362,45037,44419],{}," et\ngénérer un fichier moc pour chaque fichier ",[184,45040,45041],{},".h"," possédant une macro\n",[184,45044,44985],{},". Pour cela j'ai écrit la petite macro suivante :",[2105,45047,45049],{"className":42201,"code":45048,"language":17086,"meta":1646,"style":1646},"macro(xinx_automoc outfiles)\nqt4_get_moc_flags(moc_flags)\nqt4_extract_options(moc_files moc_options ${ARGN})\nforeach (it ${moc_files})\n    get_filename_component(it ${it} ABSOLUTE)\n    if ( EXISTS ${it} )\n        file(READ ${it} _contents)\n        string(REGEX MATCHALL \"Q_OBJECT\" _match \"${_contents}\")\n        if(_match)\n            qt4_make_output_file(${it} moc_ cxx outfile)\n            qt4_create_moc_command(${it} ${outfile} \"${moc_flags}\" \"${moc_options}\")\n            macro_add_file_dependencies(${it} ${outfile})\n            set(${outfiles} ${${outfiles}} ${outfile})\n        endif(_match)\n    endif ( EXISTS ${it} )\nendforeach(it)endmacro(xinx_automoc)\n",[184,45050,45051,45059,45064,45074,45087,45103,45119,45132,45150,45157,45167,45185,45196,45218,45225,45238],{"__ignoreMap":1646},[2113,45052,45053,45056],{"class":2115,"line":2116},[2113,45054,45055],{"class":2326},"macro",[2113,45057,45058],{"class":2119},"(xinx_automoc outfiles)\n",[2113,45060,45061],{"class":2115,"line":1647},[2113,45062,45063],{"class":2119},"qt4_get_moc_flags(moc_flags)\n",[2113,45065,45066,45069,45072],{"class":2115,"line":1652},[2113,45067,45068],{"class":2119},"qt4_extract_options(moc_files moc_options ",[2113,45070,45071],{"class":2326},"${ARGN}",[2113,45073,4660],{"class":2119},[2113,45075,45076,45079,45082,45085],{"class":2115,"line":2185},[2113,45077,45078],{"class":2326},"foreach",[2113,45080,45081],{"class":2119}," (it ",[2113,45083,45084],{"class":2326},"${moc_files}",[2113,45086,4660],{"class":2119},[2113,45088,45089,45092,45095,45098,45101],{"class":2115,"line":2230},[2113,45090,45091],{"class":2326},"    get_filename_component",[2113,45093,45094],{"class":2119},"(it ",[2113,45096,45097],{"class":2326},"${it}",[2113,45099,45100],{"class":2326}," ABSOLUTE",[2113,45102,4660],{"class":2119},[2113,45104,45105,45107,45110,45113,45116],{"class":2115,"line":2235},[2113,45106,7394],{"class":2326},[2113,45108,45109],{"class":2119}," ( ",[2113,45111,45112],{"class":2326},"EXISTS",[2113,45114,45115],{"class":2326}," ${it}",[2113,45117,45118],{"class":2119}," )\n",[2113,45120,45121,45124,45127,45129],{"class":2115,"line":2241},[2113,45122,45123],{"class":2326},"        file",[2113,45125,45126],{"class":2119},"(READ ",[2113,45128,45097],{"class":2326},[2113,45130,45131],{"class":2119}," _contents)\n",[2113,45133,45134,45136,45139,45142,45145,45148],{"class":2115,"line":2246},[2113,45135,16380],{"class":2326},[2113,45137,45138],{"class":2119},"(REGEX MATCHALL ",[2113,45140,45141],{"class":2149},"\"Q_OBJECT\"",[2113,45143,45144],{"class":2119}," _match ",[2113,45146,45147],{"class":2149},"\"${_contents}\"",[2113,45149,4660],{"class":2119},[2113,45151,45152,45154],{"class":2115,"line":2464},[2113,45153,4518],{"class":2326},[2113,45155,45156],{"class":2119},"(_match)\n",[2113,45158,45159,45162,45164],{"class":2115,"line":2085},[2113,45160,45161],{"class":2119},"            qt4_make_output_file(",[2113,45163,45097],{"class":2326},[2113,45165,45166],{"class":2119}," moc_ cxx outfile)\n",[2113,45168,45169,45172,45174,45177,45180,45183],{"class":2115,"line":2514},[2113,45170,45171],{"class":2119},"            qt4_create_moc_command(",[2113,45173,45097],{"class":2326},[2113,45175,45176],{"class":2326}," ${outfile}",[2113,45178,45179],{"class":2149}," \"${moc_flags}\"",[2113,45181,45182],{"class":2149}," \"${moc_options}\"",[2113,45184,4660],{"class":2119},[2113,45186,45187,45190,45192,45194],{"class":2115,"line":2533},[2113,45188,45189],{"class":2119},"            macro_add_file_dependencies(",[2113,45191,45097],{"class":2326},[2113,45193,45176],{"class":2326},[2113,45195,4660],{"class":2119},[2113,45197,45198,45201,45203,45206,45209,45211,45213,45216],{"class":2115,"line":2556},[2113,45199,45200],{"class":2326},"            set",[2113,45202,2423],{"class":2119},[2113,45204,45205],{"class":2326},"${outfiles}",[2113,45207,45208],{"class":2119}," ${",[2113,45210,45205],{"class":2326},[2113,45212,4224],{"class":2119},[2113,45214,45215],{"class":2326},"${outfile}",[2113,45217,4660],{"class":2119},[2113,45219,45220,45223],{"class":2115,"line":2569},[2113,45221,45222],{"class":2326},"        endif",[2113,45224,45156],{"class":2119},[2113,45226,45227,45230,45232,45234,45236],{"class":2115,"line":2575},[2113,45228,45229],{"class":2326},"    endif",[2113,45231,45109],{"class":2119},[2113,45233,45112],{"class":2326},[2113,45235,45115],{"class":2326},[2113,45237,45118],{"class":2119},[2113,45239,45240,45243],{"class":2115,"line":2596},[2113,45241,45242],{"class":2326},"endforeach",[2113,45244,45245],{"class":2119},"(it)endmacro(xinx_automoc)\n",[12,45247,45248],{},"Elle s'utilise ainsi :",[2105,45250,45252],{"className":42201,"code":45251,"language":17086,"meta":1646,"style":1646},"xinx_automoc(moc_headers ${headers})\n",[184,45253,45254],{"__ignoreMap":1646},[2113,45255,45256,45259,45262],{"class":2115,"line":2116},[2113,45257,45258],{"class":2119},"xinx_automoc(moc_headers ",[2113,45260,45261],{"class":2326},"${headers}",[2113,45263,4660],{"class":2119},[27533,45265,45267],{"id":45266},"macro-pour-générer-les-fichiers-en-tête-dinterface-graphique","Macro pour générer les fichiers en tête d'interface graphique",[12,45269,45270,45271,45274],{},"Les interfaces graphiques sont développées à l'aide de ",[362,45272,45273],{},"Qt-Designer",".\nCes fichiers sont au format XML.",[12,45276,45277],{},"La macro CMake suivante permet de transformer ces fichiers XML en\nfichier d'entête C.",[2105,45279,45281],{"className":42201,"code":45280,"language":17086,"meta":1646,"style":1646},"qt4_wrap_ui(outfiles inputfile ...)\n",[184,45282,45283],{"__ignoreMap":1646},[2113,45284,45285],{"class":2115,"line":2116},[2113,45286,45280],{"class":2119},[27533,45288,45290],{"id":45289},"macro-pour-compiler-les-fichiers-de-resources","Macro pour compiler les fichiers de resources",[12,45292,45293,45294,45296],{},"La macro CMake suivante permet de compiler le fichier de ressource\n(extension *.qrc) en fichier ",[184,45295,2389],{}," qui sera ensuite compilé avec le\nreste du programme :",[2105,45298,45300],{"className":42201,"code":45299,"language":17086,"meta":1646,"style":1646},"qt4_add_resources(outfiles inputfile ... OPTIONS ...)\n",[184,45301,45302],{"__ignoreMap":1646},[2113,45303,45304],{"class":2115,"line":2116},[2113,45305,45299],{"class":2119},[27533,45307,45309],{"id":45308},"macro-pour-compiler-les-fichiers-de-traduction","Macro pour compiler les fichiers de traduction",[12,45311,45312,45313,45315,45316,45319],{},"La macro CMake suivante est utilisée pour compiler un fichier de\ntraduction ",[184,45314,15751],{}," en fichier de traduction compilé ",[184,45317,45318],{},"qm",". Ce dernier est\nensuite à inclure dans le programme (soit par un fichier de ressource,\nsoit dans un dossier à coté).",[2105,45321,45323],{"className":42201,"code":45322,"language":17086,"meta":1646,"style":1646},"qt4_add_translation(qm_files ts_files ...)\n",[184,45324,45325],{"__ignoreMap":1646},[2113,45326,45327],{"class":2115,"line":2116},[2113,45328,45322],{"class":2119},[27533,45330,45332],{"id":45331},"compilation-dun-plugin","Compilation d'un plugin",[12,45334,45335],{},"En plus des options décrites ci-dessus, pour compiler un plugin, il faut\najouter quelques définitions supplémentaires.",[12,45337,45338,45339,45342],{},"Il faut bien faire attention à ce que le plugin soit compilé en mode\n",[184,45340,45341],{},"Release"," si le programme l'est aussi (Ce qui n'est pas forcément\npratique pour faire du debug).",[34199,45344,45346],{"id":45345},"plugin-dynamique","Plugin dynamique",[2105,45348,45350],{"className":42201,"code":45349,"language":17086,"meta":1646,"style":1646},"add_definitions(-DQT_PLUGIN)\nadd_definitions(-DQT_SHARED)\nadd_library(webplugin SHARED ${webplugin_SRCS} ${webplugin_MOC_SRCS})\n",[184,45351,45352,45359,45366],{"__ignoreMap":1646},[2113,45353,45354,45356],{"class":2115,"line":2116},[2113,45355,44878],{"class":2326},[2113,45357,45358],{"class":2119},"(-DQT_PLUGIN)\n",[2113,45360,45361,45363],{"class":2115,"line":1647},[2113,45362,44878],{"class":2326},[2113,45364,45365],{"class":2119},"(-DQT_SHARED)\n",[2113,45367,45368,45371,45374,45377,45380],{"class":2115,"line":1652},[2113,45369,45370],{"class":2326},"add_library",[2113,45372,45373],{"class":2119},"(webplugin SHARED ",[2113,45375,45376],{"class":2326},"${webplugin_SRCS}",[2113,45378,45379],{"class":2326}," ${webplugin_MOC_SRCS}",[2113,45381,4660],{"class":2119},[34199,45383,45385],{"id":45384},"plugin-static","Plugin Static",[2105,45387,45389],{"className":42201,"code":45388,"language":17086,"meta":1646,"style":1646},"add_definitions(-DQT_PLUGIN)\nadd_definitions(-DQT_STATICPLUGIN)\nif(CMAKE_SIZEOF_VOID_P MATCHES 8)\n    add_definitions(-fPIC)\nendif()\nadd_library(webplugin STATIC ${webplugin_SRCS} ${webplugin_MOC_SRCS})\n",[184,45390,45391,45397,45404,45419,45426,45432],{"__ignoreMap":1646},[2113,45392,45393,45395],{"class":2115,"line":2116},[2113,45394,44878],{"class":2326},[2113,45396,45358],{"class":2119},[2113,45398,45399,45401],{"class":2115,"line":1647},[2113,45400,44878],{"class":2326},[2113,45402,45403],{"class":2119},"(-DQT_STATICPLUGIN)\n",[2113,45405,45406,45408,45410,45413,45416],{"class":2115,"line":1652},[2113,45407,4109],{"class":2326},[2113,45409,2423],{"class":2119},[2113,45411,45412],{"class":2330},"CMAKE_SIZEOF_VOID_P",[2113,45414,45415],{"class":2326}," MATCHES",[2113,45417,45418],{"class":2119}," 8)\n",[2113,45420,45421,45423],{"class":2115,"line":2185},[2113,45422,44908],{"class":2326},[2113,45424,45425],{"class":2119},"(-fPIC)\n",[2113,45427,45428,45430],{"class":2115,"line":2230},[2113,45429,44916],{"class":2326},[2113,45431,6745],{"class":2119},[2113,45433,45434,45436,45439,45441,45443],{"class":2115,"line":2235},[2113,45435,45370],{"class":2326},[2113,45437,45438],{"class":2119},"(webplugin STATIC ",[2113,45440,45376],{"class":2326},[2113,45442,45379],{"class":2326},[2113,45444,4660],{"class":2119},[27533,45446,45448],{"id":45447},"ajout-dun-fichier-rc-pour-windows","Ajout d'un fichier RC pour windows",[12,45450,45451],{},"Les fichiers RC Windows contiennent des informations, comme par exemple,\nla version et le nom du programme.",[12,45453,45454],{},"Ces informations peuvent être utilisées par les programmes\nd'installation sous Windows ou aussi par Windows lui-même.",[12,45456,45457],{},"Afin de pouvoir ajouter un fichier RC file au programme, on peut ajouter\nceci :",[2105,45459,45461],{"className":42201,"code":45460,"language":17086,"meta":1646,"style":1646},"if(MINGW)\n        set(CMAKE_RC_COMPILER_INIT windres)\n        enable_language(RC)\n        set(CMAKE_RC_COMPILE_OBJECT \"\u003CCMAKE_RC_COMPILER> \u003CFLAGS> \u003CDEFINES> -O coff -o \u003COBJECT> \u003CSOURCE>\")\nendif(MINGW)\n",[184,45462,45463,45470,45478,45486,45498],{"__ignoreMap":1646},[2113,45464,45465,45467],{"class":2115,"line":2116},[2113,45466,4109],{"class":2326},[2113,45468,45469],{"class":2119},"(MINGW)\n",[2113,45471,45472,45475],{"class":2115,"line":1647},[2113,45473,45474],{"class":2326},"        set",[2113,45476,45477],{"class":2119},"(CMAKE_RC_COMPILER_INIT windres)\n",[2113,45479,45480,45483],{"class":2115,"line":1652},[2113,45481,45482],{"class":2326},"        enable_language",[2113,45484,45485],{"class":2119},"(RC)\n",[2113,45487,45488,45490,45493,45496],{"class":2115,"line":2185},[2113,45489,45474],{"class":2326},[2113,45491,45492],{"class":2119},"(CMAKE_RC_COMPILE_OBJECT ",[2113,45494,45495],{"class":2149},"\"\u003CCMAKE_RC_COMPILER> \u003CFLAGS> \u003CDEFINES> -O coff -o \u003COBJECT> \u003CSOURCE>\"",[2113,45497,4660],{"class":2119},[2113,45499,45500,45502],{"class":2115,"line":2230},[2113,45501,44916],{"class":2326},[2113,45503,45469],{"class":2119},[12,45505,45506,45507,45509],{},"Ensuite le fichier RC se compile comme tout autre fichier (",[184,45508,1066],{},", ...)",[27533,45511,45513],{"id":45512},"compilation-dune-librairie-static","Compilation d'une librairie static",[2105,45515,45517],{"className":42201,"code":45516,"language":17086,"meta":1646,"style":1646},"add_definitions(-DQT_SHARED)\nadd_library(xinxplugins STATIC ${xinxplugins_SRCS} ${xinxplugins_MOC_SRCS})\n",[184,45518,45519,45525],{"__ignoreMap":1646},[2113,45520,45521,45523],{"class":2115,"line":2116},[2113,45522,44878],{"class":2326},[2113,45524,45365],{"class":2119},[2113,45526,45527,45529,45532,45535,45538],{"class":2115,"line":1647},[2113,45528,45370],{"class":2326},[2113,45530,45531],{"class":2119},"(xinxplugins STATIC ",[2113,45533,45534],{"class":2326},"${xinxplugins_SRCS}",[2113,45536,45537],{"class":2326}," ${xinxplugins_MOC_SRCS}",[2113,45539,4660],{"class":2119},[133,45541,45543],{"id":45542},"utiliser-le-programme-cmake","Utiliser le programme CMake",[12,45545,45546],{},"Une fois le programme configuré, il est possible de compiler ce dernier\nà l'aide des commandes suivantes :",[2105,45548,45550],{"className":2317,"code":45549,"language":2319,"meta":1646,"style":1646},"mkdir build\ncd build\ncmake ..\u002Fsources\nmake\nmake install\n",[184,45551,45552,45558,45564,45571,45576],{"__ignoreMap":1646},[2113,45553,45554,45556],{"class":2115,"line":2116},[2113,45555,17072],{"class":2133},[2113,45557,17075],{"class":2149},[2113,45559,45560,45562],{"class":2115,"line":1647},[2113,45561,17064],{"class":2334},[2113,45563,17075],{"class":2149},[2113,45565,45566,45568],{"class":2115,"line":1652},[2113,45567,17086],{"class":2133},[2113,45569,45570],{"class":2149}," ..\u002Fsources\n",[2113,45572,45573],{"class":2115,"line":2185},[2113,45574,45575],{"class":2133},"make\n",[2113,45577,45578,45580],{"class":2115,"line":2230},[2113,45579,17103],{"class":2133},[2113,45581,38944],{"class":2149},[12,45583,45584,45585,45589,45590,179],{},"Sous Windows, il est possible de compiler en utilisant ",[49,45586,41868],{"href":45587,"rel":45588},"http:\u002F\u002Fwww.mingw.org\u002F",[347]," pour\ncompiler ou d'utiliser le compilateur de ",[184,45591,45592],{},"Visual Studio",[12,45594,45595],{},"Il est également possible de définir un emplacement pour l'installation\ndifférent du dossier par défaut :",[2105,45597,45599],{"className":2317,"code":45598,"language":2319,"meta":1646,"style":1646},"cmake -DCMAKE_INSTALL_PREFIX=\u002Fhome\u002Flogin\u002Fusr\u002F\n",[184,45600,45601],{"__ignoreMap":1646},[2113,45602,45603,45605],{"class":2115,"line":2116},[2113,45604,17086],{"class":2133},[2113,45606,45607],{"class":2166}," -DCMAKE_INSTALL_PREFIX=\u002Fhome\u002Flogin\u002Fusr\u002F\n",[23219,45609,45611,45614],{"className":45610,"dataFootnotes":1646},[23222],[128,45612,23227],{"className":45613,"id":17406},[23226],[367,45615,45616,45625],{},[370,45617,45618,45619,45621,45622],{"id":23232},"C'est ce qui fait tout l'avantage de ",[362,45620,43910],{}," mais est aussi son inconvénient car il ajoute une couche supplémentaire. C'est ensuite une question de goût. ",[49,45623,23240],{"href":23236,"ariaLabel":23237,"className":45624,"dataFootnoteBackref":1646},[23239],[370,45626,45627,45628,45630,45631],{"id":23243},"facilitant la gestion des évènements dans ",[362,45629,43910],{}," ",[49,45632,23240],{"href":23251,"ariaLabel":23252,"className":45633,"dataFootnoteBackref":1646},[23239],[3358,45635,45636],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}",{"title":1646,"searchDepth":1647,"depth":1647,"links":45638},[45639,45640,45641],{"id":44477,"depth":1652,"text":44478},{"id":45542,"depth":1652,"text":45543},{"id":17406,"depth":1647,"text":23227},"2011-06-26","Suite à un billet datant de 2008, je reviens vers vous pour ajouter\nquelques précisions sur la compilation de programme Qt avec\nCMake. En effet, pour mon programme XINX, j'ai modifié la chaîne\nde compilation actuelle utilisant QMake par une chaîne de compilation\nCMake.",{"type":9,"value":45645},[45646,45663,45676,45682,45686],[12,45647,44394,45648,44399,45650,44404,45653,44410,45656,44416,45659,44420,45661,179],{},[49,45649,44398],{"href":44397},[49,45651,43910],{"href":44402,"rel":45652},[347],[49,45654,44409],{"href":44407,"rel":45655},[347],[49,45657,44415],{"href":44413,"rel":45658},[347],[362,45660,44419],{},[362,45662,44409],{},[12,45664,45665,44427,45667,44431,45669,43549,45672,44439,45674,44442],{},[362,45666,44409],{},[184,45668,44430],{},[49,45670,44436],{"href":44434,"rel":45671},[347],[362,45673,44409],{},[184,45675,17103],{},[12,45677,45678,44447,45680,179],{},[362,45679,44409],{},[362,45681,44450],{},[12,45683,44453,45684,44457],{},[184,45685,44456],{},[506,45687,45688],{},[370,45689,45690,45692],{},[184,45691,44456],{},[506,45693,45694,45698],{},[370,45695,45696],{},[184,45697,44470],{},[370,45699,45700],{},[184,45701,32993],{},{},"\u002Fpost\u002Fqt-cmake-et-qt",{"title":44389,"description":45643},"qt-cmake-et-qt","posts\u002FProgrammation\u002F2011-06-26-qt-cmake-et-qt",[38761],"z241vjvDaj7TX5NjylClHpgb9g7dVly4_9NxleMnRPU",{"id":45710,"title":45711,"author":7,"body":45712,"category":44415,"categorySlug":45988,"date":45989,"description":14,"excerpt":45990,"extension":1764,"location":1765,"meta":46013,"navigation":1767,"path":46014,"published":1767,"seo":46015,"slug":46016,"stem":46017,"tags":46018,"timeToRead":2235,"__hash__":46021},"posts\u002Fposts\u002FXINX\u002F2011-05-03-xinx-0.10.1.md","XINX v0.10.1",{"type":9,"value":45713,"toc":45981},[45714,45716,45724,45740,45747,45751,45773,45777,45781,45787,45792,45803,45806,45809,45813,45816,45819,45836,45841,45844,45847,45851,45857,45860,45866,45869,45873,45879,45882,45885,45888,45896,45899,45903,45907,45916,45919,45923,45929,45932,45936,45941,45944,45948,45954,45957,45961,45964,45968,45971,45975,45978],[12,45715,14],{},[12,45717,45718,45719,45723],{},"Je suis heureux de vous annoncer la nouvelle version de ",[49,45720,44415],{"href":45721,"rel":45722},"http:\u002F\u002Fxinx.shadoware.org?piwik_campaign=20110504_0.10.1_blog",[347],". Cette\nversion apporte, en plus de la ré-écriture complète de la gestion du\ncontenu et de la complétion, son petit lot de nouveautés et de\ncorrections.",[12,45725,218,45726,45729,45730,45733,45734,45739],{},[362,45727,45728],{},"0.10.1"," est beaucoup plus stable que sa grande soeur ",[362,45731,45732],{},"0.9",".\nUn soin particulier a été apporté à la recherche des sources d'anomalie\npossible, ainsi que des fuites mémoires. Ces opérations ont été faites\ngrâce à des outils tels que ",[49,45735,45738],{"href":45736,"rel":45737},"http:\u002F\u002Fvalgrind.org\u002F",[347],"valgrind",", mais aussi grâce aux\nbéta-testeurs (Merci à eux).",[12,45741,45742,45743,45746],{},"De plus l'utilisation des pointeurs intelligents de ",[49,45744,43910],{"href":44402,"rel":45745},[347]," ont permis de\npasser plus de temps, sur la gestion des structures et moins de temps\nsur la gestion de la mémoire, sur des parties du code assez critiques\n(comme ContentView et CodeCompletion). Ils ont également permis un\npartage plus facile des structures entre les différents threads avec\nmoins de contrôle de synchronisation.",[133,45748,45750],{"id":45749},"liens","Liens",[506,45752,45753,45759,45766],{},[370,45754,45755],{},[49,45756,45758],{"href":45721,"rel":45757},[347],"Site Internet de XINX",[370,45760,45761],{},[49,45762,45765],{"href":45763,"rel":45764},"http:\u002F\u002Fxinx.shadoware.org\u002Fwiki\u002FDownload?piwik_campaign=20110504_0.10.1_blog",[347],"Téléchargement de XINX",[370,45767,45768],{},[49,45769,45772],{"href":45770,"rel":45771},"http:\u002F\u002Fxinx.shadoware.org\u002Fnewticket?piwik_campaign=20110504_0.10.1_blog",[347],"Faire une demande d'évolution ou reporter un bug",[133,45774,45776],{"id":45775},"gestion-projet-et-gestion-des-sessions","Gestion Projet et Gestion des sessions",[27533,45778,45780],{"id":45779},"multi-projet","Multi-projet",[12,45782,45783],{},[121,45784],{"alt":45785,"src":45786},"Project","\u002FXINX\u002Fxinx-0.10.1\u002FprojectDirectory10.png",[12,45788,45789,45791],{},[155,45790,44415],{}," est maintenant capable d'ouvrir plusieurs\nprojets en même temps. Cela permet donc plusieurs choses :",[506,45793,45794,45797,45800],{},[370,45795,45796],{},"Rechercher un fichier pouvant se trouver dans plusieurs projets\ndifférents (par exemple un projet trunk et un projet branche).",[370,45798,45799],{},"Comparer des fichiers venant de plusieurs projets différents.",[370,45801,45802],{},"Ne pas basculer d'un projet à l'autre en continu, lorsque l'on\ntravaille sur plusieurs projets en même temps.",[12,45804,45805],{},"A cause de cette gestion de plusieurs projets en même temps, toute la\npartie de gestion de projet de XINX a été ré-écrite (entre autre la\npartie concernant la session, que l'on verra ensuite).",[12,45807,45808],{},"De plus à l'ouverture du projet, l'application recherche l'ensemble des\nfichiers présents sur le disque afin de permettre une recherche beaucoup\nplus rapide. Cela a néanmoins quelques répercussions : il n'est pas\npossible de supprimer un dossier du projet sans fermer XINX.",[27533,45810,45812],{"id":45811},"inclusion-exclusion-de-chemin","Inclusion \u002F Exclusion de chemin",[12,45814,45815],{},"Afin d'avoir une vue propre, XINX affiche déjà uniquement les fichiers\nqu'il est capable de gérer. Il est également possible de lui définir une\nliste de chemin qu'il doit ignorer.",[12,45817,45818],{},"Exemple :",[506,45820,45821,45826,45831],{},[370,45822,45823],{},[184,45824,45825],{},"WEB-INF\u002Flib",[370,45827,45828],{},[184,45829,45830],{},"dico",[370,45832,45833],{},[184,45834,45835],{},"configuration",[12,45837,45838],{},[121,45839],{"alt":45785,"src":45840},"\u002FXINX\u002Fxinx-0.10.1\u002FprojectpropertyIncludeExclude10.png",[12,45842,45843],{},"Il est possible d'utiliser des expressions régulières pour définir ces\nchemins.",[12,45845,45846],{},"A l'inverse il est possible d'inclure un dossier externe au projet\ndirectement dans la vue de celui-ci. L'équivalent d'un lien symbolique\nmais propre au projet sous XINX. Il est alors possible d'inclure dans le\ndossier projet, un répertoire temporaire contenant les données utilisées\npar les feuilles de styles, ou inclure un dossier contenant des fichiers\nde logs, ...",[27533,45848,45850],{"id":45849},"sessions","Sessions",[12,45852,45853],{},[121,45854],{"alt":45855,"src":45856},"Welcom","\u002FXINX\u002Fxinx-0.10.1\u002FwelcomDialog10.png",[12,45858,45859],{},"Suite à la gestion du multi-projet, il a fallu ré-écrire\nle système de gestion des sessions. Il est maintenant plus proche des\nsystèmes de session des autres applications.",[12,45861,45862,45863,45865],{},"Alors que sur la version ",[362,45864,45732],{}," et précédente, la session était\ndirectement associée à un projet (et donc ouverte en même temps),\nmaintenant la session est séparée de la notion de projet.",[12,45867,45868],{},"Une session peut alors désormais contenir une liste de projet ouvert,\nune liste de fichier ouvert (associé ou non à un projet), des signets.\nPlusieurs sessions différentes peuvent pointer vers un même projet mais\nproposer une vue (un ensemble de fichier ouvert) différente. Une session\npar défaut est créée pour l'utilisation courante de l'application.",[133,45870,45872],{"id":45871},"modification-du-système-de-complétion","Modification du système de complétion",[12,45874,45875],{},[121,45876],{"alt":45877,"src":45878},"Code","\u002FXINX\u002Fxinx-0.10.1\u002FCompletion10.png",[12,45880,45881],{},"Le système de vue contenue et de code complétion a\ncomplètement été ré-écrit. La lecture des différents imports des\ndifférents fichiers est faite en mémoire (et non plus dans une base de\ndonnée SQL) et en parallèle (s'il y a plusieurs processeurs sur la\nmachine). La lecture des fichiers est ainsi plus rapide. Le système a\ncomplètement été ré-écrit pour que la complétion du code soit rapide et\nstable en même temps.",[12,45883,45884],{},"La complétion du code affiche désormais les différentes lignes dans des\ncatégories suivant leur emplacement (par exemple le fichier où se trouve\nchaque template), les snipets dans une catégorie différente...",[12,45886,45887],{},"XINX est maintenant capable également de compléter",[506,45889,45890,45893],{},[370,45891,45892],{},"les paramètres des templates du type call-template",[370,45894,45895],{},"les imports (proposition des différents fichiers, et des alias si le\nplugin Generix est activé)",[12,45897,45898],{},"et plein d'autres choses :)",[133,45900,45902],{"id":45901},"et-plein-dautres-nouveautés","Et plein d'autres nouveautés",[27533,45904,45906],{"id":45905},"utilisation-de-la-librairie-svn-directement","Utilisation de la librairie SVN directement",[12,45908,45909,45910,45915],{},"Un nouveau plugin pour SubVersion a été écrit. Ce plugin utilise\ndirectement les librairies de SubVersion au lieu d'utiliser l'exécutable\nsvn. Ce plugin est plus performant que le wrapper et ne nécessite pas\nl'installation d'un client SubVersion (tel que ",[49,45911,45914],{"href":45912,"rel":45913},"http:\u002F\u002Fwww.collab.net\u002Fdownloads\u002Fsubversion\u002F",[347],"CollabNet SubVersion","\nsous Windows ou le client svn sous linux.",[12,45917,45918],{},"L'ancien plugin est toujours disponible et utilisable.",[27533,45920,45922],{"id":45921},"créer-des-tickets-depuis-xinx","Créer des tickets depuis XINX",[12,45924,45925],{},[121,45926],{"alt":45927,"src":45928},"Create","\u002FXINX\u002Fxinx-0.10.1\u002FTicketCreation10.png",[12,45930,45931],{},"Une boite de dialogue permet maintenant de créer automatiquement un\ncompte et un ticket sur le site Internet de XINX. Vous pouvez ainsi\ncréer facilement des demandes d'évolution, des demandes de correction,\n...De plus lors de la sortie d'une nouvelle version, XINX vous informera\nautomatiquement qu'une nouvelle version est disponible.",[27533,45933,45935],{"id":45934},"dock-des-logs-et-des-erreurs","Dock des logs et des erreurs",[12,45937,45938],{},[121,45939],{"alt":6305,"src":45940},"\u002FXINX\u002Fxinx-0.10.1\u002FErrorLog10.png",[12,45942,45943],{},"Le dock d'affichage des erreurs a été amélioré pour ne plus perturber\nl'utilisateur en s'ouvrant de façon intempestive. Il est maintenant\npossible de définir que le dock doit rester fermé, un compteur affiche\nalors le nombre d'erreur .",[27533,45945,45947],{"id":45946},"modification-de-la-recherche","Modification de la recherche",[12,45949,45950],{},[121,45951],{"alt":45952,"src":45953},"Search","\u002FXINX\u002Fxinx-0.10.1\u002FSearchDock10.png",[12,45955,45956],{},"Suite à la gestion du multi-projet, XINX peut maintenant rechercher dans\nplusieurs projets en même temps. Il est aussi possible d'arrêter la\nrecherche en plein milieu.",[27533,45958,45960],{"id":45959},"validation-dun-fichier-xml","Validation d'un fichier XML",[12,45962,45963],{},"XINX peut également valider un fichier XML à partir d'un schéma. Il ne\npeut malheureusement pas encore valider de fichier XSL (les parseurs XSL\ntrouvés n'arrivent pas à lire les schémas des fichiers XSLT).",[27533,45965,45967],{"id":45966},"correction-de-bugs","Correction de bugs",[12,45969,45970],{},"Beaucoup de corrections de bug ont été faites ce qui rend cette version\ntrès stable.",[133,45972,45974],{"id":45973},"et-pour-la-suite","Et pour la suite ?",[12,45976,45977],{},"Dans la même série, j'ai l'intention de sortir une version avec de\nnouveaux modèles, de la documentation, ....",[12,45979,45980],{},"Pour la version suivante, le moteur JavaScript sera amélioré pour ne\nplus donner d'erreur, voir proposer de la complétion plus performante.",{"title":1646,"searchDepth":1647,"depth":1647,"links":45982},[45983,45984,45985,45986,45987],{"id":45749,"depth":1652,"text":45750},{"id":45775,"depth":1652,"text":45776},{"id":45871,"depth":1652,"text":45872},{"id":45901,"depth":1652,"text":45902},{"id":45973,"depth":1652,"text":45974},"xinx","2011-05-03",{"type":9,"value":45991},[45992,45994,45999,46008],[12,45993,14],{},[12,45995,45718,45996,45723],{},[49,45997,44415],{"href":45721,"rel":45998},[347],[12,46000,218,46001,45729,46003,45733,46005,45739],{},[362,46002,45728],{},[362,46004,45732],{},[49,46006,45738],{"href":45736,"rel":46007},[347],[12,46009,45742,46010,45746],{},[49,46011,43910],{"href":44402,"rel":46012},[347],{},"\u002Fpost\u002Fxinx-0.10.1",{"title":45711,"description":14},"xinx-0.10.1","posts\u002FXINX\u002F2011-05-03-xinx-0.10.1",[45988,46019,38761,46020],"generix-group","xml","wLQ73NHdvyayFYtxw7KT53sos1O0VxBKcuvImmDWUEQ",{"id":46023,"title":46024,"author":7,"body":46025,"category":2057,"categorySlug":2058,"date":49054,"description":1646,"excerpt":49055,"extension":1764,"location":1765,"meta":49128,"navigation":1767,"path":49129,"published":1767,"seo":49130,"slug":49131,"stem":49132,"tags":49133,"timeToRead":3236,"__hash__":49135},"posts\u002Fposts\u002FProgrammation\u002F2011-01-25-qt-performance-de-l-utilisation-de-qsharedpointer.md","C++\u002FQt - Performance de l'utilisation de QSharedPointer",{"type":9,"value":46026,"toc":49047},[46027,46029,46038,46058,46067,46128,46132,46136,46140,46153,46164,46170,46174,46195,46219,46315,46322,46332,46337,46380,46386,46409,46419,46433,46463,46481,46536,46549,46555,46564,46629,46639,46645,46691,46713,46719,46853,46869,46877,46894,46897,47043,47066,47073,47146,47150,47153,47156,47439,47442,47717,47720,47726,47729,47732,47855,47877,47881,47891,47908,47921,47925,47934,47942,48086,48101,48119,48210,48213,48216,48245,48247,48253,48267,48270,48274,48281,48284,48298,48301,48320,48323,48337,48341,48345,48354,48357,48366,48490,48553,48557,48560,48582,48586,48592,48656,48660,48666,48672,48713,48716,48773,48777,48780,48884,48890,48893,48896,48900,48906,48923,49044],[133,46028,43625],{"id":43624},[12,46030,46031,46033,46034,46037],{},[362,46032,43910],{}," est un framework orienté objet écrit en C++ et permettant de faire\ndes interfaces graphiques. Ce framework est utilisé par le projet ",[362,46035,46036],{},"KDE","\ndepuis ses débuts pour en faire un environnement de bureau très complet.",[12,46039,46040,46042,46043,46046,46051,46052,46054,46055,46057],{},[362,46041,43910],{}," fournit un ensemble de pointeur ",[362,46044,46045],{},"intelligent",[17400,46047,46048],{},[49,46049,3686],{"href":17404,"ariaDescribedBy":46050,"dataFootnoteRef":1646,"id":17407},[17406]," permettant\nde gérer plus facilement la mémoire. Le but est alors de ne plus avoir à\nsupprimer des objets. La suppression se fera soit par un pointeur\n",[362,46053,46045],{}," soit par le système de hiérarchie d'objet existant en ",[362,46056,43910],{},"\n(l'objet père qui supprime l'ensemble des objets fils qui lui sont\nrattachés).",[12,46059,46060,46062,46063,46066],{},[362,46061,43910],{}," propose l'ensemble des pointeurs ",[362,46064,46065],{},"intelligents"," suivants:",[506,46068,46069,46092,46106,46114],{},[370,46070,46071,46076,46077,46082,46083,46086,46087,179],{},[49,46072,46075],{"href":46073,"rel":46074},"http:\u002F\u002Fdoc.qt.nokia.com\u002F4.6\u002Fqshareddatapointer.html",[347],"QSharedDataPointer"," \u002F ",[49,46078,46081],{"href":46079,"rel":46080},"http:\u002F\u002Fdoc.qt.nokia.com\u002F4.6\u002Fqshareddata.html",[347],"QSharedData"," : ces deux classes\nutilisées ensemble permettent d'écrire un objet avec partage\nimplicite. Cela signifie que l'objet fonctionnera comme la classe\n",[362,46084,46085],{},"QString",". Tant que l'objet est copié, passé en paramètre, ....\nl'objet n'est pas dupliqué (tous les objets pointes vers le même\nespace mémoire). Au moment où l'objet est modifié, l'objet est\ndupliqué. C'est ce qu'on appelle le COW",[17400,46088,46089],{},[49,46090,2547],{"href":17977,"ariaDescribedBy":46091,"dataFootnoteRef":1646,"id":17979},[17406],[370,46093,46094,46076,46099,46102,46103,46105],{},[49,46095,46098],{"href":46096,"rel":46097},"http:\u002F\u002Fdoc.qt.nokia.com\u002F4.6\u002Fqexplicitlyshareddatapointer.html",[347],"QExplictlySharedDataPointer",[49,46100,46081],{"href":46079,"rel":46101},[347]," :\nQExplicitlySharedDataPointer est une variante de QSharedDataPointer.\nCe pointeur ",[362,46104,46045],{},", comme son nom l'indique, est détaché\nuniquement lorsque la méthode detach() est appelée explicitement.\nCette classe permet de faire des objets qui fonctionnent comme des\npointeurs mais qui sont utilisés sans la notion de pointeur (le *).\nLa suppression des données partagées se fait donc quand tous les\nobjets ne sont plus utilisés.",[370,46107,46108,46113],{},[49,46109,46112],{"href":46110,"rel":46111},"http:\u002F\u002Fdoc.qt.nokia.com\u002F4.6\u002Fqscopedpointer.html",[347],"QScopedPointer"," : Ce pointeur est le plus simple. Il permet de\ndéclarer un pointeur sur le tas et s'occupe de la destruction de\nl'objet, lorsque le programme sort de la portée du bloc. Cela permet\nde ne plus se soucier de la libération du pointeur dans les cas\nd'erreur (exception, retour avant la fin de la fonction car le\nfichier n'a pu être ouvert, ...).",[370,46115,46116,46121,46122,46127],{},[49,46117,46120],{"href":46118,"rel":46119},"http:\u002F\u002Fdoc.qt.nokia.com\u002F4.6\u002Fqsharedpointer.html",[347],"QSharedPointer"," : Le pointeur dont on parlera dans la suite de ce\nbillet. Il permet de partager non plus des données (comme le fait\nQSharedData) mais de partager un pointeur",[17400,46123,46124],{},[49,46125,3696],{"href":41827,"ariaDescribedBy":46126,"dataFootnoteRef":1646,"id":41829},[17406],". Nous allons voir\ndans la suite du billet, comment simplement utiliser ce pointeur, et\nles performances de ce pointeur par rapport à un pointeur standard.",[133,46129,46131],{"id":46130},"sommaire","Sommaire",[133,46133,46135],{"id":46134},"utilisation-de-qsharedpointer","Utilisation de QSharedPointer",[27533,46137,46139],{"id":46138},"a-quoi-sert-il","A quoi sert-il ?",[12,46141,46142,46143,46145,46146,46148,46149,46152],{},"L'objet ",[184,46144,46120],{}," fait partie des pointeurs ",[362,46147,46065],{},". Ces\npointeurs permettent de gérer automatiquement la libération de la\nmémoire (plus besoin de faire ",[184,46150,46151],{},"delete ptr;"," quand le pointeur n'est plus\nutilisé) tout en restant utilisable comme un pointeur normal.",[12,46154,46155,46157,46158,46160,46161,46163],{},[184,46156,46120],{}," fonctionne par comptage de référence. Après la\ndéclaration, à chaque affectation, on augmente le compteur de référence,\nlorsqu'on quitte la portée du bloc, on décrémente le compteur de\nréférence. ",[184,46159,46120],{}," détruit donc automatiquement le pointeur\nquand il n'existe plus aucune référence vers ce pointeur.\n",[184,46162,46120],{}," vient donc comme une encapsulation de notre pointeur.",[12,46165,46166],{},[121,46167],{"alt":46168,"src":46169},"QSharedPointer vers la même adresse","\u002FProgrammation\u002Fqt-performance-de-l-utilisation-de-qsharedpointer\u002FQSharedPointer1.png",[27533,46171,46173],{"id":46172},"comment-lutiliser","Comment l'utiliser ?",[12,46175,46176,46177,46180,46181,46184,46185,46188,46189,46191,46192,46194],{},"La déclaration d'un pointeur en C, se fait en écrivant ",[184,46178,46179],{},"MyObject*",". La\nsyntaxe en utilisant un QSharedPointer se fait en écrivant\n",[184,46182,46183],{},"QSharedPointer\u003CMyObject>",". Par la suite dans le programme,\nl'utilisation du pointeur ",[184,46186,46187],{},"QSharedObject"," se fera de la même manière\nqu'un pointeur C. (Avec l'opérateur ",[184,46190,4115],{}," pour appeler un membre, une\nméthode, ...) . Appelons dans la suite pointeur C, les pointeurs\nstandards et ",[184,46193,46120],{},", le pointeur intelligent.",[12,46196,46197,46198,46200,46201,46203,46204,46206,46207,46209,46210,46213,46214,179],{},"Afin d'éviter d'avoir un pointeur normal pouvant être supprimé à tout\nmoment dans l'application, lors de l'utilisation de ",[184,46199,46120],{},", il\nne faut utiliser le pointeur C résultant du ",[184,46202,3961],{}," que pour la création\ndu ",[184,46205,46120],{},". On peut donc directement créer le ",[184,46208,46120],{},"\nen utilisant le constructeur ",[184,46211,46212],{},"QSharedPointer ( T * ptr )"," qui prend en\nparamètre le pointeur C. C'est entre ces parenthèses que nous allons\ncréer le pointeur C",[17400,46215,46216],{},[49,46217,706],{"href":41857,"ariaDescribedBy":46218,"dataFootnoteRef":1646,"id":41859},[17406],[2105,46220,46222],{"className":2387,"code":46221,"language":2389,"meta":1646,"style":1646},"{\n    \u002F\u002F Création du pointeur intelligent à partir d'un pointeur normal.\n    QSharedPointer\u003CMyObject> ptr(new MyObject());\n\n    \u002F\u002F Utilisation du pointeur intelligent comme un pointeur normal.\n    if (ptr)\n    {\n        ptr->setMembre(maValeur);\n    }\n\n    \u002F\u002F Appel d'une méthode utilisant ce pointeur\n    maMethode(ptr);\n}\n",[184,46223,46224,46228,46233,46257,46261,46266,46273,46277,46290,46294,46298,46303,46311],{"__ignoreMap":1646},[2113,46225,46226],{"class":2115,"line":2116},[2113,46227,18060],{"class":2119},[2113,46229,46230],{"class":2115,"line":1647},[2113,46231,46232],{"class":2396},"    \u002F\u002F Création du pointeur intelligent à partir d'un pointeur normal.\n",[2113,46234,46235,46238,46240,46243,46245,46248,46250,46252,46255],{"class":2115,"line":1652},[2113,46236,46237],{"class":2119},"    QSharedPointer",[2113,46239,3109],{"class":2326},[2113,46241,46242],{"class":2119},"MyObject",[2113,46244,29415],{"class":2326},[2113,46246,46247],{"class":2133}," ptr",[2113,46249,2423],{"class":2119},[2113,46251,3961],{"class":2326},[2113,46253,46254],{"class":2133}," MyObject",[2113,46256,2593],{"class":2119},[2113,46258,46259],{"class":2115,"line":2185},[2113,46260,2125],{"emptyLinePlaceholder":1767},[2113,46262,46263],{"class":2115,"line":2230},[2113,46264,46265],{"class":2396},"    \u002F\u002F Utilisation du pointeur intelligent comme un pointeur normal.\n",[2113,46267,46268,46270],{"class":2115,"line":2235},[2113,46269,7394],{"class":2326},[2113,46271,46272],{"class":2119}," (ptr)\n",[2113,46274,46275],{"class":2115,"line":2241},[2113,46276,18224],{"class":2119},[2113,46278,46279,46282,46284,46287],{"class":2115,"line":2246},[2113,46280,46281],{"class":2414},"        ptr",[2113,46283,4115],{"class":2119},[2113,46285,46286],{"class":2133},"setMembre",[2113,46288,46289],{"class":2119},"(maValeur);\n",[2113,46291,46292],{"class":2115,"line":2464},[2113,46293,4665],{"class":2119},[2113,46295,46296],{"class":2115,"line":2085},[2113,46297,2125],{"emptyLinePlaceholder":1767},[2113,46299,46300],{"class":2115,"line":2514},[2113,46301,46302],{"class":2396},"    \u002F\u002F Appel d'une méthode utilisant ce pointeur\n",[2113,46304,46305,46308],{"class":2115,"line":2533},[2113,46306,46307],{"class":2133},"    maMethode",[2113,46309,46310],{"class":2119},"(ptr);\n",[2113,46312,46313],{"class":2115,"line":2556},[2113,46314,2599],{"class":2119},[12,46316,46317,46318,46321],{},"Lorsque l'on quitte le bloc, si le comptage de référence tombe à 0, on\nsupprime le pointeur. A l'intérieur de ",[184,46319,46320],{},"maMethode()"," le nombre de\nréférence sera passé à 2. Si la méthode utilise le pointeur mais ne\nl'assigne nul part, le nombre de référence devrait être retombé à 1 et\ndonc ici sera décrémenté à 0.",[12,46323,46324,46325,46327,46328,46331],{},"Si par contre, ",[184,46326,46320],{}," fait des opérations d'assignation de ",[184,46329,46330],{},"ptr","\net conserve une copie, le comptage ne tombera pas à 0 tant que l'objet\nrestera utilisé (assigné) ailleurs.",[12,46333,46334,46335,44506],{},"Regardons un exemple de ",[184,46336,46320],{},[2105,46338,46340],{"className":2387,"code":46339,"language":2389,"meta":1646,"style":1646},"void maMethode(QSharedPointer\u003CMyObject> ptr)\n{\n    ptr->setMembre2(maValeur);\n",[184,46341,46342,46364,46368],{"__ignoreMap":1646},[2113,46343,46344,46347,46350,46352,46354,46356,46358,46360,46362],{"class":2115,"line":2116},[2113,46345,46346],{"class":2326},"void",[2113,46348,46349],{"class":2133}," maMethode",[2113,46351,2423],{"class":2119},[2113,46353,46120],{"class":2414},[2113,46355,3109],{"class":2119},[2113,46357,46242],{"class":2414},[2113,46359,4435],{"class":2119},[2113,46361,46330],{"class":2429},[2113,46363,4660],{"class":2119},[2113,46365,46366],{"class":2115,"line":1647},[2113,46367,18060],{"class":2119},[2113,46369,46370,46373,46375,46378],{"class":2115,"line":1652},[2113,46371,46372],{"class":2414},"    ptr",[2113,46374,4115],{"class":2119},[2113,46376,46377],{"class":2133},"setMembre2",[2113,46379,46289],{"class":2119},[12,46381,46382,46383,46385],{},"Au début du bloc, ici le comptage de référence est à 2 et sera\ndécrémenté à la sortie de la méthode. On peut modifier les membres de\n",[184,46384,46330],{},", et dans ce cas pas de changement du comptage de référence.",[2105,46387,46389],{"className":2387,"code":46388,"language":2389,"meta":1646,"style":1646},"    this->monPtr = ptr;\n}\n",[184,46390,46391,46405],{"__ignoreMap":1646},[2113,46392,46393,46395,46397,46400,46402],{"class":2115,"line":2116},[2113,46394,20377],{"class":2414},[2113,46396,4115],{"class":2119},[2113,46398,46399],{"class":2330},"monPtr",[2113,46401,2153],{"class":2326},[2113,46403,46404],{"class":2119}," ptr;\n",[2113,46406,46407],{"class":2115,"line":1647},[2113,46408,2599],{"class":2119},[12,46410,46411,46412,46415,46416,46418],{},"Au contraire, on peut également l'assigner à un autre objet. Dans ce cas\nle comptage de référence de cet objet passera à 3. A la sortie de la\nméthode il sera décrémenté et passera alors à 2. L'objet ne sera pas\nsupprimé tant qu'on ne fera pas un ",[184,46413,46414],{},"this->monPtr.clear()"," ou que ",[184,46417,15880],{},"\nne sera pas détruit.",[12,46420,46421,46422,46424,46425,46428,46429,46432],{},"Si on veut garder une référence d'un pointeur mais qu'on ne souhaite pas\nque celle-ci incrémente le nombre de référence du ",[184,46423,46120],{},", il\nest possible de créer un pointeur ",[362,46426,46427],{},"faible",". Ce pointeur passe par\nl'objet ",[184,46430,46431],{},"QWeakPointer",". Pour obtenir ce type de pointeur, il suffit de\nfaire :",[2105,46434,46436],{"className":2387,"code":46435,"language":2389,"meta":1646,"style":1646},"QWeakPointer\u003CMyObject> ptrW = ptr->toWeakRef ();\n",[184,46437,46438],{"__ignoreMap":1646},[2113,46439,46440,46442,46444,46446,46448,46451,46453,46455,46457,46460],{"class":2115,"line":2116},[2113,46441,46431],{"class":2119},[2113,46443,3109],{"class":2326},[2113,46445,46242],{"class":2119},[2113,46447,29415],{"class":2326},[2113,46449,46450],{"class":2119}," ptrW ",[2113,46452,2335],{"class":2326},[2113,46454,46247],{"class":2414},[2113,46456,4115],{"class":2119},[2113,46458,46459],{"class":2133},"toWeakRef",[2113,46461,46462],{"class":2119}," ();\n",[12,46464,46465,46468,46469,46471,46472,46475,46476,46478,46479,179],{},[184,46466,46467],{},"ptrW"," n'incrémente donc pas le comptage de référence, cela signifie\ndonc que le pointeur peut être détruit même si un objet ",[184,46470,46431],{},"\nexiste. Il sera alors possible de faire un ",[184,46473,46474],{},"ptrW.isNull()"," pour savoir\nsi le pointeur est toujours valide. Si l'utilisateur a également besoin\nd'avoir accès à un membre de l'objet, il pourra le transformer en\n",[184,46477,46120],{}," avant de l'utiliser sauf si le pointeur est ",[184,46480,18569],{},[2105,46482,46484],{"className":2387,"code":46483,"language":2389,"meta":1646,"style":1646},"QSharedPointer\u003CMyObject> ptr2 = ptrW->toStrongRef ();\nif (ptr)\n{\n    ptr->maMethodePtr();\n}\n",[184,46485,46486,46511,46517,46521,46532],{"__ignoreMap":1646},[2113,46487,46488,46490,46492,46494,46496,46499,46501,46504,46506,46509],{"class":2115,"line":2116},[2113,46489,46120],{"class":2119},[2113,46491,3109],{"class":2326},[2113,46493,46242],{"class":2119},[2113,46495,29415],{"class":2326},[2113,46497,46498],{"class":2119}," ptr2 ",[2113,46500,2335],{"class":2326},[2113,46502,46503],{"class":2414}," ptrW",[2113,46505,4115],{"class":2119},[2113,46507,46508],{"class":2133},"toStrongRef",[2113,46510,46462],{"class":2119},[2113,46512,46513,46515],{"class":2115,"line":1647},[2113,46514,4109],{"class":2326},[2113,46516,46272],{"class":2119},[2113,46518,46519],{"class":2115,"line":1652},[2113,46520,18060],{"class":2119},[2113,46522,46523,46525,46527,46530],{"class":2115,"line":2185},[2113,46524,46372],{"class":2414},[2113,46526,4115],{"class":2119},[2113,46528,46529],{"class":2133},"maMethodePtr",[2113,46531,3944],{"class":2119},[2113,46533,46534],{"class":2115,"line":2230},[2113,46535,2599],{"class":2119},[12,46537,46538,46539,46542,46543,46545,46546,46548],{},"Il faut tester que ",[184,46540,46541],{},"ptr2"," est encore valide, car tant que la\ntransformation du pointeur ",[362,46544,46427],{}," vers le ",[184,46547,46120],{}," n'a pas\nencore été fait, il est possible que le nombre de référence vers l'objet\nsoit tombé à 0 et qu'il ait été supprimé.",[27533,46550,46552,46553],{"id":46551},"comment-utiliser-this","Comment utiliser ",[184,46554,15880],{},[12,46556,46557,46558,46560,46561,46563],{},"Un des points peu pratique de l'utilisation de ",[184,46559,46120],{}," est que\nle comptage de référence ne fonctionne pas si plusieurs ",[184,46562,46120],{},"\npointent vers le même objet mais ont tous été créés à partir du pointeur\nC. Prenons par exemple, le cas suivant :",[2105,46565,46567],{"className":2387,"code":46566,"language":2389,"meta":1646,"style":1646},"MyObject * ptr = new MyObject();\n\nQSharedPointer\u003CMyObject> ptr1 = QSharedPointer(ptr);\nQSharedPointer\u003CMyObject> ptr2 = QSharedPointer(ptr);\n",[184,46568,46569,46587,46591,46611],{"__ignoreMap":1646},[2113,46570,46571,46574,46576,46579,46581,46583,46585],{"class":2115,"line":2116},[2113,46572,46573],{"class":2119},"MyObject ",[2113,46575,2676],{"class":2326},[2113,46577,46578],{"class":2119}," ptr ",[2113,46580,2335],{"class":2326},[2113,46582,4778],{"class":2326},[2113,46584,46254],{"class":2133},[2113,46586,3944],{"class":2119},[2113,46588,46589],{"class":2115,"line":1647},[2113,46590,2125],{"emptyLinePlaceholder":1767},[2113,46592,46593,46595,46597,46599,46601,46604,46606,46609],{"class":2115,"line":1652},[2113,46594,46120],{"class":2119},[2113,46596,3109],{"class":2326},[2113,46598,46242],{"class":2119},[2113,46600,29415],{"class":2326},[2113,46602,46603],{"class":2119}," ptr1 ",[2113,46605,2335],{"class":2326},[2113,46607,46608],{"class":2133}," QSharedPointer",[2113,46610,46310],{"class":2119},[2113,46612,46613,46615,46617,46619,46621,46623,46625,46627],{"class":2115,"line":2185},[2113,46614,46120],{"class":2119},[2113,46616,3109],{"class":2326},[2113,46618,46242],{"class":2119},[2113,46620,29415],{"class":2326},[2113,46622,46498],{"class":2119},[2113,46624,2335],{"class":2326},[2113,46626,46608],{"class":2133},[2113,46628,46310],{"class":2119},[12,46630,46631,46632,46635,46636,46638],{},"Le problème d'écrire ces lignes ainsi, et que pour ",[184,46633,46634],{},"ptr1"," comme pour\n",[184,46637,46541],{},", l'objet n'est référencé qu'une fois. Ainsi le premier qui\ntombera à 0 détruira l'objet, alors que l'autre pourrait encore\nl'utiliser. Il faut donc écrire les choses comme suite :",[12,46640,46641],{},[121,46642],{"alt":46643,"src":46644},"Deux QSharedPointer créé vers la même adresse","\u002FProgrammation\u002Fqt-performance-de-l-utilisation-de-qsharedpointer\u002FQSharedPointer2.png",[2105,46646,46648],{"className":2387,"code":46647,"language":2389,"meta":1646,"style":1646},"QSharedPointer\u003CMyObject> ptr1 = QSharedPointer(new MyObject());\nQSharedPointer\u003CMyObject> ptr2 = ptr1;\n",[184,46649,46650,46674],{"__ignoreMap":1646},[2113,46651,46652,46654,46656,46658,46660,46662,46664,46666,46668,46670,46672],{"class":2115,"line":2116},[2113,46653,46120],{"class":2119},[2113,46655,3109],{"class":2326},[2113,46657,46242],{"class":2119},[2113,46659,29415],{"class":2326},[2113,46661,46603],{"class":2119},[2113,46663,2335],{"class":2326},[2113,46665,46608],{"class":2133},[2113,46667,2423],{"class":2119},[2113,46669,3961],{"class":2326},[2113,46671,46254],{"class":2133},[2113,46673,2593],{"class":2119},[2113,46675,46676,46678,46680,46682,46684,46686,46688],{"class":2115,"line":1647},[2113,46677,46120],{"class":2119},[2113,46679,3109],{"class":2326},[2113,46681,46242],{"class":2119},[2113,46683,29415],{"class":2326},[2113,46685,46498],{"class":2119},[2113,46687,2335],{"class":2326},[2113,46689,46690],{"class":2119}," ptr1;\n",[12,46692,46693,46694,792,46696,46698,46699,46701,46702,46704,46705,46707,46708,46710,46711,179],{},"Ainsi ",[184,46695,46634],{},[184,46697,46541],{}," ont bien chacun connaissance de l'existence de\nl'autre. Cela contraint donc à remplacer toutes les déclarations du type\n",[184,46700,46179],{}," par ",[184,46703,46183],{},". Ceci est donc à faire dans\nles paramètres des méthodes, dans les membres, dans la déclaration des\nvariables locales, ... . On ne peut donc plus utiliser le pointeur C\n",[184,46706,46179],{}," directement, mais seulement au travers de ",[184,46709,46120],{},"\nou de ",[184,46712,46431],{},[12,46714,46715,46716,46718],{},"Cela commence à poser problème lors de l'utilisation de ",[184,46717,15880],{}," dans un\nobjet. Imaginons une méthode d'un objet mettant à jour des membres fils\navec en paramètre le père. Nous aurions alors tendance à écrire ceci :",[2105,46720,46722],{"className":2387,"code":46721,"language":2389,"meta":1646,"style":1646},"void Object2::setParent(QSharedPointer\u003CMyObject> parent)\n{\n    ...\n}\n\n....\n\nvoid MyObject::setMember(Object2 * obj)\n{\n    _membre = obj;\n    if (obj)\n    {\n        obj->setParent(QSharedPointer\u003CMyObject>(this));\n    }\n}\n",[184,46723,46724,46750,46754,46758,46762,46766,46771,46775,46797,46801,46811,46818,46822,46845,46849],{"__ignoreMap":1646},[2113,46725,46726,46728,46731,46733,46736,46738,46740,46742,46744,46746,46748],{"class":2115,"line":2116},[2113,46727,46346],{"class":2326},[2113,46729,46730],{"class":2414}," Object2",[2113,46732,2418],{"class":2119},[2113,46734,46735],{"class":2133},"setParent",[2113,46737,2423],{"class":2119},[2113,46739,46120],{"class":2414},[2113,46741,3109],{"class":2119},[2113,46743,46242],{"class":2414},[2113,46745,4435],{"class":2119},[2113,46747,27266],{"class":2429},[2113,46749,4660],{"class":2119},[2113,46751,46752],{"class":2115,"line":1647},[2113,46753,18060],{"class":2119},[2113,46755,46756],{"class":2115,"line":1652},[2113,46757,33347],{"class":2119},[2113,46759,46760],{"class":2115,"line":2185},[2113,46761,2599],{"class":2119},[2113,46763,46764],{"class":2115,"line":2230},[2113,46765,2125],{"emptyLinePlaceholder":1767},[2113,46767,46768],{"class":2115,"line":2235},[2113,46769,46770],{"class":2119},"....\n",[2113,46772,46773],{"class":2115,"line":2241},[2113,46774,2125],{"emptyLinePlaceholder":1767},[2113,46776,46777,46779,46781,46783,46786,46788,46791,46793,46795],{"class":2115,"line":2246},[2113,46778,46346],{"class":2326},[2113,46780,46254],{"class":2414},[2113,46782,2418],{"class":2119},[2113,46784,46785],{"class":2133},"setMember",[2113,46787,2423],{"class":2119},[2113,46789,46790],{"class":2414},"Object2",[2113,46792,2671],{"class":2326},[2113,46794,9207],{"class":2429},[2113,46796,4660],{"class":2119},[2113,46798,46799],{"class":2115,"line":2464},[2113,46800,18060],{"class":2119},[2113,46802,46803,46806,46808],{"class":2115,"line":2085},[2113,46804,46805],{"class":2119},"    _membre ",[2113,46807,2335],{"class":2326},[2113,46809,46810],{"class":2119}," obj;\n",[2113,46812,46813,46815],{"class":2115,"line":2514},[2113,46814,7394],{"class":2326},[2113,46816,46817],{"class":2119}," (obj)\n",[2113,46819,46820],{"class":2115,"line":2533},[2113,46821,18224],{"class":2119},[2113,46823,46824,46827,46829,46831,46833,46835,46837,46839,46841,46843],{"class":2115,"line":2556},[2113,46825,46826],{"class":2414},"        obj",[2113,46828,4115],{"class":2119},[2113,46830,46735],{"class":2133},[2113,46832,2423],{"class":2119},[2113,46834,46120],{"class":2133},[2113,46836,3109],{"class":2119},[2113,46838,46242],{"class":2414},[2113,46840,19291],{"class":2119},[2113,46842,15880],{"class":2414},[2113,46844,5818],{"class":2119},[2113,46846,46847],{"class":2115,"line":2569},[2113,46848,4665],{"class":2119},[2113,46850,46851],{"class":2115,"line":2575},[2113,46852,2599],{"class":2119},[12,46854,46855,46856,46858,46859,43549,46864,46866,46867,179],{},"Ceci ne marchera pas car on créerait un nouvel objet ",[184,46857,46120],{},"\ncommençant son comptage de référence à 1, alors que nous en avons déjà\nau moins un autre pointant vers notre instance",[17400,46860,46861],{},[49,46862,35439],{"href":41873,"ariaDescribedBy":46863,"dataFootnoteRef":1646,"id":41875},[17406],[184,46865,46242],{},"\npourrait alors être détruit alors qu'il est encore utilisé par\n",[184,46868,46790],{},[12,46870,46871,46872,46874,46876],{},"Pour éviter cela, il faut alors passer par un pointeur ",[362,46873,46045],{},[184,46875,15880],{},". Pour cela nous allons utiliser deux choses :",[506,46878,46879,46891],{},[370,46880,46881,46882,46885,46886,46888,46890],{},"Un membre nommé ",[184,46883,46884],{},"_this"," de type pointeur ",[362,46887,46045],{},[184,46889,46431],{},", contenant une référence à l'objet lui même. (Nous\nn'utilisons pas un QSharedPointer, pour éviter une référence\ncirculaire, voir le paragraphe suivant).",[370,46892,46893],{},"Une méthode statique utilisée pour la création (nous n'allons plus\nutiliser le constructeur, car à ce moment, il n'existe pas encore de\nQSharedPointer pointant vers notre objet).",[12,46895,46896],{},"Voici un exemple de comment écrire le constructeur maison :",[2105,46898,46900],{"className":2387,"code":46899,"language":2389,"meta":1646,"style":1646},"class MyObject\n{\npublic:\n    static QSharedPointer\u003CMyObject> create(QString parametre)\n    {\n        QSharedPointer\u003CMyObject> ptr(new MyObject(parametre);\n        ptr->_this = ptr.toWeakRef();\n        return ptr;\n    }\nprivate:\n    MyObject(QString parametre)\n    {\n        ...\n    }\n\n    QWeakPointer\u003CMyObject> _this;\n};\n",[184,46901,46902,46909,46913,46918,46942,46946,46968,46986,46992,46996,47001,47009,47013,47017,47021,47025,47039],{"__ignoreMap":1646},[2113,46903,46904,46906],{"class":2115,"line":2116},[2113,46905,20538],{"class":2326},[2113,46907,46908],{"class":2414}," MyObject\n",[2113,46910,46911],{"class":2115,"line":1647},[2113,46912,18060],{"class":2119},[2113,46914,46915],{"class":2115,"line":1652},[2113,46916,46917],{"class":2326},"public:\n",[2113,46919,46920,46923,46925,46927,46929,46931,46933,46935,46937,46940],{"class":2115,"line":2185},[2113,46921,46922],{"class":2326},"    static",[2113,46924,46608],{"class":2414},[2113,46926,3109],{"class":2119},[2113,46928,46242],{"class":2414},[2113,46930,4435],{"class":2119},[2113,46932,11111],{"class":2133},[2113,46934,2423],{"class":2119},[2113,46936,46085],{"class":2414},[2113,46938,46939],{"class":2429}," parametre",[2113,46941,4660],{"class":2119},[2113,46943,46944],{"class":2115,"line":2230},[2113,46945,18224],{"class":2119},[2113,46947,46948,46951,46953,46955,46957,46959,46961,46963,46965],{"class":2115,"line":2235},[2113,46949,46950],{"class":2119},"        QSharedPointer",[2113,46952,3109],{"class":2326},[2113,46954,46242],{"class":2119},[2113,46956,29415],{"class":2326},[2113,46958,46247],{"class":2133},[2113,46960,2423],{"class":2119},[2113,46962,3961],{"class":2326},[2113,46964,46254],{"class":2133},[2113,46966,46967],{"class":2119},"(parametre);\n",[2113,46969,46970,46972,46974,46976,46978,46980,46982,46984],{"class":2115,"line":2241},[2113,46971,46281],{"class":2414},[2113,46973,4115],{"class":2119},[2113,46975,46884],{"class":2330},[2113,46977,2153],{"class":2326},[2113,46979,46247],{"class":2414},[2113,46981,179],{"class":2119},[2113,46983,46459],{"class":2133},[2113,46985,3944],{"class":2119},[2113,46987,46988,46990],{"class":2115,"line":2246},[2113,46989,28968],{"class":2326},[2113,46991,46404],{"class":2119},[2113,46993,46994],{"class":2115,"line":2464},[2113,46995,4665],{"class":2119},[2113,46997,46998],{"class":2115,"line":2085},[2113,46999,47000],{"class":2119},"private:\n",[2113,47002,47003,47006],{"class":2115,"line":2514},[2113,47004,47005],{"class":2133},"    MyObject",[2113,47007,47008],{"class":2119},"(QString parametre)\n",[2113,47010,47011],{"class":2115,"line":2533},[2113,47012,18224],{"class":2119},[2113,47014,47015],{"class":2115,"line":2556},[2113,47016,30269],{"class":2119},[2113,47018,47019],{"class":2115,"line":2569},[2113,47020,4665],{"class":2119},[2113,47022,47023],{"class":2115,"line":2575},[2113,47024,2125],{"emptyLinePlaceholder":1767},[2113,47026,47027,47030,47032,47034,47036],{"class":2115,"line":2596},[2113,47028,47029],{"class":2119},"    QWeakPointer",[2113,47031,3109],{"class":2326},[2113,47033,46242],{"class":2119},[2113,47035,29415],{"class":2326},[2113,47037,47038],{"class":2119}," _this;\n",[2113,47040,47041],{"class":2115,"line":3192},[2113,47042,2787],{"class":2119},[12,47044,47045,47046,47048,47049,47051,47052,47054,47055,47057,47058,47060,47061,47063,47064,179],{},"Le constructeur devient alors privé (ou protégé si on a besoin de la\nnotion d'héritage) afin d'obliger l'utilisateur de la classe à utiliser\nnotre méthode de création. Dans notre nouvelle méthode de création\n",[184,47047,11111],{},", qui est une méthode statique, nous allons créer le pointeur et\ninitialiser le ",[184,47050,46431],{}," de notre objet avec le pointeur\n",[362,47053,46045],{}," que nous venons de créer. Nous retournons un\n",[184,47056,46120],{},". La méthode ",[184,47059,11111],{}," devient alors notre nouveau\nconstructeur, mais créant des instances d'objets de type\n",[184,47062,46183],{}," et non plus des instances d'objet\n",[184,47065,46179],{},[12,47067,47068,47069,47072],{},"Notre méthode ",[184,47070,47071],{},"setMember()"," peut alors être ré-écrite :",[2105,47074,47076],{"className":2387,"code":47075,"language":2389,"meta":1646,"style":1646},"void MyObject::setMember(Object2 * obj)\n{\n    _membre = obj;\n    if (obj)\n    {\n        obj->setParent(_this.toStrongRef());\n    }\n}\n",[184,47077,47078,47098,47102,47110,47116,47120,47138,47142],{"__ignoreMap":1646},[2113,47079,47080,47082,47084,47086,47088,47090,47092,47094,47096],{"class":2115,"line":2116},[2113,47081,46346],{"class":2326},[2113,47083,46254],{"class":2414},[2113,47085,2418],{"class":2119},[2113,47087,46785],{"class":2133},[2113,47089,2423],{"class":2119},[2113,47091,46790],{"class":2414},[2113,47093,2671],{"class":2326},[2113,47095,9207],{"class":2429},[2113,47097,4660],{"class":2119},[2113,47099,47100],{"class":2115,"line":1647},[2113,47101,18060],{"class":2119},[2113,47103,47104,47106,47108],{"class":2115,"line":1652},[2113,47105,46805],{"class":2119},[2113,47107,2335],{"class":2326},[2113,47109,46810],{"class":2119},[2113,47111,47112,47114],{"class":2115,"line":2185},[2113,47113,7394],{"class":2326},[2113,47115,46817],{"class":2119},[2113,47117,47118],{"class":2115,"line":2230},[2113,47119,18224],{"class":2119},[2113,47121,47122,47124,47126,47128,47130,47132,47134,47136],{"class":2115,"line":2235},[2113,47123,46826],{"class":2414},[2113,47125,4115],{"class":2119},[2113,47127,46735],{"class":2133},[2113,47129,2423],{"class":2119},[2113,47131,46884],{"class":2414},[2113,47133,179],{"class":2119},[2113,47135,46508],{"class":2133},[2113,47137,2593],{"class":2119},[2113,47139,47140],{"class":2115,"line":2241},[2113,47141,4665],{"class":2119},[2113,47143,47144],{"class":2115,"line":2246},[2113,47145,2599],{"class":2119},[27533,47147,47149],{"id":47148},"comment-éviter-les-références-circulaires","Comment éviter les références circulaires",[12,47151,47152],{},"Le principe d'une référence circulaire est qu'un objet A référence\nl'objet B et l'objet B référence l'objet A.",[12,47154,47155],{},"Voici par exemple, un cas de référence circulaire :",[2105,47157,47159],{"className":2387,"code":47158,"language":2389,"meta":1646,"style":1646},"class A\n{\npublic:\n    static create()\n    {\n        QSharedPointer\u003CA> ptr(new A());\n        return ptr;\n    }\n    ~A()\n    {\n    }\nprivate:\n    A()\n    {\n        b = B::create();\n        b->setA(_this);\n    }\n    QSharedPointer\u003CB> b;\n    QWeakPointer\u003CA> _this;\n};\n\nclass B\n{\npublic:\n    static QSharedPointer\u003CB> create()\n    {\n        QSharedPointer\u003CB> ptr(new B());\n        return ptr;\n    }\n    void setA(QSharedPointer\u003CA> b);\n    QSharedPointer\u003CA> getA();\nprivate:\n    QSharedPointer\u003CA>  a;\n};\n",[184,47160,47161,47168,47172,47176,47185,47189,47210,47216,47220,47227,47231,47235,47239,47246,47250,47264,47277,47281,47295,47307,47311,47315,47322,47326,47330,47346,47350,47371,47377,47381,47403,47418,47422,47435],{"__ignoreMap":1646},[2113,47162,47163,47165],{"class":2115,"line":2116},[2113,47164,20538],{"class":2326},[2113,47166,47167],{"class":2414}," A\n",[2113,47169,47170],{"class":2115,"line":1647},[2113,47171,18060],{"class":2119},[2113,47173,47174],{"class":2115,"line":1652},[2113,47175,46917],{"class":2326},[2113,47177,47178,47180,47183],{"class":2115,"line":2185},[2113,47179,46922],{"class":2326},[2113,47181,47182],{"class":2133}," create",[2113,47184,6745],{"class":2119},[2113,47186,47187],{"class":2115,"line":2230},[2113,47188,18224],{"class":2119},[2113,47190,47191,47193,47195,47198,47200,47202,47204,47206,47208],{"class":2115,"line":2235},[2113,47192,46950],{"class":2119},[2113,47194,3109],{"class":2326},[2113,47196,47197],{"class":2119},"A",[2113,47199,29415],{"class":2326},[2113,47201,46247],{"class":2133},[2113,47203,2423],{"class":2119},[2113,47205,3961],{"class":2326},[2113,47207,42035],{"class":2133},[2113,47209,2593],{"class":2119},[2113,47211,47212,47214],{"class":2115,"line":2241},[2113,47213,28968],{"class":2326},[2113,47215,46404],{"class":2119},[2113,47217,47218],{"class":2115,"line":2246},[2113,47219,4665],{"class":2119},[2113,47221,47222,47225],{"class":2115,"line":2464},[2113,47223,47224],{"class":2133},"    ~A",[2113,47226,6745],{"class":2119},[2113,47228,47229],{"class":2115,"line":2085},[2113,47230,18224],{"class":2119},[2113,47232,47233],{"class":2115,"line":2514},[2113,47234,4665],{"class":2119},[2113,47236,47237],{"class":2115,"line":2533},[2113,47238,47000],{"class":2326},[2113,47240,47241,47244],{"class":2115,"line":2556},[2113,47242,47243],{"class":2133},"    A",[2113,47245,6745],{"class":2119},[2113,47247,47248],{"class":2115,"line":2569},[2113,47249,18224],{"class":2119},[2113,47251,47252,47255,47257,47260,47262],{"class":2115,"line":2575},[2113,47253,47254],{"class":2119},"        b ",[2113,47256,2335],{"class":2326},[2113,47258,47259],{"class":2119}," B::",[2113,47261,11111],{"class":2133},[2113,47263,3944],{"class":2119},[2113,47265,47266,47269,47271,47274],{"class":2115,"line":2596},[2113,47267,47268],{"class":2414},"        b",[2113,47270,4115],{"class":2119},[2113,47272,47273],{"class":2133},"setA",[2113,47275,47276],{"class":2119},"(_this);\n",[2113,47278,47279],{"class":2115,"line":3192},[2113,47280,4665],{"class":2119},[2113,47282,47283,47285,47287,47290,47292],{"class":2115,"line":3213},[2113,47284,46237],{"class":2119},[2113,47286,3109],{"class":2326},[2113,47288,47289],{"class":2119},"B",[2113,47291,29415],{"class":2326},[2113,47293,47294],{"class":2119}," b;\n",[2113,47296,47297,47299,47301,47303,47305],{"class":2115,"line":3236},[2113,47298,47029],{"class":2119},[2113,47300,3109],{"class":2326},[2113,47302,47197],{"class":2119},[2113,47304,29415],{"class":2326},[2113,47306,47038],{"class":2119},[2113,47308,47309],{"class":2115,"line":3248},[2113,47310,2787],{"class":2119},[2113,47312,47313],{"class":2115,"line":4899},[2113,47314,2125],{"emptyLinePlaceholder":1767},[2113,47316,47317,47319],{"class":2115,"line":1777},[2113,47318,20538],{"class":2326},[2113,47320,47321],{"class":2414}," B\n",[2113,47323,47324],{"class":2115,"line":4931},[2113,47325,18060],{"class":2119},[2113,47327,47328],{"class":2115,"line":4961},[2113,47329,46917],{"class":2326},[2113,47331,47332,47334,47336,47338,47340,47342,47344],{"class":2115,"line":4976},[2113,47333,46922],{"class":2326},[2113,47335,46608],{"class":2414},[2113,47337,3109],{"class":2119},[2113,47339,47289],{"class":2414},[2113,47341,4435],{"class":2119},[2113,47343,11111],{"class":2133},[2113,47345,6745],{"class":2119},[2113,47347,47348],{"class":2115,"line":4993},[2113,47349,18224],{"class":2119},[2113,47351,47352,47354,47356,47358,47360,47362,47364,47366,47369],{"class":2115,"line":5013},[2113,47353,46950],{"class":2119},[2113,47355,3109],{"class":2326},[2113,47357,47289],{"class":2119},[2113,47359,29415],{"class":2326},[2113,47361,46247],{"class":2133},[2113,47363,2423],{"class":2119},[2113,47365,3961],{"class":2326},[2113,47367,47368],{"class":2133}," B",[2113,47370,2593],{"class":2119},[2113,47372,47373,47375],{"class":2115,"line":5018},[2113,47374,28968],{"class":2326},[2113,47376,46404],{"class":2119},[2113,47378,47379],{"class":2115,"line":5042},[2113,47380,4665],{"class":2119},[2113,47382,47383,47386,47389,47391,47393,47395,47397,47399,47401],{"class":2115,"line":5057},[2113,47384,47385],{"class":2326},"    void",[2113,47387,47388],{"class":2133}," setA",[2113,47390,2423],{"class":2119},[2113,47392,46120],{"class":2414},[2113,47394,3109],{"class":2119},[2113,47396,47197],{"class":2414},[2113,47398,4435],{"class":2119},[2113,47400,3992],{"class":2429},[2113,47402,2553],{"class":2119},[2113,47404,47405,47407,47409,47411,47413,47416],{"class":2115,"line":5062},[2113,47406,46237],{"class":2414},[2113,47408,3109],{"class":2119},[2113,47410,47197],{"class":2414},[2113,47412,4435],{"class":2119},[2113,47414,47415],{"class":2133},"getA",[2113,47417,3944],{"class":2119},[2113,47419,47420],{"class":2115,"line":5098},[2113,47421,47000],{"class":2326},[2113,47423,47424,47426,47428,47430,47432],{"class":2115,"line":5117},[2113,47425,46237],{"class":2119},[2113,47427,3109],{"class":2326},[2113,47429,47197],{"class":2119},[2113,47431,29415],{"class":2326},[2113,47433,47434],{"class":2119},"  a;\n",[2113,47436,47437],{"class":2115,"line":5142},[2113,47438,2787],{"class":2119},[12,47440,47441],{},"Cela peut-être aussi le cas, si par exemple une instance d'objet C\nréférence des instances d'objets fils C, qui possèdent eux-même un\npointeur vers l'objet C père.",[2105,47443,47445],{"className":2387,"code":47444,"language":2389,"meta":1646,"style":1646},"class C\n{\npublic:\n    static QSharedPointer\u003CC> create()\n    {\n        QSharedPointer\u003CC> ptr(new C());\n        _this = ptr.toWeakPtr();\n        return ptr;\n    }\n\n    ~C()\n    {\n    }\n\n    void addChild(QSharedPointer\u003CC> c)\n    {\n        _childs.append(c);\n        c->setParent(_this);\n    }\n\n    void setParent(QSharedPointer\u003CC> c);\n    QSharedPointer\u003CC> getParent();\nprivate:\n    C()\n    {\n    }\n\n    QWeakPointer\u003CC> _this;\n    QSharedPointer\u003CC> _parent;\n    QList\u003C QSharedPointer\u003CC> > _childs;\n};\n",[184,47446,47447,47454,47458,47462,47479,47483,47503,47519,47525,47529,47533,47540,47544,47548,47552,47573,47577,47590,47601,47605,47609,47630,47645,47649,47656,47660,47664,47668,47680,47693,47713],{"__ignoreMap":1646},[2113,47448,47449,47451],{"class":2115,"line":2116},[2113,47450,20538],{"class":2326},[2113,47452,47453],{"class":2414}," C\n",[2113,47455,47456],{"class":2115,"line":1647},[2113,47457,18060],{"class":2119},[2113,47459,47460],{"class":2115,"line":1652},[2113,47461,46917],{"class":2326},[2113,47463,47464,47466,47468,47470,47473,47475,47477],{"class":2115,"line":2185},[2113,47465,46922],{"class":2326},[2113,47467,46608],{"class":2414},[2113,47469,3109],{"class":2119},[2113,47471,47472],{"class":2414},"C",[2113,47474,4435],{"class":2119},[2113,47476,11111],{"class":2133},[2113,47478,6745],{"class":2119},[2113,47480,47481],{"class":2115,"line":2230},[2113,47482,18224],{"class":2119},[2113,47484,47485,47487,47489,47491,47493,47495,47497,47499,47501],{"class":2115,"line":2235},[2113,47486,46950],{"class":2119},[2113,47488,3109],{"class":2326},[2113,47490,47472],{"class":2119},[2113,47492,29415],{"class":2326},[2113,47494,46247],{"class":2133},[2113,47496,2423],{"class":2119},[2113,47498,3961],{"class":2326},[2113,47500,42263],{"class":2133},[2113,47502,2593],{"class":2119},[2113,47504,47505,47508,47510,47512,47514,47517],{"class":2115,"line":2241},[2113,47506,47507],{"class":2119},"        _this ",[2113,47509,2335],{"class":2326},[2113,47511,46247],{"class":2414},[2113,47513,179],{"class":2119},[2113,47515,47516],{"class":2133},"toWeakPtr",[2113,47518,3944],{"class":2119},[2113,47520,47521,47523],{"class":2115,"line":2246},[2113,47522,28968],{"class":2326},[2113,47524,46404],{"class":2119},[2113,47526,47527],{"class":2115,"line":2464},[2113,47528,4665],{"class":2119},[2113,47530,47531],{"class":2115,"line":2085},[2113,47532,2125],{"emptyLinePlaceholder":1767},[2113,47534,47535,47538],{"class":2115,"line":2514},[2113,47536,47537],{"class":2133},"    ~C",[2113,47539,6745],{"class":2119},[2113,47541,47542],{"class":2115,"line":2533},[2113,47543,18224],{"class":2119},[2113,47545,47546],{"class":2115,"line":2556},[2113,47547,4665],{"class":2119},[2113,47549,47550],{"class":2115,"line":2569},[2113,47551,2125],{"emptyLinePlaceholder":1767},[2113,47553,47554,47556,47559,47561,47563,47565,47567,47569,47571],{"class":2115,"line":2575},[2113,47555,47385],{"class":2326},[2113,47557,47558],{"class":2133}," addChild",[2113,47560,2423],{"class":2119},[2113,47562,46120],{"class":2414},[2113,47564,3109],{"class":2119},[2113,47566,47472],{"class":2414},[2113,47568,4435],{"class":2119},[2113,47570,4097],{"class":2429},[2113,47572,4660],{"class":2119},[2113,47574,47575],{"class":2115,"line":2596},[2113,47576,18224],{"class":2119},[2113,47578,47579,47582,47584,47587],{"class":2115,"line":3192},[2113,47580,47581],{"class":2414},"        _childs",[2113,47583,179],{"class":2119},[2113,47585,47586],{"class":2133},"append",[2113,47588,47589],{"class":2119},"(c);\n",[2113,47591,47592,47595,47597,47599],{"class":2115,"line":3213},[2113,47593,47594],{"class":2414},"        c",[2113,47596,4115],{"class":2119},[2113,47598,46735],{"class":2133},[2113,47600,47276],{"class":2119},[2113,47602,47603],{"class":2115,"line":3236},[2113,47604,4665],{"class":2119},[2113,47606,47607],{"class":2115,"line":3248},[2113,47608,2125],{"emptyLinePlaceholder":1767},[2113,47610,47611,47613,47616,47618,47620,47622,47624,47626,47628],{"class":2115,"line":4899},[2113,47612,47385],{"class":2326},[2113,47614,47615],{"class":2133}," setParent",[2113,47617,2423],{"class":2119},[2113,47619,46120],{"class":2414},[2113,47621,3109],{"class":2119},[2113,47623,47472],{"class":2414},[2113,47625,4435],{"class":2119},[2113,47627,4097],{"class":2429},[2113,47629,2553],{"class":2119},[2113,47631,47632,47634,47636,47638,47640,47643],{"class":2115,"line":1777},[2113,47633,46237],{"class":2414},[2113,47635,3109],{"class":2119},[2113,47637,47472],{"class":2414},[2113,47639,4435],{"class":2119},[2113,47641,47642],{"class":2133},"getParent",[2113,47644,3944],{"class":2119},[2113,47646,47647],{"class":2115,"line":4931},[2113,47648,47000],{"class":2326},[2113,47650,47651,47654],{"class":2115,"line":4961},[2113,47652,47653],{"class":2133},"    C",[2113,47655,6745],{"class":2119},[2113,47657,47658],{"class":2115,"line":4976},[2113,47659,18224],{"class":2119},[2113,47661,47662],{"class":2115,"line":4993},[2113,47663,4665],{"class":2119},[2113,47665,47666],{"class":2115,"line":5013},[2113,47667,2125],{"emptyLinePlaceholder":1767},[2113,47669,47670,47672,47674,47676,47678],{"class":2115,"line":5018},[2113,47671,47029],{"class":2119},[2113,47673,3109],{"class":2326},[2113,47675,47472],{"class":2119},[2113,47677,29415],{"class":2326},[2113,47679,47038],{"class":2119},[2113,47681,47682,47684,47686,47688,47690],{"class":2115,"line":5042},[2113,47683,46237],{"class":2119},[2113,47685,3109],{"class":2326},[2113,47687,47472],{"class":2119},[2113,47689,29415],{"class":2326},[2113,47691,47692],{"class":2119}," _parent;\n",[2113,47694,47695,47698,47700,47702,47704,47706,47708,47710],{"class":2115,"line":5057},[2113,47696,47697],{"class":2119},"    QList",[2113,47699,3109],{"class":2326},[2113,47701,46608],{"class":2119},[2113,47703,3109],{"class":2326},[2113,47705,47472],{"class":2119},[2113,47707,29415],{"class":2326},[2113,47709,5275],{"class":2326},[2113,47711,47712],{"class":2119}," _childs;\n",[2113,47714,47715],{"class":2115,"line":5062},[2113,47716,2787],{"class":2119},[12,47718,47719],{},"Dans ces cas là, on a :- L'objet A possède la référence vers l'objet B-\nL'objet B possède une référence vers l'objet A.- Même principe avec\nl'objet C",[12,47721,47722],{},[121,47723],{"alt":47724,"src":47725},"Reference circulaire de QSharedPointer","\u002FProgrammation\u002Fqt-performance-de-l-utilisation-de-qsharedpointer\u002FQSharedPointer3.png",[12,47727,47728],{},"Dans ce cas, il restera alors toujours une référence vers A, et une vers\nB, même si plus aucune variable ne référence ces objets. Cette référence\ncirculaire fait que l'objet ne sera jamais détruit même si on n'a plus\nbesoin de l'objet.",[12,47730,47731],{},"Si on décide que l'objet A sera l'objet maitre (donc que sa destruction\nengendrera la destruction de l'objet B), on peut alors écrire les choses\nainsi pour l'objet B :",[2105,47733,47735],{"className":2387,"code":47734,"language":2389,"meta":1646,"style":1646},"class B\n{\npublic:\n    static QSharedPointer\u003CB> create()\n    {\n        QSharedPointer\u003CB> ptr(new B());\n        return ptr;\n    }\n    void setA(QWeakPointer\u003CA> b);\n    QWeakPointer\u003CA> getA();\nprivate:\n    QWeakPointer\u003CA>  a;\n};\n",[184,47736,47737,47743,47747,47751,47767,47771,47791,47797,47801,47821,47835,47839,47851],{"__ignoreMap":1646},[2113,47738,47739,47741],{"class":2115,"line":2116},[2113,47740,20538],{"class":2326},[2113,47742,47321],{"class":2414},[2113,47744,47745],{"class":2115,"line":1647},[2113,47746,18060],{"class":2119},[2113,47748,47749],{"class":2115,"line":1652},[2113,47750,46917],{"class":2326},[2113,47752,47753,47755,47757,47759,47761,47763,47765],{"class":2115,"line":2185},[2113,47754,46922],{"class":2326},[2113,47756,46608],{"class":2414},[2113,47758,3109],{"class":2119},[2113,47760,47289],{"class":2414},[2113,47762,4435],{"class":2119},[2113,47764,11111],{"class":2133},[2113,47766,6745],{"class":2119},[2113,47768,47769],{"class":2115,"line":2230},[2113,47770,18224],{"class":2119},[2113,47772,47773,47775,47777,47779,47781,47783,47785,47787,47789],{"class":2115,"line":2235},[2113,47774,46950],{"class":2119},[2113,47776,3109],{"class":2326},[2113,47778,47289],{"class":2119},[2113,47780,29415],{"class":2326},[2113,47782,46247],{"class":2133},[2113,47784,2423],{"class":2119},[2113,47786,3961],{"class":2326},[2113,47788,47368],{"class":2133},[2113,47790,2593],{"class":2119},[2113,47792,47793,47795],{"class":2115,"line":2241},[2113,47794,28968],{"class":2326},[2113,47796,46404],{"class":2119},[2113,47798,47799],{"class":2115,"line":2246},[2113,47800,4665],{"class":2119},[2113,47802,47803,47805,47807,47809,47811,47813,47815,47817,47819],{"class":2115,"line":2464},[2113,47804,47385],{"class":2326},[2113,47806,47388],{"class":2133},[2113,47808,2423],{"class":2119},[2113,47810,46431],{"class":2414},[2113,47812,3109],{"class":2119},[2113,47814,47197],{"class":2414},[2113,47816,4435],{"class":2119},[2113,47818,3992],{"class":2429},[2113,47820,2553],{"class":2119},[2113,47822,47823,47825,47827,47829,47831,47833],{"class":2115,"line":2085},[2113,47824,47029],{"class":2414},[2113,47826,3109],{"class":2119},[2113,47828,47197],{"class":2414},[2113,47830,4435],{"class":2119},[2113,47832,47415],{"class":2133},[2113,47834,3944],{"class":2119},[2113,47836,47837],{"class":2115,"line":2514},[2113,47838,47000],{"class":2326},[2113,47840,47841,47843,47845,47847,47849],{"class":2115,"line":2533},[2113,47842,47029],{"class":2119},[2113,47844,3109],{"class":2326},[2113,47846,47197],{"class":2119},[2113,47848,29415],{"class":2326},[2113,47850,47434],{"class":2119},[2113,47852,47853],{"class":2115,"line":2556},[2113,47854,2787],{"class":2119},[12,47856,47857,47858,47860,47861,45630,47863,47865,47866,47871,47872,179],{},"Dans ce cas, avec l'utilisation d'un ",[184,47859,46431],{},", lorsque qu'il\nn'existera plus de référence vers l'objet A, le pointeur ",[362,47862,46427],{},[184,47864,49],{},"\nsera mis à jour comme ne contenant plus de référence",[17400,47867,47868],{},[49,47869,41887],{"href":41884,"ariaDescribedBy":47870,"dataFootnoteRef":1646,"id":41886},[17406],".\nL'instance de l'objet A sera réellement détruite. Il n'y aura alors plus\naucune référence vers l'objet B qui sera alors également\ndétruit",[17400,47873,47874],{},[49,47875,41923],{"href":41920,"ariaDescribedBy":47876,"dataFootnoteRef":1646,"id":41922},[17406],[27533,47878,47880],{"id":47879},"utilisation-dans-les-applications-multi-thread","Utilisation dans les applications multi-thread",[12,47882,9516,47883,47885,47886,792,47888,47890],{},[184,47884,46120],{}," simplifie l'écriture des applications\nmulti-thread (les objets ",[184,47887,46120],{},[184,47889,46431],{}," sont\nthread-safe).",[12,47892,47893,47894,47899,47900,179],{},"Dans ces applications il n'y a alors plus besoin de se soucier si\nl'objet est en cours d'utilisation ailleurs dans l'application avant de\nle supprimer",[17400,47895,47896],{},[49,47897,43168],{"href":43165,"ariaDescribedBy":47898,"dataFootnoteRef":1646,"id":43167},[17406],". Lorsqu'un pointeur ne devient plus utilisé dans\nun thread donné, il ne sera détruit que s'il n'y a pas d'autres\nréférences dans d'autres threads de l'application",[17400,47901,47902],{},[49,47903,47907],{"href":47904,"ariaDescribedBy":47905,"dataFootnoteRef":1646,"id":47906},"#user-content-fn-9",[17406],"user-content-fnref-9","9",[12,47909,47910,47911,47913,47914,179],{},"Avec l'utilisation de ",[184,47912,46431],{},", un thread pourra tester\nl'existence du pointeur avant d'effectuer une opération et pourra aviser\nle cas échéant sans faire planter toute l'application",[17400,47915,47916],{},[49,47917,35474],{"href":47918,"ariaDescribedBy":47919,"dataFootnoteRef":1646,"id":47920},"#user-content-fn-10",[17406],"user-content-fnref-10",[27533,47922,47924],{"id":47923},"utilisation-dun-pool","Utilisation d'un pool",[12,47926,47927,47928,47933],{},"Si la création et la destruction d'un objet est coûteux, il est\nenvisageable de diminuer le coût de destruction et de création d'un\nthread en utilisant un Pool d'objet. Dans ce cas l'objet ",[49,47929,47932],{"href":47930,"rel":47931},"http:\u002F\u002Fdoc.qt.nokia.com\u002F4.6\u002Fqqueue.html",[347],"QQueue","\npourra être utilisé pour représenter notre Pool.",[12,47935,47936,47937,47939,47940,179],{},"Lors de la demande de création, en utilisant notre méthode ",[184,47938,11111],{},"\nci-dessus, on prend alors une valeur du pool (si disponible) et on la\nretourne sous forme d'un ",[184,47941,46120],{},[2105,47943,47945],{"className":2387,"code":47944,"language":2389,"meta":1646,"style":1646},"QSharedPointer\u003CMyObject> MyObject::create()\n{\n    MyObject * c_ptr;\n    if (_queue.size())\n    {\n    c_ptr = _queue.dequeue();\n    }\n    else\n    {\n    c_ptr = new MyObject();\n    }\n    QSharedPointer\u003CMyObject> ptr(c_ptr, ReturnToPool);\n    ptr->_this = ptr.toWeakRef();\n    return ptr;\n}\n",[184,47946,47947,47965,47969,47979,47994,47998,48015,48019,48023,48027,48039,48043,48058,48076,48082],{"__ignoreMap":1646},[2113,47948,47949,47951,47953,47955,47957,47959,47961,47963],{"class":2115,"line":2116},[2113,47950,46120],{"class":2414},[2113,47952,3109],{"class":2119},[2113,47954,46242],{"class":2414},[2113,47956,4435],{"class":2119},[2113,47958,46242],{"class":2414},[2113,47960,2418],{"class":2119},[2113,47962,11111],{"class":2133},[2113,47964,6745],{"class":2119},[2113,47966,47967],{"class":2115,"line":1647},[2113,47968,18060],{"class":2119},[2113,47970,47971,47974,47976],{"class":2115,"line":1652},[2113,47972,47973],{"class":2119},"    MyObject ",[2113,47975,2676],{"class":2326},[2113,47977,47978],{"class":2119}," c_ptr;\n",[2113,47980,47981,47983,47985,47988,47990,47992],{"class":2115,"line":2185},[2113,47982,7394],{"class":2326},[2113,47984,2495],{"class":2119},[2113,47986,47987],{"class":2414},"_queue",[2113,47989,179],{"class":2119},[2113,47991,7873],{"class":2133},[2113,47993,5229],{"class":2119},[2113,47995,47996],{"class":2115,"line":2230},[2113,47997,18224],{"class":2119},[2113,47999,48000,48003,48005,48008,48010,48013],{"class":2115,"line":2235},[2113,48001,48002],{"class":2119},"    c_ptr ",[2113,48004,2335],{"class":2326},[2113,48006,48007],{"class":2414}," _queue",[2113,48009,179],{"class":2119},[2113,48011,48012],{"class":2133},"dequeue",[2113,48014,3944],{"class":2119},[2113,48016,48017],{"class":2115,"line":2241},[2113,48018,4665],{"class":2119},[2113,48020,48021],{"class":2115,"line":2246},[2113,48022,7439],{"class":2326},[2113,48024,48025],{"class":2115,"line":2464},[2113,48026,18224],{"class":2119},[2113,48028,48029,48031,48033,48035,48037],{"class":2115,"line":2085},[2113,48030,48002],{"class":2119},[2113,48032,2335],{"class":2326},[2113,48034,4778],{"class":2326},[2113,48036,46254],{"class":2133},[2113,48038,3944],{"class":2119},[2113,48040,48041],{"class":2115,"line":2514},[2113,48042,4665],{"class":2119},[2113,48044,48045,48047,48049,48051,48053,48055],{"class":2115,"line":2533},[2113,48046,46237],{"class":2119},[2113,48048,3109],{"class":2326},[2113,48050,46242],{"class":2119},[2113,48052,29415],{"class":2326},[2113,48054,46247],{"class":2133},[2113,48056,48057],{"class":2119},"(c_ptr, ReturnToPool);\n",[2113,48059,48060,48062,48064,48066,48068,48070,48072,48074],{"class":2115,"line":2556},[2113,48061,46372],{"class":2414},[2113,48063,4115],{"class":2119},[2113,48065,46884],{"class":2330},[2113,48067,2153],{"class":2326},[2113,48069,46247],{"class":2414},[2113,48071,179],{"class":2119},[2113,48073,46459],{"class":2133},[2113,48075,3944],{"class":2119},[2113,48077,48078,48080],{"class":2115,"line":2569},[2113,48079,3137],{"class":2326},[2113,48081,46404],{"class":2119},[2113,48083,48084],{"class":2115,"line":2575},[2113,48085,2599],{"class":2119},[12,48087,48088,48089,48097,48098,48100],{},"Dans l'exemple ci-dessus",[17400,48090,48091],{},[49,48092,48096],{"href":48093,"ariaDescribedBy":48094,"dataFootnoteRef":1646,"id":48095},"#user-content-fn-11",[17406],"user-content-fnref-11","11",", on demande à la queue, qui doit\nêtre une variable globale ou statique, un élément, et si ce n'est pas\npossible, on crée un nouvel objet de type ",[184,48099,46242],{}," (dont on suppose la\ncréation coûteuse).",[12,48102,48103,48104,48106,48107,48110,48111,48114,48115,48118],{},"Lors de la création du ",[184,48105,46120],{}," on utilise alors le constructeur\n",[184,48108,48109],{},"QSharedPointer ( T * ptr, Deleter deleter )"," sur lequel on définit une\nméthode ",[184,48112,48113],{},"Deleter"," nommée ",[184,48116,48117],{},"ReturnToPool"," dont le but est de remettre les\nobjets en pool.",[2105,48120,48122],{"className":2387,"code":48121,"language":2389,"meta":1646,"style":1646},"static void ReturnToPool(MyObject *obj)\n{\n    if (_queue.size() \u003C MAX_SIZE_QUEUE)\n    {\n        _queue.enqueue(obj);\n    }\n    else\n    {\n        delete obj;\n    }\n}\n",[184,48123,48124,48143,48147,48166,48170,48183,48187,48191,48195,48202,48206],{"__ignoreMap":1646},[2113,48125,48126,48128,48130,48133,48135,48137,48139,48141],{"class":2115,"line":2116},[2113,48127,7668],{"class":2326},[2113,48129,29564],{"class":2326},[2113,48131,48132],{"class":2133}," ReturnToPool",[2113,48134,2423],{"class":2119},[2113,48136,46242],{"class":2414},[2113,48138,2671],{"class":2326},[2113,48140,9233],{"class":2429},[2113,48142,4660],{"class":2119},[2113,48144,48145],{"class":2115,"line":1647},[2113,48146,18060],{"class":2119},[2113,48148,48149,48151,48153,48155,48157,48159,48161,48163],{"class":2115,"line":1652},[2113,48150,7394],{"class":2326},[2113,48152,2495],{"class":2119},[2113,48154,47987],{"class":2414},[2113,48156,179],{"class":2119},[2113,48158,7873],{"class":2133},[2113,48160,4543],{"class":2119},[2113,48162,3109],{"class":2326},[2113,48164,48165],{"class":2119}," MAX_SIZE_QUEUE)\n",[2113,48167,48168],{"class":2115,"line":2185},[2113,48169,18224],{"class":2119},[2113,48171,48172,48175,48177,48180],{"class":2115,"line":2230},[2113,48173,48174],{"class":2414},"        _queue",[2113,48176,179],{"class":2119},[2113,48178,48179],{"class":2133},"enqueue",[2113,48181,48182],{"class":2119},"(obj);\n",[2113,48184,48185],{"class":2115,"line":2235},[2113,48186,4665],{"class":2119},[2113,48188,48189],{"class":2115,"line":2241},[2113,48190,7439],{"class":2326},[2113,48192,48193],{"class":2115,"line":2246},[2113,48194,18224],{"class":2119},[2113,48196,48197,48200],{"class":2115,"line":2464},[2113,48198,48199],{"class":2326},"        delete",[2113,48201,46810],{"class":2119},[2113,48203,48204],{"class":2115,"line":2085},[2113,48205,4665],{"class":2119},[2113,48207,48208],{"class":2115,"line":2514},[2113,48209,2599],{"class":2119},[12,48211,48212],{},"Dans ce cas de retour au pool, si le pool est rempli, on détruit l'objet\n(pour éviter de consommer trop de mémoire), sinon on l'ajoute au pool.\nDans ce cas, le pool est agrandi au fur et à mesure des besoins, jusqu'à\nune taille limite.",[12,48214,48215],{},"Bien sûr il faut que la performance de l'utilisation d'un pool soit plus\nintéressante que celle de la création de l'objet et de son\ninitialisation.",[12,48217,48218],{},[155,48219,48220,48223,48224,48226,48227,48229,48230,48232,48233,48235,48236,48238,48239,48241,48242],{},[48221,48222,38902],"ins",{}," : Ce point ne fonctionne, par contre, pas si\nl'objet (",[184,48225,46242],{},") est un descendant de ",[184,48228,44981],{},". En effet ",[184,48231,44981],{},"\ngarde une référence du ",[184,48234,46120],{}," en mémoire et lors de la\nréutilisation du ",[184,48237,44981],{}," une erreur indique que l'objet n'a pas été\ndétruit et est déjà utilisé par un ",[184,48240,46120],{},". On n'a pas le\nproblème avec ",[184,48243,48244],{},"std::tr1::shared_ptr",[133,48246,17007],{"id":17006},[12,48248,48249,48250,48252],{},"Le but du benchmark est de se faire une idée sur les performances d'une\napplication utilisant des ",[184,48251,46120],{}," à la place des pointeurs\nnormaux. Attention, ce bench ne prend pas en compte le besoin potentiel\nde Mutex, de comptage de référence manuel, ... dans les applications\nmulti-thread qui pourrait être nécessaire pour ne pas supprimer le\npointeur si besoin.",[12,48254,48255,48256,48258,48259,179],{},"Dans ce test nous allons tester également (en comparaison), le pointeur\n",[362,48257,46045],{}," du C++0x",[17400,48260,48261],{},[49,48262,48266],{"href":48263,"ariaDescribedBy":48264,"dataFootnoteRef":1646,"id":48265},"#user-content-fn-12",[17406],"user-content-fnref-12","12",[12,48268,48269],{},"Nous allons donc tester les opérations courantes de création,\ndestruction, modification, affectation.",[27533,48271,48273],{"id":48272},"code-source","Code source",[12,48275,48276,48277,48280],{},"Le code source est disponible, attaché au billet. Dans la suite du\nbillet, seuls les morceaux intéressants du benchmark seront décris. Le\nbenchmark utilise QTest. Nous avons créé un objet bidon ",[184,48278,48279],{},"ObjetTest"," qui\ndans le constructeur allouera un pointeur et remplira une liste, et le\ndestructeur supprime ce pointeur (et forcément la liste).",[12,48282,48283],{},"Pour que chaque test soit indépendant, le jeu de test sera initialisé\navant le début de chaque QBENCHMARK et détruit à la fin du bloc. Nous\naurons quatre méthodes :",[506,48285,48286,48289,48292,48295],{},[370,48287,48288],{},"Allocation",[370,48290,48291],{},"Modification",[370,48293,48294],{},"Affectation",[370,48296,48297],{},"Nettoyage",[12,48299,48300],{},"Pour chaque test nous allons faire le test avec",[506,48302,48303,48306,48313],{},[370,48304,48305],{},"un pointeur C standard",[370,48307,48308,48309,44862,48311],{},"le pointeur ",[184,48310,46120],{},[362,48312,43910],{},[370,48314,48308,48315,44862,48317],{},[184,48316,48244],{},[362,48318,48319],{},"C++0x",[12,48321,48322],{},"Pour le test d'allocation et le test de nettoyage, nous allons également\nutiliser l'optimisation possible, vu ci-dessus, d'un Pool d'objet. Nous\nallons faire le test avec :",[506,48324,48325,48331],{},[370,48326,48308,48327,44862,48329],{},[184,48328,46120],{},[362,48330,43910],{},[370,48332,48308,48333,44862,48335],{},[184,48334,48244],{},[362,48336,48319],{},[27533,48338,48340],{"id":48339},"le-jeu-de-test","Le jeu de test",[34199,48342,48344],{"id":48343},"test-de-lallocation","Test de l'allocation",[12,48346,48347,48348,48350,48351,48353],{},"La création du pointeur en utilisant ",[184,48349,46120],{}," instancie le\npointeur ainsi que le ",[184,48352,46120],{},". Le temps d'exécution est donc\npotentiellement deux fois plus long (voir le benchmark à la fin de ce\nbillet).",[12,48355,48356],{},"Pour la création du pool, nous allons utiliser une méthode qui créera le\npointeur s'il n'est pas dans le pool, et sinon prendra le pointeur du\npool. Dans notre cas de test, il y aura toujours une valeur dans le\npool, que l'on aura rempli au préalable.",[12,48358,33522,48359,792,48362,48365],{},[184,48360,48361],{},"createFromPool()",[184,48363,48364],{},"createFromBoostPool()"," est\nsensiblement identique :",[2105,48367,48369],{"className":2387,"code":48368,"language":2389,"meta":1646,"style":1646},"QSharedPointer\u003CObjetTest> createFromPool()\n{\n    ObjetTest * c_ptr;\n    if (_queue.size())\n    {\n        c_ptr = _queue.dequeue();\n    }\n    else\n    {\n        c_ptr = new ObjetTest();\n    }\n\n    QSharedPointer\u003CObjetTest> ptr(c_ptr, returnToPool);\n    return ptr;\n}\n",[184,48370,48371,48386,48390,48399,48413,48417,48432,48436,48440,48444,48457,48461,48465,48480,48486],{"__ignoreMap":1646},[2113,48372,48373,48375,48377,48379,48381,48384],{"class":2115,"line":2116},[2113,48374,46120],{"class":2414},[2113,48376,3109],{"class":2119},[2113,48378,48279],{"class":2414},[2113,48380,4435],{"class":2119},[2113,48382,48383],{"class":2133},"createFromPool",[2113,48385,6745],{"class":2119},[2113,48387,48388],{"class":2115,"line":1647},[2113,48389,18060],{"class":2119},[2113,48391,48392,48395,48397],{"class":2115,"line":1652},[2113,48393,48394],{"class":2119},"    ObjetTest ",[2113,48396,2676],{"class":2326},[2113,48398,47978],{"class":2119},[2113,48400,48401,48403,48405,48407,48409,48411],{"class":2115,"line":2185},[2113,48402,7394],{"class":2326},[2113,48404,2495],{"class":2119},[2113,48406,47987],{"class":2414},[2113,48408,179],{"class":2119},[2113,48410,7873],{"class":2133},[2113,48412,5229],{"class":2119},[2113,48414,48415],{"class":2115,"line":2230},[2113,48416,18224],{"class":2119},[2113,48418,48419,48422,48424,48426,48428,48430],{"class":2115,"line":2235},[2113,48420,48421],{"class":2119},"        c_ptr ",[2113,48423,2335],{"class":2326},[2113,48425,48007],{"class":2414},[2113,48427,179],{"class":2119},[2113,48429,48012],{"class":2133},[2113,48431,3944],{"class":2119},[2113,48433,48434],{"class":2115,"line":2241},[2113,48435,4665],{"class":2119},[2113,48437,48438],{"class":2115,"line":2246},[2113,48439,7439],{"class":2326},[2113,48441,48442],{"class":2115,"line":2464},[2113,48443,18224],{"class":2119},[2113,48445,48446,48448,48450,48452,48455],{"class":2115,"line":2085},[2113,48447,48421],{"class":2119},[2113,48449,2335],{"class":2326},[2113,48451,4778],{"class":2326},[2113,48453,48454],{"class":2133}," ObjetTest",[2113,48456,3944],{"class":2119},[2113,48458,48459],{"class":2115,"line":2514},[2113,48460,4665],{"class":2119},[2113,48462,48463],{"class":2115,"line":2533},[2113,48464,2125],{"emptyLinePlaceholder":1767},[2113,48466,48467,48469,48471,48473,48475,48477],{"class":2115,"line":2556},[2113,48468,46237],{"class":2119},[2113,48470,3109],{"class":2326},[2113,48472,48279],{"class":2119},[2113,48474,29415],{"class":2326},[2113,48476,46247],{"class":2133},[2113,48478,48479],{"class":2119},"(c_ptr, returnToPool);\n",[2113,48481,48482,48484],{"class":2115,"line":2569},[2113,48483,3137],{"class":2326},[2113,48485,46404],{"class":2119},[2113,48487,48488],{"class":2115,"line":2575},[2113,48489,2599],{"class":2119},[22,48491,48492,48501],{},[25,48493,48494],{},[28,48495,48496,48499],{},[31,48497,48498],{},"Méthode",[31,48500,45877],{},[41,48502,48503,48513,48523,48533,48543],{},[28,48504,48505,48508],{},[46,48506,48507],{},"C Pointer",[46,48509,48510],{},[184,48511,48512],{},"ObjetTest* ptr = new ObjetTest();",[28,48514,48515,48518],{},[46,48516,48517],{},"Qt Smart Pointer",[46,48519,48520],{},[184,48521,48522],{},"QSharedPointer\u003CObjetTest> ptr(new ObjetTest());",[28,48524,48525,48528],{},[46,48526,48527],{},"Qt Smart Pointer as Pool",[46,48529,48530],{},[184,48531,48532],{},"QSharedPointer\u003CObjetTest> ptr = createFromPool ();",[28,48534,48535,48538],{},[46,48536,48537],{},"C++0x Smart Pointer",[46,48539,48540],{},[184,48541,48542],{},"std::tr1::shared_ptr\u003CObjetTest> ptr(new ObjetTest());",[28,48544,48545,48548],{},[46,48546,48547],{},"C++0x Smart Pointer as Pool",[46,48549,48550],{},[184,48551,48552],{},"std::tr1::shared_ptr\u003CObjetTest> ptr = createFromBoostPool ();",[34199,48554,48556],{"id":48555},"test-de-modification-dune-donnée","Test de Modification d'une donnée",[12,48558,48559],{},"Pour la modification d'une donnée, on génère un nombre aléatoire que\nl'on va stocker (toujours le même pour chaque test, cela n'a pas\nd'importance). La génération du nombre aléatoire se fait en dehors du\nbloc, pour éviter de polluer le test avec le calcul d'un nombre\naléatoire. Ici il n'y a pas création d'affectation du pointeur, juste\nune affectation d'une valeur dans le contenu du pointeur. La syntaxe\npour le C, et pour le pointeur intelligent est identique.",[22,48561,48562,48570],{},[25,48563,48564],{},[28,48565,48566,48568],{},[31,48567,48498],{},[31,48569,45877],{},[41,48571,48572],{},[28,48573,48574,48577],{},[46,48575,48576],{},"C Pointer \u002F Qt Smart Pointer \u002F C++0x Smart Pointer",[46,48578,48579],{},[184,48580,48581],{},"obj->value = random_number;",[34199,48583,48585],{"id":48584},"test-daffectation","Test d'affectation",[12,48587,48588,48589,48591],{},"Pour l'affectation nous allons créer une nouvelle variable qui pointera\nsur le même pointeur, et sur lequel on fera une modification. La\ncréation d'un pointeur peut arriver par exemple lors du passage du\npointeur à une fonction, ou lors de la déclaration d'une variable devant\ncontenir la même valeur. Cette déclaration supplémentaire a peu d'impact\npour un pointeur C mais pour un pointeur ",[362,48590,46045],{}," oblige la\ncréation d'un objet, et l'incrément d'un nombre d'instance (qu'on\ndécrémente ici dans la même boucle).",[22,48593,48594,48602],{},[25,48595,48596],{},[28,48597,48598,48600],{},[31,48599,48498],{},[31,48601,45877],{},[41,48603,48604,48613,48622,48631,48639,48648],{},[28,48605,48606,48608],{},[46,48607,48507],{},[46,48609,48610],{},[184,48611,48612],{},"ObjetTest * obj2 = obj;",[28,48614,48615,48617],{},[46,48616],{},[46,48618,48619],{},[184,48620,48621],{},"obj2->value = random_number;",[28,48623,48624,48626],{},[46,48625,48517],{},[46,48627,48628],{},[184,48629,48630],{},"QSharedPointer\u003CObjetTest> obj2 = obj;",[28,48632,48633,48635],{},[46,48634],{},[46,48636,48637],{},[184,48638,48621],{},[28,48640,48641,48643],{},[46,48642,48537],{},[46,48644,48645],{},[184,48646,48647],{},"std::tr1::shared_ptr\u003CObjetTest> obj2 = obj;",[28,48649,48650,48652],{},[46,48651],{},[46,48653,48654],{},[184,48655,48621],{},[34199,48657,48659],{"id":48658},"test-de-destruction","Test de destruction",[12,48661,48662,48663,48665],{},"Pour ce test, nous allons initialiser une liste de pointeur, et pour le\nbenchmark, nous allons supprimer un à un chaque élément de la liste.La\ndestruction du pointeur en C se fait par un ",[184,48664,34095],{},". Pour le pointeur\n''intelligent', il n'y a pas de destruction explicite. Nous allons juste\nsupprimer le pointeur de la liste, le pointeur sera alors\nautomatiquement détruit car il n'y aura plus de référence vers ce\npointeur.",[12,48667,48668,48669,44506],{},"Pour le cas de test utilisant la notion du Pool, on aura créé le\npointeur avec le delete ",[184,48670,48671],{},"returnToPool()",[2105,48673,48675],{"className":2387,"code":48674,"language":2389,"meta":1646,"style":1646},"void returnToPool(ObjetTest *obj)\n{\n    _queue.enqueue(obj);\n}\n",[184,48676,48677,48694,48698,48709],{"__ignoreMap":1646},[2113,48678,48679,48681,48684,48686,48688,48690,48692],{"class":2115,"line":2116},[2113,48680,46346],{"class":2326},[2113,48682,48683],{"class":2133}," returnToPool",[2113,48685,2423],{"class":2119},[2113,48687,48279],{"class":2414},[2113,48689,2671],{"class":2326},[2113,48691,9233],{"class":2429},[2113,48693,4660],{"class":2119},[2113,48695,48696],{"class":2115,"line":1647},[2113,48697,18060],{"class":2119},[2113,48699,48700,48703,48705,48707],{"class":2115,"line":1652},[2113,48701,48702],{"class":2414},"    _queue",[2113,48704,179],{"class":2119},[2113,48706,48179],{"class":2133},[2113,48708,48182],{"class":2119},[2113,48710,48711],{"class":2115,"line":2185},[2113,48712,2599],{"class":2119},[12,48714,48715],{},"Cette méthode ne fait pas de réelle destruction, mais juste un ajout de\nl'objet au pool.",[22,48717,48718,48726],{},[25,48719,48720],{},[28,48721,48722,48724],{},[31,48723,48498],{},[31,48725,45877],{},[41,48727,48728,48737,48746,48755,48761,48767],{},[28,48729,48730,48732],{},[46,48731,48507],{},[46,48733,48734],{},[184,48735,48736],{},"delete c_ptr_list.at(0);",[28,48738,48739,48741],{},[46,48740],{},[46,48742,48743],{},[184,48744,48745],{},"c_ptr_list.removeFirst ();",[28,48747,48748,48750],{},[46,48749,48517],{},[46,48751,48752],{},[184,48753,48754],{},"smart_ptr_list.removeFirst ();",[28,48756,48757,48759],{},[46,48758,48527],{},[46,48760],{},[28,48762,48763,48765],{},[46,48764,48537],{},[46,48766],{},[28,48768,48769,48771],{},[46,48770,48547],{},[46,48772],{},[34199,48774,48776],{"id":48775},"résultat-du-test","Résultat du test",[12,48778,48779],{},"Le test a été fait en utilisant la version 4.6.3 de Qt. Test effectué\npour 5 000 000 itérations.",[22,48781,48782,48803],{},[25,48783,48784],{},[28,48785,48786,48788,48791,48794,48797,48800],{},[31,48787],{},[31,48789,48790],{},"Pointeur C",[31,48792,48793],{},"Pointeur Qt",[31,48795,48796],{},"Pointeur C++0x",[31,48798,48799],{},"Pool en utilisant QSharedPointer",[31,48801,48802],{},"Pool en utilisant std::tr1::shared_ptr",[41,48804,48805,48826,48844,48862],{},[28,48806,48807,48811,48814,48817,48820,48823],{},[46,48808,48809],{},[184,48810,48288],{},[46,48812,48813],{},"0.0004275 msec",[46,48815,48816],{},"0.0007692 msec",[46,48818,48819],{},"0.0006604 msec",[46,48821,48822],{},"0.0002590 msec",[46,48824,48825],{},"0.0002286 msec",[28,48827,48828,48832,48835,48838,48840,48842],{},[46,48829,48830],{},[184,48831,48291],{},[46,48833,48834],{},"0.000010 msec",[46,48836,48837],{},"0.000012 msec",[46,48839,48837],{},[46,48841],{},[46,48843],{},[28,48845,48846,48850,48852,48855,48858,48860],{},[46,48847,48848],{},[184,48849,48294],{},[46,48851,48834],{},[46,48853,48854],{},"0.0000386 msec",[46,48856,48857],{},"0.0000230 msec",[46,48859],{},[46,48861],{},[28,48863,48864,48869,48872,48875,48878,48881],{},[46,48865,48866],{},[184,48867,48868],{},"Destruction",[46,48870,48871],{},"0.000190 msec",[46,48873,48874],{},"0.0003161 msec",[46,48876,48877],{},"0.0003359 msec",[46,48879,48880],{},"0.0004003 msec",[46,48882,48883],{},"0.0003601 msec",[12,48885,48886,48887,48889],{},"Conclusion que l'on peut en tirer, le pointeur C est ce qu'il y a de\nplus rapide à partir du moment où on fait de l'allocation de\nl'affectation ou de la destruction. Par contre il n'apporte pas la\nsouplesse qu'apporte les pointeurs ",[362,48888,46065],{}," entre autre pour les\napplications multi-threadé.",[12,48891,48892],{},"On remarque que le pointeur C++0x est plus rapide pour la création, mais\napparemment plus lent en destruction. Il est également possible avec le\npool de gagner en performance (surtout en création). Par contre le coût\nde destruction de l'objet n'est pas encore assez fort pour y gagner en\nutilisant le pool.",[12,48894,48895],{},"Ensuite il est important de se faire son propre jugement selon ses\nbesoins. Si besoin le source est attaché, vous pouvez faire vos propres\ntests.",[34199,48897,48899],{"id":48898},"source-du-test","Source du test",[12,48901,48902,48903,179],{},"Vous pouvez trouver les sources du test au ",[49,48904,41360],{"href":48905},"\u002FProgrammation\u002Fqt-performance-de-l-utilisation-de-qsharedpointer\u002Fsmart_benchmark.7z",[12,48907,48908,48913,48914,48919,48920,179],{},[49,48909,48912],{"href":48910,"rel":48911},"http:\u002F\u002Fwww.boost.org\u002Fdoc\u002Flibs\u002F1_45_0\u002Flibs\u002Fsmart_ptr\u002Fshared_ptr.htm",[347],"boost::shared_ptr"," du projet ",[49,48915,48918],{"href":48916,"rel":48917},"http:\u002F\u002Fwww.boost.org\u002F",[347],"Boost",". Boost est une librairie qui\najoute beaucoup de facilité pour les programmes en C++, comme par\nexemple les smart-pointer dont certains seront inclus dans C++0x, ou de\nla boucle ",[184,48921,48922],{},"std::for_each",[23219,48924,48926,48929],{"className":48925,"dataFootnotes":1646},[23222],[128,48927,23227],{"className":48928,"id":17406},[23226],[367,48930,48931,48937,48943,48951,48967,48973,48982,48988,48997,49006,49020,49029],{},[370,48932,48933,48934],{"id":23232},"en anglais : smart-pointer ",[49,48935,23240],{"href":23236,"ariaLabel":23237,"className":48936,"dataFootnoteBackref":1646},[23239],[370,48938,48939,48940],{"id":23243},"COW = Copy On Write ",[49,48941,23240],{"href":23251,"ariaLabel":23252,"className":48942,"dataFootnoteBackref":1646},[23239],[370,48944,48945,48946,45630,48948],{"id":43495},"Ce pointeur est l'équivalent du pointeur ",[362,48947,46045],{},[49,48949,23240],{"href":43499,"ariaLabel":35010,"className":48950,"dataFootnoteBackref":1646},[23239],[370,48952,48953,48954,48957,48958,48960,48961,48963,48964],{"id":43503},"Si à un moment donné il faut utiliser le pointeur C pour une\nraison quelconque, on peut utiliser ",[184,48955,48956],{},"ptr.data()"," mais il faut s'assurer\nque le pointeur ne sera pas détruit en déclarant un ",[184,48959,46120],{},"\ndans le même bloc utilisant le pointeur C. Le ",[184,48962,46120],{}," ne devra\nêtre détruit qu'après utilisation du pointeur C. Ceci peut être fait\ndans certain cas pour des raisons de performance. ",[49,48965,23240],{"href":43507,"ariaLabel":43508,"className":48966,"dataFootnoteBackref":1646},[23239],[370,48968,48969,48970],{"id":43512},"Sinon nous ne serions plus là pour lancer la méthode ",[49,48971,23240],{"href":43520,"ariaLabel":43521,"className":48972,"dataFootnoteBackref":1646},[23239],[370,48974,1583,48975,48978,48979],{"id":43525},[184,48976,48977],{},"QWeakPointeur"," ne gardant pas d'instance d'objet, car il\nn'incrémente pas le compteur de référence ",[49,48980,23240],{"href":43529,"ariaLabel":43530,"className":48981,"dataFootnoteBackref":1646},[23239],[370,48983,48984,48985],{"id":43534},"s'il n'existe pas de référence vers l'objet B ailleurs dans\nl'application ",[49,48986,23240],{"href":43538,"ariaLabel":43539,"className":48987,"dataFootnoteBackref":1646},[23239],[370,48989,48990,48991,48993,48994],{"id":43543},"Attention quand même, ",[184,48992,46120],{}," protège le pointeur\nmais pas le contenu ",[49,48995,23240],{"href":43552,"ariaLabel":43553,"className":48996,"dataFootnoteBackref":1646},[23239],[370,48998,49000,49001],{"id":48999},"user-content-fn-9","d'autres threads pouvant inclure le thread principal ",[49,49002,23240],{"href":49003,"ariaLabel":49004,"className":49005,"dataFootnoteBackref":1646},"#user-content-fnref-9","Back to reference 9",[23239],[370,49007,49009,49010,49012,49013,45630,49015],{"id":49008},"user-content-fn-10","Si l'objet est supprimé, ",[184,49011,46431],{},", sera alors remis à\n",[184,49014,18569],{},[49,49016,23240],{"href":49017,"ariaLabel":49018,"className":49019,"dataFootnoteBackref":1646},"#user-content-fnref-10","Back to reference 10",[23239],[370,49021,49023,49024],{"id":49022},"user-content-fn-11","Dans le code en question, il faudrait ajouter la notion de\nmutex autour de la gestion de la queue, en cas de création parallèle. ",[49,49025,23240],{"href":49026,"ariaLabel":49027,"className":49028,"dataFootnoteBackref":1646},"#user-content-fnref-11","Back to reference 11",[23239],[370,49030,49032,49033,45630,49035,49038,49039],{"id":49031},"user-content-fn-12","Le pointeur ",[362,49034,46045],{},[184,49036,49037],{},"shared_ptr"," de C++0x à pour origine\nle pointeur Boost ",[49,49040,23240],{"href":49041,"ariaLabel":49042,"className":49043,"dataFootnoteBackref":1646},"#user-content-fnref-12","Back to reference 12",[23239],[3358,49045,49046],{},"html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}",{"title":1646,"searchDepth":1647,"depth":1647,"links":49048},[49049,49050,49051,49052,49053],{"id":43624,"depth":1652,"text":43625},{"id":46130,"depth":1652,"text":46131},{"id":46134,"depth":1652,"text":46135},{"id":17006,"depth":1652,"text":17007},{"id":17406,"depth":1647,"text":23227},"2011-01-25",{"type":9,"value":49056},[49057,49059,49065,49080,49086],[133,49058,43625],{"id":43624},[12,49060,49061,46033,49063,46037],{},[362,49062,43910],{},[362,49064,46036],{},[12,49066,49067,46042,49069,49071,46051,49076,46054,49078,46057],{},[362,49068,43910],{},[362,49070,46045],{},[17400,49072,49073],{},[49,49074,3686],{"href":17404,"ariaDescribedBy":49075,"dataFootnoteRef":1646,"id":17407},[17406],[362,49077,46045],{},[362,49079,43910],{},[12,49081,49082,46062,49084,46066],{},[362,49083,43910],{},[362,49085,46065],{},[506,49087,49088,49103,49113,49118],{},[370,49089,49090,46076,49093,46082,49096,46086,49098,179],{},[49,49091,46075],{"href":46073,"rel":49092},[347],[49,49094,46081],{"href":46079,"rel":49095},[347],[362,49097,46085],{},[17400,49099,49100],{},[49,49101,2547],{"href":17977,"ariaDescribedBy":49102,"dataFootnoteRef":1646,"id":17979},[17406],[370,49104,49105,46076,49108,46102,49111,46105],{},[49,49106,46098],{"href":46096,"rel":49107},[347],[49,49109,46081],{"href":46079,"rel":49110},[347],[362,49112,46045],{},[370,49114,49115,46113],{},[49,49116,46112],{"href":46110,"rel":49117},[347],[370,49119,49120,46121,49123,46127],{},[49,49121,46120],{"href":46118,"rel":49122},[347],[17400,49124,49125],{},[49,49126,3696],{"href":41827,"ariaDescribedBy":49127,"dataFootnoteRef":1646,"id":41829},[17406],{},"\u002Fpost\u002Fqt-performance-de-l-utilisation-de-qsharedpointer",{"title":46024,"description":1646},"qt-performance-de-l-utilisation-de-qsharedpointer","posts\u002FProgrammation\u002F2011-01-25-qt-performance-de-l-utilisation-de-qsharedpointer",[43895,49134,38761],"performance","XqeYjPQRtPf9XXPuxti7oBEPUUnQd2DT_XGvWB1Y7Ws",{"id":49137,"title":49138,"author":7,"body":49139,"category":2057,"categorySlug":2058,"date":50328,"description":1646,"excerpt":50329,"extension":1764,"location":1765,"meta":50341,"navigation":1767,"path":50342,"published":1767,"seo":50343,"slug":50344,"stem":50345,"tags":50346,"timeToRead":2230,"__hash__":50347},"posts\u002Fposts\u002FProgrammation\u002F2010-12-22-qt-concatenation-de-chaine-de-caracteres.md","C++\u002FQt - Concaténation de chaînes de caractères",{"type":9,"value":49140,"toc":50321},[49141,49143,49146,49149,49152,49155,49159,49165,49221,49230,49233,49237,49244,49256,49262,49324,49333,49342,49404,49406,49415,49417,49425,50275,50277,50280,50302,50305,50308,50312,50318],[133,49142,43625],{"id":43624},[12,49144,49145],{},"Qt est un framework orienté objet écrit en C++ et permettant de faire\ndes interfaces graphiques à l’aide de ces widgets. Ce framework est\nutilisé par le projet KDE depuis ses débuts pour en faire un\nenvironnement très complet.",[12,49147,49148],{},"Qt permet donc de faire des interfaces graphiques mais aussi d’accéder à\ndes bases de données SQL, de faire de la communication réseau, une\ngestion simplifiée des threads, la lecture de fichier XML. Qt intègre\naussi le moteur HTML Webkit.",[12,49150,49151],{},"Qt ajoute une couche supplémentaire au C++ permettant de faire de\nl’introspection de classe un peu plus poussée (comme l’appel d’une\nméthode dont on ne connaît le nom qu’à l’exécution). Qt permet également\nla gestion d’évènement par l’intermédiaire d’un système puissant de\nSIGNALS et de SLOTS.",[12,49153,49154],{},"Dans la suite de cet article nous allons nous concentrer sur une très\npetite partie de Qt mais qui est utilisée dans beaucoup d’applications\nécrites en Qt : les chaînes de caractères, et plus précisément, la\nconcaténation de chaînes de caractères.",[128,49156,49158],{"id":49157},"concaténation-de-chaînes-de-caractères","Concaténation de chaînes de caractères",[12,49160,49161,49162,49164],{},"Comme dans d’autres langages, la concaténation de chaînes de caractères\nse fait à l'aide de l'opérateur ",[184,49163,3061],{},". Prenons un exemple simple :",[2105,49166,49168],{"className":2387,"code":49167,"language":2389,"meta":1646,"style":1646},"#include \u003CQString>\n\nQString abc = \"abc\", def = \"def\";\nQString result;\nresult = abc + def;\n",[184,49169,49170,49177,49181,49201,49206],{"__ignoreMap":1646},[2113,49171,49172,49174],{"class":2115,"line":2116},[2113,49173,42057],{"class":2326},[2113,49175,49176],{"class":2149}," \u003CQString>\n",[2113,49178,49179],{"class":2115,"line":1647},[2113,49180,2125],{"emptyLinePlaceholder":1767},[2113,49182,49183,49186,49188,49191,49194,49196,49199],{"class":2115,"line":1652},[2113,49184,49185],{"class":2119},"QString abc ",[2113,49187,2335],{"class":2326},[2113,49189,49190],{"class":2149}," \"abc\"",[2113,49192,49193],{"class":2119},", def ",[2113,49195,2335],{"class":2326},[2113,49197,49198],{"class":2149}," \"def\"",[2113,49200,2487],{"class":2119},[2113,49202,49203],{"class":2115,"line":2185},[2113,49204,49205],{"class":2119},"QString result;\n",[2113,49207,49208,49211,49213,49216,49218],{"class":2115,"line":2230},[2113,49209,49210],{"class":2119},"result ",[2113,49212,2335],{"class":2326},[2113,49214,49215],{"class":2119}," abc ",[2113,49217,3061],{"class":2326},[2113,49219,49220],{"class":2119}," def;\n",[12,49222,49223,49224,49226,49227,49229],{},"L'utilisation de l'opérateur ",[184,49225,3061],{}," facilite l'écriture de la concaténation\ndes chaînes de caractères (tout comme l'opérateur ",[184,49228,4132],{}," permet de\nfacilement comparer des chaînes de caractères de type QString).",[12,49231,49232],{},"Malheureusement cette écriture rend le code peu performant pour diverses\nraisons (que l’on peut retrouver dans la documentation de l’objet\nQString).",[133,49234,49236],{"id":49235},"concaténation-rapide-de-chaînes-de-caractères","Concaténation rapide de chaînes de caractères",[12,49238,49239,49240,49243],{},"A partir de Qt 4.6, Nokia, a ajouté un ",[184,49241,49242],{},"template"," nommé QStringBuilder\n(cet objet fait beaucoup penser à l'objet StringBuilder de JAVA\npermettant d'accélérer les concaténations de chaînes de caractères).",[12,49245,49246,49247,49250,49251,46701,49253,49255],{},"Ce template ne s’utilise pas directement mais au travers de l'opérateur\n",[184,49248,49249],{},"%",". En remplaçant donc l'opérateur ",[184,49252,3061],{},[184,49254,49249],{}," on gagne en performance\n(dixit la doc de Qt).",[12,49257,49258,49259,44506],{},"Pour pouvoir utiliser l'opérateur, il suffit d'inclure le fichier\n",[184,49260,49261],{},"QStringBuilder",[2105,49263,49265],{"className":2387,"code":49264,"language":2389,"meta":1646,"style":1646},"#include \u003CQStringBuilder>\n\nQString abc = \"abc\", def = \"def\", ghi = \"ghi\";\nQString result;\nresult = abc % def % ghi;\n",[184,49266,49267,49274,49278,49302,49306],{"__ignoreMap":1646},[2113,49268,49269,49271],{"class":2115,"line":2116},[2113,49270,42057],{"class":2326},[2113,49272,49273],{"class":2149}," \u003CQStringBuilder>\n",[2113,49275,49276],{"class":2115,"line":1647},[2113,49277,2125],{"emptyLinePlaceholder":1767},[2113,49279,49280,49282,49284,49286,49288,49290,49292,49295,49297,49300],{"class":2115,"line":1652},[2113,49281,49185],{"class":2119},[2113,49283,2335],{"class":2326},[2113,49285,49190],{"class":2149},[2113,49287,49193],{"class":2119},[2113,49289,2335],{"class":2326},[2113,49291,49198],{"class":2149},[2113,49293,49294],{"class":2119},", ghi ",[2113,49296,2335],{"class":2326},[2113,49298,49299],{"class":2149}," \"ghi\"",[2113,49301,2487],{"class":2119},[2113,49303,49304],{"class":2115,"line":2185},[2113,49305,49205],{"class":2119},[2113,49307,49308,49310,49312,49314,49316,49319,49321],{"class":2115,"line":2230},[2113,49309,49210],{"class":2119},[2113,49311,2335],{"class":2326},[2113,49313,49215],{"class":2119},[2113,49315,49249],{"class":2326},[2113,49317,49318],{"class":2119}," def ",[2113,49320,49249],{"class":2326},[2113,49322,49323],{"class":2119}," ghi;\n",[12,49325,49326,49327,34384,49330,179],{},"Une autre possibilité est de définir (par exemple dans votre fichier\n.pro ou dans un include général de votre application) les définitions\n",[184,49328,49329],{},"QT_USE_FAST_CONCATENATION",[184,49331,49332],{},"QT_USE_FAST_OPERATOR_PLUS",[12,49334,49335,49336,49338,49339,49341],{},"Ces définitions permettent de remplacer l'opérateur ",[184,49337,3061],{}," par l'opérateur\n",[184,49340,49249],{}," dans toute l'application. Par contre cette opération peut demander\nquelques modifications de votre code.",[2105,49343,49345],{"className":2387,"code":49344,"language":2389,"meta":1646,"style":1646},"#define QT_USE_FAST_CONCATENATION\n#define QT_USE_FAST_OPERATOR_PLUS\n#include \u003CQString>\n\nQString abc = \"abc\", def = \"def\";\nQString result;\nresult = abc + def;\n",[184,49346,49347,49355,49362,49368,49372,49388,49392],{"__ignoreMap":1646},[2113,49348,49349,49352],{"class":2115,"line":2116},[2113,49350,49351],{"class":2326},"#define",[2113,49353,49354],{"class":2133}," QT_USE_FAST_CONCATENATION\n",[2113,49356,49357,49359],{"class":2115,"line":1647},[2113,49358,49351],{"class":2326},[2113,49360,49361],{"class":2133}," QT_USE_FAST_OPERATOR_PLUS\n",[2113,49363,49364,49366],{"class":2115,"line":1652},[2113,49365,42057],{"class":2326},[2113,49367,49176],{"class":2149},[2113,49369,49370],{"class":2115,"line":2185},[2113,49371,2125],{"emptyLinePlaceholder":1767},[2113,49373,49374,49376,49378,49380,49382,49384,49386],{"class":2115,"line":2230},[2113,49375,49185],{"class":2119},[2113,49377,2335],{"class":2326},[2113,49379,49190],{"class":2149},[2113,49381,49193],{"class":2119},[2113,49383,2335],{"class":2326},[2113,49385,49198],{"class":2149},[2113,49387,2487],{"class":2119},[2113,49389,49390],{"class":2115,"line":2235},[2113,49391,49205],{"class":2119},[2113,49393,49394,49396,49398,49400,49402],{"class":2115,"line":2241},[2113,49395,49210],{"class":2119},[2113,49397,2335],{"class":2326},[2113,49399,49215],{"class":2119},[2113,49401,3061],{"class":2326},[2113,49403,49220],{"class":2119},[133,49405,17007],{"id":17006},[12,49407,49408,49409,49411,49412,49414],{},"Nous allons donc dans la suite du document vérifier que l'opérateur ",[184,49410,49249],{},"\nest plus rapide que l'opérateur ",[184,49413,3061],{},". Pour cela nous allons utiliser le\nmodule de benchmark de Qt.",[27533,49416,48273],{"id":48272},[12,49418,49419,49420,49422,49423,179],{},"Voici le petit programme permettant de tester la performance de la\nconcaténation. Notre exemple simple va constituer à la concaténation de\n26 chaînes de caractères créées au début. L'appel se fera une première\nfois en utilisant l'opérateur ",[184,49421,3061],{}," et une seconde fois en utilisant\nl'opérateur ",[184,49424,49249],{},[2105,49426,49428],{"className":2387,"code":49427,"language":2389,"meta":1646,"style":1646},"#include \u003CQtCore\u002FQString>\n#include \u003CQtTest\u002FQtTest>\n#include \u003CQStringBuilder>\n\nclass TestPerf : public QObject\n{\n    Q_OBJECT\npublic:\n    TestPerf();\nprivate Q_SLOTS:\n    void concat();\n    void concat_data();\n};\n\nTestPerf::TestPerf()\n{\n} \n\nvoid TestPerf::concat()\n{\n    QFETCH(bool, useStringBuilder);\n    QString result;\n    QString a(\"a\"), b(\"b\"), c(\"c\"), d(\"d\"), e(\"e\"), f(\"f\"), g(\"g\"),\n    h(\"h\"), i(\"i\"), j(\"j\"), k(\"k\"), l(\"l\"), m(\"m\"), n(\"n\"), o(\"o\"),\n    p(\"p\"), q(\"q\"), r(\"r\"), s(\"s\"), t(\"t\"), u(\"u\"), v(\"v\"), w(\"w\"),\n    x(\"x\"), y(\"y\"), z(\"z\"); \n\n    if (useStringBuilder)\n    {\n        QBENCHMARK {\n            \u002F\u002F Concaténation en utilisant QStringBuilder\n            \u002F\u002F Dans ce cas la concaténation devrait être plus rapide\n            result = a % b % c % d % e % f % g % h % i % j % k % l %\n            m % n % o % p % q % r % s % t % u % v % w % x % y % z;\n        }\n    }\n    else\n    {\n        QBENCHMARK\n        {\n            \u002F\u002F Concaténation en n'utilisant pas QStringBuilder. On\n            \u002F\u002F utilise alors la concaténation normal de chaînes de\n            \u002F\u002F caractères en utilisant QString\n            result = a + b + c + d + e + f + g + h + i + j + k + l +\n            m + n + o + p + q + r + s + t + u + v + w + x + y + z;\n        }\n    }\n}\n\nvoid TestPerf::concat_data()\n{\n    QTest::addColumn\u003Cbool>(\"useStringBuilder\");\n    QTest::newRow(\"Don't use QStringBuilder\") \u003C\u003C false;\n    QTest::newRow(\"Use QStringBuilder\") \u003C\u003C true;\n}\n\nQTEST_APPLESS_MAIN(TestPerf);\n\n#include \"tst_testperf.moc\"\n",[184,49429,49430,49437,49444,49450,49454,49469,49473,49478,49482,49489,49494,49503,49512,49516,49520,49531,49535,49540,49544,49557,49561,49573,49578,49647,49727,49808,49840,49844,49851,49855,49860,49865,49870,49937,50006,50010,50014,50018,50022,50027,50031,50036,50041,50046,50101,50157,50161,50165,50169,50173,50186,50190,50209,50229,50248,50252,50256,50264,50268],{"__ignoreMap":1646},[2113,49431,49432,49434],{"class":2115,"line":2116},[2113,49433,42057],{"class":2326},[2113,49435,49436],{"class":2149}," \u003CQtCore\u002FQString>\n",[2113,49438,49439,49441],{"class":2115,"line":1647},[2113,49440,42057],{"class":2326},[2113,49442,49443],{"class":2149}," \u003CQtTest\u002FQtTest>\n",[2113,49445,49446,49448],{"class":2115,"line":1652},[2113,49447,42057],{"class":2326},[2113,49449,49273],{"class":2149},[2113,49451,49452],{"class":2115,"line":2185},[2113,49453,2125],{"emptyLinePlaceholder":1767},[2113,49455,49456,49458,49461,49464,49466],{"class":2115,"line":2230},[2113,49457,20538],{"class":2326},[2113,49459,49460],{"class":2414}," TestPerf",[2113,49462,49463],{"class":2119}," : ",[2113,49465,35106],{"class":2326},[2113,49467,49468],{"class":2414}," QObject\n",[2113,49470,49471],{"class":2115,"line":2235},[2113,49472,18060],{"class":2119},[2113,49474,49475],{"class":2115,"line":2241},[2113,49476,49477],{"class":2119},"    Q_OBJECT\n",[2113,49479,49480],{"class":2115,"line":2246},[2113,49481,46917],{"class":2326},[2113,49483,49484,49487],{"class":2115,"line":2464},[2113,49485,49486],{"class":2133},"    TestPerf",[2113,49488,3944],{"class":2119},[2113,49490,49491],{"class":2115,"line":2085},[2113,49492,49493],{"class":2119},"private Q_SLOTS:\n",[2113,49495,49496,49498,49501],{"class":2115,"line":2514},[2113,49497,47385],{"class":2326},[2113,49499,49500],{"class":2133}," concat",[2113,49502,3944],{"class":2119},[2113,49504,49505,49507,49510],{"class":2115,"line":2533},[2113,49506,47385],{"class":2326},[2113,49508,49509],{"class":2133}," concat_data",[2113,49511,3944],{"class":2119},[2113,49513,49514],{"class":2115,"line":2556},[2113,49515,2787],{"class":2119},[2113,49517,49518],{"class":2115,"line":2569},[2113,49519,2125],{"emptyLinePlaceholder":1767},[2113,49521,49522,49525,49527,49529],{"class":2115,"line":2575},[2113,49523,49524],{"class":2414},"TestPerf",[2113,49526,2418],{"class":2119},[2113,49528,49524],{"class":2133},[2113,49530,6745],{"class":2119},[2113,49532,49533],{"class":2115,"line":2596},[2113,49534,18060],{"class":2119},[2113,49536,49537],{"class":2115,"line":3192},[2113,49538,49539],{"class":2119},"} \n",[2113,49541,49542],{"class":2115,"line":3213},[2113,49543,2125],{"emptyLinePlaceholder":1767},[2113,49545,49546,49548,49550,49552,49555],{"class":2115,"line":3236},[2113,49547,46346],{"class":2326},[2113,49549,49460],{"class":2414},[2113,49551,2418],{"class":2119},[2113,49553,49554],{"class":2133},"concat",[2113,49556,6745],{"class":2119},[2113,49558,49559],{"class":2115,"line":3248},[2113,49560,18060],{"class":2119},[2113,49562,49563,49566,49568,49570],{"class":2115,"line":4899},[2113,49564,49565],{"class":2133},"    QFETCH",[2113,49567,2423],{"class":2119},[2113,49569,4716],{"class":2326},[2113,49571,49572],{"class":2119},", useStringBuilder);\n",[2113,49574,49575],{"class":2115,"line":1777},[2113,49576,49577],{"class":2119},"    QString result;\n",[2113,49579,49580,49583,49585,49587,49590,49592,49594,49596,49599,49601,49603,49605,49608,49610,49612,49614,49617,49619,49621,49623,49626,49628,49630,49632,49635,49637,49640,49642,49645],{"class":2115,"line":4931},[2113,49581,49582],{"class":2119},"    QString ",[2113,49584,49],{"class":2133},[2113,49586,2423],{"class":2119},[2113,49588,49589],{"class":2149},"\"a\"",[2113,49591,8507],{"class":2119},[2113,49593,3992],{"class":2133},[2113,49595,2423],{"class":2119},[2113,49597,49598],{"class":2149},"\"b\"",[2113,49600,8507],{"class":2119},[2113,49602,4097],{"class":2133},[2113,49604,2423],{"class":2119},[2113,49606,49607],{"class":2149},"\"c\"",[2113,49609,8507],{"class":2119},[2113,49611,29933],{"class":2133},[2113,49613,2423],{"class":2119},[2113,49615,49616],{"class":2149},"\"d\"",[2113,49618,8507],{"class":2119},[2113,49620,5601],{"class":2133},[2113,49622,2423],{"class":2119},[2113,49624,49625],{"class":2149},"\"e\"",[2113,49627,8507],{"class":2119},[2113,49629,3966],{"class":2133},[2113,49631,2423],{"class":2119},[2113,49633,49634],{"class":2149},"\"f\"",[2113,49636,8507],{"class":2119},[2113,49638,49639],{"class":2133},"g",[2113,49641,2423],{"class":2119},[2113,49643,49644],{"class":2149},"\"g\"",[2113,49646,7142],{"class":2119},[2113,49648,49649,49652,49654,49657,49659,49661,49663,49666,49668,49671,49673,49676,49678,49681,49683,49686,49688,49691,49693,49696,49698,49701,49703,49706,49708,49710,49712,49715,49717,49720,49722,49725],{"class":2115,"line":4961},[2113,49650,49651],{"class":2133},"    h",[2113,49653,2423],{"class":2119},[2113,49655,49656],{"class":2149},"\"h\"",[2113,49658,8507],{"class":2119},[2113,49660,7919],{"class":2133},[2113,49662,2423],{"class":2119},[2113,49664,49665],{"class":2149},"\"i\"",[2113,49667,8507],{"class":2119},[2113,49669,49670],{"class":2133},"j",[2113,49672,2423],{"class":2119},[2113,49674,49675],{"class":2149},"\"j\"",[2113,49677,8507],{"class":2119},[2113,49679,49680],{"class":2133},"k",[2113,49682,2423],{"class":2119},[2113,49684,49685],{"class":2149},"\"k\"",[2113,49687,8507],{"class":2119},[2113,49689,49690],{"class":2133},"l",[2113,49692,2423],{"class":2119},[2113,49694,49695],{"class":2149},"\"l\"",[2113,49697,8507],{"class":2119},[2113,49699,49700],{"class":2133},"m",[2113,49702,2423],{"class":2119},[2113,49704,49705],{"class":2149},"\"m\"",[2113,49707,8507],{"class":2119},[2113,49709,9433],{"class":2133},[2113,49711,2423],{"class":2119},[2113,49713,49714],{"class":2149},"\"n\"",[2113,49716,8507],{"class":2119},[2113,49718,49719],{"class":2133},"o",[2113,49721,2423],{"class":2119},[2113,49723,49724],{"class":2149},"\"o\"",[2113,49726,7142],{"class":2119},[2113,49728,49729,49732,49734,49737,49739,49742,49744,49747,49749,49752,49754,49757,49759,49762,49764,49767,49769,49772,49774,49777,49779,49781,49783,49786,49788,49791,49793,49796,49798,49801,49803,49806],{"class":2115,"line":4976},[2113,49730,49731],{"class":2133},"    p",[2113,49733,2423],{"class":2119},[2113,49735,49736],{"class":2149},"\"p\"",[2113,49738,8507],{"class":2119},[2113,49740,49741],{"class":2133},"q",[2113,49743,2423],{"class":2119},[2113,49745,49746],{"class":2149},"\"q\"",[2113,49748,8507],{"class":2119},[2113,49750,49751],{"class":2133},"r",[2113,49753,2423],{"class":2119},[2113,49755,49756],{"class":2149},"\"r\"",[2113,49758,8507],{"class":2119},[2113,49760,49761],{"class":2133},"s",[2113,49763,2423],{"class":2119},[2113,49765,49766],{"class":2149},"\"s\"",[2113,49768,8507],{"class":2119},[2113,49770,49771],{"class":2133},"t",[2113,49773,2423],{"class":2119},[2113,49775,49776],{"class":2149},"\"t\"",[2113,49778,8507],{"class":2119},[2113,49780,2550],{"class":2133},[2113,49782,2423],{"class":2119},[2113,49784,49785],{"class":2149},"\"u\"",[2113,49787,8507],{"class":2119},[2113,49789,49790],{"class":2133},"v",[2113,49792,2423],{"class":2119},[2113,49794,49795],{"class":2149},"\"v\"",[2113,49797,8507],{"class":2119},[2113,49799,49800],{"class":2133},"w",[2113,49802,2423],{"class":2119},[2113,49804,49805],{"class":2149},"\"w\"",[2113,49807,7142],{"class":2119},[2113,49809,49810,49813,49815,49818,49820,49823,49825,49828,49830,49833,49835,49837],{"class":2115,"line":4993},[2113,49811,49812],{"class":2133},"    x",[2113,49814,2423],{"class":2119},[2113,49816,49817],{"class":2149},"\"x\"",[2113,49819,8507],{"class":2119},[2113,49821,49822],{"class":2133},"y",[2113,49824,2423],{"class":2119},[2113,49826,49827],{"class":2149},"\"y\"",[2113,49829,8507],{"class":2119},[2113,49831,49832],{"class":2133},"z",[2113,49834,2423],{"class":2119},[2113,49836,34047],{"class":2149},[2113,49838,49839],{"class":2119},"); \n",[2113,49841,49842],{"class":2115,"line":5013},[2113,49843,2125],{"emptyLinePlaceholder":1767},[2113,49845,49846,49848],{"class":2115,"line":5018},[2113,49847,7394],{"class":2326},[2113,49849,49850],{"class":2119}," (useStringBuilder)\n",[2113,49852,49853],{"class":2115,"line":5042},[2113,49854,18224],{"class":2119},[2113,49856,49857],{"class":2115,"line":5057},[2113,49858,49859],{"class":2119},"        QBENCHMARK {\n",[2113,49861,49862],{"class":2115,"line":5062},[2113,49863,49864],{"class":2396},"            \u002F\u002F Concaténation en utilisant QStringBuilder\n",[2113,49866,49867],{"class":2115,"line":5098},[2113,49868,49869],{"class":2396},"            \u002F\u002F Dans ce cas la concaténation devrait être plus rapide\n",[2113,49871,49872,49875,49877,49880,49882,49885,49887,49890,49892,49895,49897,49900,49902,49905,49907,49910,49912,49915,49917,49920,49922,49925,49927,49930,49932,49934],{"class":2115,"line":5117},[2113,49873,49874],{"class":2119},"            result ",[2113,49876,2335],{"class":2326},[2113,49878,49879],{"class":2119}," a ",[2113,49881,49249],{"class":2326},[2113,49883,49884],{"class":2119}," b ",[2113,49886,49249],{"class":2326},[2113,49888,49889],{"class":2119}," c ",[2113,49891,49249],{"class":2326},[2113,49893,49894],{"class":2119}," d ",[2113,49896,49249],{"class":2326},[2113,49898,49899],{"class":2119}," e ",[2113,49901,49249],{"class":2326},[2113,49903,49904],{"class":2119}," f ",[2113,49906,49249],{"class":2326},[2113,49908,49909],{"class":2119}," g ",[2113,49911,49249],{"class":2326},[2113,49913,49914],{"class":2119}," h ",[2113,49916,49249],{"class":2326},[2113,49918,49919],{"class":2119}," i ",[2113,49921,49249],{"class":2326},[2113,49923,49924],{"class":2119}," j ",[2113,49926,49249],{"class":2326},[2113,49928,49929],{"class":2119}," k ",[2113,49931,49249],{"class":2326},[2113,49933,29631],{"class":2119},[2113,49935,49936],{"class":2326},"%\n",[2113,49938,49939,49942,49944,49946,49948,49951,49953,49956,49958,49961,49963,49966,49968,49971,49973,49976,49978,49981,49983,49986,49988,49991,49993,49996,49998,50001,50003],{"class":2115,"line":5142},[2113,49940,49941],{"class":2119},"            m ",[2113,49943,49249],{"class":2326},[2113,49945,33025],{"class":2119},[2113,49947,49249],{"class":2326},[2113,49949,49950],{"class":2119}," o ",[2113,49952,49249],{"class":2326},[2113,49954,49955],{"class":2119}," p ",[2113,49957,49249],{"class":2326},[2113,49959,49960],{"class":2119}," q ",[2113,49962,49249],{"class":2326},[2113,49964,49965],{"class":2119}," r ",[2113,49967,49249],{"class":2326},[2113,49969,49970],{"class":2119}," s ",[2113,49972,49249],{"class":2326},[2113,49974,49975],{"class":2119}," t ",[2113,49977,49249],{"class":2326},[2113,49979,49980],{"class":2119}," u ",[2113,49982,49249],{"class":2326},[2113,49984,49985],{"class":2119}," v ",[2113,49987,49249],{"class":2326},[2113,49989,49990],{"class":2119}," w ",[2113,49992,49249],{"class":2326},[2113,49994,49995],{"class":2119}," x ",[2113,49997,49249],{"class":2326},[2113,49999,50000],{"class":2119}," y ",[2113,50002,49249],{"class":2326},[2113,50004,50005],{"class":2119}," z;\n",[2113,50007,50008],{"class":2115,"line":5148},[2113,50009,4578],{"class":2119},[2113,50011,50012],{"class":2115,"line":5163},[2113,50013,4665],{"class":2119},[2113,50015,50016],{"class":2115,"line":5169},[2113,50017,7439],{"class":2326},[2113,50019,50020],{"class":2115,"line":5175},[2113,50021,18224],{"class":2119},[2113,50023,50024],{"class":2115,"line":5180},[2113,50025,50026],{"class":2119},"        QBENCHMARK\n",[2113,50028,50029],{"class":2115,"line":5199},[2113,50030,35499],{"class":2119},[2113,50032,50033],{"class":2115,"line":5204},[2113,50034,50035],{"class":2396},"            \u002F\u002F Concaténation en n'utilisant pas QStringBuilder. On\n",[2113,50037,50038],{"class":2115,"line":5209},[2113,50039,50040],{"class":2396},"            \u002F\u002F utilise alors la concaténation normal de chaînes de\n",[2113,50042,50043],{"class":2115,"line":5232},[2113,50044,50045],{"class":2396},"            \u002F\u002F caractères en utilisant QString\n",[2113,50047,50048,50050,50052,50054,50056,50058,50060,50062,50064,50066,50068,50070,50072,50074,50076,50078,50080,50082,50084,50086,50088,50090,50092,50094,50096,50098],{"class":2115,"line":5237},[2113,50049,49874],{"class":2119},[2113,50051,2335],{"class":2326},[2113,50053,49879],{"class":2119},[2113,50055,3061],{"class":2326},[2113,50057,49884],{"class":2119},[2113,50059,3061],{"class":2326},[2113,50061,49889],{"class":2119},[2113,50063,3061],{"class":2326},[2113,50065,49894],{"class":2119},[2113,50067,3061],{"class":2326},[2113,50069,49899],{"class":2119},[2113,50071,3061],{"class":2326},[2113,50073,49904],{"class":2119},[2113,50075,3061],{"class":2326},[2113,50077,49909],{"class":2119},[2113,50079,3061],{"class":2326},[2113,50081,49914],{"class":2119},[2113,50083,3061],{"class":2326},[2113,50085,49919],{"class":2119},[2113,50087,3061],{"class":2326},[2113,50089,49924],{"class":2119},[2113,50091,3061],{"class":2326},[2113,50093,49929],{"class":2119},[2113,50095,3061],{"class":2326},[2113,50097,29631],{"class":2119},[2113,50099,50100],{"class":2326},"+\n",[2113,50102,50103,50105,50107,50109,50111,50113,50115,50117,50119,50121,50123,50125,50127,50129,50131,50133,50135,50137,50139,50141,50143,50145,50147,50149,50151,50153,50155],{"class":2115,"line":5242},[2113,50104,49941],{"class":2119},[2113,50106,3061],{"class":2326},[2113,50108,33025],{"class":2119},[2113,50110,3061],{"class":2326},[2113,50112,49950],{"class":2119},[2113,50114,3061],{"class":2326},[2113,50116,49955],{"class":2119},[2113,50118,3061],{"class":2326},[2113,50120,49960],{"class":2119},[2113,50122,3061],{"class":2326},[2113,50124,49965],{"class":2119},[2113,50126,3061],{"class":2326},[2113,50128,49970],{"class":2119},[2113,50130,3061],{"class":2326},[2113,50132,49975],{"class":2119},[2113,50134,3061],{"class":2326},[2113,50136,49980],{"class":2119},[2113,50138,3061],{"class":2326},[2113,50140,49985],{"class":2119},[2113,50142,3061],{"class":2326},[2113,50144,49990],{"class":2119},[2113,50146,3061],{"class":2326},[2113,50148,49995],{"class":2119},[2113,50150,3061],{"class":2326},[2113,50152,50000],{"class":2119},[2113,50154,3061],{"class":2326},[2113,50156,50005],{"class":2119},[2113,50158,50159],{"class":2115,"line":5267},[2113,50160,4578],{"class":2119},[2113,50162,50163],{"class":2115,"line":5282},[2113,50164,4665],{"class":2119},[2113,50166,50167],{"class":2115,"line":5295},[2113,50168,2599],{"class":2119},[2113,50170,50171],{"class":2115,"line":5310},[2113,50172,2125],{"emptyLinePlaceholder":1767},[2113,50174,50175,50177,50179,50181,50184],{"class":2115,"line":5315},[2113,50176,46346],{"class":2326},[2113,50178,49460],{"class":2414},[2113,50180,2418],{"class":2119},[2113,50182,50183],{"class":2133},"concat_data",[2113,50185,6745],{"class":2119},[2113,50187,50188],{"class":2115,"line":5320},[2113,50189,18060],{"class":2119},[2113,50191,50192,50195,50198,50200,50202,50204,50207],{"class":2115,"line":8625},[2113,50193,50194],{"class":2119},"    QTest::",[2113,50196,50197],{"class":2133},"addColumn",[2113,50199,3109],{"class":2119},[2113,50201,4716],{"class":2326},[2113,50203,19291],{"class":2119},[2113,50205,50206],{"class":2149},"\"useStringBuilder\"",[2113,50208,2553],{"class":2119},[2113,50210,50211,50213,50216,50218,50221,50223,50225,50227],{"class":2115,"line":8631},[2113,50212,50194],{"class":2119},[2113,50214,50215],{"class":2133},"newRow",[2113,50217,2423],{"class":2119},[2113,50219,50220],{"class":2149},"\"Don't use QStringBuilder\"",[2113,50222,5709],{"class":2119},[2113,50224,9427],{"class":2326},[2113,50226,5052],{"class":2166},[2113,50228,2487],{"class":2119},[2113,50230,50231,50233,50235,50237,50240,50242,50244,50246],{"class":2115,"line":8637},[2113,50232,50194],{"class":2119},[2113,50234,50215],{"class":2133},[2113,50236,2423],{"class":2119},[2113,50238,50239],{"class":2149},"\"Use QStringBuilder\"",[2113,50241,5709],{"class":2119},[2113,50243,9427],{"class":2326},[2113,50245,4872],{"class":2166},[2113,50247,2487],{"class":2119},[2113,50249,50250],{"class":2115,"line":8652},[2113,50251,2599],{"class":2119},[2113,50253,50254],{"class":2115,"line":8674},[2113,50255,2125],{"emptyLinePlaceholder":1767},[2113,50257,50258,50261],{"class":2115,"line":8706},[2113,50259,50260],{"class":2133},"QTEST_APPLESS_MAIN",[2113,50262,50263],{"class":2119},"(TestPerf);\n",[2113,50265,50266],{"class":2115,"line":8724},[2113,50267,2125],{"emptyLinePlaceholder":1767},[2113,50269,50270,50272],{"class":2115,"line":8729},[2113,50271,42057],{"class":2326},[2113,50273,50274],{"class":2149}," \"tst_testperf.moc\"\n",[27533,50276,48776],{"id":48775},[12,50278,50279],{},"Le test a été fait en utilisant la version 4.6.3 de Qt. Le résultat est\nsensiblement le même avec la version 4.7.0",[22,50281,50282,50292],{},[25,50283,50284],{},[28,50285,50286,50289],{},[31,50287,50288],{},"Sans QStringBuilder",[31,50290,50291],{},"Avec QStringBuilder",[41,50293,50294],{},[28,50295,50296,50299],{},[46,50297,50298],{},"0.00367 msec",[46,50300,50301],{},"0.00046 msec",[12,50303,50304],{},"On voit dans ce test que la version en utilisant QStringBuilder est 8\nfois plus rapide que sans. Bien sûr vu le temps que prend la\nconcaténation de chaînes de caractères, ce test commence à avoir de\nl'intérêt uniquement à partir du moment où on fait beaucoup de\nconcaténation dans la seconde, ou si les temps de réponses sont\ncritiques.",[12,50306,50307],{},"Sur ce, je vous souhaite à tous un Joyeux noël et une bonne année.",[27533,50309,50311],{"id":50310},"le-programme","Le programme",[12,50313,50314,50315,179],{},"Vous pouvez trouver le programme ",[49,50316,43548],{"href":50317},"\u002FProgrammation\u002Fqt-concatenation-de-chaine-de-caracteres\u002Ftest-perfconcat.7z",[3358,50319,50320],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}",{"title":1646,"searchDepth":1647,"depth":1647,"links":50322},[50323,50324],{"id":43624,"depth":1652,"text":43625},{"id":49157,"depth":1647,"text":49158,"children":50325},[50326,50327],{"id":49235,"depth":1652,"text":49236},{"id":17006,"depth":1652,"text":17007},"2010-12-22",{"type":9,"value":50330},[50331,50333,50335,50337,50339],[133,50332,43625],{"id":43624},[12,50334,49145],{},[12,50336,49148],{},[12,50338,49151],{},[12,50340,49154],{},{},"\u002Fpost\u002Fqt-concatenation-de-chaine-de-caracteres",{"title":49138,"description":1646},"qt-concatenation-de-chaine-de-caracteres","posts\u002FProgrammation\u002F2010-12-22-qt-concatenation-de-chaine-de-caracteres",[49134,38761],"tnYQV8OJKsLZvtUPPbMHph26Eitmdp4-hd-STXhGP2A",{"id":50349,"title":50350,"author":7,"body":50351,"category":2057,"categorySlug":2058,"date":51976,"description":1646,"excerpt":51977,"extension":1764,"location":1765,"meta":52012,"navigation":1767,"path":52013,"published":1767,"seo":52014,"slug":52015,"stem":52016,"tags":52017,"timeToRead":2241,"__hash__":52018},"posts\u002Fposts\u002FProgrammation\u002F2010-06-06-calcul-de-la-distance.md","Calcul de la distance entre deux fichiers",{"type":9,"value":50352,"toc":51970},[50353,50355,50363,50369,50377,50397,50399,50402,50405,50408,50411,50414,50417,50430,50441,50444,50459,50462,50478,50482,50495,50498,50979,50982,51086,51936,51939,51942,51949,51967],[133,50354,43625],{"id":43624},[12,50356,44394,50357,50362],{},[49,50358,50361],{"href":50359,"rel":50360},"http:\u002F\u002Flinuxfr.org\u002Fforums\u002F20\u002F28366.html",[347],"billet sur LinuxFR",", où je demandais comment calculer la\ndistance (ou le pourcentage de similitude entre deux logiciels), j'ai\nobtenu la formule suivante :",[2105,50364,50367],{"className":50365,"code":50366,"language":26023},[26021],"distance = 1 - ( C(A) + C(B) - C(AB) ) \u002F Max(C(A), C(B))\n",[184,50368,50366],{"__ignoreMap":1646},[12,50370,50371,50372,179],{},"où C(X) est la taille du fichier X compressé",[17400,50373,50374],{},[49,50375,3686],{"href":17404,"ariaDescribedBy":50376,"dataFootnoteRef":1646,"id":17407},[17406],[12,50378,50379,50380,932,50383,792,50386,50389,50390,50392,50393,50396],{},"Après avoir testé les formats ",[184,50381,50382],{},"gzip",[184,50384,50385],{},"bzip2",[184,50387,50388],{},"lzma",", j'ai conclu que\nle format de compression le plus performant pour le calcul, est le\nformat ",[184,50391,50388],{},", car le dictionnaire avec ",[362,50394,50395],{},"la mise en commun"," était le\nplus gros, et donc le calcul de distance est plus efficace.",[133,50398,50311],{"id":50310},[12,50400,50401],{},"J'ai donc décidé d'écrire un programme parcourant un dossier (avec\nplusieurs milliers de fichiers) et de calculer pour toutes les\ncombinaisons des fichiers la distance entre chaque fichier. Ce programme\nconsommant énormément de mémoire, il se peut que pour un grand nombre de\nfichier, le programme se plante avec une erreur d'allocation de mémoire.",[12,50403,50404],{},"Ce programme permet de pouvoir faire une cartographie de ces fichiers et\nainsi de pouvoir les classer. Testé sur des fichiers textes (sources de\nlogiciels), le programme est assez efficace.",[12,50406,50407],{},"Testé sur des images, ou des vidéos, les fichiers sont considérés comme\ntous éloignés les uns des autres, même s'ils sont identiques à\ncompression différente près, ou s'ils sont identiques à un mouvement\nprès sur la photo (du genre une photo où une même personne se tient dans\nune position différente).",[12,50409,50410],{},"Si vous avez une idée sur comment calculer la distance entre deux\nfichiers déjà compressés (avec perte qui plus est), ca m'intéresse. :)",[12,50412,50413],{},"Comme l'exécution est très lente (compression lzma niveau 9), le\nprogramme peut être arrêté et redémarré.",[12,50415,50416],{},"Pour démarrer le programme, il faut exécuter la commande :",[2105,50418,50420],{"className":2317,"code":50419,"language":2319,"meta":1646,"style":1646},".\u002Fcalcul_distance \u002Fmon\u002Fdossier\n",[184,50421,50422],{"__ignoreMap":1646},[2113,50423,50424,50427],{"class":2115,"line":2116},[2113,50425,50426],{"class":2133},".\u002Fcalcul_distance",[2113,50428,50429],{"class":2149}," \u002Fmon\u002Fdossier\n",[12,50431,50432,50433,50436,50437,50440],{},"Le programme va alors créer un fichier\n",[184,50434,50435],{},"{5ac1fd3c-504f-4110-9f7e-d6fc89e57bdb}.db"," où\n",[184,50438,50439],{},"{5ac1fd3c-504f-4110-9f7e-d6fc89e57bdb}"," est différent à chaque nouveau\nlancement.",[12,50442,50443],{},"Pour reprendre le traitement où il était, il suffit alors de lancer :",[2105,50445,50447],{"className":2317,"code":50446,"language":2319,"meta":1646,"style":1646},".\u002Fcalcul_distance --uuid {5ac1fd3c-504f-4110-9f7e-d6fc89e57bdb}\n",[184,50448,50449],{"__ignoreMap":1646},[2113,50450,50451,50453,50456],{"class":2115,"line":2116},[2113,50452,50426],{"class":2133},[2113,50454,50455],{"class":2166}," --uuid",[2113,50457,50458],{"class":2149}," {5ac1fd3c-504f-4110-9f7e-d6fc89e57bdb}\n",[12,50460,50461],{},"Si on veut ajouter un dossier au traitement après coup :",[2105,50463,50465],{"className":2317,"code":50464,"language":2319,"meta":1646,"style":1646},".\u002Fcalcul_distance \u002Fnouveau\u002Fdossier --uuid {5ac1fd3c-504f-4110-9f7e-d6fc89e57bdb}\n",[184,50466,50467],{"__ignoreMap":1646},[2113,50468,50469,50471,50474,50476],{"class":2115,"line":2116},[2113,50470,50426],{"class":2133},[2113,50472,50473],{"class":2149}," \u002Fnouveau\u002Fdossier",[2113,50475,50455],{"class":2166},[2113,50477,50458],{"class":2149},[133,50479,50481],{"id":50480},"exemple","Exemple",[12,50483,50484,50485,50487,50488,50490,50491,50494],{},"Par exemple soit les différentes versions d'un logiciel, mis dans des\narchives ",[184,50486,43752],{},". Pour ne prendre que le code source des versions, on va\nsupprimer tous les fichiers suivants de l'archive ",[184,50489,43752],{}," :\n",[184,50492,50493],{},"*.jpg, *.png, *.ico, *.qm, *.db",", ... les sources externes à\nl'application. On ne garde donc que les fichiers textes dont nous avons\nla possession.",[12,50496,50497],{},"Après le nettoyage des dossiers :",[2105,50499,50501],{"className":2317,"code":50500,"language":2319,"meta":1646,"style":1646},"$ for i in `ls` ; do\n> tar -c $i\u002F*\n> $i.tar\n> done\n$ ls -l\n-rw-r--r-- 1 phoenix phoenix 2,5M  6 juin  13:24 v0.6.10.tar\n-rw-r--r-- 1 phoenix phoenix 440K  6 juin  13:24 v0.6.4.tar\n-rw-r--r-- 1 phoenix phoenix 530K  6 juin  13:24 v0.6.5.tar\n-rw-r--r-- 1 phoenix phoenix 610K  6 juin  13:24 v0.6.6.tar\n-rw-r--r-- 1 phoenix phoenix 690K  6 juin  13:24 v0.6.7.tar\n-rw-r--r-- 1 phoenix phoenix 1,2M  6 juin  13:24 v0.6.8.tar\n-rw-r--r-- 1 phoenix phoenix 1,3M  6 juin  13:24 v0.6.9.tar\n-rw-r--r-- 1 phoenix phoenix 2,9M  6 juin  13:24 v0.7.0.tar\n-rw-r--r-- 1 phoenix phoenix 2,2M  6 juin  13:24 v0.7.1.tar\n-rw-r--r-- 1 phoenix phoenix 3,3M  6 juin  13:24 v0.7.2.tar\n-rw-r--r-- 1 phoenix phoenix 3,3M  6 juin  13:24 v0.8.0.tar\n-rw-r--r-- 1 phoenix phoenix 3,1M  6 juin  13:24 v0.8.1_services.tar\n-rw-r--r-- 1 phoenix phoenix 3,1M  6 juin  13:24 v0.8.1.tar\n-rw-r--r-- 1 phoenix phoenix 3,8M  6 juin  13:24 v0.9.0.tar\n$ cd ..\n$ calcul_distance .\u002Fxinx\u002F\nStep 0 : Create database\nStep 1 : Create file list\nStep 2 : Compress single file\nStep 3 : Compress pair file\n$ ls\n{051b93a0-d9a9-4778-ac73-81ee01a3905d}.db\nxinx\n$ sqlite3 {051b93a0-d9a9-4778-ac73-81ee01a3905d}.db\n",[184,50502,50503,50525,50536,50545,50551,50559,50586,50608,50630,50652,50674,50696,50718,50740,50762,50784,50805,50827,50848,50870,50880,50890,50905,50920,50937,50952,50959,50964,50969],{"__ignoreMap":1646},[2113,50504,50505,50507,50509,50511,50513,50515,50518,50520,50523],{"class":2115,"line":2116},[2113,50506,41275],{"class":2133},[2113,50508,4440],{"class":2149},[2113,50510,7910],{"class":2149},[2113,50512,10067],{"class":2149},[2113,50514,17813],{"class":2149},[2113,50516,50517],{"class":2133},"ls",[2113,50519,20427],{"class":2149},[2113,50521,50522],{"class":2119}," ; ",[2113,50524,7384],{"class":2326},[2113,50526,50527,50530,50533],{"class":2115,"line":1647},[2113,50528,50529],{"class":2119},"> tar -c ",[2113,50531,50532],{"class":2330},"$i",[2113,50534,50535],{"class":2119},"\u002F*\n",[2113,50537,50538,50540,50542],{"class":2115,"line":1652},[2113,50539,4435],{"class":2119},[2113,50541,50532],{"class":2330},[2113,50543,50544],{"class":2119},".tar\n",[2113,50546,50547,50549],{"class":2115,"line":2185},[2113,50548,4435],{"class":2119},[2113,50550,7474],{"class":2326},[2113,50552,50553,50555,50557],{"class":2115,"line":2230},[2113,50554,41275],{"class":2133},[2113,50556,25016],{"class":2149},[2113,50558,25458],{"class":2166},[2113,50560,50561,50564,50566,50569,50571,50574,50577,50580,50583],{"class":2115,"line":2235},[2113,50562,50563],{"class":2133},"-rw-r--r--",[2113,50565,2850],{"class":2166},[2113,50567,50568],{"class":2149}," phoenix",[2113,50570,50568],{"class":2149},[2113,50572,50573],{"class":2149}," 2,5M",[2113,50575,50576],{"class":2166},"  6",[2113,50578,50579],{"class":2149}," juin",[2113,50581,50582],{"class":2149},"  13:24",[2113,50584,50585],{"class":2149}," v0.6.10.tar\n",[2113,50587,50588,50590,50592,50594,50596,50599,50601,50603,50605],{"class":2115,"line":2241},[2113,50589,50563],{"class":2133},[2113,50591,2850],{"class":2166},[2113,50593,50568],{"class":2149},[2113,50595,50568],{"class":2149},[2113,50597,50598],{"class":2149}," 440K",[2113,50600,50576],{"class":2166},[2113,50602,50579],{"class":2149},[2113,50604,50582],{"class":2149},[2113,50606,50607],{"class":2149}," v0.6.4.tar\n",[2113,50609,50610,50612,50614,50616,50618,50621,50623,50625,50627],{"class":2115,"line":2246},[2113,50611,50563],{"class":2133},[2113,50613,2850],{"class":2166},[2113,50615,50568],{"class":2149},[2113,50617,50568],{"class":2149},[2113,50619,50620],{"class":2149}," 530K",[2113,50622,50576],{"class":2166},[2113,50624,50579],{"class":2149},[2113,50626,50582],{"class":2149},[2113,50628,50629],{"class":2149}," v0.6.5.tar\n",[2113,50631,50632,50634,50636,50638,50640,50643,50645,50647,50649],{"class":2115,"line":2464},[2113,50633,50563],{"class":2133},[2113,50635,2850],{"class":2166},[2113,50637,50568],{"class":2149},[2113,50639,50568],{"class":2149},[2113,50641,50642],{"class":2149}," 610K",[2113,50644,50576],{"class":2166},[2113,50646,50579],{"class":2149},[2113,50648,50582],{"class":2149},[2113,50650,50651],{"class":2149}," v0.6.6.tar\n",[2113,50653,50654,50656,50658,50660,50662,50665,50667,50669,50671],{"class":2115,"line":2085},[2113,50655,50563],{"class":2133},[2113,50657,2850],{"class":2166},[2113,50659,50568],{"class":2149},[2113,50661,50568],{"class":2149},[2113,50663,50664],{"class":2149}," 690K",[2113,50666,50576],{"class":2166},[2113,50668,50579],{"class":2149},[2113,50670,50582],{"class":2149},[2113,50672,50673],{"class":2149}," v0.6.7.tar\n",[2113,50675,50676,50678,50680,50682,50684,50687,50689,50691,50693],{"class":2115,"line":2514},[2113,50677,50563],{"class":2133},[2113,50679,2850],{"class":2166},[2113,50681,50568],{"class":2149},[2113,50683,50568],{"class":2149},[2113,50685,50686],{"class":2149}," 1,2M",[2113,50688,50576],{"class":2166},[2113,50690,50579],{"class":2149},[2113,50692,50582],{"class":2149},[2113,50694,50695],{"class":2149}," v0.6.8.tar\n",[2113,50697,50698,50700,50702,50704,50706,50709,50711,50713,50715],{"class":2115,"line":2533},[2113,50699,50563],{"class":2133},[2113,50701,2850],{"class":2166},[2113,50703,50568],{"class":2149},[2113,50705,50568],{"class":2149},[2113,50707,50708],{"class":2149}," 1,3M",[2113,50710,50576],{"class":2166},[2113,50712,50579],{"class":2149},[2113,50714,50582],{"class":2149},[2113,50716,50717],{"class":2149}," v0.6.9.tar\n",[2113,50719,50720,50722,50724,50726,50728,50731,50733,50735,50737],{"class":2115,"line":2556},[2113,50721,50563],{"class":2133},[2113,50723,2850],{"class":2166},[2113,50725,50568],{"class":2149},[2113,50727,50568],{"class":2149},[2113,50729,50730],{"class":2149}," 2,9M",[2113,50732,50576],{"class":2166},[2113,50734,50579],{"class":2149},[2113,50736,50582],{"class":2149},[2113,50738,50739],{"class":2149}," v0.7.0.tar\n",[2113,50741,50742,50744,50746,50748,50750,50753,50755,50757,50759],{"class":2115,"line":2569},[2113,50743,50563],{"class":2133},[2113,50745,2850],{"class":2166},[2113,50747,50568],{"class":2149},[2113,50749,50568],{"class":2149},[2113,50751,50752],{"class":2149}," 2,2M",[2113,50754,50576],{"class":2166},[2113,50756,50579],{"class":2149},[2113,50758,50582],{"class":2149},[2113,50760,50761],{"class":2149}," v0.7.1.tar\n",[2113,50763,50764,50766,50768,50770,50772,50775,50777,50779,50781],{"class":2115,"line":2575},[2113,50765,50563],{"class":2133},[2113,50767,2850],{"class":2166},[2113,50769,50568],{"class":2149},[2113,50771,50568],{"class":2149},[2113,50773,50774],{"class":2149}," 3,3M",[2113,50776,50576],{"class":2166},[2113,50778,50579],{"class":2149},[2113,50780,50582],{"class":2149},[2113,50782,50783],{"class":2149}," v0.7.2.tar\n",[2113,50785,50786,50788,50790,50792,50794,50796,50798,50800,50802],{"class":2115,"line":2596},[2113,50787,50563],{"class":2133},[2113,50789,2850],{"class":2166},[2113,50791,50568],{"class":2149},[2113,50793,50568],{"class":2149},[2113,50795,50774],{"class":2149},[2113,50797,50576],{"class":2166},[2113,50799,50579],{"class":2149},[2113,50801,50582],{"class":2149},[2113,50803,50804],{"class":2149}," v0.8.0.tar\n",[2113,50806,50807,50809,50811,50813,50815,50818,50820,50822,50824],{"class":2115,"line":3192},[2113,50808,50563],{"class":2133},[2113,50810,2850],{"class":2166},[2113,50812,50568],{"class":2149},[2113,50814,50568],{"class":2149},[2113,50816,50817],{"class":2149}," 3,1M",[2113,50819,50576],{"class":2166},[2113,50821,50579],{"class":2149},[2113,50823,50582],{"class":2149},[2113,50825,50826],{"class":2149}," v0.8.1_services.tar\n",[2113,50828,50829,50831,50833,50835,50837,50839,50841,50843,50845],{"class":2115,"line":3213},[2113,50830,50563],{"class":2133},[2113,50832,2850],{"class":2166},[2113,50834,50568],{"class":2149},[2113,50836,50568],{"class":2149},[2113,50838,50817],{"class":2149},[2113,50840,50576],{"class":2166},[2113,50842,50579],{"class":2149},[2113,50844,50582],{"class":2149},[2113,50846,50847],{"class":2149}," v0.8.1.tar\n",[2113,50849,50850,50852,50854,50856,50858,50861,50863,50865,50867],{"class":2115,"line":3236},[2113,50851,50563],{"class":2133},[2113,50853,2850],{"class":2166},[2113,50855,50568],{"class":2149},[2113,50857,50568],{"class":2149},[2113,50859,50860],{"class":2149}," 3,8M",[2113,50862,50576],{"class":2166},[2113,50864,50579],{"class":2149},[2113,50866,50582],{"class":2149},[2113,50868,50869],{"class":2149}," v0.9.0.tar\n",[2113,50871,50872,50874,50877],{"class":2115,"line":3248},[2113,50873,41275],{"class":2133},[2113,50875,50876],{"class":2149}," cd",[2113,50878,50879],{"class":2149}," ..\n",[2113,50881,50882,50884,50887],{"class":2115,"line":4899},[2113,50883,41275],{"class":2133},[2113,50885,50886],{"class":2149}," calcul_distance",[2113,50888,50889],{"class":2149}," .\u002Fxinx\u002F\n",[2113,50891,50892,50895,50897,50899,50902],{"class":2115,"line":1777},[2113,50893,50894],{"class":2133},"Step",[2113,50896,3208],{"class":2166},[2113,50898,2384],{"class":2149},[2113,50900,50901],{"class":2149}," Create",[2113,50903,50904],{"class":2149}," database\n",[2113,50906,50907,50909,50911,50913,50915,50917],{"class":2115,"line":4931},[2113,50908,50894],{"class":2133},[2113,50910,2850],{"class":2166},[2113,50912,2384],{"class":2149},[2113,50914,50901],{"class":2149},[2113,50916,7379],{"class":2149},[2113,50918,50919],{"class":2149}," list\n",[2113,50921,50922,50924,50926,50928,50931,50934],{"class":2115,"line":4961},[2113,50923,50894],{"class":2133},[2113,50925,2484],{"class":2166},[2113,50927,2384],{"class":2149},[2113,50929,50930],{"class":2149}," Compress",[2113,50932,50933],{"class":2149}," single",[2113,50935,50936],{"class":2149}," file\n",[2113,50938,50939,50941,50943,50945,50947,50950],{"class":2115,"line":4976},[2113,50940,50894],{"class":2133},[2113,50942,16106],{"class":2166},[2113,50944,2384],{"class":2149},[2113,50946,50930],{"class":2149},[2113,50948,50949],{"class":2149}," pair",[2113,50951,50936],{"class":2149},[2113,50953,50954,50956],{"class":2115,"line":4993},[2113,50955,41275],{"class":2133},[2113,50957,50958],{"class":2149}," ls\n",[2113,50960,50961],{"class":2115,"line":5013},[2113,50962,50963],{"class":2119},"{051b93a0-d9a9-4778-ac73-81ee01a3905d}.db\n",[2113,50965,50966],{"class":2115,"line":5018},[2113,50967,50968],{"class":2133},"xinx\n",[2113,50970,50971,50973,50976],{"class":2115,"line":5042},[2113,50972,41275],{"class":2133},[2113,50974,50975],{"class":2149}," sqlite3",[2113,50977,50978],{"class":2149}," {051b93a0-d9a9-4778-ac73-81ee01a3905d}.db\n",[12,50980,50981],{},"Nous allons maintenant faire une requête dans la base de données sqlite :",[2105,50983,50987],{"className":50984,"code":50985,"language":50986,"meta":1646,"style":1646},"language-sql shiki shiki-themes one-dark-pro","$ .TABLES\ndistances  files\n$ SELECT files1.path, files2.path, distances.distance FROM distances, files files1, files files2 WHERE distances.id1=files1.id AND distances.id2=files2.id ORDER BY distance ASC;\n","sql",[184,50988,50989,50994,50999],{"__ignoreMap":1646},[2113,50990,50991],{"class":2115,"line":2116},[2113,50992,50993],{"class":2119},"$ .TABLES\n",[2113,50995,50996],{"class":2115,"line":1647},[2113,50997,50998],{"class":2119},"distances  files\n",[2113,51000,51001,51004,51007,51010,51012,51014,51016,51019,51021,51023,51025,51028,51030,51032,51035,51038,51041,51044,51046,51049,51051,51054,51056,51058,51061,51063,51065,51068,51070,51072,51074,51076,51079,51081,51084],{"class":2115,"line":1652},[2113,51002,51003],{"class":2119},"$ ",[2113,51005,51006],{"class":2326},"SELECT",[2113,51008,51009],{"class":2166}," files1",[2113,51011,179],{"class":2119},[2113,51013,6554],{"class":2166},[2113,51015,932],{"class":2119},[2113,51017,51018],{"class":2166},"files2",[2113,51020,179],{"class":2119},[2113,51022,6554],{"class":2166},[2113,51024,932],{"class":2119},[2113,51026,51027],{"class":2166},"distances",[2113,51029,179],{"class":2119},[2113,51031,30675],{"class":2166},[2113,51033,51034],{"class":2326}," FROM",[2113,51036,51037],{"class":2119}," distances, files files1, files files2 ",[2113,51039,51040],{"class":2326},"WHERE",[2113,51042,51043],{"class":2166}," distances",[2113,51045,179],{"class":2119},[2113,51047,51048],{"class":2166},"id1",[2113,51050,2335],{"class":2334},[2113,51052,51053],{"class":2166},"files1",[2113,51055,179],{"class":2119},[2113,51057,20628],{"class":2166},[2113,51059,51060],{"class":2326}," AND",[2113,51062,51043],{"class":2166},[2113,51064,179],{"class":2119},[2113,51066,51067],{"class":2166},"id2",[2113,51069,2335],{"class":2334},[2113,51071,51018],{"class":2166},[2113,51073,179],{"class":2119},[2113,51075,20628],{"class":2166},[2113,51077,51078],{"class":2326}," ORDER BY",[2113,51080,30384],{"class":2119},[2113,51082,51083],{"class":2326},"ASC",[2113,51085,2487],{"class":2119},[22,51087,51088,51101],{},[25,51089,51090],{},[28,51091,51092,51095,51098],{},[31,51093,51094],{},"Version 1",[31,51096,51097],{},"Version 2",[31,51099,51100],{},"Distance",[41,51102,51103,51114,51125,51135,51144,51155,51165,51175,51186,51196,51205,51214,51223,51232,51242,51251,51260,51270,51279,51288,51297,51306,51315,51324,51333,51342,51351,51360,51369,51378,51387,51396,51405,51414,51423,51432,51441,51450,51459,51468,51477,51486,51495,51504,51513,51522,51531,51540,51549,51558,51567,51576,51585,51594,51603,51612,51621,51630,51639,51648,51657,51666,51675,51684,51693,51702,51711,51720,51729,51738,51747,51756,51765,51774,51783,51792,51801,51810,51819,51828,51837,51846,51855,51864,51873,51882,51891,51900,51909,51918,51927],{},[28,51104,51105,51108,51111],{},[46,51106,51107],{},"v0.8.1",[46,51109,51110],{},"v0.8.1_services",[46,51112,51113],{},"0.0350740694634633",[28,51115,51116,51119,51122],{},[46,51117,51118],{},"v0.6.8",[46,51120,51121],{},"v0.6.9",[46,51123,51124],{},"0.132275346477201",[28,51126,51127,51130,51132],{},[46,51128,51129],{},"v0.8.0",[46,51131,51107],{},[46,51133,51134],{},"0.142321125298336",[28,51136,51137,51139,51141],{},[46,51138,51129],{},[46,51140,51110],{},[46,51142,51143],{},"0.161719318637048",[28,51145,51146,51149,51152],{},[46,51147,51148],{},"v0.6.5",[46,51150,51151],{},"v0.6.6",[46,51153,51154],{},"0.196933113059686",[28,51156,51157,51160,51162],{},[46,51158,51159],{},"v0.6.4",[46,51161,51148],{},[46,51163,51164],{},"0.231812199675573",[28,51166,51167,51169,51172],{},[46,51168,51151],{},[46,51170,51171],{},"v0.6.7",[46,51173,51174],{},"0.266593999923953",[28,51176,51177,51180,51183],{},[46,51178,51179],{},"v0.6.10",[46,51181,51182],{},"v0.7.0",[46,51184,51185],{},"0.27412838729727",[28,51187,51188,51191,51193],{},[46,51189,51190],{},"v0.7.2",[46,51192,51129],{},[46,51194,51195],{},"0.312111739912996",[28,51197,51198,51200,51202],{},[46,51199,51148],{},[46,51201,51171],{},[46,51203,51204],{},"0.351347925829225",[28,51206,51207,51209,51211],{},[46,51208,51159],{},[46,51210,51151],{},[46,51212,51213],{},"0.364115163581424",[28,51215,51216,51218,51220],{},[46,51217,51190],{},[46,51219,51107],{},[46,51221,51222],{},"0.386971922637303",[28,51224,51225,51227,51229],{},[46,51226,51190],{},[46,51228,51110],{},[46,51230,51231],{},"0.401055941017259",[28,51233,51234,51237,51239],{},[46,51235,51236],{},"v0.7.1",[46,51238,51190],{},[46,51240,51241],{},"0.436705836223058",[28,51243,51244,51246,51248],{},[46,51245,51182],{},[46,51247,51236],{},[46,51249,51250],{},"0.465121645779551",[28,51252,51253,51255,51257],{},[46,51254,51159],{},[46,51256,51171],{},[46,51258,51259],{},"0.468472350726879",[28,51261,51262,51264,51267],{},[46,51263,51110],{},[46,51265,51266],{},"v0.9.0",[46,51268,51269],{},"0.516795574578859",[28,51271,51272,51274,51276],{},[46,51273,51107],{},[46,51275,51266],{},[46,51277,51278],{},"0.51733623689019",[28,51280,51281,51283,51285],{},[46,51282,51129],{},[46,51284,51266],{},[46,51286,51287],{},"0.544376861655528",[28,51289,51290,51292,51294],{},[46,51291,51171],{},[46,51293,51118],{},[46,51295,51296],{},"0.558824765667689",[28,51298,51299,51301,51303],{},[46,51300,51236],{},[46,51302,51129],{},[46,51304,51305],{},"0.560609480175814",[28,51307,51308,51310,51312],{},[46,51309,51179],{},[46,51311,51121],{},[46,51313,51314],{},"0.594036969567445",[28,51316,51317,51319,51321],{},[46,51318,51236],{},[46,51320,51107],{},[46,51322,51323],{},"0.604226316444666",[28,51325,51326,51328,51330],{},[46,51327,51236],{},[46,51329,51110],{},[46,51331,51332],{},"0.613613062086946",[28,51334,51335,51337,51339],{},[46,51336,51171],{},[46,51338,51121],{},[46,51340,51341],{},"0.622950487834501",[28,51343,51344,51346,51348],{},[46,51345,51182],{},[46,51347,51190],{},[46,51349,51350],{},"0.631060763867616",[28,51352,51353,51355,51357],{},[46,51354,51179],{},[46,51356,51236],{},[46,51358,51359],{},"0.632444883185258",[28,51361,51362,51364,51366],{},[46,51363,51179],{},[46,51365,51118],{},[46,51367,51368],{},"0.637234847328374",[28,51370,51371,51373,51375],{},[46,51372,51151],{},[46,51374,51118],{},[46,51376,51377],{},"0.6494052372746",[28,51379,51380,51382,51384],{},[46,51381,51121],{},[46,51383,51236],{},[46,51385,51386],{},"0.666200571812458",[28,51388,51389,51391,51393],{},[46,51390,51190],{},[46,51392,51266],{},[46,51394,51395],{},"0.678423568871868",[28,51397,51398,51400,51402],{},[46,51399,51148],{},[46,51401,51118],{},[46,51403,51404],{},"0.692250570944195",[28,51406,51407,51409,51411],{},[46,51408,51118],{},[46,51410,51236],{},[46,51412,51413],{},"0.701063946130892",[28,51415,51416,51418,51420],{},[46,51417,51151],{},[46,51419,51121],{},[46,51421,51422],{},"0.701698986545754",[28,51424,51425,51427,51429],{},[46,51426,51121],{},[46,51428,51182],{},[46,51430,51431],{},"0.717262408423359",[28,51433,51434,51436,51438],{},[46,51435,51182],{},[46,51437,51129],{},[46,51439,51440],{},"0.720311680104721",[28,51442,51443,51445,51447],{},[46,51444,51148],{},[46,51446,51121],{},[46,51448,51449],{},"0.738013305804084",[28,51451,51452,51454,51456],{},[46,51453,51159],{},[46,51455,51118],{},[46,51457,51458],{},"0.747495551539097",[28,51460,51461,51463,51465],{},[46,51462,51179],{},[46,51464,51190],{},[46,51466,51467],{},"0.747929200720491",[28,51469,51470,51472,51474],{},[46,51471,51118],{},[46,51473,51182],{},[46,51475,51476],{},"0.748031544518325",[28,51478,51479,51481,51483],{},[46,51480,51182],{},[46,51482,51107],{},[46,51484,51485],{},"0.758510349354368",[28,51487,51488,51490,51492],{},[46,51489,51182],{},[46,51491,51110],{},[46,51493,51494],{},"0.767204482779187",[28,51496,51497,51499,51501],{},[46,51498,51159],{},[46,51500,51121],{},[46,51502,51503],{},"0.772451857549627",[28,51505,51506,51508,51510],{},[46,51507,51179],{},[46,51509,51129],{},[46,51511,51512],{},"0.796649043913944",[28,51514,51515,51517,51519],{},[46,51516,51236],{},[46,51518,51266],{},[46,51520,51521],{},"0.801221496333008",[28,51523,51524,51526,51528],{},[46,51525,51121],{},[46,51527,51190],{},[46,51529,51530],{},"0.804765901655414",[28,51532,51533,51535,51537],{},[46,51534,51118],{},[46,51536,51190],{},[46,51538,51539],{},"0.819917045496318",[28,51541,51542,51544,51546],{},[46,51543,51179],{},[46,51545,51107],{},[46,51547,51548],{},"0.823540395867048",[28,51550,51551,51553,51555],{},[46,51552,51179],{},[46,51554,51110],{},[46,51556,51557],{},"0.831055117394626",[28,51559,51560,51562,51564],{},[46,51561,51179],{},[46,51563,51171],{},[46,51565,51566],{},"0.838850951377793",[28,51568,51569,51571,51573],{},[46,51570,51121],{},[46,51572,51129],{},[46,51574,51575],{},"0.849001032539087",[28,51577,51578,51580,51582],{},[46,51579,51171],{},[46,51581,51236],{},[46,51583,51584],{},"0.853848016623182",[28,51586,51587,51589,51591],{},[46,51588,51182],{},[46,51590,51266],{},[46,51592,51593],{},"0.860291356912217",[28,51595,51596,51598,51600],{},[46,51597,51118],{},[46,51599,51129],{},[46,51601,51602],{},"0.86075956509228",[28,51604,51605,51607,51609],{},[46,51606,51179],{},[46,51608,51151],{},[46,51610,51611],{},"0.863340151908353",[28,51613,51614,51616,51618],{},[46,51615,51121],{},[46,51617,51107],{},[46,51619,51620],{},"0.872265541006983",[28,51622,51623,51625,51627],{},[46,51624,51151],{},[46,51626,51236],{},[46,51628,51629],{},"0.873275579233521",[28,51631,51632,51634,51636],{},[46,51633,51179],{},[46,51635,51148],{},[46,51637,51638],{},"0.875332023895544",[28,51640,51641,51643,51645],{},[46,51642,51121],{},[46,51644,51110],{},[46,51646,51647],{},"0.87987037883801",[28,51649,51650,51652,51654],{},[46,51651,51171],{},[46,51653,51182],{},[46,51655,51656],{},"0.879941907871053",[28,51658,51659,51661,51663],{},[46,51660,51118],{},[46,51662,51107],{},[46,51664,51665],{},"0.881174812987336",[28,51667,51668,51670,51672],{},[46,51669,51148],{},[46,51671,51236],{},[46,51673,51674],{},"0.886172606121206",[28,51676,51677,51679,51681],{},[46,51678,51118],{},[46,51680,51110],{},[46,51682,51683],{},"0.889515283880001",[28,51685,51686,51688,51690],{},[46,51687,51179],{},[46,51689,51159],{},[46,51691,51692],{},"0.892364892140999",[28,51694,51695,51697,51699],{},[46,51696,51151],{},[46,51698,51182],{},[46,51700,51701],{},"0.894375709613201",[28,51703,51704,51706,51708],{},[46,51705,51179],{},[46,51707,51266],{},[46,51709,51710],{},"0.895694726039398",[28,51712,51713,51715,51717],{},[46,51714,51171],{},[46,51716,51190],{},[46,51718,51719],{},"0.898582911617307",[28,51721,51722,51724,51726],{},[46,51723,51159],{},[46,51725,51236],{},[46,51727,51728],{},"0.902038058337369",[28,51730,51731,51733,51735],{},[46,51732,51148],{},[46,51734,51182],{},[46,51736,51737],{},"0.904821831317442",[28,51739,51740,51742,51744],{},[46,51741,51151],{},[46,51743,51190],{},[46,51745,51746],{},"0.910637046476578",[28,51748,51749,51751,51753],{},[46,51750,51159],{},[46,51752,51182],{},[46,51754,51755],{},"0.918347943544789",[28,51757,51758,51760,51762],{},[46,51759,51148],{},[46,51761,51190],{},[46,51763,51764],{},"0.919716704855963",[28,51766,51767,51769,51771],{},[46,51768,51121],{},[46,51770,51266],{},[46,51772,51773],{},"0.921714099772221",[28,51775,51776,51778,51780],{},[46,51777,51171],{},[46,51779,51129],{},[46,51781,51782],{},"0.924717744438112",[28,51784,51785,51787,51789],{},[46,51786,51118],{},[46,51788,51266],{},[46,51790,51791],{},"0.925849165227404",[28,51793,51794,51796,51798],{},[46,51795,51159],{},[46,51797,51190],{},[46,51799,51800],{},"0.928888262611658",[28,51802,51803,51805,51807],{},[46,51804,51171],{},[46,51806,51107],{},[46,51808,51809],{},"0.933685479216414",[28,51811,51812,51814,51816],{},[46,51813,51151],{},[46,51815,51129],{},[46,51817,51818],{},"0.933940067594635",[28,51820,51821,51823,51825],{},[46,51822,51171],{},[46,51824,51110],{},[46,51826,51827],{},"0.939898268404416",[28,51829,51830,51832,51834],{},[46,51831,51148],{},[46,51833,51129],{},[46,51835,51836],{},"0.940789807767177",[28,51838,51839,51841,51843],{},[46,51840,51151],{},[46,51842,51107],{},[46,51844,51845],{},"0.94210246370087",[28,51847,51848,51850,51852],{},[46,51849,51151],{},[46,51851,51110],{},[46,51853,51854],{},"0.946464528845253",[28,51856,51857,51859,51861],{},[46,51858,51159],{},[46,51860,51129],{},[46,51862,51863],{},"0.948830071149278",[28,51865,51866,51868,51870],{},[46,51867,51148],{},[46,51869,51107],{},[46,51871,51872],{},"0.94922082772201",[28,51874,51875,51877,51879],{},[46,51876,51148],{},[46,51878,51110],{},[46,51880,51881],{},"0.951278461132112",[28,51883,51884,51886,51888],{},[46,51885,51159],{},[46,51887,51107],{},[46,51889,51890],{},"0.955572468114482",[28,51892,51893,51895,51897],{},[46,51894,51159],{},[46,51896,51110],{},[46,51898,51899],{},"0.95567206186826",[28,51901,51902,51904,51906],{},[46,51903,51171],{},[46,51905,51266],{},[46,51907,51908],{},"0.96084453455483",[28,51910,51911,51913,51915],{},[46,51912,51151],{},[46,51914,51266],{},[46,51916,51917],{},"0.964619158469125",[28,51919,51920,51922,51924],{},[46,51921,51148],{},[46,51923,51266],{},[46,51925,51926],{},"0.965955795849916",[28,51928,51929,51931,51933],{},[46,51930,51159],{},[46,51932,51266],{},[46,51934,51935],{},"0.968866861905835",[12,51937,51938],{},"On peut ainsi voir d'après ce tableau, les versions ayant peu de\ndifférences et celles qui ont fait des plus gros bonds en avant.",[12,51940,51941],{},"On voit ainsi qu'il y a plus de différences entre la version 0.8.0 et la\n0.9.0, qu'il y en a eu entre la version 0.6.8 et la 0.6.9. On peut\négalement voir que les versions 0.6.4 et 0.9.0 n'ont plus rien à voir\nentre elles.",[12,51943,51944,51945,179],{},"Vous pouvez télécharger le programme attaché à ",[49,51946,51948],{"href":51947},"\u002FProgrammation\u002Fcalcul-de-la-distance\u002Fcalcul_distance.7z","ce lien",[23219,51950,51952,51955],{"className":51951,"dataFootnotes":1646},[23222],[128,51953,23227],{"className":51954,"id":17406},[23226],[367,51956,51957],{},[370,51958,51959,51960,45630,51964],{"id":23232},"On peut retrouver l'explication de cette formule ",[49,51961,43548],{"href":51962,"rel":51963},"http:\u002F\u002Finterstices.info\u002Fjcms\u002Fc_21828\u002Fclasser-musiques-langues-images-textes-et-genomes",[347],[49,51965,23240],{"href":23236,"ariaLabel":23237,"className":51966,"dataFootnoteBackref":1646},[23239],[3358,51968,51969],{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}",{"title":1646,"searchDepth":1647,"depth":1647,"links":51971},[51972,51973,51974,51975],{"id":43624,"depth":1652,"text":43625},{"id":50310,"depth":1652,"text":50311},{"id":50480,"depth":1652,"text":50481},{"id":17406,"depth":1647,"text":23227},"2010-06-06",{"type":9,"value":51978},[51979,51981,51986,51991,51998,52010],[133,51980,43625],{"id":43624},[12,51982,44394,51983,50362],{},[49,51984,50361],{"href":50359,"rel":51985},[347],[2105,51987,51989],{"className":51988,"code":50366,"language":26023},[26021],[184,51990,50366],{"__ignoreMap":1646},[12,51992,50371,51993,179],{},[17400,51994,51995],{},[49,51996,3686],{"href":17404,"ariaDescribedBy":51997,"dataFootnoteRef":1646,"id":17407},[17406],[12,51999,50379,52000,932,52002,792,52004,50389,52006,50392,52008,50396],{},[184,52001,50382],{},[184,52003,50385],{},[184,52005,50388],{},[184,52007,50388],{},[362,52009,50395],{},[3358,52011,51969],{},{},"\u002Fpost\u002Fcalcul-de-la-distance",{"title":50350,"description":1646},"calcul-de-la-distance","posts\u002FProgrammation\u002F2010-06-06-calcul-de-la-distance",[38761,49134],"f5dJI_rOrb9a2Kflve8ejZKhwpF_zkBfNrrcWae0dOg",{"id":52020,"title":52021,"author":7,"body":52022,"category":44415,"categorySlug":45988,"date":52655,"description":52656,"excerpt":52657,"extension":1764,"location":1765,"meta":52685,"navigation":1767,"path":52686,"published":1767,"seo":52687,"slug":52688,"stem":52689,"tags":52690,"timeToRead":2246,"__hash__":52691},"posts\u002Fposts\u002FXINX\u002F2010-05-02-presentation-de-xinx.md","Présentation de XINX",{"type":9,"value":52023,"toc":52642},[52024,52043,52049,52058,52063,52067,52083,52086,52111,52114,52122,52128,52134,52143,52146,52159,52162,52166,52174,52186,52188,52194,52199,52219,52230,52233,52250,52254,52284,52300,52304,52321,52327,52331,52338,52341,52398,52402,52406,52409,52412,52418,52422,52428,52438,52472,52492,52497,52501,52511,52514,52520,52524,52527,52538,52542,52550,52555,52561,52563,52574,52580,52586,52589,52605,52613,52639],[12,52025,52026,52027,52029,52030,52032,52033,52036,52037,792,52040,52042],{},"Ce billet fait suite à la sortie de la version 0.9.0 de ",[155,52028,44415],{}," et va\nvous décrire ce logiciel. ",[155,52031,44415],{}," est éditeur de feuille de style ",[362,52034,52035],{},"XSL","\n(il fait également éditeur ",[362,52038,52039],{},"JavaScript",[362,52041,40215],{}," mais actuellement moins\nbien que ce que peuvent faire d'autres).",[12,52044,52045,52046,179],{},"Il permet l'édition des feuilles de styles en proposant de compléter sur\nle nom des balises (normal), sur les attributs (normal), sur les valeurs\nd'attributs : les variables, les paramètres, ... Il propose également\nautomatiquement les différents paramètres lors de l'utilisation d'un\n",[184,52047,52048],{},"call:template",[12,52050,52051,52052,52054,52055,179],{},"L'éditeur est fortement tourné vers l'édition de feuille de style ",[362,52053,52035],{},"\npour génération d*'HTML* mais peut être utilisé pour générer du ",[362,52056,52057],{},"XML",[12,52059,52060],{},[121,52061],{"alt":44415,"src":52062},"\u002FXINX\u002Fpresentation-de-xinx\u002Fsplash.png",[133,52064,52066],{"id":52065},"historique","Historique",[12,52068,52069,52070,52073,52074,52079,52080,179],{},"Depuis un certain temps déjà (5 ans), je travaille pour la société\n",[362,52071,52072],{},"Generix Group"," éditant et intégrant un ERP",[17400,52075,52076],{},[49,52077,3686],{"href":17404,"ariaDescribedBy":52078,"dataFootnoteRef":1646,"id":17407},[17406]," du nom de ",[362,52081,52082],{},"GCE",[12,52084,52085],{},"Sur la partie Web de cet ERP, il est possible d'intervenir à plusieurs\nendroits pour modifier son fonctionnement :",[506,52087,52088,52095,52105],{},[370,52089,52090,52091,52094],{},"Le paramétrage (",[362,52092,52093],{},"BDD",") : Modification d'un paramètre pour agir sur\nle comportement du logiciel",[370,52096,52097,52098,52101,52102,52104],{},"La couche métier (",[362,52099,52100],{},"JAVA",") : Lit le paramétrage (",[362,52103,52093],{},") et réalise\nles traitements. Il est possible de développer de nouveaux objets,\nmais aussi de dériver ceux existants.",[370,52106,52107,52108,52110],{},"La couche de présentation (",[362,52109,52035],{},") : A l'aide d'un flux de\nprésentation généré par la couche métier et d'un fichier de\nconfiguration, présente les données à l'écran.",[12,52112,52113],{},"La dernière couche est donc l'interprétation d'un flux de donnée,\ngénérée à partir de la couche métier (JAVA), à l'aide de feuille de\nstyle XSL.",[12,52115,52116,52118,52119,52121],{},[155,52117,44415],{}," sert à modifier la couche de présentation (",[362,52120,52035],{},"). Sur cette\ndernière couche, on crée et modifie des feuilles de styles pour modifier\nla présentation des données à l'écran (Déplacer des champs, supprimer\ndes champs, ajouter des champs, règle de gestion simple ...).",[12,52123,52124,52125,52127],{},"En mode ",[362,52126,44456],{},", chez les clients, les modifications ne sont pas faites\ndirectement sur les feuilles de styles standard livrées, mais sur des\nfeuilles de styles dérivées, afin de faciliter les migrations, et la\ncorrection des anomalies.",[12,52129,52130],{},[121,52131],{"alt":52132,"src":52133},"Structure","\u002FXINX\u002Fpresentation-de-xinx\u002Fstructure_dossier.png",[12,52135,52136,52137,52140,52141,179],{},"Lors de la modification de feuille de style ",[362,52138,52139],{},"standard",", il faut alors\nrenommer et placer ce fichier dans un dossier séparé. C'est de cette\ntâche récurrente qu'est venue la création de l'éditeur de feuille de\nstyle ",[155,52142,44415],{},[12,52144,52145],{},"Le but était alors, lors de la sauvegarde d'un fichier standard, de\nproposer automatiquement la sauvegarde dans le bon dossier.",[12,52147,52148,52149,52151,52152,52154,52155,52158],{},"L'autre point à l'origine de la création de ",[155,52150,44415],{}," est la\nnon-existence d'un logiciel de développement de feuille de style ",[362,52153,52035],{},"\ngratuit et\u002Fou libre (Il y a bien ",[362,52156,52157],{},"XML Spy"," qui correspondait à peu prés\nmais qui était payant, et dont nous n'avions pas la licence).",[12,52160,52161],{},"Sont alors venu s'ajouter d'autres fonctionnalités : la complétion sur\nles balises, les attributs, les variables, .... :)",[133,52163,52165],{"id":52164},"quest-que-xinx-aujourdhui","Qu'est que XINX aujourd'hui ?",[12,52167,52168,52170,52171,52173],{},[155,52169,44415],{}," prévu initialement comme aide au développement des consultants\nqui font des développements de feuille de style, est maintenant un\néditeur de feuille de style ",[362,52172,52035],{}," avec gestion des plugins. L'un des\nplugins contient des petites fonctionnalités utiles au quotidien par les\nconsultants travaillants sur l'ERP, fonctionnalité auparavant implémenté\ndirectement dans le logiciel.",[12,52175,52176,52177,52179,52180,52182,52183,52185],{},"Dit autrement, cette nouvelle version marque donc une étape : La partie\nde ",[155,52178,44415],{}," propre à ",[362,52181,52072],{}," à été séparée dans un plugin.\n",[155,52184,44415],{}," en lui même n'est alors plus qu'un éditeur de feuille de style,\nextensible via des plugins, mais il retrouve les mêmes fonctionnalités\ngrâce à ce plugin.",[133,52187,1075],{"id":43811},[12,52189,52190],{},[121,52191],{"alt":52192,"src":52193},"Licence GPLv3","http:\u002F\u002Fwww.gnu.org\u002Fgraphics\u002Fgplv3-127x51.png",[12,52195,52196,52198],{},[155,52197,44415],{}," est un logiciel libre dont la licence choisie est la GPLv3. Ce\nqui signifie que vous pouvez :",[506,52200,52201,52204,52207,52210,52213,52216],{},[370,52202,52203],{},"télécharger le logiciel",[370,52205,52206],{},"utiliser le logiciel pour faire ce que vous voulez",[370,52208,52209],{},"télécharger les sources du logiciels",[370,52211,52212],{},"modifier les sources du logiciels",[370,52214,52215],{},"utiliser le logiciel modifié par vos soins",[370,52217,52218],{},"distribuer le logiciel ou la version modifiée (attention à\ndifférencier les parties originales du logiciel avec le bon\ncopyright, et les parties modifiées).",[12,52220,52221,52222,52224,52225,52229],{},"En tant que logiciel libre ",[155,52223,44415],{}," possède une plateforme de\ncontribution (ticket, wiki, source, ...) qui se trouve à l'adresse\n",[49,52226,52227],{"href":52227,"rel":52228},"http:\u002F\u002Fxinx.shadoware.org",[347],". Vous pouvez donc y aller pour contribuer.",[12,52231,52232],{},"Vous pouvez contribuer de plusieurs façons :",[506,52234,52235,52238,52244,52247],{},[370,52236,52237],{},"Développement d'un Plugin",[370,52239,52240,52241],{},"Développement d'un script ",[362,52242,52243],{},"ECMAScript",[370,52245,52246],{},"Développement d'un petit patch",[370,52248,52249],{},"Développement d'un gros patch",[133,52251,52253],{"id":52252},"technologie","Technologie",[12,52255,52256,52258,52259,52261,52262,52265,52266,52268,52269,52272,52273,52276,52277,792,52280,52283],{},[155,52257,44415],{}," est un logiciel développé en ",[362,52260,43910],{},". Il compile et tourne sous\n",[362,52263,52264],{},"Gnu\u002FDebian"," (normalement n'importe quelle distribution devrait faire\nl'affaire, mais ",[362,52267,52264],{}," est la seule distribution testée), et sous\n",[362,52270,52271],{},"MS\u002FWindows",". Il devrait normalement également tourner sous ",[362,52274,52275],{},"MacOS"," (si\nles dépendances comme les librairies ",[362,52278,52279],{},"libxml2",[362,52281,52282],{},"libxslt"," sont\ncompilables sous cet OS).",[12,52285,52286,52287,52289,52290,52293,52294,52296,52297,52299],{},"A chaque version un paquet ",[362,52288,52264],{}," est mis à disposition sur le\ndépôt (",[184,52291,52292],{},"apt.shadoware.org",") et un binaire ",[362,52295,52271],{}," est proposé en\ntéléchargement. ",[155,52298,44415],{}," est toujours compilable à partir des sources\npour les autres distributions ou juste parce que vous préférez ainsi. 😄",[133,52301,52303],{"id":52302},"intégration-continue-tests","Intégration continue & Tests",[12,52305,52306,52307,52310,52311,52314,52315,52320],{},"A chaque ",[362,52308,52309],{},"validation"," sur le référenciel ",[362,52312,52313],{},"SubVersion",", l'application est\ncompilée et testée (vous pouvez voir le résultat des tests à l'adresse\n",[49,52316,52319],{"href":52317,"rel":52318},"http:\u002F\u002Fxinx.shadoware.org\u002Fbuild",[347],"suivante",". A la sortie de la version 0.9.0, les tests couvrent 27% de\nla librairie principale. Je dois donc encore écrire d'autres jeux de\ntests.",[12,52322,52323],{},[121,52324],{"alt":52325,"src":52326},"Intégration","\u002FXINX\u002Fpresentation-de-xinx\u002Fbitten.png",[133,52328,52330],{"id":52329},"code-réutilisable-pour-dautres-projets","Code réutilisable pour d'autres projets",[12,52332,52333,52335,52336,179],{},[155,52334,44415],{}," peut également être intéressant pour d'autres développeurs\n",[362,52337,43910],{},[12,52339,52340],{},"En effet, vous pouvez trouver dans la dernière version, ainsi que dans\nles versions précédentes plusieurs classes utilitaires.Par exemple\n(liste presque non exhaustive) :",[506,52342,52343,52351,52359,52367,52375,52390],{},[370,52344,52345,52350],{},[49,52346,52349],{"href":52347,"rel":52348},"http:\u002F\u002Fxinx.shadoware.org\u002Fdoxygen\u002Fdevel\u002Fhtml\u002FclassTreeProxyItemModel.html",[347],"TreeProxyItemModel"," : convertit un modèle format table en modèle\nformat arbre. Utile lorsque l'arbre est représenté dans une base de\ndonnée relationnelle par exemple.",[370,52352,52353,52358],{},[49,52354,52357],{"href":52355,"rel":52356},"http:\u002F\u002Fxinx.shadoware.org\u002Fdoxygen\u002Fdevel\u002Fhtml\u002FclassRecursiveSortFilterProxyModel.html",[347],"RecursiveSortFilterProxyModel"," : Utilisé pour rechercher\nfacilement des données dans un arbre et permet d'afficher les\nenfants.",[370,52360,52361,52366],{},[49,52362,52365],{"href":52363,"rel":52364},"http:\u002F\u002Fxinx.shadoware.org\u002Fdoxygen\u002Fdevel\u002Fhtml\u002FclassXinxLineEdit.html",[347],"XinxLineEdit"," : Inspiré de KDE, affiche un bouton pour effacer le\ntexte dans la ligne d'édition",[370,52368,52369,52374],{},[49,52370,52373],{"href":52371,"rel":52372},"http:\u002F\u002Fxinx.shadoware.org\u002Fdoxygen\u002Fdevel\u002Fhtml\u002FclassXinxListWidgetImpl.html",[347],"XinxListWidgetImpl"," : Permet de modifier une liste de valeur\n(ajout, suppression, valeur par défaut, classement, ...)",[370,52376,52377,52382,52383,52386,52387,179],{},[49,52378,52381],{"href":52379,"rel":52380},"http:\u002F\u002Fxinx.shadoware.org\u002Fdoxygen\u002Fdevel\u002Fhtml\u002FclassPluginSelector.html",[347],"PluginSelector"," : Inspiré de KDE, permet d'afficher une liste des\nplugins, avec un bouton ",[362,52384,52385],{},"A propos de ..."," et un bouton ",[362,52388,52389],{},"Configurer\n...",[370,52391,52392,52397],{},[49,52393,52396],{"href":52394,"rel":52395},"http:\u002F\u002Fxinx.shadoware.org\u002Fdoxygen\u002Fdevel\u002Fhtml\u002FclassQMenuView.html",[347],"QMenuView"," : Inspiré d'un exemple de Qt et permet d'afficher un\nmenu à partir d'un modèle.",[133,52399,52401],{"id":52400},"fonctionnalités","Fonctionnalités",[27533,52403,52405],{"id":52404},"recherche-parmi-les-fichiers-dun-dossier","Recherche parmi les fichiers d'un dossier",[12,52407,52408],{},"Un dock sur le coté permet en mode projet de rechercher et d'ouvrir un\nfichier parmi une multitude de fichier d'un projet (utile pour les\nfichiers avec beaucoup de feuille de style).",[12,52410,52411],{},"La recherche peut se faire avec des Jockers (*)",[12,52413,52414],{},[121,52415],{"alt":52416,"src":52417},"Dock","\u002FXINX\u002Fpresentation-de-xinx\u002Fdockprojet.png",[27533,52419,52421],{"id":52420},"la-complétion-xsl","La complétion XSL",[12,52423,52424],{},[121,52425],{"alt":52426,"src":52427},"Completion","\u002FXINX\u002Fpresentation-de-xinx\u002Fcompletion.png",[12,52429,52430,52432,52433,44506],{},[155,52431,44415],{}," permet de compléter sur les feuilles de styles à plusieurs\nniveaux",[17400,52434,52435],{},[49,52436,2547],{"href":17977,"ariaDescribedBy":52437,"dataFootnoteRef":1646,"id":17979},[17406],[506,52439,52440,52446,52452],{},[370,52441,52442,52443,52445],{},"la balise (fonctionnalité standard vous allez me dire) : ",[155,52444,44415],{},"\npermet de compléter sur les balises xsl:, les balises html ...",[370,52447,52448,52449,52451],{},"les attributs (encore standard). Pour chaque balise, ",[155,52450,44415],{}," est\ncapable de compléter sur les attributs de la balise saisie.",[370,52453,52454,52455,52457,52458,52460,52461,932,52463,52465,52466,23767,52469,52471],{},"les valeurs d'attribut (là c'est intéressant). Permet de compléter\nsur les variables déclarées dans le fichier ",[362,52456,52035],{},", ainsi que dans\nles fichiers importés (pour retrouver les fichiers importés,\n",[155,52459,44415],{},", utilise le mode projet et les paramètres qui y sont\ndéfinits). De plus sur les attributs non ",[362,52462,52035],{},[155,52464,44415],{}," ajoute\nautomatiquement les accolades ",[184,52467,52468],{},"{ }",[184,52470,41275],{}," pour les variables\net les paramètres.",[12,52473,52474,52475,52477,52478,52481,52482,52485,52486,45630,52489,52491],{},"De plus lors de l'utilisation de la balise call:template, ",[155,52476,44415],{}," va\nchercher automatiquement les paramètres (",[184,52479,52480],{},"xsl:param",") du template donné,\net les ajoute automatiquement en ",[184,52483,52484],{},"xsl:with-param",".Pour les balises\n",[184,52487,52488],{},"xsl:apply-templates",[155,52490,44415],{}," propose la liste des modes des templates\nexistants.",[12,52493,52494],{},[121,52495],{"alt":52426,"src":52496},"\u002FXINX\u002Fpresentation-de-xinx\u002Fcompletion2.png",[27533,52498,52500],{"id":52499},"le-parsing-de-la-feuille-de-style","Le parsing de la feuille de style",[12,52502,52503,52504,52506,52507,52510],{},"Fonctionnalité du moment, si vous indiquez un flux de données et une\nfeuille de style, ",[155,52505,44415],{}," est capable d'afficher le résultat (la source\n",[362,52508,52509],{},"HTML"," et le résultat dans un navigateur Webkit)",[12,52512,52513],{},"Si l'emplacement où doivent s'exécuter les pages est définit alors, les\nfeuilles de styles, les images, ... pourront être affichées.",[12,52515,52516],{},[121,52517],{"alt":52518,"src":52519},"Représentation","\u002FXINX\u002Fpresentation-de-xinx\u002Frepresentation_html.png",[133,52521,52523],{"id":52522},"a-venir","A venir",[12,52525,52526],{},"Bien que l'avenir du logiciel dépend de ces utilisateurs, voici les\nnouvelles fonctionnalités que devrait apporter la prochaine version :",[506,52528,52529,52532,52535],{},[370,52530,52531],{},"Déverminage de feuille de style XSL",[370,52533,52534],{},"Amélioration du parseur JavaScript et CSS",[370,52536,52537],{},"Quelques réécritures (Vue projet, ...)",[133,52539,52541],{"id":52540},"le-plugin-gce","Le plugin GCE",[12,52543,52544,52545,52547,52548,179],{},"Comme indiqué ci-dessous, il existe un plugin ",[362,52546,52082],{}," utilisé par les\nconsultants travaillants sur l'ERP ",[362,52549,52082],{},[12,52551,52552,52553,179],{},"Ce plugin permet de dupliquer une feuille de style d'un dossier à un\nautre (sans modification des fichiers standards), de lire les\ndictionnaires de libellé de l'ERP GCE, la lecture du fichier de\nconfiguration du lecteur ",[362,52554,52082],{},[12,52556,52557],{},[121,52558],{"alt":52559,"src":52560},"Dérivation","\u002FXINX\u002Fpresentation-de-xinx\u002Fderivation.png",[133,52562,43823],{"id":43822},[12,52564,52565,52566,52568,52569,52573],{},"Vous pouvez télécharger ",[155,52567,44415],{}," à l'adresse\n",[49,52570,52571],{"href":52571,"rel":52572},"http:\u002F\u002Fxinx.shadoware.org\u002Fdownloads",[347]," (source et version Windows).",[12,52575,52576,52577,52579],{},"Si vous voulez installer ",[155,52578,44415],{}," sur une Gnu\u002FDebian (32 ou 64 bits)\nvous pouvez inclure le dépôt suivant :",[2105,52581,52584],{"className":52582,"code":52583,"language":26023},[26021],"# Shadoware.Org\ndeb http:\u002F\u002Fapt.shadoware.org\u002F squeeze main\n",[184,52585,52583],{"__ignoreMap":1646},[12,52587,52588],{},"Et installé de la façon suivante :",[2105,52590,52592],{"className":2317,"code":52591,"language":2319,"meta":1646,"style":1646},"aptitude install xinx\n",[184,52593,52594],{"__ignoreMap":1646},[2113,52595,52596,52599,52602],{"class":2115,"line":2116},[2113,52597,52598],{"class":2133},"aptitude",[2113,52600,52601],{"class":2149}," install",[2113,52603,52604],{"class":2149}," xinx\n",[12,52606,52607,52608],{},"Vous pouvez vous tenir au courant des sorties de version grâce à la\n",[49,52609,52612],{"href":52610,"rel":52611},"http:\u002F\u002Flists.shadoware.org\u002Flistinfo\u002Fxinx",[347],"mailing list",[23219,52614,52616,52619],{"className":52615,"dataFootnotes":1646},[23222],[128,52617,23227],{"className":52618,"id":17406},[23226],[367,52620,52621,52627],{},[370,52622,52623,52624],{"id":23232},"Un ERP et un logiciel de gestion d'entreprise. Un ERP permet de gérer, par exemple, les produits, les clients, les fournisseurs, les stocks, les ventes, les achats, la logistique, la comptabilité, .... Je ne vais pas vous faire une présentation des ERPs, car même si cela pourrait faire un billet intéressant, ce n'est pas le but ici. ",[49,52625,23240],{"href":23236,"ariaLabel":23237,"className":52626,"dataFootnoteBackref":1646},[23239],[370,52628,52629,52630,52635,52636],{"id":23243},"Merci à ",[49,52631,52634],{"href":52632,"rel":52633},"http:\u002F\u002Fqcodeedit.edyuk.org\u002F",[347],"QCodeEdit"," qui me fournit quelques fonctionnalités utiles. ",[49,52637,23240],{"href":23251,"ariaLabel":23252,"className":52638,"dataFootnoteBackref":1646},[23239],[3358,52640,52641],{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":1646,"searchDepth":1647,"depth":1647,"links":52643},[52644,52645,52646,52647,52648,52649,52650,52651,52652,52653,52654],{"id":52065,"depth":1652,"text":52066},{"id":52164,"depth":1652,"text":52165},{"id":43811,"depth":1652,"text":1075},{"id":52252,"depth":1652,"text":52253},{"id":52302,"depth":1652,"text":52303},{"id":52329,"depth":1652,"text":52330},{"id":52400,"depth":1652,"text":52401},{"id":52522,"depth":1652,"text":52523},{"id":52540,"depth":1652,"text":52541},{"id":43822,"depth":1652,"text":43823},{"id":17406,"depth":1647,"text":23227},"2010-05-02","Ce billet fait suite à la sortie de la version 0.9.0 de XINX et va\nvous décrire ce logiciel. XINX est éditeur de feuille de style XSL\n(il fait également éditeur JavaScript et CSS mais actuellement moins\nbien que ce que peuvent faire d'autres).",{"type":9,"value":52658},[52659,52671,52675,52681],[12,52660,52026,52661,52029,52663,52032,52665,52036,52667,792,52669,52042],{},[155,52662,44415],{},[155,52664,44415],{},[362,52666,52035],{},[362,52668,52039],{},[362,52670,40215],{},[12,52672,52045,52673,179],{},[184,52674,52048],{},[12,52676,52051,52677,52054,52679,179],{},[362,52678,52035],{},[362,52680,52057],{},[12,52682,52683],{},[121,52684],{"alt":44415,"src":52062},{},"\u002Fpost\u002Fpresentation-de-xinx",{"title":52021,"description":52656},"presentation-de-xinx","posts\u002FXINX\u002F2010-05-02-presentation-de-xinx",[45988,46019,38761,46020],"wli16MFeBVMaJbc921USFquc6EZTDzFocvtCkto8yo8",{"id":52693,"title":52694,"author":7,"body":52695,"category":2057,"categorySlug":2058,"date":55168,"description":52699,"excerpt":55169,"extension":1764,"location":1765,"meta":55183,"navigation":1767,"path":55184,"published":1767,"seo":55185,"slug":55186,"stem":55187,"tags":55188,"timeToRead":2556,"__hash__":55189},"posts\u002Fposts\u002FProgrammation\u002F2009-09-21-qt-transformation-d-une-vue-tableau-en-une-vue-hierarchique.md","Qt - Transformation d'une vue tableau en une vue hiérarchique",{"type":9,"value":52696,"toc":55161},[52697,52700,52706,52709,52715,52718,52724,52727,52735,52739,52742,52748,52751,52767,52782,52789,52793,52797,52808,52814,52817,52821,52824,53014,53021,53037,53057,53067,53071,53075,53078,53166,53173,53190,53193,53197,53200,53207,53210,53246,53253,53530,53538,53541,53544,53748,53751,53758,53761,53764,53767,53770,53842,53845,53905,53920,54005,54011,54083,54092,54098,54118,54121,54124,54130,54201,54204,54238,54241,54289,54292,54327,54333,54351,54366,54369,54384,54421,54424,54508,54518,54575,54578,54597,54606,54609,54612,54671,54681,54764,54770,54782,54785,54791,54822,54825,54888,54891,55004,55011,55019,55022,55025,55038,55042,55045,55079,55083,55086,55120,55124,55127,55141,55144,55158],[12,52698,52699],{},"Pour visualiser des données internes à l'écran, Nokia\u002FQt nous propose\nl'architecture MVC à l'aide des classes QAbstractItemModel et de ses\nsous classes (QAbstractListModel, QAbstractTableModel, ...). Le but de\nl'architecture MVC est de séparer la représentation mémoire des données,\nde l'affichage qu'elles auront.",[12,52701,52702],{},[121,52703],{"alt":52704,"src":52705},"mvc1","\u002FProgrammation\u002Fqt-transformation-d-une-vue-tableau-en-une-vue-hierarchique\u002Fmvc1.png",[12,52707,52708],{},"Si les données sont par exemple issue d'une requête SQL, le modèle\nQSqlQueryModel permet de représenter la sélection SQL, à l'écran dans un\ncomposant QTableView. Ces données sont alors représentées sous forme\nd'un tableau deux dimensions.",[12,52710,52711],{},[121,52712],{"alt":52713,"src":52714},"tableau","\u002FProgrammation\u002Fqt-transformation-d-une-vue-tableau-en-une-vue-hierarchique\u002Ftableau.png",[12,52716,52717],{},"Si l'on veut représenter les dossiers de l'ordinateur, le modèle\nQDirView permet d'afficher les données de façon hiérarchique.",[12,52719,52720],{},[121,52721],{"alt":52722,"src":52723},"qdirview","\u002FProgrammation\u002Fqt-transformation-d-une-vue-tableau-en-une-vue-hierarchique\u002Fqdirview.png",[12,52725,52726],{},"Il est également possible d'écrire nos propres modèles pour représenter\nnos propres données.",[12,52728,52729,52730,179],{},"Le but de cet article est de présenter l'écriture d'un modèle,\ntransformant une vue plane (tableau deux dimensions) en vue\nhiérarchique. Pour cela nous allons prendre l'exemple de l'affichage\nd'une liste de catégorie",[17400,52731,52732],{},[49,52733,3686],{"href":17404,"ariaDescribedBy":52734,"dataFootnoteRef":1646,"id":17407},[17406],[133,52736,52738],{"id":52737},"présentation-de-lexemple","Présentation de l'exemple",[12,52740,52741],{},"Nous allons voir ci-dessous comment transformer une vue plane en une vue\nhiérarchique. Pour cela nous allons prendre l'exemple d'une liste de\ncatégorie. Ces catégories seront stockées dans une base de données\nSqlite. Ceci n'a pas d'importance mais permet d'expliquer pourquoi les\ndonnées en mémoire ne sont pas déjà sous forme hiérarchique.",[12,52743,52744],{},[121,52745],{"alt":52746,"src":52747},"proxymodel","\u002FProgrammation\u002Fqt-transformation-d-une-vue-tableau-en-une-vue-hierarchique\u002Fproxymodel.png",[12,52749,52750],{},"La description d'une catégorie passe donc par :",[367,52752,52753,52758,52761],{},[370,52754,52755,52756],{},"un identifiant unique appelé ",[362,52757,20628],{},[370,52759,52760],{},"un nom de catégorie",[370,52762,52763,52764],{},"un lien vers la catégorie parente appelé ",[362,52765,52766],{},"parent_id",[12,52768,52769,52770,52772,52773,52776,52777,52779,52780,179],{},"C'est le champ ",[362,52771,52766],{}," qui permettra de déterminer dans quelle\ncatégorie notre sous-catégorie se trouve. Dans notre cas, ",[362,52774,52775],{},"test3"," a\ncomme ",[362,52778,52766],{}," l'identifiant 11 qui est la catégorie ",[362,52781,42141],{},[12,52783,52784,52785,52788],{},"Dans notre exemple, nous allons nous conditionner à un modèle en lecture\nseul. Il est également possible de faire un modèle en lecture\u002Fécriture\nmais nous n'en parlerons pas dans cet article. Nous nous cantonnerons\ndonc à un modèle lecture seul sur un modèle source de type\n",[362,52786,52787],{},"QSqlTableView",". (Un modèle en lecture\u002Fécriture peut nécessiter de\nmettre à jour notre structure, ce qui peut être assez compliqué, par\nexemple en cas de changement de la catégorie parente).",[133,52790,52792],{"id":52791},"déclaration-dun-modèle","Déclaration d'un modèle",[27533,52794,52796],{"id":52795},"quest-quun-proxy","Qu'est qu'un proxy ?",[12,52798,52799,52800,52803,52804,52807],{},"Notre interface se basera sur la classe ",[362,52801,52802],{},"QAbstractProxyModel",". Le but de\ncette classe est de permettre de faire une transposition entre un\npremier modèle et une vue. Elle peut par exemple (à l'aide de\n",[362,52805,52806],{},"QSortFilterProxyModel",") permettre de filtrer ou trier les données d'une\nvue.",[12,52809,52810],{},[121,52811],{"alt":52812,"src":52813},"mvc","\u002FProgrammation\u002Fqt-transformation-d-une-vue-tableau-en-une-vue-hierarchique\u002Fmvc.png",[12,52815,52816],{},"Cette vue nous permettra de faire la transposition entre un modèle\ntableau et une vue arborescente.",[27533,52818,52820],{"id":52819},"interface-du-proxy","Interface du proxy",[12,52822,52823],{},"Les méthodes à ré-implémenter pour faire fonctionner une proxy, sont les\nsuivantes :",[2105,52825,52827],{"className":2387,"code":52826,"language":2389,"meta":1646,"style":1646},"virtual QModelIndex index( int row, int column, const QModelIndex & parent ) const;\nvirtual QModelIndex parent( const QModelIndex & index ) const;\n\nvirtual int rowCount( const QModelIndex & parent = QModelIndex() ) const;\nvirtual int columnCount( const QModelIndex & parent = QModelIndex() ) const;\n\nvirtual QModelIndex mapFromSource ( const QModelIndex & sourceIndex ) const;\nvirtual QModelIndex mapToSource ( const QModelIndex & proxyIndex ) const;\n",[184,52828,52829,52871,52895,52899,52929,52958,52962,52988],{"__ignoreMap":1646},[2113,52830,52831,52834,52837,52840,52842,52844,52847,52849,52851,52854,52856,52858,52860,52862,52864,52867,52869],{"class":2115,"line":2116},[2113,52832,52833],{"class":2326},"virtual",[2113,52835,52836],{"class":2414}," QModelIndex",[2113,52838,52839],{"class":2133}," index",[2113,52841,29622],{"class":2119},[2113,52843,30984],{"class":2326},[2113,52845,52846],{"class":2429}," row",[2113,52848,932],{"class":2119},[2113,52850,30984],{"class":2326},[2113,52852,52853],{"class":2429}," column",[2113,52855,932],{"class":2119},[2113,52857,7785],{"class":2326},[2113,52859,52836],{"class":2414},[2113,52861,5932],{"class":2326},[2113,52863,27287],{"class":2429},[2113,52865,52866],{"class":2119}," ) ",[2113,52868,7785],{"class":2326},[2113,52870,2487],{"class":2119},[2113,52872,52873,52875,52877,52879,52881,52883,52885,52887,52889,52891,52893],{"class":2115,"line":1647},[2113,52874,52833],{"class":2326},[2113,52876,52836],{"class":2414},[2113,52878,27287],{"class":2133},[2113,52880,29622],{"class":2119},[2113,52882,7785],{"class":2326},[2113,52884,52836],{"class":2414},[2113,52886,5932],{"class":2326},[2113,52888,52839],{"class":2429},[2113,52890,52866],{"class":2119},[2113,52892,7785],{"class":2326},[2113,52894,2487],{"class":2119},[2113,52896,52897],{"class":2115,"line":1652},[2113,52898,2125],{"emptyLinePlaceholder":1767},[2113,52900,52901,52903,52905,52908,52910,52912,52914,52916,52918,52920,52922,52925,52927],{"class":2115,"line":2185},[2113,52902,52833],{"class":2326},[2113,52904,2842],{"class":2326},[2113,52906,52907],{"class":2133}," rowCount",[2113,52909,29622],{"class":2119},[2113,52911,7785],{"class":2326},[2113,52913,52836],{"class":2414},[2113,52915,5932],{"class":2326},[2113,52917,27287],{"class":2429},[2113,52919,2153],{"class":2326},[2113,52921,52836],{"class":2133},[2113,52923,52924],{"class":2119},"() ) ",[2113,52926,7785],{"class":2326},[2113,52928,2487],{"class":2119},[2113,52930,52931,52933,52935,52938,52940,52942,52944,52946,52948,52950,52952,52954,52956],{"class":2115,"line":2230},[2113,52932,52833],{"class":2326},[2113,52934,2842],{"class":2326},[2113,52936,52937],{"class":2133}," columnCount",[2113,52939,29622],{"class":2119},[2113,52941,7785],{"class":2326},[2113,52943,52836],{"class":2414},[2113,52945,5932],{"class":2326},[2113,52947,27287],{"class":2429},[2113,52949,2153],{"class":2326},[2113,52951,52836],{"class":2133},[2113,52953,52924],{"class":2119},[2113,52955,7785],{"class":2326},[2113,52957,2487],{"class":2119},[2113,52959,52960],{"class":2115,"line":2235},[2113,52961,2125],{"emptyLinePlaceholder":1767},[2113,52963,52964,52966,52968,52971,52973,52975,52977,52979,52982,52984,52986],{"class":2115,"line":2241},[2113,52965,52833],{"class":2326},[2113,52967,52836],{"class":2414},[2113,52969,52970],{"class":2133}," mapFromSource",[2113,52972,45109],{"class":2119},[2113,52974,7785],{"class":2326},[2113,52976,52836],{"class":2414},[2113,52978,5932],{"class":2326},[2113,52980,52981],{"class":2429}," sourceIndex",[2113,52983,52866],{"class":2119},[2113,52985,7785],{"class":2326},[2113,52987,2487],{"class":2119},[2113,52989,52990,52992,52994,52997,52999,53001,53003,53005,53008,53010,53012],{"class":2115,"line":2246},[2113,52991,52833],{"class":2326},[2113,52993,52836],{"class":2414},[2113,52995,52996],{"class":2133}," mapToSource",[2113,52998,45109],{"class":2119},[2113,53000,7785],{"class":2326},[2113,53002,52836],{"class":2414},[2113,53004,5932],{"class":2326},[2113,53006,53007],{"class":2429}," proxyIndex",[2113,53009,52866],{"class":2119},[2113,53011,7785],{"class":2326},[2113,53013,2487],{"class":2119},[12,53015,53016,53017,53020],{},"Les quatre premières méthodes appartiennent à l'objet\n",[362,53018,53019],{},"QAbstractItemModel"," et seront questionnées par la vue (liste, arbre, …)\npour connaître la représentation à l'écran.",[12,53022,34136,53023,34384,53026,53029,53030,53032,53033,53036],{},[362,53024,53025],{},"mapFromSource()",[362,53027,53028],{},"mapToSource()"," sont à implémenter\npour l'objet ",[362,53031,52802],{},". Elles permettent de faire la liaison\nentre les indexes (",[362,53034,53035],{},"QModelIndex",") du modèle d'origine et les indexes du\nnouveau modèle.",[12,53038,34136,53039,932,53042,932,53045,53048,53049,53051,53052,792,53054,53056],{},[362,53040,53041],{},"data()",[362,53043,53044],{},"headerData()",[362,53046,53047],{},"flags()"," sont réimplémentées par\nl'objet ",[362,53050,52802],{}," et utilisent les méthodes\n",[362,53053,53025],{},[362,53055,53028],{}," pour, lors de l'appel d'une de ces\nméthodes, récupérer les informations stockées sur le modèle d'origine.",[12,53058,53059,53060,792,53063,53066],{},"Des méthodes ",[362,53061,53062],{},"sourceModel()",[362,53064,53065],{},"setSourceModel()"," permettent de\nspécifier le modèle d'origine.",[133,53068,53070],{"id":53069},"implémentation-de-notre-modèle","Implémentation de notre modèle",[27533,53072,53074],{"id":53073},"structure-interne","Structure interne",[12,53076,53077],{},"Nous allons, dans cette partie, parler de la structure interne utilisée\npar notre objet.",[2105,53079,53081],{"className":2387,"code":53080,"language":2389,"meta":1646,"style":1646},"struct Mapping {\n    int id, parrentId;\n    int index, parentIndex;\n    QVector\u003Cint> source_rows;\n};\ntypedef QMap\u003Cint,Mapping*> IndexMap;\n\nIndexMap m_sourcesIndexMapping;\nQHash\u003Cint,int> m_categoryIdMapping;\n",[184,53082,53083,53092,53100,53107,53118,53122,53142,53146,53151],{"__ignoreMap":1646},[2113,53084,53085,53087,53090],{"class":2115,"line":2116},[2113,53086,2641],{"class":2326},[2113,53088,53089],{"class":2414}," Mapping",[2113,53091,2647],{"class":2119},[2113,53093,53094,53097],{"class":2115,"line":1647},[2113,53095,53096],{"class":2326},"    int",[2113,53098,53099],{"class":2119}," id, parrentId;\n",[2113,53101,53102,53104],{"class":2115,"line":1652},[2113,53103,53096],{"class":2326},[2113,53105,53106],{"class":2119}," index, parentIndex;\n",[2113,53108,53109,53112,53115],{"class":2115,"line":2185},[2113,53110,53111],{"class":2119},"    QVector",[2113,53113,53114],{"class":2326},"\u003Cint>",[2113,53116,53117],{"class":2119}," source_rows;\n",[2113,53119,53120],{"class":2115,"line":2230},[2113,53121,2787],{"class":2119},[2113,53123,53124,53127,53130,53133,53136,53139],{"class":2115,"line":2235},[2113,53125,53126],{"class":2326},"typedef",[2113,53128,53129],{"class":2119}," QMap",[2113,53131,53132],{"class":2326},"\u003Cint",[2113,53134,53135],{"class":2119},",Mapping",[2113,53137,53138],{"class":2326},"*>",[2113,53140,53141],{"class":2119}," IndexMap;\n",[2113,53143,53144],{"class":2115,"line":2241},[2113,53145,2125],{"emptyLinePlaceholder":1767},[2113,53147,53148],{"class":2115,"line":2246},[2113,53149,53150],{"class":2119},"IndexMap m_sourcesIndexMapping;\n",[2113,53152,53153,53156,53158,53160,53163],{"class":2115,"line":2464},[2113,53154,53155],{"class":2119},"QHash",[2113,53157,53132],{"class":2326},[2113,53159,26987],{"class":2119},[2113,53161,53162],{"class":2326},"int>",[2113,53164,53165],{"class":2119}," m_categoryIdMapping;\n",[12,53167,53168,53169,53172],{},"Nous allons créer une structure ",[362,53170,53171],{},"Mapping"," qui associera pour une ligne\ndans le modèle source, les informations concernant :",[367,53174,53175,53178,53181,53184,53187],{},[370,53176,53177],{},"l'identifiant de la ligne.",[370,53179,53180],{},"l'identifiant de la ligne parente.",[370,53182,53183],{},"l'index dans le modèle source.",[370,53185,53186],{},"l'index du parent dans le modèle source.",[370,53188,53189],{},"la liste des indexes dans le modèle source des enfants dans notre\nmodèle.",[12,53191,53192],{},"Une table de hashage permettra de retrouver à partir d'un identifiant\n(id), la ligne dans le modèle source correspondante. On ne gardera que\nla seconde colonne (le nom de la catégorie), on ne fera donc\nl'association qu'entre le nom de la colonne de notre modèle et le modèle\nsource en dure dans le programme.",[27533,53194,53196],{"id":53195},"createmapping","createMapping()",[12,53198,53199],{},"Le but de cette méthode est de générer l'arborescence qui sera ensuite\nutilisée dans la suite des méthodes. Cette arborescence étant générée\nune fois pour toute (ou à chaque fois que le modèle source sera\nréinitialisé), il faudra parcourir l'ensemble des lignes du modèle\nsource pour construire notre arbre. Parcourir l'ensemble des lignes du\nmodèle source peut prendre du temps si ce dernier contient beaucoup de\nligne.",[12,53201,53202,53203,53206],{},"Nous allons considérer dans la suite, que l'identifiant de ligne 1 (id)\ncorrespond à la catégorie de plus haut niveau (ci-dessus du nom de\n",[362,53204,53205],{},"Categories","). Nous allons créer une catégorie fictive 0 qui\ncorrespondra à la racine de notre modèle. Cette catégorie sera donc la\ncatégorie parente de celle à l'identifiant 1.Dans notre modèle source,\nla catégorie 0 n'a pas d'équivalence parmi les lignes du modèle source.\nNous allons donc lui associer la ligne -1 dans le modèle source.",[12,53208,53209],{},"Maintenant voyons la génération de la structure interne.",[2105,53211,53213],{"className":2387,"code":53212,"language":2389,"meta":1646,"style":1646},"qDeleteAll( m_sourcesIndexMapping );\nm_sourcesIndexMapping.clear();\nm_categoryIdMapping.clear();\n",[184,53214,53215,53223,53235],{"__ignoreMap":1646},[2113,53216,53217,53220],{"class":2115,"line":2116},[2113,53218,53219],{"class":2133},"qDeleteAll",[2113,53221,53222],{"class":2119},"( m_sourcesIndexMapping );\n",[2113,53224,53225,53228,53230,53233],{"class":2115,"line":1647},[2113,53226,53227],{"class":2414},"m_sourcesIndexMapping",[2113,53229,179],{"class":2119},[2113,53231,53232],{"class":2133},"clear",[2113,53234,3944],{"class":2119},[2113,53236,53237,53240,53242,53244],{"class":2115,"line":1652},[2113,53238,53239],{"class":2414},"m_categoryIdMapping",[2113,53241,179],{"class":2119},[2113,53243,53232],{"class":2133},[2113,53245,3944],{"class":2119},[12,53247,53248,53249,53252],{},"Nous commençons ci-dessous par nettoyer les différentes structures afin\nde les régénérer. Il faudra à la suite de la création du mapping appeler\nla méthode ",[362,53250,53251],{},"reset()"," afin de prévenir la vue que l'ensemble des indexes\ndu modèle sont obsolètes.",[2105,53254,53256],{"className":2387,"code":53255,"language":2389,"meta":1646,"style":1646},"int source_rows = m_sourceModel->rowCount();\nfor( int i = -1; i \u003C source_rows; ++i ) {\n    Mapping * m = new Mapping;\n    IndexMap::const_iterator it = IndexMap::const_iterator( m_sourcesIndexMapping.insert( i, m ) );\n    m->index = i;\n    m->parrentId = 0;\n    m->parentIndex = 0;\n\n    if( i >= 0 ) {\n        QSqlRecord record = m_sourceModel->record( i );\n        m_categoryIdMapping[ record.value( list_id ).toInt() ] = i;\n        m->id = record.value( list_id ).toInt();\n    } else { \u002F\u002F Create the root Item\n        m->id = 0;\n        m_categoryIdMapping[ 0 ] = -1;\n    }\n}\n",[184,53257,53258,53277,53306,53323,53348,53363,53378,53393,53397,53410,53427,53454,53478,53489,53503,53522,53526],{"__ignoreMap":1646},[2113,53259,53260,53262,53265,53267,53270,53272,53275],{"class":2115,"line":2116},[2113,53261,30984],{"class":2326},[2113,53263,53264],{"class":2119}," source_rows ",[2113,53266,2335],{"class":2326},[2113,53268,53269],{"class":2414}," m_sourceModel",[2113,53271,4115],{"class":2119},[2113,53273,53274],{"class":2133},"rowCount",[2113,53276,3944],{"class":2119},[2113,53278,53279,53281,53283,53285,53287,53289,53291,53293,53296,53298,53301,53303],{"class":2115,"line":1647},[2113,53280,8677],{"class":2326},[2113,53282,29622],{"class":2119},[2113,53284,30984],{"class":2326},[2113,53286,49919],{"class":2119},[2113,53288,2335],{"class":2326},[2113,53290,2270],{"class":2326},[2113,53292,3686],{"class":2166},[2113,53294,53295],{"class":2119},"; i ",[2113,53297,3109],{"class":2326},[2113,53299,53300],{"class":2119}," source_rows; ",[2113,53302,7931],{"class":2326},[2113,53304,53305],{"class":2119},"i ) {\n",[2113,53307,53308,53311,53313,53316,53318,53320],{"class":2115,"line":1652},[2113,53309,53310],{"class":2119},"    Mapping ",[2113,53312,2676],{"class":2326},[2113,53314,53315],{"class":2119}," m ",[2113,53317,2335],{"class":2326},[2113,53319,4778],{"class":2326},[2113,53321,53322],{"class":2119}," Mapping;\n",[2113,53324,53325,53328,53330,53333,53336,53338,53340,53342,53345],{"class":2115,"line":2185},[2113,53326,53327],{"class":2119},"    IndexMap::const_iterator it ",[2113,53329,2335],{"class":2326},[2113,53331,53332],{"class":2119}," IndexMap::",[2113,53334,53335],{"class":2133},"const_iterator",[2113,53337,29622],{"class":2119},[2113,53339,53227],{"class":2414},[2113,53341,179],{"class":2119},[2113,53343,53344],{"class":2133},"insert",[2113,53346,53347],{"class":2119},"( i, m ) );\n",[2113,53349,53350,53353,53355,53358,53360],{"class":2115,"line":2230},[2113,53351,53352],{"class":2414},"    m",[2113,53354,4115],{"class":2119},[2113,53356,53357],{"class":2330},"index",[2113,53359,2153],{"class":2326},[2113,53361,53362],{"class":2119}," i;\n",[2113,53364,53365,53367,53369,53372,53374,53376],{"class":2115,"line":2235},[2113,53366,53352],{"class":2414},[2113,53368,4115],{"class":2119},[2113,53370,53371],{"class":2330},"parrentId",[2113,53373,2153],{"class":2326},[2113,53375,3208],{"class":2166},[2113,53377,2487],{"class":2119},[2113,53379,53380,53382,53384,53387,53389,53391],{"class":2115,"line":2241},[2113,53381,53352],{"class":2414},[2113,53383,4115],{"class":2119},[2113,53385,53386],{"class":2330},"parentIndex",[2113,53388,2153],{"class":2326},[2113,53390,3208],{"class":2166},[2113,53392,2487],{"class":2119},[2113,53394,53395],{"class":2115,"line":2246},[2113,53396,2125],{"emptyLinePlaceholder":1767},[2113,53398,53399,53401,53404,53406,53408],{"class":2115,"line":2464},[2113,53400,7394],{"class":2326},[2113,53402,53403],{"class":2119},"( i ",[2113,53405,2481],{"class":2326},[2113,53407,3208],{"class":2166},[2113,53409,4165],{"class":2119},[2113,53411,53412,53415,53417,53419,53421,53424],{"class":2115,"line":2085},[2113,53413,53414],{"class":2119},"        QSqlRecord record ",[2113,53416,2335],{"class":2326},[2113,53418,53269],{"class":2414},[2113,53420,4115],{"class":2119},[2113,53422,53423],{"class":2133},"record",[2113,53425,53426],{"class":2119},"( i );\n",[2113,53428,53429,53432,53435,53437,53439,53441,53444,53447,53450,53452],{"class":2115,"line":2514},[2113,53430,53431],{"class":2414},"        m_categoryIdMapping",[2113,53433,53434],{"class":2119},"[ ",[2113,53436,53423],{"class":2414},[2113,53438,179],{"class":2119},[2113,53440,21478],{"class":2133},[2113,53442,53443],{"class":2119},"( list_id ).",[2113,53445,53446],{"class":2133},"toInt",[2113,53448,53449],{"class":2119},"() ] ",[2113,53451,2335],{"class":2326},[2113,53453,53362],{"class":2119},[2113,53455,53456,53459,53461,53463,53465,53468,53470,53472,53474,53476],{"class":2115,"line":2533},[2113,53457,53458],{"class":2414},"        m",[2113,53460,4115],{"class":2119},[2113,53462,20628],{"class":2330},[2113,53464,2153],{"class":2326},[2113,53466,53467],{"class":2414}," record",[2113,53469,179],{"class":2119},[2113,53471,21478],{"class":2133},[2113,53473,53443],{"class":2119},[2113,53475,53446],{"class":2133},[2113,53477,3944],{"class":2119},[2113,53479,53480,53482,53484,53486],{"class":2115,"line":2556},[2113,53481,20671],{"class":2119},[2113,53483,4227],{"class":2326},[2113,53485,38076],{"class":2119},[2113,53487,53488],{"class":2396}," \u002F\u002F Create the root Item\n",[2113,53490,53491,53493,53495,53497,53499,53501],{"class":2115,"line":2569},[2113,53492,53458],{"class":2414},[2113,53494,4115],{"class":2119},[2113,53496,20628],{"class":2330},[2113,53498,2153],{"class":2326},[2113,53500,3208],{"class":2166},[2113,53502,2487],{"class":2119},[2113,53504,53505,53507,53509,53511,53514,53516,53518,53520],{"class":2115,"line":2575},[2113,53506,53431],{"class":2414},[2113,53508,53434],{"class":2119},[2113,53510,3095],{"class":2166},[2113,53512,53513],{"class":2119}," ] ",[2113,53515,2335],{"class":2326},[2113,53517,2270],{"class":2326},[2113,53519,3686],{"class":2166},[2113,53521,2487],{"class":2119},[2113,53523,53524],{"class":2115,"line":2596},[2113,53525,4665],{"class":2119},[2113,53527,53528],{"class":2115,"line":3192},[2113,53529,2599],{"class":2119},[12,53531,53532,53533,53535,53536,179],{},"Nous récupérons le nombre de ligne dans le modèle source et commençons à\ninitialiser notre structure. Le but de cette boucle est surtout\nd'initialiser dans notre correspondance identifiant\u002Findex\n(",[362,53534,53239],{},"), l'emplacement des identifiants dans notre\ntableau. Ainsi dans la suite lorsque l'on essaiera d'associer une ligne\nà son parent, nous pourrons savoir à quel emplacement retrouver notre id\ndans la structure ",[362,53537,53227],{},[12,53539,53540],{},"Nous profitons également de cette boucle pour initialiser les différents\nchamps de notre structure comme l'index (qui correspond à la ligne dans\nle modèle source), et l'id qui correspond à un identifiant unique dans\nnotre liste.",[12,53542,53543],{},"Nous n'oublions pas notre cas particulier que représente notre racine\n(ligne source -1 inexistante).",[2105,53545,53547],{"className":2387,"code":53546,"language":2389,"meta":1646,"style":1646},"for( int i = 0; i \u003C source_rows; ++i ) {\n    QSqlRecord record = m_sourceModel->record( i );\n\n    int parentCategoryId = record.value( list_parentid ).toInt();\n    int parentCategoryIndex = m_categoryIdMapping.value( parentCategoryId, -2 );\n    Q_ASSERT( parentCategoryIndex > -2 );\n    Mapping * mapping = m_sourcesIndexMapping.value( i );\n    mapping->parentIndex = parentCategoryIndex;\n    mapping->parrentId   = parentCategoryId;\n\n    Mapping * categoryMapping = m_sourcesIndexMapping.value( parentCategoryIndex );\n    categoryMapping->source_rows.append( i );\n}\n",[184,53548,53549,53573,53588,53592,53614,53640,53656,53676,53690,53704,53708,53728,53744],{"__ignoreMap":1646},[2113,53550,53551,53553,53555,53557,53559,53561,53563,53565,53567,53569,53571],{"class":2115,"line":2116},[2113,53552,8677],{"class":2326},[2113,53554,29622],{"class":2119},[2113,53556,30984],{"class":2326},[2113,53558,49919],{"class":2119},[2113,53560,2335],{"class":2326},[2113,53562,3208],{"class":2166},[2113,53564,53295],{"class":2119},[2113,53566,3109],{"class":2326},[2113,53568,53300],{"class":2119},[2113,53570,7931],{"class":2326},[2113,53572,53305],{"class":2119},[2113,53574,53575,53578,53580,53582,53584,53586],{"class":2115,"line":1647},[2113,53576,53577],{"class":2119},"    QSqlRecord record ",[2113,53579,2335],{"class":2326},[2113,53581,53269],{"class":2414},[2113,53583,4115],{"class":2119},[2113,53585,53423],{"class":2133},[2113,53587,53426],{"class":2119},[2113,53589,53590],{"class":2115,"line":1652},[2113,53591,2125],{"emptyLinePlaceholder":1767},[2113,53593,53594,53596,53599,53601,53603,53605,53607,53610,53612],{"class":2115,"line":2185},[2113,53595,53096],{"class":2326},[2113,53597,53598],{"class":2119}," parentCategoryId ",[2113,53600,2335],{"class":2326},[2113,53602,53467],{"class":2414},[2113,53604,179],{"class":2119},[2113,53606,21478],{"class":2133},[2113,53608,53609],{"class":2119},"( list_parentid ).",[2113,53611,53446],{"class":2133},[2113,53613,3944],{"class":2119},[2113,53615,53616,53618,53621,53623,53626,53628,53630,53633,53635,53637],{"class":2115,"line":2230},[2113,53617,53096],{"class":2326},[2113,53619,53620],{"class":2119}," parentCategoryIndex ",[2113,53622,2335],{"class":2326},[2113,53624,53625],{"class":2414}," m_categoryIdMapping",[2113,53627,179],{"class":2119},[2113,53629,21478],{"class":2133},[2113,53631,53632],{"class":2119},"( parentCategoryId, ",[2113,53634,3242],{"class":2326},[2113,53636,2547],{"class":2166},[2113,53638,53639],{"class":2119}," );\n",[2113,53641,53642,53645,53648,53650,53652,53654],{"class":2115,"line":2235},[2113,53643,53644],{"class":2133},"    Q_ASSERT",[2113,53646,53647],{"class":2119},"( parentCategoryIndex ",[2113,53649,29415],{"class":2326},[2113,53651,2270],{"class":2326},[2113,53653,2547],{"class":2166},[2113,53655,53639],{"class":2119},[2113,53657,53658,53660,53662,53665,53667,53670,53672,53674],{"class":2115,"line":2241},[2113,53659,53310],{"class":2119},[2113,53661,2676],{"class":2326},[2113,53663,53664],{"class":2119}," mapping ",[2113,53666,2335],{"class":2326},[2113,53668,53669],{"class":2414}," m_sourcesIndexMapping",[2113,53671,179],{"class":2119},[2113,53673,21478],{"class":2133},[2113,53675,53426],{"class":2119},[2113,53677,53678,53681,53683,53685,53687],{"class":2115,"line":2246},[2113,53679,53680],{"class":2414},"    mapping",[2113,53682,4115],{"class":2119},[2113,53684,53386],{"class":2330},[2113,53686,2153],{"class":2326},[2113,53688,53689],{"class":2119}," parentCategoryIndex;\n",[2113,53691,53692,53694,53696,53698,53701],{"class":2115,"line":2464},[2113,53693,53680],{"class":2414},[2113,53695,4115],{"class":2119},[2113,53697,53371],{"class":2330},[2113,53699,53700],{"class":2326},"   =",[2113,53702,53703],{"class":2119}," parentCategoryId;\n",[2113,53705,53706],{"class":2115,"line":2085},[2113,53707,2125],{"emptyLinePlaceholder":1767},[2113,53709,53710,53712,53714,53717,53719,53721,53723,53725],{"class":2115,"line":2514},[2113,53711,53310],{"class":2119},[2113,53713,2676],{"class":2326},[2113,53715,53716],{"class":2119}," categoryMapping ",[2113,53718,2335],{"class":2326},[2113,53720,53669],{"class":2414},[2113,53722,179],{"class":2119},[2113,53724,21478],{"class":2133},[2113,53726,53727],{"class":2119},"( parentCategoryIndex );\n",[2113,53729,53730,53733,53735,53738,53740,53742],{"class":2115,"line":2533},[2113,53731,53732],{"class":2414},"    categoryMapping",[2113,53734,4115],{"class":2119},[2113,53736,53737],{"class":2414},"source_rows",[2113,53739,179],{"class":2119},[2113,53741,47586],{"class":2133},[2113,53743,53426],{"class":2119},[2113,53745,53746],{"class":2115,"line":2556},[2113,53747,2599],{"class":2119},[12,53749,53750],{},"Enfin pour chaque enregistrement de notre modèle source, nous récupérons\nl'identifiant du parent et recherchons dans la structure que nous venons\nde créer l'index dans le modèle source de la catégorie parente.",[12,53752,53753,53754,53757],{},"Ces valeurs sont alors renseignées dans le modèle. Nos ajoutons\négalement dans la structure de ",[362,53755,53756],{},"mapping"," de la catégorie parent, notre\nligne dans la liste des enfants.",[12,53759,53760],{},"Notre structure est ainsi complètement initialisé. Un identifiant\nconnaît donc son parent, et la liste de ses enfants. C'est sur cette\nstructure que se basera le reste des méthodes de notre modèle.",[27533,53762,53025],{"id":53763},"mapfromsource",[12,53765,53766],{},"Le but de cette méthode est de convertir notre index venant du modèle\nsource vers un index de notre modèle à nous.",[12,53768,53769],{},"L'index du modèle source nous donne l'emplacement de la donnée dans le\nmodèle source sous forme (ligne, colonne) alors que l'index de notre\nmodèle doit donner l'emplacement dans notre modèle sous la forme (ligne,\ncolonne, pointeur indiquant le parent).",[2105,53771,53773],{"className":2387,"code":53772,"language":2389,"meta":1646,"style":1646},"if( ! sourceIndex.isValid() ) return QModelIndex();\nif( sourceIndex.model() != m_sourceModel ) {\n    qWarning( \"CategoryItemModel: index from wrong model passed to mapFromSource\" );\n    return QModelIndex();\n}\n",[184,53774,53775,53798,53818,53830,53838],{"__ignoreMap":1646},[2113,53776,53777,53779,53781,53783,53785,53787,53790,53792,53794,53796],{"class":2115,"line":2116},[2113,53778,4109],{"class":2326},[2113,53780,29622],{"class":2119},[2113,53782,3199],{"class":2334},[2113,53784,52981],{"class":2414},[2113,53786,179],{"class":2119},[2113,53788,53789],{"class":2133},"isValid",[2113,53791,52924],{"class":2119},[2113,53793,3205],{"class":2326},[2113,53795,52836],{"class":2133},[2113,53797,3944],{"class":2119},[2113,53799,53800,53802,53804,53807,53809,53811,53813,53815],{"class":2115,"line":1647},[2113,53801,4109],{"class":2326},[2113,53803,29622],{"class":2119},[2113,53805,53806],{"class":2414},"sourceIndex",[2113,53808,179],{"class":2119},[2113,53810,26168],{"class":2133},[2113,53812,4543],{"class":2119},[2113,53814,31969],{"class":2326},[2113,53816,53817],{"class":2119}," m_sourceModel ) {\n",[2113,53819,53820,53823,53825,53828],{"class":2115,"line":1652},[2113,53821,53822],{"class":2133},"    qWarning",[2113,53824,29622],{"class":2119},[2113,53826,53827],{"class":2149},"\"CategoryItemModel: index from wrong model passed to mapFromSource\"",[2113,53829,53639],{"class":2119},[2113,53831,53832,53834,53836],{"class":2115,"line":2185},[2113,53833,3137],{"class":2326},[2113,53835,52836],{"class":2133},[2113,53837,3944],{"class":2119},[2113,53839,53840],{"class":2115,"line":2230},[2113,53841,2599],{"class":2119},[12,53843,53844],{},"Il faut donc d'abord vérifier que l'index du modèle source est valide.\nS'il n'est pas valide, alors il ne sera pas valide non plus dans notre\nmodèle à nous.",[2105,53846,53848],{"className":2387,"code":53847,"language":2389,"meta":1646,"style":1646},"int row = sourceIndex.row();\nIndexMap::const_iterator it = m_sourcesIndexMapping.constFind( row );\nQ_ASSERT( it != m_sourcesIndexMapping.constEnd() );\n",[184,53849,53850,53868,53885],{"__ignoreMap":1646},[2113,53851,53852,53854,53857,53859,53861,53863,53866],{"class":2115,"line":2116},[2113,53853,30984],{"class":2326},[2113,53855,53856],{"class":2119}," row ",[2113,53858,2335],{"class":2326},[2113,53860,52981],{"class":2414},[2113,53862,179],{"class":2119},[2113,53864,53865],{"class":2133},"row",[2113,53867,3944],{"class":2119},[2113,53869,53870,53873,53875,53877,53879,53882],{"class":2115,"line":1647},[2113,53871,53872],{"class":2119},"IndexMap::const_iterator it ",[2113,53874,2335],{"class":2326},[2113,53876,53669],{"class":2414},[2113,53878,179],{"class":2119},[2113,53880,53881],{"class":2133},"constFind",[2113,53883,53884],{"class":2119},"( row );\n",[2113,53886,53887,53890,53893,53895,53897,53899,53902],{"class":2115,"line":1652},[2113,53888,53889],{"class":2133},"Q_ASSERT",[2113,53891,53892],{"class":2119},"( it ",[2113,53894,31969],{"class":2326},[2113,53896,53669],{"class":2414},[2113,53898,179],{"class":2119},[2113,53900,53901],{"class":2133},"constEnd",[2113,53903,53904],{"class":2119},"() );\n",[12,53906,53907,53908,53910,53911,53913,53914,53916,53917,53919],{},"Nous récupérons l'index dans le modèle source, et à l'aide de notre\ntableau d'index source, nous allons retrouver l'objet ",[362,53909,53171],{},"\ncorrespondant. Cette objet ",[362,53912,53171],{}," correspondra à celui de notre index\nsource. Ce dont nous avons besoin pour notre modèle est l'objet\n",[362,53915,53171],{}," de l'objet parent (En effet, notre index est construit sous la\nforme : ligne, colonne, pointeur sur la structure ",[362,53918,53171],{}," du parent de\nnotre index). Nous récupérons donc l'index de la ligne source dans\nl'objet parent.",[2105,53921,53923],{"className":2387,"code":53922,"language":2389,"meta":1646,"style":1646},"int parentRow = it.value()->parentIndex;\nIndexMap::const_iterator parentIt = m_sourcesIndexMapping.constFind( parentRow );\nQ_ASSERT( parentIt != m_sourcesIndexMapping.constEnd() );\n\nMapping * m = parentIt.value();\n",[184,53924,53925,53948,53964,53981,53985],{"__ignoreMap":1646},[2113,53926,53927,53929,53932,53934,53937,53939,53941,53944,53946],{"class":2115,"line":2116},[2113,53928,30984],{"class":2326},[2113,53930,53931],{"class":2119}," parentRow ",[2113,53933,2335],{"class":2326},[2113,53935,53936],{"class":2414}," it",[2113,53938,179],{"class":2119},[2113,53940,21478],{"class":2133},[2113,53942,53943],{"class":2119},"()->",[2113,53945,53386],{"class":2330},[2113,53947,2487],{"class":2119},[2113,53949,53950,53953,53955,53957,53959,53961],{"class":2115,"line":1647},[2113,53951,53952],{"class":2119},"IndexMap::const_iterator parentIt ",[2113,53954,2335],{"class":2326},[2113,53956,53669],{"class":2414},[2113,53958,179],{"class":2119},[2113,53960,53881],{"class":2133},[2113,53962,53963],{"class":2119},"( parentRow );\n",[2113,53965,53966,53968,53971,53973,53975,53977,53979],{"class":2115,"line":1652},[2113,53967,53889],{"class":2133},[2113,53969,53970],{"class":2119},"( parentIt ",[2113,53972,31969],{"class":2326},[2113,53974,53669],{"class":2414},[2113,53976,179],{"class":2119},[2113,53978,53901],{"class":2133},[2113,53980,53904],{"class":2119},[2113,53982,53983],{"class":2115,"line":2185},[2113,53984,2125],{"emptyLinePlaceholder":1767},[2113,53986,53987,53990,53992,53994,53996,53999,54001,54003],{"class":2115,"line":2230},[2113,53988,53989],{"class":2119},"Mapping ",[2113,53991,2676],{"class":2326},[2113,53993,53315],{"class":2119},[2113,53995,2335],{"class":2326},[2113,53997,53998],{"class":2414}," parentIt",[2113,54000,179],{"class":2119},[2113,54002,21478],{"class":2133},[2113,54004,3944],{"class":2119},[12,54006,54007,54008,54010],{},"Cela nous permet de récupérer l'objet ",[362,54009,53171],{}," de l'index parent.",[2105,54012,54014],{"className":2387,"code":54013,"language":2389,"meta":1646,"style":1646},"int proxyRow    = m->source_rows.indexOf( row );\nint proxyColumn = sourceColumnToProxy( sourceIndex.column() );\nif( proxyColumn == -1 ) return QModelIndex();\n",[184,54015,54016,54039,54062],{"__ignoreMap":1646},[2113,54017,54018,54020,54023,54025,54028,54030,54032,54034,54037],{"class":2115,"line":2116},[2113,54019,30984],{"class":2326},[2113,54021,54022],{"class":2119}," proxyRow    ",[2113,54024,2335],{"class":2326},[2113,54026,54027],{"class":2414}," m",[2113,54029,4115],{"class":2119},[2113,54031,53737],{"class":2414},[2113,54033,179],{"class":2119},[2113,54035,54036],{"class":2133},"indexOf",[2113,54038,53884],{"class":2119},[2113,54040,54041,54043,54046,54048,54051,54053,54055,54057,54060],{"class":2115,"line":1647},[2113,54042,30984],{"class":2326},[2113,54044,54045],{"class":2119}," proxyColumn ",[2113,54047,2335],{"class":2326},[2113,54049,54050],{"class":2133}," sourceColumnToProxy",[2113,54052,29622],{"class":2119},[2113,54054,53806],{"class":2414},[2113,54056,179],{"class":2119},[2113,54058,54059],{"class":2133},"column",[2113,54061,53904],{"class":2119},[2113,54063,54064,54066,54069,54071,54073,54075,54077,54079,54081],{"class":2115,"line":1652},[2113,54065,4109],{"class":2326},[2113,54067,54068],{"class":2119},"( proxyColumn ",[2113,54070,4132],{"class":2326},[2113,54072,2270],{"class":2326},[2113,54074,3686],{"class":2166},[2113,54076,52866],{"class":2119},[2113,54078,3205],{"class":2326},[2113,54080,52836],{"class":2133},[2113,54082,3944],{"class":2119},[12,54084,33522,54085,54088,54089,179],{},[362,54086,54087],{},"sourceColumnToProxy()"," permet de transformer une colonne de\nl'objet source en une colonne de l'objet courant. Cela signifie que la\ncolonne 2 sera convertie en colonne 1, et que les colonnes 1 et 3 ne\nseront pas converties (et cachées). Dans ce dernier cas, nous retournons\n",[362,54090,54091],{},"QModelIndex()",[12,54093,54094,54095,54097],{},"L'index de la ligne dans notre modèle sera donné par la position dans la\nliste ",[362,54096,53737],{}," dans notre structure de conversion.",[2105,54099,54101],{"className":2387,"code":54100,"language":2389,"meta":1646,"style":1646},"return createIndex( proxyRow, proxyColumn, *parentIt );\n",[184,54102,54103],{"__ignoreMap":1646},[2113,54104,54105,54107,54110,54113,54115],{"class":2115,"line":2116},[2113,54106,3205],{"class":2326},[2113,54108,54109],{"class":2133}," createIndex",[2113,54111,54112],{"class":2119},"( proxyRow, proxyColumn, ",[2113,54114,2676],{"class":2326},[2113,54116,54117],{"class":2119},"parentIt );\n",[12,54119,54120],{},"Nous pouvons alors créer notre index.",[27533,54122,53028],{"id":54123},"maptosource",[12,54125,54126,54127,54129],{},"De la manière inverse à la méthode ",[362,54128,53025],{},", cette méthode\npermet de convertir un index de notre objet en un index du modèle\nsource.",[2105,54131,54133],{"className":2387,"code":54132,"language":2389,"meta":1646,"style":1646},"if( ! proxyIndex.isValid() ) return QModelIndex();\nif( proxyIndex.model() != this ) {\n    qWarning( \"CategoryItemModel: index from wrong model passed to mapToSource\" );\n    return QModelIndex();\n}\n",[184,54134,54135,54157,54178,54189,54197],{"__ignoreMap":1646},[2113,54136,54137,54139,54141,54143,54145,54147,54149,54151,54153,54155],{"class":2115,"line":2116},[2113,54138,4109],{"class":2326},[2113,54140,29622],{"class":2119},[2113,54142,3199],{"class":2334},[2113,54144,53007],{"class":2414},[2113,54146,179],{"class":2119},[2113,54148,53789],{"class":2133},[2113,54150,52924],{"class":2119},[2113,54152,3205],{"class":2326},[2113,54154,52836],{"class":2133},[2113,54156,3944],{"class":2119},[2113,54158,54159,54161,54163,54166,54168,54170,54172,54174,54176],{"class":2115,"line":1647},[2113,54160,4109],{"class":2326},[2113,54162,29622],{"class":2119},[2113,54164,54165],{"class":2414},"proxyIndex",[2113,54167,179],{"class":2119},[2113,54169,26168],{"class":2133},[2113,54171,4543],{"class":2119},[2113,54173,31969],{"class":2326},[2113,54175,15832],{"class":2414},[2113,54177,4165],{"class":2119},[2113,54179,54180,54182,54184,54187],{"class":2115,"line":1652},[2113,54181,53822],{"class":2133},[2113,54183,29622],{"class":2119},[2113,54185,54186],{"class":2149},"\"CategoryItemModel: index from wrong model passed to mapToSource\"",[2113,54188,53639],{"class":2119},[2113,54190,54191,54193,54195],{"class":2115,"line":2185},[2113,54192,3137],{"class":2326},[2113,54194,52836],{"class":2133},[2113,54196,3944],{"class":2119},[2113,54198,54199],{"class":2115,"line":2230},[2113,54200,2599],{"class":2119},[12,54202,54203],{},"Si le modèle est invalide dans notre modèle, il l'est aussi dans le\nmodèle source. Il n'y a pas d'équivalent dans le modèle source de\nl'index racine.",[2105,54205,54207],{"className":2387,"code":54206,"language":2389,"meta":1646,"style":1646},"Mapping * m = static_cast\u003CMapping*>( proxyIndex.internalPointer() );\n",[184,54208,54209],{"__ignoreMap":1646},[2113,54210,54211,54213,54215,54217,54219,54221,54223,54225,54227,54229,54231,54233,54236],{"class":2115,"line":2116},[2113,54212,53989],{"class":2119},[2113,54214,2676],{"class":2326},[2113,54216,53315],{"class":2119},[2113,54218,2335],{"class":2326},[2113,54220,3140],{"class":2119},[2113,54222,3109],{"class":2326},[2113,54224,53171],{"class":2119},[2113,54226,53138],{"class":2326},[2113,54228,29622],{"class":2119},[2113,54230,54165],{"class":2414},[2113,54232,179],{"class":2119},[2113,54234,54235],{"class":2133},"internalPointer",[2113,54237,53904],{"class":2119},[12,54239,54240],{},"Nous récupérons la structure dans le pointeur interne de notre index.",[2105,54242,54244],{"className":2387,"code":54243,"language":2389,"meta":1646,"style":1646},"int sourceColumn = proxyColumnToSource( proxyIndex.column() );\nif( sourceColumn == -1 ) return QModelIndex();\n",[184,54245,54246,54268],{"__ignoreMap":1646},[2113,54247,54248,54250,54253,54255,54258,54260,54262,54264,54266],{"class":2115,"line":2116},[2113,54249,30984],{"class":2326},[2113,54251,54252],{"class":2119}," sourceColumn ",[2113,54254,2335],{"class":2326},[2113,54256,54257],{"class":2133}," proxyColumnToSource",[2113,54259,29622],{"class":2119},[2113,54261,54165],{"class":2414},[2113,54263,179],{"class":2119},[2113,54265,54059],{"class":2133},[2113,54267,53904],{"class":2119},[2113,54269,54270,54272,54275,54277,54279,54281,54283,54285,54287],{"class":2115,"line":1647},[2113,54271,4109],{"class":2326},[2113,54273,54274],{"class":2119},"( sourceColumn ",[2113,54276,4132],{"class":2326},[2113,54278,2270],{"class":2326},[2113,54280,3686],{"class":2166},[2113,54282,52866],{"class":2119},[2113,54284,3205],{"class":2326},[2113,54286,52836],{"class":2133},[2113,54288,3944],{"class":2119},[12,54290,54291],{},"Nous récupérons également les informations sur notre colonne (Soit,\nconversion de la colonne 0 dans notre modèle, en la colonne 1 dans le\nmodèle source).",[2105,54293,54295],{"className":2387,"code":54294,"language":2389,"meta":1646,"style":1646},"int sourceRow = m->source_rows.at( proxyIndex.row() );\n",[184,54296,54297],{"__ignoreMap":1646},[2113,54298,54299,54301,54304,54306,54308,54310,54312,54314,54317,54319,54321,54323,54325],{"class":2115,"line":2116},[2113,54300,30984],{"class":2326},[2113,54302,54303],{"class":2119}," sourceRow ",[2113,54305,2335],{"class":2326},[2113,54307,54027],{"class":2414},[2113,54309,4115],{"class":2119},[2113,54311,53737],{"class":2414},[2113,54313,179],{"class":2119},[2113,54315,54316],{"class":2133},"at",[2113,54318,29622],{"class":2119},[2113,54320,54165],{"class":2414},[2113,54322,179],{"class":2119},[2113,54324,53865],{"class":2133},[2113,54326,53904],{"class":2119},[12,54328,54329,54330,54332],{},"Nous recherchons dans notre liste ",[362,54331,53737],{}," la ligne dans le modèle\nsource qui correspond à la ligne (relative au père) indiqué par notre\nindex et nous construisons un index de type (ligne, colonne)\ncorrespondant au modèle source.",[2105,54334,54336],{"className":2387,"code":54335,"language":2389,"meta":1646,"style":1646},"return m_sourceModel->index( sourceRow, sourceColumn );\n",[184,54337,54338],{"__ignoreMap":1646},[2113,54339,54340,54342,54344,54346,54348],{"class":2115,"line":2116},[2113,54341,3205],{"class":2326},[2113,54343,53269],{"class":2414},[2113,54345,4115],{"class":2119},[2113,54347,53357],{"class":2133},[2113,54349,54350],{"class":2119},"( sourceRow, sourceColumn );\n",[12,54352,54353,54354,792,54356,54358,54359,932,54361,34384,54363,54365],{},"L'implémentation des méthodes ",[362,54355,53025],{},[362,54357,53028],{},"\npermettent de faire fonctionner les implémentations des méthodes\n",[362,54360,53041],{},[362,54362,53044],{},[362,54364,53047],{},". Il n'y aura donc aucun intérêt à\nré-implémenter ces méthodes à moins de vouloir traiter les données de\nces fonctions. Nous ne les décrirons donc pas ici, mais vous pourrez\nvoir dans le fichier attaché à la fin du billet, un exemple d'utilisation.",[27533,54367,54368],{"id":53357},"index()",[12,54370,54371,54372,4706,54375,932,54377,932,54380,932,54382,179],{},"Le principe de cette méthode est de générer un index pour notre modèle.\nL'index doit être valide et réutilisable dans les méthodes ",[362,54373,54374],{},"rowCount()",[362,54376,53047],{},[362,54378,54379],{},"columnCount()",[362,54381,53041],{},[362,54383,53028],{},[2105,54385,54387],{"className":2387,"code":54386,"language":2389,"meta":1646,"style":1646},"if( ( row \u003C 0 ) || ( column \u003C 0 ) ) return QModelIndex();\n",[184,54388,54389],{"__ignoreMap":1646},[2113,54390,54391,54393,54396,54398,54400,54402,54405,54408,54410,54412,54415,54417,54419],{"class":2115,"line":2116},[2113,54392,4109],{"class":2326},[2113,54394,54395],{"class":2119},"( ( row ",[2113,54397,3109],{"class":2326},[2113,54399,3208],{"class":2166},[2113,54401,52866],{"class":2119},[2113,54403,54404],{"class":2334},"||",[2113,54406,54407],{"class":2119}," ( column ",[2113,54409,3109],{"class":2326},[2113,54411,3208],{"class":2166},[2113,54413,54414],{"class":2119}," ) ) ",[2113,54416,3205],{"class":2326},[2113,54418,52836],{"class":2133},[2113,54420,3944],{"class":2119},[12,54422,54423],{},"Si la ligne ou la colonne est inférieure à 0, l'index n'est pas valide.",[2105,54425,54427],{"className":2387,"code":54426,"language":2389,"meta":1646,"style":1646},"IndexMap::const_iterator it = m_sourcesIndexMapping.constFind( -1 );\n\nQModelIndex sourceParent = mapToSource( parent );\nif( sourceParent.isValid() ) {\n    it = m_sourcesIndexMapping.constFind( sourceParent.row() );\n}\n",[184,54428,54429,54449,54453,54465,54481,54504],{"__ignoreMap":1646},[2113,54430,54431,54433,54435,54437,54439,54441,54443,54445,54447],{"class":2115,"line":2116},[2113,54432,53872],{"class":2119},[2113,54434,2335],{"class":2326},[2113,54436,53669],{"class":2414},[2113,54438,179],{"class":2119},[2113,54440,53881],{"class":2133},[2113,54442,29622],{"class":2119},[2113,54444,3242],{"class":2326},[2113,54446,3686],{"class":2166},[2113,54448,53639],{"class":2119},[2113,54450,54451],{"class":2115,"line":1647},[2113,54452,2125],{"emptyLinePlaceholder":1767},[2113,54454,54455,54458,54460,54462],{"class":2115,"line":1652},[2113,54456,54457],{"class":2119},"QModelIndex sourceParent ",[2113,54459,2335],{"class":2326},[2113,54461,52996],{"class":2133},[2113,54463,54464],{"class":2119},"( parent );\n",[2113,54466,54467,54469,54471,54474,54476,54478],{"class":2115,"line":2185},[2113,54468,4109],{"class":2326},[2113,54470,29622],{"class":2119},[2113,54472,54473],{"class":2414},"sourceParent",[2113,54475,179],{"class":2119},[2113,54477,53789],{"class":2133},[2113,54479,54480],{"class":2119},"() ) {\n",[2113,54482,54483,54486,54488,54490,54492,54494,54496,54498,54500,54502],{"class":2115,"line":2230},[2113,54484,54485],{"class":2119},"    it ",[2113,54487,2335],{"class":2326},[2113,54489,53669],{"class":2414},[2113,54491,179],{"class":2119},[2113,54493,53881],{"class":2133},[2113,54495,29622],{"class":2119},[2113,54497,54473],{"class":2414},[2113,54499,179],{"class":2119},[2113,54501,53865],{"class":2133},[2113,54503,53904],{"class":2119},[2113,54505,54506],{"class":2115,"line":2235},[2113,54507,2599],{"class":2119},[12,54509,54510,54511,54513,54514,54517],{},"A partir de l'index ",[362,54512,27266],{}," nous retrouvons l'index qui correspond dans\nle modèle source (",[362,54515,54516],{},"sourceParent'). Si l'on ne trouve pas d'index dans le\nmodèle source, nous considérons être sur l'élément racine de notre\narbre. Sinon nous recherchons dans la table de","Mapping'' la structure\nqui correspond.",[2105,54519,54521],{"className":2387,"code":54520,"language":2389,"meta":1646,"style":1646},"Q_ASSERT( it != m_sourcesIndexMapping.constEnd() );\nif( it.value()->source_rows.count() \u003C= row )\n    return QModelIndex();\n",[184,54522,54523,54539,54567],{"__ignoreMap":1646},[2113,54524,54525,54527,54529,54531,54533,54535,54537],{"class":2115,"line":2116},[2113,54526,53889],{"class":2133},[2113,54528,53892],{"class":2119},[2113,54530,31969],{"class":2326},[2113,54532,53669],{"class":2414},[2113,54534,179],{"class":2119},[2113,54536,53901],{"class":2133},[2113,54538,53904],{"class":2119},[2113,54540,54541,54543,54545,54548,54550,54552,54554,54556,54558,54560,54562,54564],{"class":2115,"line":1647},[2113,54542,4109],{"class":2326},[2113,54544,29622],{"class":2119},[2113,54546,54547],{"class":2414},"it",[2113,54549,179],{"class":2119},[2113,54551,21478],{"class":2133},[2113,54553,53943],{"class":2119},[2113,54555,53737],{"class":2414},[2113,54557,179],{"class":2119},[2113,54559,5579],{"class":2133},[2113,54561,4543],{"class":2119},[2113,54563,37729],{"class":2326},[2113,54565,54566],{"class":2119}," row )\n",[2113,54568,54569,54571,54573],{"class":2115,"line":1652},[2113,54570,3137],{"class":2326},[2113,54572,52836],{"class":2133},[2113,54574,3944],{"class":2119},[12,54576,54577],{},"Nous vérifions le nombre d'élément dans la structure et nous retournons\nun index non valide si l'index demandé va au delà de la taille du\ntableau.",[2105,54579,54581],{"className":2387,"code":54580,"language":2389,"meta":1646,"style":1646},"return createIndex( row, column, *it );\n",[184,54582,54583],{"__ignoreMap":1646},[2113,54584,54585,54587,54589,54592,54594],{"class":2115,"line":2116},[2113,54586,3205],{"class":2326},[2113,54588,54109],{"class":2133},[2113,54590,54591],{"class":2119},"( row, column, ",[2113,54593,2676],{"class":2326},[2113,54595,54596],{"class":2119},"it );\n",[12,54598,54599,54600,54602,54603,179],{},"Finalement nous retournons l'index créé avec le pointeur vers la\nstructure ",[362,54601,53171],{}," du père en ",[362,54604,54605],{},"internalData",[27533,54607,54608],{"id":27266},"parent()",[12,54610,54611],{},"Cette méthode permet de retourner pour l'index donné du proxy, l'index\ndu parent. Un index invalide n'a pas de parent.",[2105,54613,54615],{"className":2387,"code":54614,"language":2389,"meta":1646,"style":1646},"if( ! index.isValid() ) return QModelIndex();\n\nMapping * m = static_cast\u003CMapping*>( index.internalPointer() );\n",[184,54616,54617,54639,54643],{"__ignoreMap":1646},[2113,54618,54619,54621,54623,54625,54627,54629,54631,54633,54635,54637],{"class":2115,"line":2116},[2113,54620,4109],{"class":2326},[2113,54622,29622],{"class":2119},[2113,54624,3199],{"class":2334},[2113,54626,52839],{"class":2414},[2113,54628,179],{"class":2119},[2113,54630,53789],{"class":2133},[2113,54632,52924],{"class":2119},[2113,54634,3205],{"class":2326},[2113,54636,52836],{"class":2133},[2113,54638,3944],{"class":2119},[2113,54640,54641],{"class":2115,"line":1647},[2113,54642,2125],{"emptyLinePlaceholder":1767},[2113,54644,54645,54647,54649,54651,54653,54655,54657,54659,54661,54663,54665,54667,54669],{"class":2115,"line":1652},[2113,54646,53989],{"class":2119},[2113,54648,2676],{"class":2326},[2113,54650,53315],{"class":2119},[2113,54652,2335],{"class":2326},[2113,54654,3140],{"class":2119},[2113,54656,3109],{"class":2326},[2113,54658,53171],{"class":2119},[2113,54660,53138],{"class":2326},[2113,54662,29622],{"class":2119},[2113,54664,53357],{"class":2414},[2113,54666,179],{"class":2119},[2113,54668,54235],{"class":2133},[2113,54670,53904],{"class":2119},[12,54672,54673,54674,54677,54678,54680],{},"On récupère la structure de correspondance stockée dans le pointeur\ninterne de l'objet. Cette structure nous donne les informations du\nparent (en effet dans ",[362,54675,54676],{},"internalPointer()",", on stock la structure\n",[362,54679,53171],{}," du parent), et donc l'index dans le modèle source.",[2105,54682,54684],{"className":2387,"code":54683,"language":2389,"meta":1646,"style":1646},"int sourceRow = m->index;\nif( sourceRow == -1 ) return QModelIndex();\n\nQModelIndex sourceParent = m_sourceModel->index( sourceRow, proxyColumnToSource( 0 ) );\nQModelIndex proxyParent = mapFromSource( sourceParent )\n",[184,54685,54686,54702,54723,54727,54752],{"__ignoreMap":1646},[2113,54687,54688,54690,54692,54694,54696,54698,54700],{"class":2115,"line":2116},[2113,54689,30984],{"class":2326},[2113,54691,54303],{"class":2119},[2113,54693,2335],{"class":2326},[2113,54695,54027],{"class":2414},[2113,54697,4115],{"class":2119},[2113,54699,53357],{"class":2330},[2113,54701,2487],{"class":2119},[2113,54703,54704,54706,54709,54711,54713,54715,54717,54719,54721],{"class":2115,"line":1647},[2113,54705,4109],{"class":2326},[2113,54707,54708],{"class":2119},"( sourceRow ",[2113,54710,4132],{"class":2326},[2113,54712,2270],{"class":2326},[2113,54714,3686],{"class":2166},[2113,54716,52866],{"class":2119},[2113,54718,3205],{"class":2326},[2113,54720,52836],{"class":2133},[2113,54722,3944],{"class":2119},[2113,54724,54725],{"class":2115,"line":1652},[2113,54726,2125],{"emptyLinePlaceholder":1767},[2113,54728,54729,54731,54733,54735,54737,54739,54742,54745,54747,54749],{"class":2115,"line":2185},[2113,54730,54457],{"class":2119},[2113,54732,2335],{"class":2326},[2113,54734,53269],{"class":2414},[2113,54736,4115],{"class":2119},[2113,54738,53357],{"class":2133},[2113,54740,54741],{"class":2119},"( sourceRow, ",[2113,54743,54744],{"class":2133},"proxyColumnToSource",[2113,54746,29622],{"class":2119},[2113,54748,3095],{"class":2166},[2113,54750,54751],{"class":2119}," ) );\n",[2113,54753,54754,54757,54759,54761],{"class":2115,"line":2230},[2113,54755,54756],{"class":2119},"QModelIndex proxyParent ",[2113,54758,2335],{"class":2326},[2113,54760,52970],{"class":2133},[2113,54762,54763],{"class":2119},"( sourceParent )\n",[12,54765,54766,54767,54769],{},"On utilise alors notre méthode ",[362,54768,53025],{}," pour retrouver l'index\ndu parent dans le référentiel du proxy.",[2105,54771,54773],{"className":2387,"code":54772,"language":2389,"meta":1646,"style":1646},"return proxyParent;\n",[184,54774,54775],{"__ignoreMap":1646},[2113,54776,54777,54779],{"class":2115,"line":2116},[2113,54778,3205],{"class":2326},[2113,54780,54781],{"class":2119}," proxyParent;\n",[27533,54783,54374],{"id":54784},"rowcount",[12,54786,54787,54788,54790],{},"Le but de cette méthode est de retourner le nombre de ligne enfant pour\nun index. Dans le modèle source les indexes n'ont pas d'enfant.Lorsque\nl'index vaut ",[362,54789,54091],{},", cela signifie qu'il faut retourner le\nnombre de ligne pour notre racine.",[2105,54792,54794],{"className":2387,"code":54793,"language":2389,"meta":1646,"style":1646},"if( index.column() > 0 ) return 0;\n",[184,54795,54796],{"__ignoreMap":1646},[2113,54797,54798,54800,54802,54804,54806,54808,54810,54812,54814,54816,54818,54820],{"class":2115,"line":2116},[2113,54799,4109],{"class":2326},[2113,54801,29622],{"class":2119},[2113,54803,53357],{"class":2414},[2113,54805,179],{"class":2119},[2113,54807,54059],{"class":2133},[2113,54809,4543],{"class":2119},[2113,54811,29415],{"class":2326},[2113,54813,3208],{"class":2166},[2113,54815,52866],{"class":2119},[2113,54817,3205],{"class":2326},[2113,54819,3208],{"class":2166},[2113,54821,2487],{"class":2119},[12,54823,54824],{},"Seul la première colonne a des enfants.",[2105,54826,54828],{"className":2387,"code":54827,"language":2389,"meta":1646,"style":1646},"if( ! index.isValid() ) {\n    Mapping * rootMapping = m_sourcesIndexMapping.value( -1 );\n    return rootMapping->source_rows.count();\n",[184,54829,54830,54846,54871],{"__ignoreMap":1646},[2113,54831,54832,54834,54836,54838,54840,54842,54844],{"class":2115,"line":2116},[2113,54833,4109],{"class":2326},[2113,54835,29622],{"class":2119},[2113,54837,3199],{"class":2334},[2113,54839,52839],{"class":2414},[2113,54841,179],{"class":2119},[2113,54843,53789],{"class":2133},[2113,54845,54480],{"class":2119},[2113,54847,54848,54850,54852,54855,54857,54859,54861,54863,54865,54867,54869],{"class":2115,"line":1647},[2113,54849,53310],{"class":2119},[2113,54851,2676],{"class":2326},[2113,54853,54854],{"class":2119}," rootMapping ",[2113,54856,2335],{"class":2326},[2113,54858,53669],{"class":2414},[2113,54860,179],{"class":2119},[2113,54862,21478],{"class":2133},[2113,54864,29622],{"class":2119},[2113,54866,3242],{"class":2326},[2113,54868,3686],{"class":2166},[2113,54870,53639],{"class":2119},[2113,54872,54873,54875,54878,54880,54882,54884,54886],{"class":2115,"line":1652},[2113,54874,3137],{"class":2326},[2113,54876,54877],{"class":2414}," rootMapping",[2113,54879,4115],{"class":2119},[2113,54881,53737],{"class":2414},[2113,54883,179],{"class":2119},[2113,54885,5579],{"class":2133},[2113,54887,3944],{"class":2119},[12,54889,54890],{},"Pour l'index racine, on récupère le nombre de ligne à la ligne -1 du\nmodèle source.",[2105,54892,54894],{"className":2387,"code":54893,"language":2389,"meta":1646,"style":1646},"} else {\n    Mapping * parrentMapping = static_cast\u003CMapping*>( index.internalPointer() );\n    int sourceRowIndex = parrentMapping->source_rows.at( index.row() );\n    Mapping * rowMapping = m_sourcesIndexMapping.value( sourceRowIndex );\n\n    return rowMapping->source_rows.count();\n",[184,54895,54896,54904,54933,54963,54983,54987],{"__ignoreMap":1646},[2113,54897,54898,54900,54902],{"class":2115,"line":2116},[2113,54899,4224],{"class":2119},[2113,54901,4227],{"class":2326},[2113,54903,2647],{"class":2119},[2113,54905,54906,54908,54910,54913,54915,54917,54919,54921,54923,54925,54927,54929,54931],{"class":2115,"line":1647},[2113,54907,53310],{"class":2119},[2113,54909,2676],{"class":2326},[2113,54911,54912],{"class":2119}," parrentMapping ",[2113,54914,2335],{"class":2326},[2113,54916,3140],{"class":2119},[2113,54918,3109],{"class":2326},[2113,54920,53171],{"class":2119},[2113,54922,53138],{"class":2326},[2113,54924,29622],{"class":2119},[2113,54926,53357],{"class":2414},[2113,54928,179],{"class":2119},[2113,54930,54235],{"class":2133},[2113,54932,53904],{"class":2119},[2113,54934,54935,54937,54940,54942,54945,54947,54949,54951,54953,54955,54957,54959,54961],{"class":2115,"line":1652},[2113,54936,53096],{"class":2326},[2113,54938,54939],{"class":2119}," sourceRowIndex ",[2113,54941,2335],{"class":2326},[2113,54943,54944],{"class":2414}," parrentMapping",[2113,54946,4115],{"class":2119},[2113,54948,53737],{"class":2414},[2113,54950,179],{"class":2119},[2113,54952,54316],{"class":2133},[2113,54954,29622],{"class":2119},[2113,54956,53357],{"class":2414},[2113,54958,179],{"class":2119},[2113,54960,53865],{"class":2133},[2113,54962,53904],{"class":2119},[2113,54964,54965,54967,54969,54972,54974,54976,54978,54980],{"class":2115,"line":2185},[2113,54966,53310],{"class":2119},[2113,54968,2676],{"class":2326},[2113,54970,54971],{"class":2119}," rowMapping ",[2113,54973,2335],{"class":2326},[2113,54975,53669],{"class":2414},[2113,54977,179],{"class":2119},[2113,54979,21478],{"class":2133},[2113,54981,54982],{"class":2119},"( sourceRowIndex );\n",[2113,54984,54985],{"class":2115,"line":2230},[2113,54986,2125],{"emptyLinePlaceholder":1767},[2113,54988,54989,54991,54994,54996,54998,55000,55002],{"class":2115,"line":2235},[2113,54990,3137],{"class":2326},[2113,54992,54993],{"class":2414}," rowMapping",[2113,54995,4115],{"class":2119},[2113,54997,53737],{"class":2414},[2113,54999,179],{"class":2119},[2113,55001,5579],{"class":2133},[2113,55003,3944],{"class":2119},[12,55005,55006,55007,55010],{},"Sinon on récupère la structure stockée dans le pointeur interne. Cette\nstructure est celle du parent de notre objet. On récupère à l'aide de la\nméthode ",[362,55008,55009],{},"row()"," de l'index l'emplacement de la ligne source. Cette ligne\nsource nous permet de récupérer la structure de correspondance de notre\nindex, et ainsi le nombre de ligne de notre index.",[2105,55012,55013],{"className":2387,"code":2599,"language":2389,"meta":1646,"style":1646},[184,55014,55015],{"__ignoreMap":1646},[2113,55016,55017],{"class":2115,"line":2116},[2113,55018,2599],{"class":2119},[27533,55020,54379],{"id":55021},"columncount",[12,55023,55024],{},"Notre modèle ne possède qu'une seule colonne.",[2105,55026,55028],{"className":2387,"code":55027,"language":2389,"meta":1646,"style":1646},"return 1;\n",[184,55029,55030],{"__ignoreMap":1646},[2113,55031,55032,55034,55036],{"class":2115,"line":2116},[2113,55033,3205],{"class":2326},[2113,55035,2850],{"class":2166},[2113,55037,2487],{"class":2119},[27533,55039,55041],{"id":55040},"proxycolumntosource","proxyColumnToSource()",[12,55043,55044],{},"Si la colonne du proxy vaut 0, alors on retourne la colonne 1, sinon on\nretourne la colonne -1 : la colonne n'est pas convertible, elle n'existe\npas dans notre proxy.",[2105,55046,55048],{"className":2387,"code":55047,"language":2389,"meta":1646,"style":1646},"if( proxyColumn == 0 )\n    return list_name;\nreturn -1;\n",[184,55049,55050,55062,55069],{"__ignoreMap":1646},[2113,55051,55052,55054,55056,55058,55060],{"class":2115,"line":2116},[2113,55053,4109],{"class":2326},[2113,55055,54068],{"class":2119},[2113,55057,4132],{"class":2326},[2113,55059,3208],{"class":2166},[2113,55061,45118],{"class":2119},[2113,55063,55064,55066],{"class":2115,"line":1647},[2113,55065,3137],{"class":2326},[2113,55067,55068],{"class":2119}," list_name;\n",[2113,55070,55071,55073,55075,55077],{"class":2115,"line":1652},[2113,55072,3205],{"class":2326},[2113,55074,2270],{"class":2326},[2113,55076,3686],{"class":2166},[2113,55078,2487],{"class":2119},[27533,55080,55082],{"id":55081},"sourcecolumtoproxy","sourceColumToProxy()",[12,55084,55085],{},"Si la colonne source est 1, alors nous convertissons celle-ci en la\ncolonne 0. Sinon la colonne n'existe pas dans notre proxy.",[2105,55087,55089],{"className":2387,"code":55088,"language":2389,"meta":1646,"style":1646},"if( sourceColumn == list_name )\n    return 0;\nreturn -1;\n",[184,55090,55091,55102,55110],{"__ignoreMap":1646},[2113,55092,55093,55095,55097,55099],{"class":2115,"line":2116},[2113,55094,4109],{"class":2326},[2113,55096,54274],{"class":2119},[2113,55098,4132],{"class":2326},[2113,55100,55101],{"class":2119}," list_name )\n",[2113,55103,55104,55106,55108],{"class":2115,"line":1647},[2113,55105,3137],{"class":2326},[2113,55107,3208],{"class":2166},[2113,55109,2487],{"class":2119},[2113,55111,55112,55114,55116,55118],{"class":2115,"line":1652},[2113,55113,3205],{"class":2326},[2113,55115,2270],{"class":2326},[2113,55117,3686],{"class":2166},[2113,55119,2487],{"class":2119},[133,55121,55123],{"id":55122},"les-sources","Les sources",[12,55125,55126],{},"Vous pouvez trouver les sources suivants décrivants ce que l'on trouve\nci-dessus :",[506,55128,55129,55135],{},[370,55130,55131],{},[49,55132,55134],{"href":55133},"\u002FProgrammation\u002Fqt-transformation-d-une-vue-tableau-en-une-vue-hierarchique\u002Fcategoryitemmodel.h","categoryitemmodel.h",[370,55136,55137],{},[49,55138,55140],{"href":55139},"\u002FProgrammation\u002Fqt-transformation-d-une-vue-tableau-en-une-vue-hierarchique\u002Fcategoryitemmodel.cpp","categoryitemmodel.cpp",[12,55142,55143],{},"l'objet QSortFilterProxyModel de Nokia.",[23219,55145,55147,55150],{"className":55146,"dataFootnotes":1646},[23222],[128,55148,23227],{"className":55149,"id":17406},[23226],[367,55151,55152],{},[370,55153,55154,55155],{"id":23232},"La construction de notre objet s'est basé sur l'analyse de ",[49,55156,23240],{"href":23236,"ariaLabel":23237,"className":55157,"dataFootnoteBackref":1646},[23239],[3358,55159,55160],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}",{"title":1646,"searchDepth":1647,"depth":1647,"links":55162},[55163,55164,55165,55166,55167],{"id":52737,"depth":1652,"text":52738},{"id":52791,"depth":1652,"text":52792},{"id":53069,"depth":1652,"text":53070},{"id":55122,"depth":1652,"text":55123},{"id":17406,"depth":1647,"text":23227},"2009-09-21",{"type":9,"value":55170},[55171,55173,55177,55179],[12,55172,52699],{},[12,55174,55175],{},[121,55176],{"alt":52704,"src":52705},[12,55178,52708],{},[12,55180,55181],{},[121,55182],{"alt":52713,"src":52714},{},"\u002Fpost\u002Fqt-transformation-d-une-vue-tableau-en-une-vue-hierarchique",{"title":52694,"description":52699},"qt-transformation-d-une-vue-tableau-en-une-vue-hierarchique","posts\u002FProgrammation\u002F2009-09-21-qt-transformation-d-une-vue-tableau-en-une-vue-hierarchique",[38761],"9hW60XZGhuixfVazFGe6eySQJPcsFBX02vlKbBhDNJs",{"id":55191,"title":55192,"author":7,"body":55193,"category":2057,"categorySlug":2058,"date":56355,"description":55197,"excerpt":56356,"extension":1764,"location":1765,"meta":56364,"navigation":1767,"path":56365,"published":1767,"seo":56366,"slug":56367,"stem":56368,"tags":56369,"timeToRead":2241,"__hash__":56370},"posts\u002Fposts\u002FProgrammation\u002F2009-05-31-parseur-xml.md","Parseur XML",{"type":9,"value":55194,"toc":56349},[55195,55198,55205,55208,55212,55215,55218,55221,55224,55228,55231,55234,55278,55287,55431,55434,55437,55440,55446,55450,55453,55467,55470,55474,55486,55489,55495,55498,55504,55507,55511,55514,55517,56166,56180,56183,56189,56192,56326,56346],[12,55196,55197],{},"Bonjours à tous,",[12,55199,55200,55201,55204],{},"L'utilisation des fichiers XML est, à ce jour, un fait dans la plupart\ndes logiciels et est fortement à la mode. Une entreprise qui ne fait pas\nun peu de XML est souvent ",[362,55202,55203],{},"has-been",". On utilise alors le XML à bon ou\nmauvais escient.",[3358,55206,55207],{},"\nth {background-color: #E4E4E4;weigh: bold;}\n .veryslow {background-color: #FF0000;}\n .slow {background-color: #FF8686;}\n .good {background-color: #C3FFC7;}\n .ignored {background-color: #E4E4E4;}\n",[133,55209,55211],{"id":55210},"avantages-inconvénients","Avantages \u002F Inconvénients",[12,55213,55214],{},"Pourquoi utiliser les fichiers XML ? Les fichiers XML sont, pour\ncommencer, des fichiers textes, il seront donc toujours lisibles, ce qui\ngarantit une meilleur pérennité de l'information. Les fichiers XML sont\nstructurés hiérarchiquement et suivent une syntaxe stricte. Ainsi le XML\nest lisible informatiquement par les différents langages de\nprogrammation existant, pour organiser vos données, en utilisant\ndifférents niveaux. Les fichiers XML peuvent être commenté ce qui peut\naméliorer la lisibilité pour un humain.",[12,55216,55217],{},"Le gros inconvénient du XML est sa verbosité. Pour chaque noeud dans la\nhiérarchie, il y a une balise de début, et une balise de fin contenant\nle nom de la balise. Le fichier est moins compact que s'il avait été\nécrit en binaire. Cela peut poser des problèmes comme alourdir les\ncommunications réseaux (ex: pour les webservices). Le fichier est aussi\nplus long à lire qu'un fichier binaire et peut contenir des erreurs.",[12,55219,55220],{},"Si le fichier n'a pas besoin d'être hiérarchique, il est possible\nd'utiliser une structure de fichier INI pour ses données.",[12,55222,55223],{},"Nous nous arrêterons là pour les avantages\u002Finconvénients. L'utilisation\nou non d'un fichier XML est ensuite une question (besoin, éthique, ...)\nà se poser pour ses données et la nécessité de pouvoir les lire\nautrement que par le programme lui-même.",[133,55225,55227],{"id":55226},"performance-des-parseurs","Performance des parseurs.",[12,55229,55230],{},"Je me suis amusé à effectuer le test de lecture de différents fichiers\nde donnée au format XML de taille différentes par une variété de parseur\nXML en construisant si possible un arbre DOM. Le but est donc de créer\nun objet par noeud\u002Fattribut du fichier XML et de reconstituer la\nhiérarchie.",[12,55232,55233],{},"Les parseurs choisis pour ce test sont :",[506,55235,55236,55242,55248,55254,55260,55266,55272],{},[370,55237,55238,55241],{},[155,55239,55240],{},"QtXml"," : Le module XML de la librairie Qt (de la société Nokia)",[370,55243,55244,55247],{},[155,55245,55246],{},"LibXml2"," : Une librairie C permettant de faire du parsing XML en\nutilisant DOM ou SAX",[370,55249,55250,55253],{},[155,55251,55252],{},"LibExpat"," : Un parseur orienté flux (plus proche de la méthode\nSAX que du DOM)",[370,55255,55256,55259],{},[155,55257,55258],{},"SCEW"," : Un parseur basé sur Expat générant un arbre DOM (donc un\npeu plus comparable aux autre parseur).",[370,55261,55262,55265],{},[155,55263,55264],{},"TinyXML"," : Encore un autre parseur XML",[370,55267,55268,55271],{},[155,55269,55270],{},"Xerces-C++"," : Le parseur XML d’Apache",[370,55273,55274,55277],{},[155,55275,55276],{},"Oracle XML Parser"," : Le parseur d’Oracle propriétaire utiliser\ndans le serveur d’application.",[12,55279,55280,55281,55286],{},"Pour faire cette suite de benchmark",[17400,55282,55283],{},[49,55284,3686],{"href":17404,"ariaDescribedBy":55285,"dataFootnoteRef":1646,"id":17407},[17406],", j’ai utilisé la librairie\nQTestLib de Nokia\u002FQt. Je vous offre donc en avant première le résultat\nde ce test :",[22,55288,55290,55294],{"style":55289},"width:100%",[55291,55292,55293],"caption",{},"Temps de lecture d'un fichier XML en ms",[41,55295,55296,55312,55330,55349,55368,55383,55399,55417],{},[28,55297,55298,55301,55304,55305,55308,55309,55304],{},[46,55299,55300],{},"               ",[31,55302,55303],{},"250k","                     ",[31,55306,55307],{},"9300k","                      ",[31,55310,55311],{},"11300k",[28,55313,55314,55317,55322,55326],{},[31,55315,55316],{},"Qt DOM         ",[46,55318,55321],{"className":55319},[55320],"veryslow","41,42 ms",[46,55323,55325],{"className":55324},[55320],"2184,34 ms",[46,55327,55329],{"className":55328},[55320],"2648,71 ms",[28,55331,55332,55335,55338,55339,55344,55345,55344],{},[31,55333,55334],{},"LibXML2        ",[46,55336,55337],{},"16,55 ms","                 ",[46,55340,55343],{"className":55341},[55342],"good","710,09 ms","     ",[46,55346,55348],{"className":55347},[55342],"874,42 ms",[28,55350,55351,55354,55359,55360,55359,55364,55359],{},[31,55352,55353],{},"Expat (SAX)    ",[46,55355,55358],{"className":55356},[55357],"ignored","5,95 ms","  ",[46,55361,55363],{"className":55362},[55357],"312,02 ms",[46,55365,55367],{"className":55366},[55357],"393,58 ms",[28,55369,55370,55373,55338,55376,55379,55380,55338],{},[31,55371,55372],{},"Oracle         ",[46,55374,55375],{},"23,24 ms",[46,55377,55378],{},"928,93 ms","                  ",[46,55381,55382],{},"1135,86 ms",[28,55384,55385,55388,55392,55393,55379,55396,55338],{},[31,55386,55387],{},"Scew           ",[46,55389,55391],{"className":55390},[55342],"12,05 ms","    ",[46,55394,55395],{},"944,37 ms",[46,55397,55398],{},"1184,46 ms",[28,55400,55401,55404,55392,55409,55344,55413,55344],{},[31,55402,55403],{},"Tiny XML       ",[46,55405,55408],{"className":55406},[55407],"slow","28,91 ms",[46,55410,55412],{"className":55411},[55407],"1101,8 ms",[46,55414,55416],{"className":55415},[55407],"1369,8 ms",[28,55418,55419,55422,55338,55425,55379,55428,55379],{},[31,55420,55421],{},"Xerces (Apache)",[46,55423,55424],{},"25,03 ms",[46,55426,55427],{},"742,22 ms",[46,55429,55430],{},"904,62 ms",[12,55432,55433],{},"La conclusion de ce résultat signifie que le parseur inclu dans Qt (pour\nla construction d'un arbre DOM) est très lent ;). Mais pour une\nutilisation dans une interface graphique, sur de petits fichiers, ne\ndevrait pas poser de problème.",[12,55435,55436],{},"Enfin le parseur le plus rapide (pour les gros fichiers) est Libxml2. Le\nparseur C d'oracle, bien que propriétaire n'a rien d'exceptionnel (si on\ncompte avec les problèmes présenté également ci-dessous). Peut-être que\nla version Java est elle plus performante.",[12,55438,55439],{},"Vous pouvez retrouver quelques graphiques ci-dessous :",[12,55441,55442],{},[121,55443],{"alt":55444,"src":55445},"TestXMLPerf","\u002FProgrammation\u002Fparseur-xml\u002FTestXMLPerf.png",[133,55447,55449],{"id":55448},"le-source-du-benchmark","Le source du benchmark",[12,55451,55452],{},"Voici de suite :",[506,55454,55455,55461],{},[370,55456,55457],{},[49,55458,55460],{"href":55459},"\u002FProgrammation\u002Fparseur-xml\u002Fxmlparser-bench.7z","les sources de l'application",[370,55462,55463,179],{},[49,55464,55466],{"href":55465},"\u002FProgrammation\u002Fparseur-xml\u002Fbenchmark_100iterations.ods","le résultat du bench",[12,55468,55469],{},"Pour la partie utilisation du Parseur Oracle, il a fallut contourner\nplusieurs petits problèmes. Ceci est la joie des librairies propriétaires\nbien documentées.",[27533,55471,55473],{"id":55472},"compilation-avec-le-xdk-9","Compilation avec le XDK 9",[12,55475,55476,55477,55480,55481,55483,55484,179],{},"Lors de la compilation avec le XDK 9, l'application ne dépasse pas le\nstade du ",[362,55478,55479],{},"linkage",". Les méthodes sont pourtant bien dans les ",[362,55482,23027],{},".\nDe plus les librairies sont bien précisées pour le ",[362,55485,55479],{},[12,55487,55488],{},"La définition faite dans le projet est :",[2105,55490,55493],{"className":55491,"code":55492,"language":26023},[26021],"XDKPATH = xdk9\nXDK_LIB = -lxmlg9 -lxml9 -lxsd9\nORA_LIB = -lcore9 -lnls9 -lunls9 -lcore9 -lnls9 -lcore9\nNET_LIB = -lnsl\nLIBS += -L$$XDKPATH\u002Flib $$XDK_LIB $$ORA_LIB $$NET_LIB -lpthread\nINCLUDEPATH += $$XDKPATH\u002Fxdk\u002Finclude\n",[184,55494,55492],{"__ignoreMap":1646},[12,55496,55497],{},"A la suite de ça, lors de la phase de compilation, on se retrouve avec\nles messages d'erreurs suivants :",[2105,55499,55502],{"className":55500,"code":55501,"language":26023},[26021],"g++ -Wl,-O1 -o xmlparserbenchmark libxml2parser.o expatparser.o xercescppparser.o oracleparser.o qtparser.o scewparser.o tinyparser.o tinyxml.o tinystr.o tinyxmlerror.o tinyxmlparser.o xmlparserbenchmark.o moc_xmlparserbenchmark.o -L\u002Fusr\u002Flib -lxml2 -lexpat -lxerces-c -Lxdk9\u002Flib -lxmlg9 -lxml9 -lxsd9 -lcore9 -lnls9 -lunls9 -lcore9 -lnls9 -lcore9 -lnsl -lpthread -Lscew\u002Fscew -lscew -lQtTest -lQtXml -lQtGui -lQtCore -lpthread\noracleparser.o: In function `parseWithOracleParser(QString const&)':\noracleparser.cpp:(.text+0x82): undefined reference to `XMLParser::xmlinit(unsigned char*, void (*)(void*, unsigned char const*, unsigned int), void*, xmlsaxcb*, void*, unsigned char*)'\noracleparser.cpp:(.text+0xa8): undefined reference to `XMLParser::xmlparse(unsigned char*, unsigned char*, unsigned int)'\noracleparser.cpp:(.text+0x2b4): undefined reference to `XMLParser::getDocumentElement()'\noracleparser.cpp:(.text+0x2ca): undefined reference to `XMLParser::xmlterm()'\n",[184,55503,55501],{"__ignoreMap":1646},[12,55505,55506],{},"J'ai alors essayé de recompiler les exemples de démonstrations mais là,\nmême problème ...",[27533,55508,55510],{"id":55509},"compilation-avec-le-xdk-10","Compilation avec le XDK 10",[12,55512,55513],{},"Avec cette version du XDK, nous avons le droit à deux problèmes. Le\npremier est une grosse fuite mémoire (si on écrit le programme tel que\ndécrit dans la démo), et à un problème d'initialisation du parseur.",[12,55515,55516],{},"Le code permettant de parser le fichier XML et de générer l'arbre DOM\nressemble à ceci :",[2105,55518,55520],{"className":2387,"code":55519,"language":2389,"meta":1646,"style":1646},"CXmlCtx * ctxp = 0;\ntry {\n    ctxp = new CXmlCtx();\n} catch( XmlException & e ) {\n    unsigned ecode = e.getCode();\n    QFAIL( qPrintable( QString(\"Failed to initialize XML context, error %1\").arg( ecode ) ) );\n}\n\nFactory\u003CCXmlCtx,xmlnode> * fp = 0;\ntry {\n    fp = new Factory\u003CCXmlCtx,xmlnode>( ctxp );\n} catch( FactoryException & fe ) {\n    unsigned ecode = fe.getCode();\n    QFAIL( qPrintable( QString(\"Failed to create create parser, error %1\").arg( ecode ) ) );\n}\n\nDOMParser\u003CCXmlCtx,xmlnode> * parserp = 0;\ntry {\n    parserp = fp->createDOMParser( DOMParCXml, NULL );\n} catch( FactoryException & fe ) {\n    delete fp;\n    unsigned ecode = fe.getCode();\n    QFAIL( qPrintable( QString(\"Failed to create parser, error %1\").arg( ecode ) ) );\n}\n\nconst char * fname = filename;\nFileSource * isrcp = new FileSource( (oratext*) fname );\ntry {\n    DocumentRef\u003Cxmlnode> * docrefp = parserp->parse( isrcp, FALSE );\n    if( docrefp == NULL ) {\n        QFAIL( qPrintable( QString(\"NULL document\") ) );\n        return;\n    }\n    xmlnode * np = docrefp->getDocumentElement();\n    if( np == NULL ) {\n        QFAIL( qPrintable( QString(\"Empty document\") ) );\n        return;\n    }\n    docrefp->markToDelete();\n    delete docrefp;\n} catch( ParserException & pe ) {\n    delete parserp;\n    delete isrcp;\n    delete fp;\n\n    unsigned ecode = pe.getCode();\n    QFAIL( qPrintable( QString( \"Failed to parse the document, error %1\").arg( ecode ) ) );\n}\n\u002F\u002F    delete parserp;\n\u002F\u002F    delete isrcp;\n\u002F\u002F    delete fp;\n\u002F\u002F    delete ctxp;\n",[184,55521,55522,55538,55544,55558,55572,55591,55618,55622,55626,55649,55655,55680,55694,55711,55734,55738,55742,55764,55770,55793,55805,55813,55829,55852,55856,55860,55877,55902,55908,55936,55950,55971,55977,55981,56003,56016,56035,56041,56045,56057,56064,56078,56085,56092,56098,56102,56119,56142,56146,56151,56156,56161],{"__ignoreMap":1646},[2113,55523,55524,55527,55529,55532,55534,55536],{"class":2115,"line":2116},[2113,55525,55526],{"class":2119},"CXmlCtx ",[2113,55528,2676],{"class":2326},[2113,55530,55531],{"class":2119}," ctxp ",[2113,55533,2335],{"class":2326},[2113,55535,3208],{"class":2166},[2113,55537,2487],{"class":2119},[2113,55539,55540,55542],{"class":2115,"line":1647},[2113,55541,8945],{"class":2326},[2113,55543,2647],{"class":2119},[2113,55545,55546,55549,55551,55553,55556],{"class":2115,"line":1652},[2113,55547,55548],{"class":2119},"    ctxp ",[2113,55550,2335],{"class":2326},[2113,55552,4778],{"class":2326},[2113,55554,55555],{"class":2133}," CXmlCtx",[2113,55557,3944],{"class":2119},[2113,55559,55560,55562,55564,55567,55569],{"class":2115,"line":2185},[2113,55561,4224],{"class":2119},[2113,55563,8973],{"class":2326},[2113,55565,55566],{"class":2119},"( XmlException ",[2113,55568,37503],{"class":2334},[2113,55570,55571],{"class":2119}," e ) {\n",[2113,55573,55574,55577,55580,55582,55584,55586,55589],{"class":2115,"line":2230},[2113,55575,55576],{"class":2326},"    unsigned",[2113,55578,55579],{"class":2119}," ecode ",[2113,55581,2335],{"class":2326},[2113,55583,31385],{"class":2414},[2113,55585,179],{"class":2119},[2113,55587,55588],{"class":2133},"getCode",[2113,55590,3944],{"class":2119},[2113,55592,55593,55596,55598,55601,55603,55605,55607,55610,55612,55615],{"class":2115,"line":2235},[2113,55594,55595],{"class":2133},"    QFAIL",[2113,55597,29622],{"class":2119},[2113,55599,55600],{"class":2133},"qPrintable",[2113,55602,29622],{"class":2119},[2113,55604,46085],{"class":2133},[2113,55606,2423],{"class":2119},[2113,55608,55609],{"class":2149},"\"Failed to initialize XML context, error %1\"",[2113,55611,3938],{"class":2119},[2113,55613,55614],{"class":2133},"arg",[2113,55616,55617],{"class":2119},"( ecode ) ) );\n",[2113,55619,55620],{"class":2115,"line":2241},[2113,55621,2599],{"class":2119},[2113,55623,55624],{"class":2115,"line":2246},[2113,55625,2125],{"emptyLinePlaceholder":1767},[2113,55627,55628,55631,55633,55636,55638,55640,55643,55645,55647],{"class":2115,"line":2464},[2113,55629,55630],{"class":2119},"Factory",[2113,55632,3109],{"class":2326},[2113,55634,55635],{"class":2119},"CXmlCtx,xmlnode",[2113,55637,29415],{"class":2326},[2113,55639,2671],{"class":2326},[2113,55641,55642],{"class":2119}," fp ",[2113,55644,2335],{"class":2326},[2113,55646,3208],{"class":2166},[2113,55648,2487],{"class":2119},[2113,55650,55651,55653],{"class":2115,"line":2085},[2113,55652,8945],{"class":2326},[2113,55654,2647],{"class":2119},[2113,55656,55657,55660,55662,55664,55667,55669,55672,55674,55677],{"class":2115,"line":2514},[2113,55658,55659],{"class":2119},"    fp ",[2113,55661,2335],{"class":2326},[2113,55663,4778],{"class":2326},[2113,55665,55666],{"class":2133}," Factory",[2113,55668,3109],{"class":2119},[2113,55670,55671],{"class":2414},"CXmlCtx",[2113,55673,26987],{"class":2119},[2113,55675,55676],{"class":2414},"xmlnode",[2113,55678,55679],{"class":2119},">( ctxp );\n",[2113,55681,55682,55684,55686,55689,55691],{"class":2115,"line":2533},[2113,55683,4224],{"class":2119},[2113,55685,8973],{"class":2326},[2113,55687,55688],{"class":2119},"( FactoryException ",[2113,55690,37503],{"class":2334},[2113,55692,55693],{"class":2119}," fe ) {\n",[2113,55695,55696,55698,55700,55702,55705,55707,55709],{"class":2115,"line":2556},[2113,55697,55576],{"class":2326},[2113,55699,55579],{"class":2119},[2113,55701,2335],{"class":2326},[2113,55703,55704],{"class":2414}," fe",[2113,55706,179],{"class":2119},[2113,55708,55588],{"class":2133},[2113,55710,3944],{"class":2119},[2113,55712,55713,55715,55717,55719,55721,55723,55725,55728,55730,55732],{"class":2115,"line":2569},[2113,55714,55595],{"class":2133},[2113,55716,29622],{"class":2119},[2113,55718,55600],{"class":2133},[2113,55720,29622],{"class":2119},[2113,55722,46085],{"class":2133},[2113,55724,2423],{"class":2119},[2113,55726,55727],{"class":2149},"\"Failed to create create parser, error %1\"",[2113,55729,3938],{"class":2119},[2113,55731,55614],{"class":2133},[2113,55733,55617],{"class":2119},[2113,55735,55736],{"class":2115,"line":2575},[2113,55737,2599],{"class":2119},[2113,55739,55740],{"class":2115,"line":2596},[2113,55741,2125],{"emptyLinePlaceholder":1767},[2113,55743,55744,55747,55749,55751,55753,55755,55758,55760,55762],{"class":2115,"line":3192},[2113,55745,55746],{"class":2119},"DOMParser",[2113,55748,3109],{"class":2326},[2113,55750,55635],{"class":2119},[2113,55752,29415],{"class":2326},[2113,55754,2671],{"class":2326},[2113,55756,55757],{"class":2119}," parserp ",[2113,55759,2335],{"class":2326},[2113,55761,3208],{"class":2166},[2113,55763,2487],{"class":2119},[2113,55765,55766,55768],{"class":2115,"line":3213},[2113,55767,8945],{"class":2326},[2113,55769,2647],{"class":2119},[2113,55771,55772,55775,55777,55780,55782,55785,55788,55791],{"class":2115,"line":3236},[2113,55773,55774],{"class":2119},"    parserp ",[2113,55776,2335],{"class":2326},[2113,55778,55779],{"class":2414}," fp",[2113,55781,4115],{"class":2119},[2113,55783,55784],{"class":2133},"createDOMParser",[2113,55786,55787],{"class":2119},"( DOMParCXml, ",[2113,55789,55790],{"class":2166},"NULL",[2113,55792,53639],{"class":2119},[2113,55794,55795,55797,55799,55801,55803],{"class":2115,"line":3248},[2113,55796,4224],{"class":2119},[2113,55798,8973],{"class":2326},[2113,55800,55688],{"class":2119},[2113,55802,37503],{"class":2334},[2113,55804,55693],{"class":2119},[2113,55806,55807,55810],{"class":2115,"line":4899},[2113,55808,55809],{"class":2326},"    delete",[2113,55811,55812],{"class":2119}," fp;\n",[2113,55814,55815,55817,55819,55821,55823,55825,55827],{"class":2115,"line":1777},[2113,55816,55576],{"class":2326},[2113,55818,55579],{"class":2119},[2113,55820,2335],{"class":2326},[2113,55822,55704],{"class":2414},[2113,55824,179],{"class":2119},[2113,55826,55588],{"class":2133},[2113,55828,3944],{"class":2119},[2113,55830,55831,55833,55835,55837,55839,55841,55843,55846,55848,55850],{"class":2115,"line":4931},[2113,55832,55595],{"class":2133},[2113,55834,29622],{"class":2119},[2113,55836,55600],{"class":2133},[2113,55838,29622],{"class":2119},[2113,55840,46085],{"class":2133},[2113,55842,2423],{"class":2119},[2113,55844,55845],{"class":2149},"\"Failed to create parser, error %1\"",[2113,55847,3938],{"class":2119},[2113,55849,55614],{"class":2133},[2113,55851,55617],{"class":2119},[2113,55853,55854],{"class":2115,"line":4961},[2113,55855,2599],{"class":2119},[2113,55857,55858],{"class":2115,"line":4976},[2113,55859,2125],{"emptyLinePlaceholder":1767},[2113,55861,55862,55864,55867,55869,55872,55874],{"class":2115,"line":4993},[2113,55863,7785],{"class":2326},[2113,55865,55866],{"class":2326}," char",[2113,55868,2671],{"class":2326},[2113,55870,55871],{"class":2119}," fname ",[2113,55873,2335],{"class":2326},[2113,55875,55876],{"class":2119}," filename;\n",[2113,55878,55879,55882,55884,55887,55889,55891,55894,55897,55899],{"class":2115,"line":5013},[2113,55880,55881],{"class":2119},"FileSource ",[2113,55883,2676],{"class":2326},[2113,55885,55886],{"class":2119}," isrcp ",[2113,55888,2335],{"class":2326},[2113,55890,4778],{"class":2326},[2113,55892,55893],{"class":2133}," FileSource",[2113,55895,55896],{"class":2119},"( (oratext",[2113,55898,2676],{"class":2326},[2113,55900,55901],{"class":2119},") fname );\n",[2113,55903,55904,55906],{"class":2115,"line":5018},[2113,55905,8945],{"class":2326},[2113,55907,2647],{"class":2119},[2113,55909,55910,55913,55915,55917,55919,55921,55924,55926,55929,55931,55933],{"class":2115,"line":5042},[2113,55911,55912],{"class":2119},"    DocumentRef",[2113,55914,3109],{"class":2326},[2113,55916,55676],{"class":2119},[2113,55918,29415],{"class":2326},[2113,55920,2671],{"class":2326},[2113,55922,55923],{"class":2119}," docrefp ",[2113,55925,2335],{"class":2326},[2113,55927,55928],{"class":2414}," parserp",[2113,55930,4115],{"class":2119},[2113,55932,7039],{"class":2133},[2113,55934,55935],{"class":2119},"( isrcp, FALSE );\n",[2113,55937,55938,55940,55943,55945,55948],{"class":2115,"line":5057},[2113,55939,7394],{"class":2326},[2113,55941,55942],{"class":2119},"( docrefp ",[2113,55944,4132],{"class":2326},[2113,55946,55947],{"class":2166}," NULL",[2113,55949,4165],{"class":2119},[2113,55951,55952,55955,55957,55959,55961,55963,55965,55968],{"class":2115,"line":5062},[2113,55953,55954],{"class":2133},"        QFAIL",[2113,55956,29622],{"class":2119},[2113,55958,55600],{"class":2133},[2113,55960,29622],{"class":2119},[2113,55962,46085],{"class":2133},[2113,55964,2423],{"class":2119},[2113,55966,55967],{"class":2149},"\"NULL document\"",[2113,55969,55970],{"class":2119},") ) );\n",[2113,55972,55973,55975],{"class":2115,"line":5098},[2113,55974,28968],{"class":2326},[2113,55976,2487],{"class":2119},[2113,55978,55979],{"class":2115,"line":5117},[2113,55980,4665],{"class":2119},[2113,55982,55983,55986,55988,55991,55993,55996,55998,56001],{"class":2115,"line":5142},[2113,55984,55985],{"class":2119},"    xmlnode ",[2113,55987,2676],{"class":2326},[2113,55989,55990],{"class":2119}," np ",[2113,55992,2335],{"class":2326},[2113,55994,55995],{"class":2414}," docrefp",[2113,55997,4115],{"class":2119},[2113,55999,56000],{"class":2133},"getDocumentElement",[2113,56002,3944],{"class":2119},[2113,56004,56005,56007,56010,56012,56014],{"class":2115,"line":5148},[2113,56006,7394],{"class":2326},[2113,56008,56009],{"class":2119},"( np ",[2113,56011,4132],{"class":2326},[2113,56013,55947],{"class":2166},[2113,56015,4165],{"class":2119},[2113,56017,56018,56020,56022,56024,56026,56028,56030,56033],{"class":2115,"line":5163},[2113,56019,55954],{"class":2133},[2113,56021,29622],{"class":2119},[2113,56023,55600],{"class":2133},[2113,56025,29622],{"class":2119},[2113,56027,46085],{"class":2133},[2113,56029,2423],{"class":2119},[2113,56031,56032],{"class":2149},"\"Empty document\"",[2113,56034,55970],{"class":2119},[2113,56036,56037,56039],{"class":2115,"line":5169},[2113,56038,28968],{"class":2326},[2113,56040,2487],{"class":2119},[2113,56042,56043],{"class":2115,"line":5175},[2113,56044,4665],{"class":2119},[2113,56046,56047,56050,56052,56055],{"class":2115,"line":5180},[2113,56048,56049],{"class":2414},"    docrefp",[2113,56051,4115],{"class":2119},[2113,56053,56054],{"class":2133},"markToDelete",[2113,56056,3944],{"class":2119},[2113,56058,56059,56061],{"class":2115,"line":5199},[2113,56060,55809],{"class":2326},[2113,56062,56063],{"class":2119}," docrefp;\n",[2113,56065,56066,56068,56070,56073,56075],{"class":2115,"line":5204},[2113,56067,4224],{"class":2119},[2113,56069,8973],{"class":2326},[2113,56071,56072],{"class":2119},"( ParserException ",[2113,56074,37503],{"class":2334},[2113,56076,56077],{"class":2119}," pe ) {\n",[2113,56079,56080,56082],{"class":2115,"line":5209},[2113,56081,55809],{"class":2326},[2113,56083,56084],{"class":2119}," parserp;\n",[2113,56086,56087,56089],{"class":2115,"line":5232},[2113,56088,55809],{"class":2326},[2113,56090,56091],{"class":2119}," isrcp;\n",[2113,56093,56094,56096],{"class":2115,"line":5237},[2113,56095,55809],{"class":2326},[2113,56097,55812],{"class":2119},[2113,56099,56100],{"class":2115,"line":5242},[2113,56101,2125],{"emptyLinePlaceholder":1767},[2113,56103,56104,56106,56108,56110,56113,56115,56117],{"class":2115,"line":5267},[2113,56105,55576],{"class":2326},[2113,56107,55579],{"class":2119},[2113,56109,2335],{"class":2326},[2113,56111,56112],{"class":2414}," pe",[2113,56114,179],{"class":2119},[2113,56116,55588],{"class":2133},[2113,56118,3944],{"class":2119},[2113,56120,56121,56123,56125,56127,56129,56131,56133,56136,56138,56140],{"class":2115,"line":5282},[2113,56122,55595],{"class":2133},[2113,56124,29622],{"class":2119},[2113,56126,55600],{"class":2133},[2113,56128,29622],{"class":2119},[2113,56130,46085],{"class":2133},[2113,56132,29622],{"class":2119},[2113,56134,56135],{"class":2149},"\"Failed to parse the document, error %1\"",[2113,56137,3938],{"class":2119},[2113,56139,55614],{"class":2133},[2113,56141,55617],{"class":2119},[2113,56143,56144],{"class":2115,"line":5295},[2113,56145,2599],{"class":2119},[2113,56147,56148],{"class":2115,"line":5310},[2113,56149,56150],{"class":2396},"\u002F\u002F    delete parserp;\n",[2113,56152,56153],{"class":2115,"line":5315},[2113,56154,56155],{"class":2396},"\u002F\u002F    delete isrcp;\n",[2113,56157,56158],{"class":2115,"line":5320},[2113,56159,56160],{"class":2396},"\u002F\u002F    delete fp;\n",[2113,56162,56163],{"class":2115,"line":8625},[2113,56164,56165],{"class":2396},"\u002F\u002F    delete ctxp;\n",[12,56167,56168,56169,56171,56172,56174,56175,179],{},"Le premier problème se situe lors de la suppression du contexte\n(dernière ligne, en commentaire). Si cette ligne est exécutée, alors\nnous avons une grosse erreur de segmentation. J'ai le problème, quels\nque soient les ",[184,56170,34095],{}," que je fais avant.Même en faisant le maximum de\n",[184,56173,34095],{}," (soit parserp, isrcp, fp), le fait de ne pas supprimer le\ncontext (ctxp), fait qu'au bout de plusieurs itérations, nous avons une\nbonne fuite mémoire",[17400,56176,56177],{},[49,56178,2547],{"href":17977,"ariaDescribedBy":56179,"dataFootnoteRef":1646,"id":17979},[17406],[12,56181,56182],{},"Vient ensuite le second problème, celui des erreurs d'intialisation. Si\nle parseur est lancé plusieurs fois de suite, alors l'application\naffiche les erreurs suivantes à l'écran :",[2105,56184,56187],{"className":56185,"code":56186,"language":26023},[26021],"LPX-00202: Message 202 not found; No message file for product=XDK, facility=LPX\nFAIL!  : XmlParserBenchmark::oracleParser(file250k.xml) Failed to parse the document, error 202\n    Loc: [oracleparser.cpp(73)]\n",[184,56188,56186],{"__ignoreMap":1646},[12,56190,56191],{},"Aucune explication sur le pourquoi. Parfois ça marche, parfois non...\nPour contourner le problème, j'ai forké le parseur pour l'exécuter isolé\ndu reste.",[2105,56193,56195],{"className":4095,"code":56194,"language":4097,"meta":1646,"style":1646},"pid_t pid = fork();\nif( pid > 0 ) {\n    waitpid( pid, (int*)0, 0 );\n} else if( pid == 0 ) {\n    if( ! filename.isEmpty() )\n        parse( qPrintable( filename ) );\n    exit( 0 );\n} else {\n    QFAIL( \"Cannot fork\" );\n}\n",[184,56196,56197,56212,56225,56246,56262,56280,56292,56303,56311,56322],{"__ignoreMap":1646},[2113,56198,56199,56202,56205,56207,56210],{"class":2115,"line":2116},[2113,56200,56201],{"class":2326},"pid_t",[2113,56203,56204],{"class":2119}," pid ",[2113,56206,2335],{"class":2326},[2113,56208,56209],{"class":2133}," fork",[2113,56211,3944],{"class":2119},[2113,56213,56214,56216,56219,56221,56223],{"class":2115,"line":1647},[2113,56215,4109],{"class":2326},[2113,56217,56218],{"class":2119},"( pid ",[2113,56220,29415],{"class":2326},[2113,56222,3208],{"class":2166},[2113,56224,4165],{"class":2119},[2113,56226,56227,56230,56233,56236,56238,56240,56242,56244],{"class":2115,"line":1652},[2113,56228,56229],{"class":2133},"    waitpid",[2113,56231,56232],{"class":2119},"( pid, (",[2113,56234,56235],{"class":2326},"int*",[2113,56237,17832],{"class":2119},[2113,56239,3095],{"class":2166},[2113,56241,932],{"class":2119},[2113,56243,3095],{"class":2166},[2113,56245,53639],{"class":2119},[2113,56247,56248,56250,56252,56254,56256,56258,56260],{"class":2115,"line":2185},[2113,56249,4224],{"class":2119},[2113,56251,4227],{"class":2326},[2113,56253,4230],{"class":2326},[2113,56255,56218],{"class":2119},[2113,56257,4132],{"class":2326},[2113,56259,3208],{"class":2166},[2113,56261,4165],{"class":2119},[2113,56263,56264,56266,56268,56270,56272,56274,56277],{"class":2115,"line":2230},[2113,56265,7394],{"class":2326},[2113,56267,29622],{"class":2119},[2113,56269,3199],{"class":2334},[2113,56271,16790],{"class":2414},[2113,56273,179],{"class":2119},[2113,56275,56276],{"class":2133},"isEmpty",[2113,56278,56279],{"class":2119},"() )\n",[2113,56281,56282,56285,56287,56289],{"class":2115,"line":2235},[2113,56283,56284],{"class":2133},"        parse",[2113,56286,29622],{"class":2119},[2113,56288,55600],{"class":2133},[2113,56290,56291],{"class":2119},"( filename ) );\n",[2113,56293,56294,56297,56299,56301],{"class":2115,"line":2241},[2113,56295,56296],{"class":2133},"    exit",[2113,56298,29622],{"class":2119},[2113,56300,3095],{"class":2166},[2113,56302,53639],{"class":2119},[2113,56304,56305,56307,56309],{"class":2115,"line":2246},[2113,56306,4224],{"class":2119},[2113,56308,4227],{"class":2326},[2113,56310,2647],{"class":2119},[2113,56312,56313,56315,56317,56320],{"class":2115,"line":2464},[2113,56314,55595],{"class":2133},[2113,56316,29622],{"class":2119},[2113,56318,56319],{"class":2149},"\"Cannot fork\"",[2113,56321,53639],{"class":2119},[2113,56323,56324],{"class":2115,"line":2085},[2113,56325,2599],{"class":2119},[23219,56327,56329,56332],{"className":56328,"dataFootnotes":1646},[23222],[128,56330,23227],{"className":56331,"id":17406},[23226],[367,56333,56334,56340],{},[370,56335,56336,56337],{"id":23232},"Ces tests ont été réalisé dans un chroot 32-bits allant à 2,4GHz. Les temps sont une moyenne sur 100 itérations ",[49,56338,23240],{"href":23236,"ariaLabel":23237,"className":56339,"dataFootnoteBackref":1646},[23239],[370,56341,56342,56343],{"id":23243},"Si quelqu'un a déjà utilisé ce parseur, et qu'il sait comment il fonctionne, il peut m'écrire ",[49,56344,23240],{"href":23251,"ariaLabel":23252,"className":56345,"dataFootnoteBackref":1646},[23239],[3358,56347,56348],{},"html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":1646,"searchDepth":1647,"depth":1647,"links":56350},[56351,56352,56353,56354],{"id":55210,"depth":1652,"text":55211},{"id":55226,"depth":1652,"text":55227},{"id":55448,"depth":1652,"text":55449},{"id":17406,"depth":1647,"text":23227},"2009-05-31",{"type":9,"value":56357},[56358,56360],[12,56359,55197],{},[12,56361,55200,56362,55204],{},[362,56363,55203],{},{},"\u002Fpost\u002Fparseur-xml",{"title":55192,"description":55197},"parseur-xml","posts\u002FProgrammation\u002F2009-05-31-parseur-xml",[49134,38761,46020],"G7dHisiUAKK_MNKBFUElPa66LJWw54bB5Z9U4-7H4Ck",{"id":56372,"title":56373,"author":7,"body":56374,"category":44415,"categorySlug":45988,"date":56545,"description":14,"excerpt":56546,"extension":1764,"location":1765,"meta":56560,"navigation":1767,"path":56561,"published":1767,"seo":56562,"slug":56563,"stem":56564,"tags":56565,"timeToRead":2185,"__hash__":56566},"posts\u002Fposts\u002FXINX\u002F2009-05-26-xinx-0.8.1.0.md","XINX 0.8.1.0",{"type":9,"value":56375,"toc":56537},[56376,56378,56386,56394,56398,56404,56407,56410,56416,56422,56426,56432,56435,56439,56446,56452,56465,56468,56471,56477,56481,56488,56492,56495,56498,56500,56515,56523],[12,56377,14],{},[12,56379,56380],{},[48221,56381,56382,56385],{},[155,56383,56384],{},"Erratum :"," La dernière version est numéroté 0.8.1.1. Un petit\nbug (pouvant être gênant lors de l'ouverture de feuille de style sans\nutilisation du mode projet) a été corrigé dans la foulé. La page est\ncorrigé en conséquence",[12,56387,56388,56389,56393],{},"Je souhaite vous annoncer la sortie de la version ",[56390,56391,56392],"strike",{},"0.8.1.0","\n0.8.1.1 de XINX. Pas d'énorme changement pour vous depuis la version 0.8.0.0\n(sinon nous serions passés à la version 0.9.0.0) mais de gros changements\ninternes.",[133,56395,56397],{"id":56396},"vue-contenue","Vue Contenue",[12,56399,56400],{},[121,56401],{"alt":56402,"src":56403},"ContentViewDock.png","\u002FXINX\u002Fxinx-0.8.1.0\u002FContentViewDock.png",[12,56405,56406],{},"En fait, la partie gérant la vue contenue a été complètement ré-écrite.\nElle est maintenant parallélisée et partagée entre les différents\néditeurs. Au final l'empreinte mémoire, ainsi que la fluidité a chargé\nles différents fichiers de XINX devrait être amélioré. (Enfin j'espère,\ncar je n'ai pas fait de benchmark). De ce coté le code est lui aussi\nbeaucoup plus propre et plus souple.",[12,56408,56409],{},"XINX étant toujours dans les versions 0.XX, je n'ose vous rappeler que\nce n'est pas ce qu'on appelle une version finie, mais une série de bêta\n;) . Étant donné le peu de test que j'ai pu faire sur cette version en\nparticulier j'aurais dû la nommer en -rc ou en -beta mais je me suis dit\nque cela serait redondant, et que si ça ne convenait pas vous ajouteriez\nun ticket sur ce site pour me prévenir. Enfin j'espère que cette version\nne sera pas trop miner. 😄",[12,56411,56412,56413,56415],{},"La ré-écriture de la partie ",[362,56414,56397],{}," permet donc de préciser dans\nles propriétés du projet quelques fichiers qui seront pré-chargés au\nchargement du projet. Ces fichiers étant pré-chargés, l'ouverture des\nfeuilles de styles devrait être accéléré. Si de votre coté, XINX\nprésente quelques faiblesses, merci de me prévenir. La complétion a\négalement été améliorée (avec la suppression des bug, comme l'affichage\nde balise with-param en double). Lors de vos déplacements dans\nl'éditeur, la vue contenue vous indique votre emplacement dans la vue\ncontenue.",[12,56417,56418],{},[121,56419],{"alt":56420,"src":56421},"PreloadedFiles.png","\u002FXINX\u002Fxinx-0.8.1.0\u002FPreloadedFiles.png",[133,56423,56425],{"id":56424},"dictionnaire","Dictionnaire",[12,56427,56428],{},[121,56429],{"alt":56430,"src":56431},"Dictionary.png","\u002FXINX\u002Fxinx-0.8.1.0\u002FDictionary.png",[12,56433,56434],{},"L'autre nouveau point et l'ajout d'un nouveau Dock par l'intermédiaire\ndes plugins. Ce Dock vous propose l'affichage du dictionnaire dans les\nversions GCE130 et GCE140. Pour l'instant le seul intérêt de ce dock est\nl'affichage du dictionnaire à la recherche d'un label. C'est un point de\ndépart pouvant vous servir à faire des tickets sur des améliorations à\napporter. (Celle que j'ai en tête est la gestion de la complétion sur\nles libellés du dictionnaire). Merci de passer par le système de ticket\npour proposer d'autres améliorations sur le DOCK Dictionnaire.",[133,56436,56438],{"id":56437},"pré-visualisation","Pré-visualisation",[12,56440,56441,56442,56445],{},"Enfin, dernière évolution intéressante, est l'affichage de la\npré-visualisation de la page. Cette fonctionnalité est à noter comme\n",[362,56443,56444],{},"extrêmement expérimental",",et n'évoluera peut-être pas (selon les\ndemandes qui sont faites dessus, ainsi que suivant les problèmes\ntechniques rencontrés), et ne fonctionnera peut-être pas avec les\nprochaines versions de GCE. (En version 140, les libellés ne sont pas\nremplacés).",[12,56447,56448],{},[121,56449],{"alt":56450,"src":56451},"CustomWebPlugin.png","\u002FXINX\u002Fxinx-0.8.1.0\u002FCustomWebPlugin.png",[12,56453,56454,56455,56458,56459,56464],{},"Pour faire fonctionner la prévisualisation, vous devez renseigner\nl'emplacement de la ",[362,56456,56457],{},"ServletControl",", configurer, dans les propriétés du\nplugin WEB, le parseur à utiliser",[17400,56460,56461],{},[49,56462,3686],{"href":17404,"ariaDescribedBy":56463,"dataFootnoteRef":1646,"id":17407},[17406]," et de configurer le lecteur\n(je vous conseille Webkit).",[12,56466,56467],{},"De là, lors des développements de feuille de style en mode projet, si\nvous sélectionnez un flux de présentation XML (dans le dock associé) et\nappuyé sur F9, vous devriez avoir la prévisualisation de votre feuille\nde style.",[12,56469,56470],{},"Le même principe peut-être appliqué pour une page HTML. Là, pas besoin\nde parseur, ni de flux de présentation. Vous pouvez avoir directement la\nprévisualisation de votre page HTML.",[12,56472,56473],{},[121,56474],{"alt":56475,"src":56476},"ParseXsl.png","\u002FXINX\u002Fxinx-0.8.1.0\u002FParseXsl.png",[133,56478,56480],{"id":56479},"gestion-des-plugins","Gestion des plugins",[12,56482,56483,56484,56487],{},"Enfin, la gestion des plugins n'a cessé de s'améliorer. Il est de plus\nen plus facile d'étendre ",[362,56485,56486],{},"'XINX","'. Ceci par l'intermédiare de Script ou\nde Plugins. Si une fonctionnalité vous manque, vous pouvez donc non\nseulement la proposer, mais aussi l'écrire en script, ou si le script\nest trop limité, vous pouvez aussi écrire le plugin. Les plugins\npermettent donc d'ajouter des actions à XINX, des Docks, des éditeurs,\ndes gestionnaires de version, des pages de configuration du projet, ...",[133,56489,56491],{"id":56490},"d-bus","D-BUS",[12,56493,56494],{},"Alors que D-BUS avait été ajouté dans les précédentes versions afin de\npermettre une plus grande interopérabilité, ce dernier a été retiré pour\ncette version. Ainsi XINX est maintenant indépendant de D-BUS. Il a été\ndécidé de retirer D-BUS pour plusieurs raisons : Simplification de la\ncompilation, source de problème sous MS\u002FWindows. Il devient maintenant\nbeaucoup plus simple de compiler XINX sous Windows. L'interface avec\nD-BUS reviendra dans une future version mais sera alors optionnel.",[12,56496,56497],{},"Je vous souhaite une bonne installation.",[27533,56499,43823],{"id":43822},[506,56501,56502,56509],{},[370,56503,56504],{},[49,56505,56508],{"href":56506,"rel":56507},"http:\u002F\u002Fxinx.shadoware.org\u002Fdownloads\u002F45",[347],"binaire i386 windows",[370,56510,56511],{},[49,56512,44470],{"href":56513,"rel":56514},"http:\u002F\u002Fxinx.shadoware.org\u002Fdownloads\u002F46",[347],[12,56516,56517,56518,56520],{},"Sous Debian, ajout du dépôt :",[15567,56519],{},[184,56521,56522],{},"deb http:\u002F\u002Fapt.shadoware.org\u002F sid main",[23219,56524,56526,56529],{"className":56525,"dataFootnotes":1646},[23222],[128,56527,23227],{"className":56528,"id":17406},[23226],[367,56530,56531],{},[370,56532,56533,56534],{"id":23232},"je vous conseille celui d'Oracle, Il est plus long à se lancer mais permet de gérer les noms de domaine Generix. ",[49,56535,23240],{"href":23236,"ariaLabel":23237,"className":56536,"dataFootnoteBackref":1646},[23239],{"title":1646,"searchDepth":1647,"depth":1647,"links":56538},[56539,56540,56541,56542,56543,56544],{"id":56396,"depth":1652,"text":56397},{"id":56424,"depth":1652,"text":56425},{"id":56437,"depth":1652,"text":56438},{"id":56479,"depth":1652,"text":56480},{"id":56490,"depth":1652,"text":56491},{"id":17406,"depth":1647,"text":23227},"2009-05-26",{"type":9,"value":56547},[56548,56550,56556],[12,56549,14],{},[12,56551,56552],{},[48221,56553,56554,56385],{},[155,56555,56384],{},[12,56557,56388,56558,56393],{},[56390,56559,56392],{},{},"\u002Fpost\u002Fxinx-0.8.1.0",{"title":56373,"description":14},"xinx-0.8.1.0","posts\u002FXINX\u002F2009-05-26-xinx-0.8.1.0",[45988,46019,38761,46020],"8aKIzfM_YqZZxhEq2D3RIfltjcXI7zx2CQJUUEok8_U",{"id":56568,"title":56569,"author":7,"body":56570,"category":24866,"categorySlug":24867,"date":56664,"description":1646,"excerpt":56665,"extension":1764,"location":1765,"meta":56679,"navigation":1767,"path":56680,"published":1767,"seo":56681,"slug":56682,"stem":56683,"tags":56684,"timeToRead":1647,"__hash__":56685},"posts\u002Fposts\u002FLogiciels\u002F2009-05-23-kmymoney-logiciel-de-compte.md","KMyMoney - Logiciel de compte",{"type":9,"value":56571,"toc":56661},[56572,56578,56581,56587,56590,56596,56605,56611,56614,56620,56627,56630,56636,56639,56644,56647],[12,56573,56574],{},[121,56575],{"alt":56576,"src":56577},"Logo","\u002FLogiciels\u002Fkmymoney-logiciel-de-compte\u002Flogo.png",[12,56579,56580],{},"Voilà un bon logiciel sur lequel on peut compter (mouarf ! mouarf !\nmouarf !), KMyMoney est un éditeur de compte. Je l'utilise\nquotidiennement, et je décide donc d'en parler un peu, pour que d'autres\npersonnes puissent s'intéresser aussi à ce logiciel.",[12,56582,56583],{},[121,56584],{"alt":56585,"src":56586},"Summary","\u002FLogiciels\u002Fkmymoney-logiciel-de-compte\u002FSummary.png",[12,56588,56589],{},"KMyMoney est un logiciel de gestion de compte pour particulier. Il vous\npermet de gérer plusieurs de vos comptes en banque, en vous permettant\nde saisir les dépenses et les recettes sur chacun de vos comptes.",[12,56591,56592],{},[121,56593],{"alt":56594,"src":56595},"Registres","\u002FLogiciels\u002Fkmymoney-logiciel-de-compte\u002FRegistres.png",[12,56597,56598,56599,56604],{},"Vous pouvez également lui indiquer vos échéances (par exemple : forfait\ntéléphone, prêt immobilier",[17400,56600,56601],{},[49,56602,3686],{"href":17404,"ariaDescribedBy":56603,"dataFootnoteRef":1646,"id":17407},[17406],", mais aussi salaire, vos virements\ninternes ou externes, ...) qu'il s'occupera de mettre à jour dans le\nregistre automatiquement ou à votre demande.",[12,56606,56607],{},[121,56608],{"alt":56609,"src":56610},"Echeancier","\u002FLogiciels\u002Fkmymoney-logiciel-de-compte\u002FEcheancier.png",[12,56612,56613],{},"Après une année d'utilisation, il vous permet également de sortir un\nrapport détaillé sur vos dépenses et vos recettes suivant les catégories\nsaisies pour vos opérations. Cette information peut vous être très utile\npour voir où se trouve le \"trou\" de votre porte monnaie et ainsi\nfaire une rustine. Il vous permet aussi de vous projeter vers l'avenir\net de faire votre budget pour l'année prochaine. KMyMoney vous offre\npour cela, la saisie de votre budget, ainsi que plusieurs rapports\nd'écart, vous permettant ainsi de retrouver facilement où vous avez\nfinalement dépensé plus que le budget prévu, et où vous avez dépensé\nmoins.",[12,56615,56616],{},[121,56617],{"alt":56618,"src":56619},"Rapport","\u002FLogiciels\u002Fkmymoney-logiciel-de-compte\u002FRapport.png",[12,56621,56622,56623,56626],{},"En plus de vos comptes en banque ",[362,56624,56625],{},"normaux",", KMyMoney vous permet\négalement de gérer vos comptes titre, et les actions que vous y avez\nmises. Vous pouvez alors facilement suivre la chute vertigineuse de la\nbourse et votre portefeuille fondre littéralement, mais aussi la\nremontée (on espère), des actions d'ici quelques ... siècles.",[12,56628,56629],{},"Enfin pour les paranos, vos comptes peuvent être cryptés à l'aide de\nvotre clé GnuPG automatiquement par KMyMoney :)",[12,56631,56632],{},[121,56633],{"alt":56634,"src":56635},"Cryptage","\u002FLogiciels\u002Fkmymoney-logiciel-de-compte\u002FCryptage.png",[12,56637,56638],{},"Après cette courte présentation, je vous souhaite bon compte ;)",[1886,56640,56641],{},[12,56642,56643],{},"Pour une vie équilibrée, n'oubliez pas de profiter du soleil, au lieu\nde passer votre temps sur votre ordinateur",[12,56645,56646],{},"... en fonction des différents chiffres que vous allez lui entrer.",[23219,56648,56650,56653],{"className":56649,"dataFootnotes":1646},[23222],[128,56651,23227],{"className":56652,"id":17406},[23226],[367,56654,56655],{},[370,56656,56657,56658],{"id":23232},"Il s'occupera d'ailleurs de calculer la durée des échéances, ",[49,56659,23240],{"href":23236,"ariaLabel":23237,"className":56660,"dataFootnoteBackref":1646},[23239],{"title":1646,"searchDepth":1647,"depth":1647,"links":56662},[56663],{"id":17406,"depth":1647,"text":23227},"2009-05-23",{"type":9,"value":56666},[56667,56671,56673,56677],[12,56668,56669],{},[121,56670],{"alt":56576,"src":56577},[12,56672,56580],{},[12,56674,56675],{},[121,56676],{"alt":56585,"src":56586},[12,56678,56589],{},{},"\u002Fpost\u002Fkmymoney-logiciel-de-compte",{"title":56569,"description":1646},"kmymoney-logiciel-de-compte","posts\u002FLogiciels\u002F2009-05-23-kmymoney-logiciel-de-compte",[43895,38761],"GMoM34deHfpP44yg0dNX_5SNO5lCcgbkTT8_04fCe6c",{"id":56687,"title":56688,"author":7,"body":56689,"category":24866,"categorySlug":24867,"date":56742,"description":56693,"excerpt":56743,"extension":1764,"location":1765,"meta":56761,"navigation":1767,"path":56762,"published":1767,"seo":56763,"slug":56764,"stem":56765,"tags":56766,"timeToRead":1647,"__hash__":56767},"posts\u002Fposts\u002FLogiciels\u002F2009-04-11-kde4-devient-instable.md","KDE4 devient instable ...",{"type":9,"value":56690,"toc":56740},[56691,56694,56697,56710,56717,56720,56734,56737],[12,56692,56693],{},"Title: KDE4 devient instable ...\nTags: debian, kde, qt",[12,56695,56696],{},"Titre trolleur, cela ne veux pas dire que KDE 4 est moins stable\nqu'avant mais qu'il a passé une étape supplémentaire chez Debian.",[12,56698,56699,56700,56702,56703,56706,56707,179],{},"Voilà quelques jours déjà que la distribution ",[155,56701,52264],{}," intègre\n",[155,56704,56705],{},"KDE 4"," dans sa version instable. C'est une grande nouvelle car cela\nva permettre à toute les personnes se trouvant en instable et n'ayant\npas touchées à la branche expérimental de Debian de pouvoir tester ",[155,56708,56709],{},"KDE\n4",[12,56711,56712,56713,56716],{},"Malheureusement pour les ",[362,56714,56715],{},"traumatisé de KDE 4",", l'intégration de ce\ndernier remplace KDE 3.5 et ne viens pas se positionner en parallèle. Il\nme semble pourtant que lors du passage de KDE 2 à KDE 3, il était\npossible d'avoir les deux bureaux en même temps sur sa machine et de\nswitcher de l'un à l'autre sans difficulté. Cela signifie donc peut-être\naussi la perte d'utilisateur pour KDE sous Gnu\u002FDebian au profit d'un\nautre environnement de bureau.",[12,56718,56719],{},"Afin d'effectuer la migration en douceur, Gnu\u002FDebian propose un\nassistant de migration de KDE du nom de Kaboom. Cet assistant, démarrer\nlors de la première connexion de l'utilisateur et vous propose alors de\nsauvegarder votre ancien dossier de configuration KDE 3 et de migrer le\ndossier .kde vers la version 4.",[12,56721,56722,56726,56730],{},[121,56723],{"alt":56724,"src":56725},"kaboom1","\u002FLogiciels\u002Fkde4-devient-instable\u002Fkaboom1_s.jpg",[121,56727],{"alt":56728,"src":56729},"kaboom2","\u002FLogiciels\u002Fkde4-devient-instable\u002Fkaboom2_s.jpg",[121,56731],{"alt":56732,"src":56733},"kaboom3","\u002FLogiciels\u002Fkde4-devient-instable\u002Fkaboom3_s.jpg",[12,56735,56736],{},"Je viens donc de passer à KDE 4 et m'émerveille des nouvelles\nfonctionnalités de KWin (cube, placement des fenêtres) que je n'avais\npas avant, car n'avait jamais installé Compiz ...",[12,56738,56739],{},"Bonne installation,",{"title":1646,"searchDepth":1647,"depth":1647,"links":56741},[],"2009-04-11",{"type":9,"value":56744},[56745,56747,56749,56757],[12,56746,56693],{},[12,56748,56696],{},[12,56750,56699,56751,56702,56753,56706,56755,179],{},[155,56752,52264],{},[155,56754,56705],{},[155,56756,56709],{},[12,56758,56712,56759,56716],{},[362,56760,56715],{},{},"\u002Fpost\u002Fkde4-devient-instable",{"title":56688,"description":56693},"kde4-devient-instable","posts\u002FLogiciels\u002F2009-04-11-kde4-devient-instable",[38764,43895,38761],"Oj3jqh3sY77Ym6LYJ2tDQUMs5alVjGgGLz0JxRhI7k8",{"id":56769,"title":56770,"author":7,"body":56771,"category":44415,"categorySlug":45988,"date":57177,"description":1646,"excerpt":57178,"extension":1764,"location":1765,"meta":57194,"navigation":1767,"path":57195,"published":1767,"seo":57196,"slug":57197,"stem":57198,"tags":57199,"timeToRead":2185,"__hash__":57200},"posts\u002Fposts\u002FXINX\u002F2009-03-06-xinx-0.8.0.0.md","XINX 0.8.0.0",{"type":9,"value":56772,"toc":57170},[56773,56779,56784,56792,56795,56803,56806,56809,56813,56824,56831,56834,56839,56845,56862,56866,56872,56875,56878,56882,56887,56895,57072,57082,57086,57089,57097,57099,57103,57115,57118,57122,57125,57131,57134,57161,57167],[12,56774,56775],{},[121,56776],{"alt":56777,"src":56778},"Editeur","\u002FXINX\u002Fxinx-0.8.0.0\u002Fediteur_de_texte.png",[12,56780,56781,56782,179],{},"J'ai le plaisir de vous annoncer la sortie de la nouvelle version de\n",[155,56783,44415],{},[12,56785,56786,56788,56789,179],{},[155,56787,44415],{}," est un éditeur de projet web basé sur les technologies XSL,\nHTML, CSS, JavaScript. Il possède un mode projet permettant de dériver\nles projets à la manière des consultants GCE de l'entreprise ",[362,56790,56791],{},"Generix\nGroup",[12,56793,56794],{},"Cette dernière version a mis beaucoup de temps pour sortir et est moins\ncomplète que celle que je souhaitais faire à l'origine. Cela viens de\nplusieurs raison :",[506,56796,56797,56800],{},[370,56798,56799],{},"de gros changement interne",[370,56801,56802],{},"moins de temps le soir et le week-end.",[12,56804,56805],{},"La prochaine version 0.8.1 contiendra les développements que je n'ai pu\nmettre dans cette version. N'hésitez pas à faire un retour sur les\nproblèmes que vous rencontrez pour que ces derniers soit également\ncorriger pour la prochaine version.",[12,56807,56808],{},"Nous allons détailler ci-dessous les différentes amélioration incluse\ndans cette version.",[133,56810,56812],{"id":56811},"qcodeedit-le-nouvel-éditeur","QCodeEdit - Le nouvel éditeur",[12,56814,56815,56816,56819,56820,56823],{},"L'éditeur de texte de cette version a complètement été remplacé. On est\npassé de QTextEdit (",[362,56817,56818],{},"éditeur de texte de Qt Software",") à QCodeEdit\n(",[362,56821,56822],{},"Développé par Luc Bruant aka fullmetalcoder","). Heureusement que les\nAPIs de ces deux éditeurs sont très similaire ce qui a permis\nd'effectuer les changements assez rapidement (quelques mois au lieu de\nquelques années ;) ).",[12,56825,56826,56827,56830],{},"Je tiens également à remercier ",[362,56828,56829],{},"fullmetalcoder"," de sa réactivité sur le\ndéveloppement de QCodeEdit.",[12,56832,56833],{},"Les nouveautés qu'apportent ce nouvel éditeur par rapport à QTextEdit\nsont :",[506,56835,56836],{},[370,56837,56838],{},"le folding",[12,56840,56841],{},[121,56842],{"alt":56843,"src":56844},"Mark","\u002FXINX\u002Fxinx-0.8.0.0\u002Fmarqueur.png",[506,56846,56847,56850,56856,56859],{},[370,56848,56849],{},"les couleurs de fond sur les marque pages et les erreurs (pour une\nplus grande clarté).",[370,56851,56852,56853,17832],{},"un chargement plus rapide des fichiers (exemple :\n",[184,56854,56855],{},"configuration.xml",[370,56857,56858],{},"pourvoir faire des sélections verticales (à l'aide de Ctrl+Shift)\ncomme le font certain éditeurs",[370,56860,56861],{},"afficher les correspondances de parenthèses",[133,56863,56865],{"id":56864},"test-du-xpath","Test du XPATH",[12,56867,56868],{},[121,56869],{"alt":56870,"src":56871},"X-Quey","\u002FXINX\u002Fxinx-0.8.0.0\u002Fxpath.png",[12,56873,56874],{},"Cette boite de dialogue permet d'exécuter un X-PATH (ou une requête\nXQuery) sur un fichier de présentation. Cela permet de tester rapidement\nles X-PATH compliqué avant de lancer la page sous le navigateur.",[12,56876,56877],{},"Il est possible de saisir des X-PATH relativement à un X-PATH\nsélectionné dans le flux de présentation ou de manière absolu.",[133,56879,56881],{"id":56880},"script-automatique","Script automatique",[12,56883,56884,56886],{},[155,56885,44415],{}," permet également d'appeler un script lors de la sauvegarde d'un\nfichier. Pour cela un nouvel objet permettant de modifier le contenue de\nl'éditeur plus facilement a été créé.Comme exemple à la sauvegarde de\nscript ont été mis à jours :",[506,56888,56889,56892],{},[370,56890,56891],{},"La mise à jours des key('url-param','') par des X-PATH",[370,56893,56894],{},"La mise à jours du nom du fichier dans les feuilles de styles.",[2105,56896,56899],{"className":56897,"code":56898,"language":3510,"meta":1646,"style":1646},"language-javascript shiki shiki-themes one-dark-pro","obj.beforeSave = function() {\n  this.run();\n};\n\nobj.run = function() {\n  var search = new DocumentSearch(textEdit);\n\n  search.options.regExp = true;\n  search.searchText = \"key\\\\('url-param', '(.*)'\\\\)\";\n  search.replaceText =\n    \"\u002Flayout_data\u002Fapplication_data\u002Ftemporaire\u002Fparam[@name='\\\\1']\";\n\n  while (search.next()) {}\n};\n",[184,56900,56901,56916,56928,56932,56936,56950,56972,56976,56996,57023,57034,57046,57050,57068],{"__ignoreMap":1646},[2113,56902,56903,56905,56907,56910,56912,56914],{"class":2115,"line":2116},[2113,56904,9233],{"class":2414},[2113,56906,179],{"class":2119},[2113,56908,56909],{"class":2133},"beforeSave",[2113,56911,2153],{"class":2334},[2113,56913,17728],{"class":2326},[2113,56915,3912],{"class":2119},[2113,56917,56918,56921,56923,56926],{"class":2115,"line":1647},[2113,56919,56920],{"class":2414},"  this",[2113,56922,179],{"class":2119},[2113,56924,56925],{"class":2133},"run",[2113,56927,3944],{"class":2119},[2113,56929,56930],{"class":2115,"line":1652},[2113,56931,2787],{"class":2119},[2113,56933,56934],{"class":2115,"line":2185},[2113,56935,2125],{"emptyLinePlaceholder":1767},[2113,56937,56938,56940,56942,56944,56946,56948],{"class":2115,"line":2230},[2113,56939,9233],{"class":2414},[2113,56941,179],{"class":2119},[2113,56943,56925],{"class":2133},[2113,56945,2153],{"class":2334},[2113,56947,17728],{"class":2326},[2113,56949,3912],{"class":2119},[2113,56951,56952,56955,56958,56960,56962,56965,56967,56970],{"class":2115,"line":2235},[2113,56953,56954],{"class":2326},"  var",[2113,56956,56957],{"class":2330}," search",[2113,56959,2153],{"class":2334},[2113,56961,4778],{"class":2326},[2113,56963,56964],{"class":2133}," DocumentSearch",[2113,56966,2423],{"class":2119},[2113,56968,56969],{"class":2330},"textEdit",[2113,56971,2553],{"class":2119},[2113,56973,56974],{"class":2115,"line":2241},[2113,56975,2125],{"emptyLinePlaceholder":1767},[2113,56977,56978,56981,56983,56985,56987,56990,56992,56994],{"class":2115,"line":2246},[2113,56979,56980],{"class":2414},"  search",[2113,56982,179],{"class":2119},[2113,56984,17539],{"class":2414},[2113,56986,179],{"class":2119},[2113,56988,56989],{"class":2330},"regExp",[2113,56991,2153],{"class":2334},[2113,56993,4872],{"class":2166},[2113,56995,2487],{"class":2119},[2113,56997,56998,57000,57002,57005,57007,57010,57013,57016,57018,57021],{"class":2115,"line":2464},[2113,56999,56980],{"class":2414},[2113,57001,179],{"class":2119},[2113,57003,57004],{"class":2330},"searchText",[2113,57006,2153],{"class":2334},[2113,57008,57009],{"class":2149}," \"key",[2113,57011,57012],{"class":2334},"\\\\",[2113,57014,57015],{"class":2149},"('url-param', '(.*)'",[2113,57017,57012],{"class":2334},[2113,57019,57020],{"class":2149},")\"",[2113,57022,2487],{"class":2119},[2113,57024,57025,57027,57029,57032],{"class":2115,"line":2085},[2113,57026,56980],{"class":2414},[2113,57028,179],{"class":2119},[2113,57030,57031],{"class":2330},"replaceText",[2113,57033,43045],{"class":2334},[2113,57035,57036,57039,57041,57044],{"class":2115,"line":2514},[2113,57037,57038],{"class":2149},"    \"\u002Flayout_data\u002Fapplication_data\u002Ftemporaire\u002Fparam[@name='",[2113,57040,57012],{"class":2334},[2113,57042,57043],{"class":2149},"1']\"",[2113,57045,2487],{"class":2119},[2113,57047,57048],{"class":2115,"line":2533},[2113,57049,2125],{"emptyLinePlaceholder":1767},[2113,57051,57052,57055,57057,57060,57062,57065],{"class":2115,"line":2556},[2113,57053,57054],{"class":2326},"  while",[2113,57056,2495],{"class":2119},[2113,57058,57059],{"class":2414},"search",[2113,57061,179],{"class":2119},[2113,57063,57064],{"class":2133},"next",[2113,57066,57067],{"class":2119},"()) {}\n",[2113,57069,57070],{"class":2115,"line":2569},[2113,57071,2787],{"class":2119},[12,57073,57074,57075,57077,57078,57081],{},"La nouveauté se trouve dans l'ajout de quatre nouvelle méthodes. La\nméthode utilisé ici est ",[184,57076,56909],{}," et permet de faire des\nmodifications au texte avant sauvegarde. Tout comme ",[184,57079,57080],{},"afterSave"," permet\nd'effectuer des modifications au texte après sauvegarde. Nous avons\négalement deux méthodes beforeLoad et afterLoad pour les opérations à\nfaire lors de la lecture.",[133,57083,57085],{"id":57084},"autres-nouveautés","Autres nouveautés",[12,57087,57088],{},"Bien sur cet version apporte aussi d'autres nouveautés comme :",[506,57090,57091,57094],{},[370,57092,57093],{},"la fermeture automatique de balise",[370,57095,57096],{},"des nouvelles options, ...",[133,57098,38898],{"id":38897},[27533,57100,57102],{"id":57101},"sous-mswindows","Sous Ms\u002FWindows",[12,57104,57105,57106,57108,57109,57114],{},"Pour installer la dernière version de ",[155,57107,44415],{}," vous pouvez télécharger\nle ",[49,57110,57113],{"href":57111,"rel":57112},"http:\u002F\u002Fxinx.shadoware.org\u002Fdownloads\u002F40",[347],"programme d'installation"," et le lancer. Il remplacera\nautomatiquement l'ancienne version.",[12,57116,57117],{},"Si vous avez des fichiers de données modifiés (template, script, ...)\npensez à les sauvegarder.",[27533,57119,57121],{"id":57120},"sous-gnulinux","Sous Gnu\u002FLinux",[12,57123,57124],{},"Sous Debian, vous pouvez ajouter le dépôt dans le fichier\n\u002Fetc\u002Fapt\u002Fsources.list. Ainsi vous serez automatiquement prévenu des\nmises à jours éventuelle.",[2105,57126,57129],{"className":57127,"code":57128,"language":26023},[26021],"deb http:\u002F\u002Fapt.shadoware.org\u002F sid main\n",[184,57130,57128],{"__ignoreMap":1646},[12,57132,57133],{},"puis dans une console :",[2105,57135,57137],{"className":2317,"code":57136,"language":2319,"meta":1646,"style":1646},"sudo apt-cache search xinx\nsudo aptitude install xinx\n",[184,57138,57139,57150],{"__ignoreMap":1646},[2113,57140,57141,57143,57146,57148],{"class":2115,"line":2116},[2113,57142,43135],{"class":2133},[2113,57144,57145],{"class":2149}," apt-cache",[2113,57147,56957],{"class":2149},[2113,57149,52604],{"class":2149},[2113,57151,57152,57154,57157,57159],{"class":2115,"line":1647},[2113,57153,43135],{"class":2133},[2113,57155,57156],{"class":2149}," aptitude",[2113,57158,52601],{"class":2149},[2113,57160,52604],{"class":2149},[12,57162,57163,57164,57166],{},"Pour les autres distributions ou pour les machines MacOS vous pouvez\ncompiler ",[155,57165,44415],{}," à partir des sources.",[3358,57168,57169],{},"html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":1646,"searchDepth":1647,"depth":1647,"links":57171},[57172,57173,57174,57175,57176],{"id":56811,"depth":1652,"text":56812},{"id":56864,"depth":1652,"text":56865},{"id":56880,"depth":1652,"text":56881},{"id":57084,"depth":1652,"text":57085},{"id":38897,"depth":1652,"text":38898},"2009-03-06",{"type":9,"value":57179},[57180,57184,57188],[12,57181,57182],{},[121,57183],{"alt":56777,"src":56778},[12,57185,56781,57186,179],{},[155,57187,44415],{},[12,57189,57190,56788,57192,179],{},[155,57191,44415],{},[362,57193,56791],{},{},"\u002Fpost\u002Fxinx-0.8.0.0",{"title":56770,"description":1646},"xinx-0.8.0.0","posts\u002FXINX\u002F2009-03-06-xinx-0.8.0.0",[38764,45988,46019,38761,46020],"2LVP73RxAuNHiNwoDLW7n9zx61WC1hKRo8CPZUCY5rk",{"id":57202,"title":57203,"author":7,"body":57204,"category":24866,"categorySlug":24867,"date":57393,"description":57394,"excerpt":57395,"extension":1764,"location":1765,"meta":57445,"navigation":1767,"path":57446,"published":1767,"seo":57447,"slug":57448,"stem":57449,"tags":57450,"timeToRead":1647,"__hash__":57451},"posts\u002Fposts\u002FLogiciels\u002F2009-03-03-qt-4.5-est-sortie.md","Qt 4.5 est sortie",{"type":9,"value":57205,"toc":57390},[57206,57212,57217,57225,57268,57273,57297,57308,57313,57344,57358,57364],[12,57207,57208,57209,57211],{},"Voilà quelques heures que la dernière version de ",[362,57210,43910],{}," est sortie. Cette\nversion, numéroté 4.5, est essentiellement orienté performance.",[12,57213,57214],{},[121,57215],{"alt":56576,"src":57216},"\u002FLogiciels\u002Fqt-4.5-est-sortie\u002Flogo_nokia_qt.png",[12,57218,57219,57220],{},"Elle contient entre autre",[17400,57221,57222],{},[49,57223,3686],{"href":17404,"ariaDescribedBy":57224,"dataFootnoteRef":1646,"id":17407},[17406],[506,57226,57227,57242,57248,57254,57257],{},[370,57228,57229,57230,57233,57234,57237,57238,57241],{},"Une mise à jour de ",[362,57231,57232],{},"QtWebKit"," vers un version de ",[362,57235,57236],{},"WebKit"," plus\nrécente (avec un nouveau moteur JavaScript plus rapide, la\npossibilité d'ajouter des greffons comme ",[362,57239,57240],{},"Flash","...). Il est\négalement possible d'utiliser les balises d'HTML 5.",[370,57243,57244,57245,179],{},"Amélioration des performances sur le moteur de rendu ainsi que sur\nle rendu du texte. Une librairie permettant de faire des tests de\nperformance a été incluse : ",[362,57246,57247],{},"QtBenchLib",[370,57249,57250,57251],{},"Le support de ",[362,57252,57253],{},"Mac OS X Cocoa",[370,57255,57256],{},"La possibilité de faire des transformations XSLT",[370,57258,57259,57260,57263],{},"Un débuggeur ",[362,57261,57262],{},"QtScript",[17400,57264,57265],{},[49,57266,2547],{"href":17977,"ariaDescribedBy":57267,"dataFootnoteRef":1646,"id":17979},[17406],[12,57269,57270],{},[121,57271],{"alt":43910,"src":57272},"\u002FLogiciels\u002Fqt-4.5-est-sortie\u002Fqtscript-debugger-small.png",[506,57274,57275,57282,57285,57294],{},[370,57276,57277,57278,57281],{},"Le support du format ",[362,57279,57280],{},"OpenDocument"," (ODF version 1.0, ne supportant\nque le texte et les images)",[370,57283,57284],{},"Amélioration du support des proxys",[370,57286,57287,57288,932,57291],{},"Amélioration de ",[362,57289,57290],{},"QtDesigner",[362,57292,57293],{},"QtLinguist",[370,57295,57296],{},"Une meilleur intégration au thème Gtk",[12,57298,57299,57300,57303,57304,57307],{},"Cette nouvelle version apporte également une grande nouveauté\npuisqu'elle est ",[362,57301,57302],{},"LGPL",". Il est donc désormais possible de faire une\napplication propriétaire à l'aide de cette librairie sans prendre une\nlicence chez ",[362,57305,57306],{},"Qt Software",". Par contre si une entreprise souhaite un\nsupport pour la librairie, il faudra tout de même acheter une licence.",[12,57309,57310],{},[121,57311],{"alt":43823,"src":57312},"\u002FLogiciels\u002Fqt-4.5-est-sortie\u002FScreenShot166.png",[12,57314,57315,57316,57318,57319,57322,57323,57325,57326,57328,57329,57332,57333,57335,57336,932,57338,57340,57341,57343],{},"La sortie de ",[362,57317,43910],{}," 4.5 est accompagné de ",[362,57320,57321],{},"Qt Creator"," 1.0. ",[362,57324,57321],{},"\nest l'outil dernier né de chez ",[362,57327,57306],{}," (branche de ",[362,57330,57331],{},"Nokia",") et est\nun IDE de développement de programme ",[362,57334,43910],{},". Un paquet est proposé\ncontenant ",[362,57337,43910],{},[362,57339,57321],{},", ainsi que ",[362,57342,41868],{}," et permettant d'avoir\nune installation tout en un d'un poste de développement.",[12,57345,57346,57347,57349,57350,57352,57353,179],{},"Vous pouvez donc aller télécharger la dernière version de ",[362,57348,43910],{},", ainsi\nque de ",[362,57351,57321],{}," (attention les serveurs sont surchargés) à cette\n",[49,57354,57357],{"href":57355,"rel":57356},"http:\u002F\u002Fwww.qtsoftware.com\u002Fdownloads",[347],"adresse",[12,57359,57360,57361,57363],{},"Je remercie toute l'équipe de nouvellement ",[362,57362,57306],{}," de cette\nnouvelle version.",[23219,57365,57367,57370],{"className":57366,"dataFootnotes":1646},[23222],[128,57368,23227],{"className":57369,"id":17406},[23226],[367,57371,57372,57382],{},[370,57373,57374,57375,45630,57379],{"id":23232},"pour plus de détail voir ",[49,57376,57377],{"href":57377,"rel":57378},"http:\u002F\u002Fdoc.trolltech.com\u002F4.5\u002Fqt4-5-intro.html",[347],[49,57380,23240],{"href":23236,"ariaLabel":23237,"className":57381,"dataFootnoteBackref":1646},[23239],[370,57383,57384,57385,45630,57387],{"id":23243},"L'image du debugger viens du site ",[362,57386,57306],{},[49,57388,23240],{"href":23251,"ariaLabel":23252,"className":57389,"dataFootnoteBackref":1646},[23239],{"title":1646,"searchDepth":1647,"depth":1647,"links":57391},[57392],{"id":17406,"depth":1647,"text":23227},"2009-03-03","Voilà quelques heures que la dernière version de Qt est sortie. Cette\nversion, numéroté 4.5, est essentiellement orienté performance.",{"type":9,"value":57396},[57397,57401,57405,57412,57441],[12,57398,57208,57399,57211],{},[362,57400,43910],{},[12,57402,57403],{},[121,57404],{"alt":56576,"src":57216},[12,57406,57219,57407],{},[17400,57408,57409],{},[49,57410,3686],{"href":17404,"ariaDescribedBy":57411,"dataFootnoteRef":1646,"id":17407},[17406],[506,57413,57414,57422,57426,57430,57432],{},[370,57415,57229,57416,57233,57418,57237,57420,57241],{},[362,57417,57232],{},[362,57419,57236],{},[362,57421,57240],{},[370,57423,57244,57424,179],{},[362,57425,57247],{},[370,57427,57250,57428],{},[362,57429,57253],{},[370,57431,57256],{},[370,57433,57259,57434,57436],{},[362,57435,57262],{},[17400,57437,57438],{},[49,57439,2547],{"href":17977,"ariaDescribedBy":57440,"dataFootnoteRef":1646,"id":17979},[17406],[12,57442,57443],{},[121,57444],{"alt":43910,"src":57272},{},"\u002Fpost\u002Fqt-4.5-est-sortie",{"title":57203,"description":57394},"qt-4.5-est-sortie","posts\u002FLogiciels\u002F2009-03-03-qt-4.5-est-sortie",[38761],"gWjLlhm92HnoKh4gr6rJd2ET4T4kKlZIkeq4_SEDikE",{"id":57453,"title":57454,"author":7,"body":57455,"category":24866,"categorySlug":24867,"date":57477,"description":1646,"excerpt":57478,"extension":1764,"location":1765,"meta":57479,"navigation":1767,"path":57480,"published":1767,"seo":57481,"slug":57482,"stem":57483,"tags":57484,"timeToRead":2116,"__hash__":57485},"posts\u002Fposts\u002FLogiciels\u002F2009-03-02-le-net-en-france.md","Le net en france ...",{"type":9,"value":57456,"toc":57475},[57457,57462,57465],[1886,57458,57459],{},[12,57460,57461],{},"Devant le ridicule d'un gouvernement qui s'entête à vouloir déconnecter\ndu Net des familles entières sans preuves valables ni procès, la\nQuadrature appelle les citoyens épris de liberté à procéder au\n« black-out » de leurs sites, blogs, profils, avatars, etc. Comme en\nNouvelle-Zélande, seul pays avec la France où la « riposte graduée »\ndevait être imposée par la loi, pour finalement être repoussée : pour\nprotester contre cette loi imbécile et sa « liste blanche » de sites\nautorisés, le Net français doit agir et se draper de noir.",[12,57463,57464],{},"La quadrature du net, Paris, le 25 février",[239,57466,57467],{},[49,57468,57471],{"href":57469,"title":57470},"http:\u002F\u002Fwww.laquadrature.net\u002FHADOPI","HADOPI - Le Net en France : black-out",[121,57472],{"src":57473,"border":57474,"alt":57470},"http:\u002F\u002Fmedia.laquadrature.net\u002FQuadrature_black-out_HADOPI_336x280px.gif",0,{"title":1646,"searchDepth":1647,"depth":1647,"links":57476},[],"2009-03-02",null,{},"\u002Fpost\u002Fle-net-en-france",{"title":57454,"description":1646},"le-net-en-france","posts\u002FLogiciels\u002F2009-03-02-le-net-en-france",[],"ycX5DqVFF5BYDBrgIjbzqu3gxFtcs6IULHX0C0scofw",{"id":57487,"title":57488,"author":7,"body":57489,"category":24866,"categorySlug":24867,"date":57591,"description":1646,"excerpt":57592,"extension":1764,"location":1765,"meta":57618,"navigation":1767,"path":57619,"published":1767,"seo":57620,"slug":57621,"stem":57622,"tags":57623,"timeToRead":1652,"__hash__":57624},"posts\u002Fposts\u002FLogiciels\u002F2009-02-20-outil-de-securisation-non-interoperable.md","Outil de sécurisation non intéropérable",{"type":9,"value":57490,"toc":57588},[57491,57497,57511,57518,57523,57528,57535,57542,57547,57550,57559,57564,57567,57574],[12,57492,57493],{},[121,57494],{"alt":57495,"src":57496},"Piracy","\u002FLogiciels\u002Foutil-de-securisation-non-interoperable\u002FDooFi_Skull.png",[12,57498,57499,57500,57503,57504,57507,57508,179],{},"Alors que l'UMP est accusé de contrefaçon du groupe\n",[2113,57501,57502],{},"MGMT",", et est donc accusé du terme de ",[362,57505,57506],{},"piratage"," selon le mot\nutilisé à mauvais escient, nous avons des nouvelles sur le ",[2113,57509,57510],{},"logiciel\nespion du gouvernement",[12,57512,57513,57514,57517],{},"Ce dernier sera non seulement payant mais également non interopérable.\nPour ",[362,57515,57516],{},"le rapporteur Riester",", l'interopérabilité est un frein au choix\net n'est pas nécessaire :",[1886,57519,57520],{},[12,57521,57522],{},"Je n’y suis pas favorable, a-t-il expliqué avant de poursuivre il\nfaut laisser au consommateur sa totale liberté de choix en fonction de\nson système d’exploitation. L’interopérabilité n’est pas nécessaire pour\nles consommateurs et elle est trop contraignante pour les éditeurs de\nlogiciels",[1886,57524,57525],{},[12,57526,57527],{},"les moyens de sécurisation mis en place ne sauraient être gratuits, à\nl’image des logiciels de contrôle parental, mis à la disposition des\nconsommateurs à titre payant, même si leur prix est modique",[12,57529,57530,57531,57534],{},"En plus de l'abonnement internet et du modem ADSL de l'opérateur, il\nfaudra également donc s'acheter ce logiciel espion, ainsi que le système\nd'exploitation ",[362,57532,57533],{},"Redmondien"," qui va avec. En effet, aucun éditeur de\nlogiciel ne trouvera un intérêt (monétaire) à développer un tel logiciel\nespion (qui sera, de surcroit, à source fermé) pour un système\nd'exploitation libre.Comment donc pourrions-nous nous défendre, nous\nutilisateurs du logiciel libre, si vis-à-vis de la loi, nous ne pourrons\nnous justifier d'un téléchargement illégal même s'il n'en est pas le\ncas.",[12,57536,57537,57538,57541],{},"Alors que l'on parle de vente lié, ",[362,57539,57540],{},"M. Henrard"," répond",[1886,57543,57544],{},[12,57545,57546],{},"On crée une incitation et chacun se détermine en fonction de ses\nobligations. Il y a des aléas dont on veut se protéger et si on est un\ntout petit peu rationnel, il est évident qu’on va demander à son FAI\nl’utilisation d’un tel dispositif.",[12,57548,57549],{},"et donc payer ...",[12,57551,57552,57553,57558],{},"Le président de l*'APRIL*",[17400,57554,57555],{},[49,57556,3686],{"href":17404,"ariaDescribedBy":57557,"dataFootnoteRef":1646,"id":17407},[17406]," s'insurge qu'",[1886,57560,57561],{},[12,57562,57563],{},"il reste encore à l'Assemblée nationale des députés qui sont opposés à\nl'interopérabilité, opposés à la libre concurrence, opposés au libre-choix\ndes utilisateurs, et opposés au logiciel libre",[12,57565,57566],{},"Quand au logiciel en lui-même, personne ne connait son fonctionnement :-\nQu'enverra-t-il comme donnée ?- A qui enverra-t-il les données (au FAI ?\nau gens plein de fric (major) ? au marketing ? au gouvernement ? à tout\nces gens ?)- Devras-t-il être sur chaque ordinateur (avec une licence\npar ordinateur à payer) ? Sur le modem ADSL ? Sur un seul des\nordinateurs (et il nous sera alors interdis d'avoir plus d'un ordinateur\npar foyer) ?",[12,57568,57569,57570,57573],{},"La France apparaitra alors surement bientôt (pour 2009) dans les pays\n",[2113,57571,57572],{},"ennemis d'Internet",". Vive la république et vive la France.",[23219,57575,57577,57580],{"className":57576,"dataFootnotes":1646},[23222],[128,57578,23227],{"className":57579,"id":17406},[23226],[367,57581,57582],{},[370,57583,57584,57585],{"id":23232},"Benoît Sibaud, président de l*'APRIL* ",[49,57586,23240],{"href":23236,"ariaLabel":23237,"className":57587,"dataFootnoteBackref":1646},[23239],{"title":1646,"searchDepth":1647,"depth":1647,"links":57589},[57590],{"id":17406,"depth":1647,"text":23227},"2009-02-20",{"type":9,"value":57593},[57594,57598,57606,57610,57614],[12,57595,57596],{},[121,57597],{"alt":57495,"src":57496},[12,57599,57499,57600,57503,57602,57507,57604,179],{},[2113,57601,57502],{},[362,57603,57506],{},[2113,57605,57510],{},[12,57607,57513,57608,57517],{},[362,57609,57516],{},[1886,57611,57612],{},[12,57613,57522],{},[1886,57615,57616],{},[12,57617,57527],{},{},"\u002Fpost\u002Foutil-de-securisation-non-interoperable",{"title":57488,"description":1646},"outil-de-securisation-non-interoperable","posts\u002FLogiciels\u002F2009-02-20-outil-de-securisation-non-interoperable",[],"M5zgP1THqf4OdrqtbfTEfuNJwbh_q3bfCRf6ItywFSk",{"id":57626,"title":57627,"author":7,"body":57628,"category":44415,"categorySlug":45988,"date":57928,"description":55197,"excerpt":57929,"extension":1764,"location":1765,"meta":57945,"navigation":1767,"path":57946,"published":1767,"seo":57947,"slug":57948,"stem":57949,"tags":57950,"timeToRead":2185,"__hash__":57951},"posts\u002Fposts\u002FXINX\u002F2009-02-18-xinx-editeur-de-feuille-de-style.md","XINX - Editeur de feuille de style",{"type":9,"value":57629,"toc":57923},[57630,57632,57641,57644,57650,57652,57666,57693,57715,57720,57744,57779,57783,57788,57793,57846,57866],[12,57631,55197],{},[12,57633,57634,57635,57637,57638,57640],{},"Ce billet, parle de ",[155,57636,44415],{},", un logiciel libre que j'ai développé et\nque je continue à développer sur mon temps libre. Le but de se billet\nest de présenter ",[155,57639,44415],{}," et d'être le début d'une longue série de\nbillet parlant de son développement.",[12,57642,57643],{},"En effet, même si le programme est propre à une utilisation particulière\npour un cercle de personne restreint, les méthodes, classes, et idées\nutilisées dans ce programme peuvent intéresser d'autres personnes.",[12,57645,57646],{},[121,57647],{"alt":57648,"src":57649},"Nouvelle","\u002FXINX\u002Fxinx-editeur-de-feuille-de-style\u002FScreenShot151.png",[133,57651,43625],{"id":43624},[12,57653,57654,57656,57657,57660,57661,179],{},[155,57655,44415],{}," est un éditeur de feuille de style sur lequel je travail depuis\ndébut 2006. En faite j'avais commencé un autre éditeur à l'aide de\n",[155,57658,57659],{},"Borland\u002FDelphi 7"," mais il a été rapidement arrêté pour prendre une\nnouvelle direction dans un nouveau langage avec un nouveau\ndépart",[17400,57662,57663],{},[49,57664,3686],{"href":17404,"ariaDescribedBy":57665,"dataFootnoteRef":1646,"id":17407},[17406],[12,57667,57668,57669,57672,57673,57680,57685,57686,57688,57689,57692],{},"Cet éditeur de feuille de style à la particularité de pouvoir être\nutilisé dans le cadre de mon travail : sur l'application ",[362,57670,57671],{},"eGx"," (de la\nsociété ",[155,57674,57675],{},[49,57676,57679],{"href":57677,"rel":57678},"http:\u002F\u002Fwww.fr.generixgroup.com",[347],"Générix",[17400,57681,57682],{},[49,57683,2547],{"href":17977,"ariaDescribedBy":57684,"dataFootnoteRef":1646,"id":17979},[17406],"). ",[155,57687,44415],{}," permet non seulement\nl'édition de feuille de style (XSL), en proposant une ",[362,57690,57691],{},"complétion","\nintuitive, mais propose également un mode projet, dans lequel il est\npossible de dérivé une feuille de style plutôt que de la remplacer (de\nfaçon spécifique à notre méthode de travail en projet chez le client).",[12,57694,57695,57697,57698,57703,57704,57707,57708,57710,57711,57714],{},[155,57696,44415],{}," est un acronyme récursive",[17400,57699,57700],{},[49,57701,3696],{"href":41827,"ariaDescribedBy":57702,"dataFootnoteRef":1646,"id":41829},[17406]," qui signifie ",[362,57705,57706],{},"XINX Is Not\nXDME",". Ce nom a été choisis car ",[155,57709,44415],{}," est le complément idéal à\n",[155,57712,57713],{},"XDME"," (l'éditeur de fichier de configuration d*'eGx*).",[12,57716,57717],{},[121,57718],{"alt":56576,"src":57719},"\u002FXINX\u002Fxinx-editeur-de-feuille-de-style\u002Flogo_nokia_qt.png",[12,57721,57722,57723,57726,57727,932,57729,57731,57732,57734,57735,57737,57738,57741,57743],{},"Au niveau du choix du langage, a contrario des autres applications\n",[155,57724,57725],{},"Generix"," écritent en ",[362,57728,52100],{},[155,57730,44415],{}," a été programmé en ",[362,57733,1066],{},". Le\nframework ",[362,57736,43910],{}," de la société ",[56390,57739,57740],{},"Trolltech",[155,57742,57331],{}," a été\nchoisis pour simplifier la programmation. Pourquoi ce choix de langage :",[506,57745,57746,57754,57766],{},[370,57747,57748,57750,57751,57753],{},[155,57749,44415],{}," est une application écrite sur mon temps libre, j'ai donc\nchoisis un langage que je souhaité apprendre. En faite la raison de\nl'existence de ",[155,57752,44415],{},", et la volonté d'apprendre à utiliser ce\nframework.",[370,57755,57756,57757,57759,57760,57762,57763,179],{},"Le framework ",[362,57758,43910],{}," est utilisé par l'environnement de bureau ",[155,57761,46036],{},".\nCet environnement de bureau est celui que j'utilise tous les jours,\npendant mon temps libre (chez moi), sous ",[155,57764,57765],{},"Gnu\u002FLinux",[370,57767,57768,57769,57771,57772,17832,57774,179],{},"La version 4 de ce framework est utilisable sous ",[155,57770,52271],{},",\nsans payer de licence, dans le cadre de l'écriture d'un logiciel\nlibre (au bureau tout nos poste sont sous ",[155,57773,52271],{},[17400,57775,57776],{},[49,57777,706],{"href":41857,"ariaDescribedBy":57778,"dataFootnoteRef":1646,"id":41859},[17406],[133,57780,57782],{"id":57781},"architecture","Architecture",[12,57784,57785],{},[121,57786],{"alt":57782,"src":57787},"\u002FXINX\u002Fxinx-editeur-de-feuille-de-style\u002FarchiXINX.png",[12,57789,57790,57792],{},[155,57791,44415],{}," est décomposé en plusieurs parties :",[506,57794,57795,57798,57801,57821,57824,57830,57841],{},[370,57796,57797],{},"la librairie de composant (contenant un gestionnaire de plugin en\nQt, un combobox pour choisir une couleur, une liste de valeur avec\nles boutons pour ajouter ou supprimer des éléments, une barre\nd'édition pour faciliter l'édition de dossier)",[370,57799,57800],{},"la librairie principale (externalisant ce qui peut être nécessaire\naux plugins).",[370,57802,57803,57804,57806,57807],{},"une série de plugin permettant, dans ",[155,57805,44415],{}," de",[506,57808,57809,57812,57815,57818],{},[370,57810,57811],{},"gérer CVS",[370,57813,57814],{},"gérer SubVersion",[370,57816,57817],{},"gérer les extentions XSL, JS, XML, HTML",[370,57819,57820],{},"gérer les WebServices",[370,57822,57823],{},"l'application elle-même",[370,57825,57826,57827,57829],{},"une application de migration de projet (pour migrer les fichiers\nprojets ",[155,57828,44415],{}," quand ces derniers changent)",[370,57831,57832,57833,57836,17832],{},"une librairie gérant l'éditeur de code (l'éditeur utilisé pour la\nprochaine version est ",[49,57834,52634],{"href":52632,"rel":57835},[347],[17400,57837,57838],{},[49,57839,35439],{"href":41873,"ariaDescribedBy":57840,"dataFootnoteRef":1646,"id":41875},[17406],[370,57842,57843,57844],{},"les librairies du framework ",[362,57845,43910],{},[12,57847,57848,57849,57853,57854,57859,57860,57865],{},"Vous pouvez une documentation sur l'écriture de ",[49,57850,19699],{"href":57851,"rel":57852},"http:\u002F\u002Fxinx.shadoware.org\u002Fwiki\u002FXinxPlugins",[347]," sur le site\nde ",[155,57855,57856],{},[49,57857,44415],{"href":52227,"rel":57858},[347]," ainsi qu'une documentation au format ",[49,57861,57864],{"href":57862,"rel":57863},"http:\u002F\u002Fxinx.shadoware.org\u002Fdoxygen\u002Fmain.html",[347],"Doxygen"," des\nlibrairies.",[23219,57867,57869,57872],{"className":57868,"dataFootnotes":1646},[23222],[128,57870,23227],{"className":57871,"id":17406},[23226],[367,57873,57874,57882,57893,57906,57917],{},[370,57875,57876,57878,57879],{"id":23232},[155,57877,57659],{}," étant limité à la plateforme de Microsoft, et l'application ayant pris dés le début une mauvaise direction (orienté fichier de configuration plutôt que feuille de style) ",[49,57880,23240],{"href":23236,"ariaLabel":23237,"className":57881,"dataFootnoteBackref":1646},[23239],[370,57883,57884,57886,57887,45630,57890],{"id":23243},[155,57885,57725],{}," est un éditeur de logiciel de gestion d'entreprise, dit ",[362,57888,57889],{},"ERP",[49,57891,23240],{"href":23251,"ariaLabel":23252,"className":57892,"dataFootnoteBackref":1646},[23239],[370,57894,57895,57896,57899,57900,45630,57903],{"id":43495},"Un acronyme qui contient l'acronyme même, comme ",[155,57897,57898],{},"Gnu"," pour ",[362,57901,57902],{},"Gnu Is not Unix",[49,57904,23240],{"href":43499,"ariaLabel":35010,"className":57905,"dataFootnoteBackref":1646},[23239],[370,57907,57908,57910,57911,57913,57914],{"id":43503},[155,57909,44415],{}," est un logiciel libre non parce que je n'avais pas le choix dû à la double licence de la librairie ",[362,57912,43910],{}," mais bien par choix personnelle de développer du logiciel libre sur mon temps personnel. ",[49,57915,23240],{"href":43507,"ariaLabel":43508,"className":57916,"dataFootnoteBackref":1646},[23239],[370,57918,57919,57920],{"id":43512},"QCodeEdit est un éditeur de code écrit pour Qt. Il est très performant, et possède beaucoup de fonctionnalité utile. ",[49,57921,23240],{"href":43520,"ariaLabel":43521,"className":57922,"dataFootnoteBackref":1646},[23239],{"title":1646,"searchDepth":1647,"depth":1647,"links":57924},[57925,57926,57927],{"id":43624,"depth":1652,"text":43625},{"id":57781,"depth":1652,"text":57782},{"id":17406,"depth":1647,"text":23227},"2009-02-18",{"type":9,"value":57930},[57931,57933,57939,57941],[12,57932,55197],{},[12,57934,57634,57935,57637,57937,57640],{},[155,57936,44415],{},[155,57938,44415],{},[12,57940,57643],{},[12,57942,57943],{},[121,57944],{"alt":57648,"src":57649},{},"\u002Fpost\u002Fxinx-editeur-de-feuille-de-style",{"title":57627,"description":55197},"xinx-editeur-de-feuille-de-style","posts\u002FXINX\u002F2009-02-18-xinx-editeur-de-feuille-de-style",[45988,46019,38761,46020],"zpIhRB-KvKQE1lDggeBl1D-gfvOgwt-duiaJNbpQxj4",{"id":57953,"title":57954,"author":7,"body":57955,"category":24866,"categorySlug":24867,"date":58049,"description":55197,"excerpt":58050,"extension":1764,"location":1765,"meta":58079,"navigation":1767,"path":58080,"published":1767,"seo":58081,"slug":58082,"stem":58083,"tags":58084,"timeToRead":2116,"__hash__":58085},"posts\u002Fposts\u002FLogiciels\u002F2009-02-15-debian-lenny-est-sortie.md","Debian Lenny est sortie",{"type":9,"value":57956,"toc":58046},[57957,57959,57981,57986,57995,58014,58020],[12,57958,55197],{},[12,57960,57961,57962,57965,57966,57968,57969,57972,57973,57976,179],{},"Voici la grande nouvelle de ce Week-End :Hier, le jour de la ",[155,57963,57964],{},"St\nValentin",", la distribution stable ",[155,57967,52264],{}," est passé de ",[155,57970,57971],{},"Etch","\nà ",[155,57974,57975],{},"Lenny",[17400,57977,57978],{},[49,57979,3686],{"href":17404,"ariaDescribedBy":57980,"dataFootnoteRef":1646,"id":17407},[17406],[12,57982,57983],{},[121,57984],{"alt":56576,"src":57985},"\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png",[12,57987,57988,57989,57991,57992,179],{},"Cela annonce donc non seulement une nouvelle version stable de\n",[155,57990,52264],{}," (ce qui est loin d'arriver tous les jours) mais aussi le\ndéblocage de ",[155,57993,57994],{},"sid",[12,57996,57997,57998,58000,58001,58006,58007,58009,58010,58013],{},"Attention lors des prochaines mises à jour, ",[155,57999,56705],{}," (ainsi que les\ndernières versions de tous les logiciels) arrivera doucement sur votre\nbureau",[17400,58002,58003],{},[49,58004,2547],{"href":17977,"ariaDescribedBy":58005,"dataFootnoteRef":1646,"id":17979},[17406]," si vous êtes en ",[155,58008,57994],{}," ou en ",[155,58011,58012],{},"testing",". Cela va de\nmême pour les dernières versions du noyau qui devrait arriver également.",[12,58015,58016,58017,179],{},"La prochaine, future distribution stable (qui apparaîtra sûrement d'ici\n5 ans) sera ",[155,58018,58019],{},"squeeze",[23219,58021,58023,58026],{"className":58022,"dataFootnotes":1646},[23222],[128,58024,23227],{"className":58025,"id":17406},[23226],[367,58027,58028,58037],{},[370,58029,58030,45630,58034],{"id":23232},[49,58031,58032],{"href":58032,"rel":58033},"http:\u002F\u002Fblog.ganneff.de\u002Fblog\u002F2009\u002F02\u002F14\u002Flenny-release.html",[347],[49,58035,23240],{"href":23236,"ariaLabel":23237,"className":58036,"dataFootnoteBackref":1646},[23239],[370,58038,58039,45630,58043],{"id":23243},[49,58040,58041],{"href":58041,"rel":58042},"http:\u002F\u002Fpusling.com\u002Fblog\u002F?p=94",[347],[49,58044,23240],{"href":23251,"ariaLabel":23252,"className":58045,"dataFootnoteBackref":1646},[23239],{"title":1646,"searchDepth":1647,"depth":1647,"links":58047},[58048],{"id":17406,"depth":1647,"text":23227},"2009-02-15",{"type":9,"value":58051},[58052,58054,58069,58073],[12,58053,55197],{},[12,58055,57961,58056,57965,58058,57968,58060,57972,58062,58064,179],{},[155,58057,57964],{},[155,58059,52264],{},[155,58061,57971],{},[155,58063,57975],{},[17400,58065,58066],{},[49,58067,3686],{"href":17404,"ariaDescribedBy":58068,"dataFootnoteRef":1646,"id":17407},[17406],[12,58070,58071],{},[121,58072],{"alt":56576,"src":57985},[12,58074,57988,58075,57991,58077,179],{},[155,58076,52264],{},[155,58078,57994],{},{},"\u002Fpost\u002Fdebian-lenny-est-sortie",{"title":57954,"description":55197},"debian-lenny-est-sortie","posts\u002FLogiciels\u002F2009-02-15-debian-lenny-est-sortie",[38764,43895],"EsfsGGVO7y_WEe21bi9WNtGHTe0ADRHkYFuj9haww40",{"id":58087,"title":58088,"author":7,"body":58089,"category":24866,"categorySlug":24867,"date":58709,"description":58710,"excerpt":58711,"extension":1764,"location":1765,"meta":58723,"navigation":1767,"path":58724,"published":1767,"seo":58725,"slug":58493,"stem":58726,"tags":58727,"timeToRead":2185,"__hash__":58728},"posts\u002Fposts\u002FLogiciels\u002F2008-10-22-email2trac.md","Email2Trac",{"type":9,"value":58090,"toc":58702},[58091,58100,58103,58106,58118,58126,58128,58138,58142,58147,58176,58180,58198,58232,58243,58247,58261,58326,58333,58337,58340,58358,58369,58373,58391,58395,58410,58416,58421,58427,58434,58449,58455,58475,58495,58499,58505,58526,58532,58538,58550,58575,58590,58596,58601,58620,58629,58633,58636,58655,58658,58674,58699],[12,58092,58093,58095,58096,58099],{},[362,58094,58088],{}," est un logiciel permettant de créer un ticket sous ",[362,58097,58098],{},"Trac","\nen envoyant un mail au système de suivi de ticket.",[12,58101,58102],{},"L'intérêt d'envoyer un mail au système de suivi plutôt que de se\nconnecter sur le site Internet pour faire ses rapports d'anomalie, est\nde faciliter l'accès à de nouvelles personnes.En effet, il est parfois\nplus facile d'envoyer un mail avec son client mail préféré (en\nconsidérant que le logiciel est quasiment toujours ouvert, ce qui doit\nêtre le cas en entreprise) que d'ouvrir le navigateur pour envoyer une\nanomalie de temps en temps (ouvrir le site, se logger, remplir tous les\nchamps, ...).",[12,58104,58105],{},"Ceci peut également être utilisé par un outil de rapport de bug,\nenvoyant un mail automatique (avec confirmation de l'utilisateur) au\ncrash dudit logiciel.",[12,58107,58108,58109,58112,58117],{},"Comme dit précédemment, ",[362,58110,58111],{},"EMail2Trac",[17400,58113,58114],{},[49,58115,3686],{"href":17404,"ariaDescribedBy":58116,"dataFootnoteRef":1646,"id":17407},[17406]," permet",[506,58119,58120,58123],{},[370,58121,58122],{},"d'ajouter un ticket en envoyant un mail, avec des fichiers attachés",[370,58124,58125],{},"mettre à jour un ticket (en répondant au mail du ticket par exemple)",[133,58127,38898],{"id":38897},[12,58129,58130,58131,58133,58134,58137],{},"Pour installer ",[362,58132,58088],{},", il faut télécharger le ",[49,58135,44470],{"href":58136},"ftp:\u002F\u002Fftp.sara.nl\u002Fpub\u002Foutgoing\u002Femail2trac.tar.gz"," et le\ndécompresser dans un dossier temporaire (ou utilisateur).",[27533,58139,58141],{"id":58140},"sans-gestionnaire-de-paquet","Sans gestionnaire de paquet",[12,58143,58144,58145],{},"Ensuite la compilation se fait à l'aide des habituelles ",[184,58146,44436],{},[2105,58148,58150],{"className":2317,"code":58149,"language":2319,"meta":1646,"style":1646},"configure --prefix=\u002Fusr\u002Flocal --with-mta_user=nobody --with-trac_user=www-data\nmake\nmake install\n",[184,58151,58152,58166,58170],{"__ignoreMap":1646},[2113,58153,58154,58157,58160,58163],{"class":2115,"line":2116},[2113,58155,58156],{"class":2133},"configure",[2113,58158,58159],{"class":2166}," --prefix=\u002Fusr\u002Flocal",[2113,58161,58162],{"class":2166}," --with-mta_user=nobody",[2113,58164,58165],{"class":2166}," --with-trac_user=www-data\n",[2113,58167,58168],{"class":2115,"line":1647},[2113,58169,45575],{"class":2133},[2113,58171,58172,58174],{"class":2115,"line":1652},[2113,58173,17103],{"class":2133},[2113,58175,38944],{"class":2149},[27533,58177,58179],{"id":58178},"sous-debian","Sous Debian",[12,58181,58182,58183,792,58186,58189,58190,58193,58194,58197],{},"Après avoir installé les paquets ",[184,58184,58185],{},"cdbs",[184,58187,58188],{},"autotools-dev",", vous pouvez\nexécuter les commandes suivante en ",[362,58191,58192],{},"root"," (ou la première à l'aide de\n",[362,58195,58196],{},"fakeroot",") :",[2105,58199,58201],{"className":2317,"code":58200,"language":2319,"meta":1646,"style":1646},"debian\u002Frules binary\ndpkg -i ..\u002Femail2trac_\u003Cversion>.deb\n",[184,58202,58203,58211],{"__ignoreMap":1646},[2113,58204,58205,58208],{"class":2115,"line":2116},[2113,58206,58207],{"class":2133},"debian\u002Frules",[2113,58209,58210],{"class":2149}," binary\n",[2113,58212,58213,58216,58218,58221,58223,58226,58229],{"class":2115,"line":1647},[2113,58214,58215],{"class":2133},"dpkg",[2113,58217,37383],{"class":2166},[2113,58219,58220],{"class":2149}," ..\u002Femail2trac_",[2113,58222,3109],{"class":2119},[2113,58224,58225],{"class":2149},"versio",[2113,58227,58228],{"class":2119},"n>",[2113,58230,58231],{"class":2149},".deb\n",[12,58233,58234,58235,58238,58239],{},"Pour le gestionnaire de paquets ",[362,58236,58237],{},"RPM"," vous pouvez visiter la page\n",[49,58240,58241],{"href":58241,"rel":58242},"https:\u002F\u002Fsubtrac.sara.nl\u002Foss\u002Femail2trac\u002Fwiki\u002FEmail2tracInstallation",[347],[133,58244,58246],{"id":58245},"configuration-de-lapplication","Configuration de l'application",[12,58248,58249,58250,58253,58254,46701,58259,179],{},"La deuxième étape est l'édition du fichier ",[184,58251,58252],{},"\u002Fetc\u002Femail2trac.conf",". Dans\nce fichier les informations à renseigner concerne la gestion du projet\nTrac",[17400,58255,58256],{},[49,58257,2547],{"href":17977,"ariaDescribedBy":58258,"dataFootnoteRef":1646,"id":17979},[17406],[362,58260,58088],{},[2105,58262,58264],{"className":43018,"code":58263,"language":43020,"meta":1646,"style":1646},"[DEFAULT]\nproject: \u002Fwww\u002Fsite\u002Ftrac\u002Fprojet1\nspam_level: 5\ndrop_spam: 1\nticket_update: 1\nstrip_signature: 1\n\n[projet2]\nproject: \u002Fwww\u002Fsite\u002Ftrac\u002Fprojet2\nspam_level: 5\ndrop_spam: 1\nticket_update: 1\nstrip_signature: 1\n",[184,58265,58266,58271,58276,58281,58286,58291,58296,58300,58305,58310,58314,58318,58322],{"__ignoreMap":1646},[2113,58267,58268],{"class":2115,"line":2116},[2113,58269,58270],{"class":2133},"[DEFAULT]\n",[2113,58272,58273],{"class":2115,"line":1647},[2113,58274,58275],{"class":2149},"project: \u002Fwww\u002Fsite\u002Ftrac\u002Fprojet1\n",[2113,58277,58278],{"class":2115,"line":1652},[2113,58279,58280],{"class":2149},"spam_level: 5\n",[2113,58282,58283],{"class":2115,"line":2185},[2113,58284,58285],{"class":2149},"drop_spam: 1\n",[2113,58287,58288],{"class":2115,"line":2230},[2113,58289,58290],{"class":2149},"ticket_update: 1\n",[2113,58292,58293],{"class":2115,"line":2235},[2113,58294,58295],{"class":2149},"strip_signature: 1\n",[2113,58297,58298],{"class":2115,"line":2241},[2113,58299,2125],{"emptyLinePlaceholder":1767},[2113,58301,58302],{"class":2115,"line":2246},[2113,58303,58304],{"class":2133},"[projet2]\n",[2113,58306,58307],{"class":2115,"line":2464},[2113,58308,58309],{"class":2149},"project: \u002Fwww\u002Fsite\u002Ftrac\u002Fprojet2\n",[2113,58311,58312],{"class":2115,"line":2085},[2113,58313,58280],{"class":2149},[2113,58315,58316],{"class":2115,"line":2514},[2113,58317,58285],{"class":2149},[2113,58319,58320],{"class":2115,"line":2533},[2113,58321,58290],{"class":2149},[2113,58323,58324],{"class":2115,"line":2556},[2113,58325,58295],{"class":2149},[12,58327,58328,58329,179],{},"Les différentes options dépendent de ce que vous souhaitez avoir. Les\ndifférentes possibilités sont à l'adresse ",[49,58330,52319],{"href":58331,"rel":58332},"https:\u002F\u002Fsubtrac.sara.nl\u002Foss\u002Femail2trac\u002Fwiki\u002FEmail2tracConfiguration",[347],[27533,58334,58336],{"id":58335},"spam","Spam",[12,58338,58339],{},"La gestion du SPAM se fait à l'aide des 3 lignes :",[2105,58341,58343],{"className":43018,"code":58342,"language":43020,"meta":1646,"style":1646},"spam_level: 5\ndrop_spam: 1\nspam_header: X-Spam-Level\n",[184,58344,58345,58349,58353],{"__ignoreMap":1646},[2113,58346,58347],{"class":2115,"line":2116},[2113,58348,58280],{"class":2149},[2113,58350,58351],{"class":2115,"line":1647},[2113,58352,58285],{"class":2149},[2113,58354,58355],{"class":2115,"line":1652},[2113,58356,58357],{"class":2149},"spam_header: X-Spam-Level\n",[12,58359,58360,58361,58364,58365,58368],{},"La ligne ",[184,58362,58363],{},"spam_header"," étant à configurer suivant votre politique de\ngestion des spams (utilisation de ",[362,58366,58367],{},"SpamAssassin"," ou d'un autre\nutilitaire).",[133,58370,58372],{"id":58371},"configuration-de-postfix","Configuration de Postfix",[12,58374,58375,58376,58378,58379,58381,58382,58385,58386],{},"Il faut maintenant configurer notre système de messagerie pour intégrer\n",[362,58377,58088],{},". Nous allons détailler ici la mise en place de ",[362,58380,58088],{},"\nsous ",[362,58383,58384],{},"Postfix",". Pour les autres serveurs mails vous pouvez visiter la\npage : ",[49,58387,58390],{"href":58388,"rel":58389},"http:\u002F\u002Fsubtrac.sara.nl\u002Foss\u002Femail2trac\u002Fwiki",[347],"http:\u002F\u002Fsubtrac.sara.nl\u002Foss\u002Femail2trac\u002Fwiki\u002FEmail2tracMta",[27533,58392,58394],{"id":58393},"aliases","Aliases",[12,58396,58397,58398,58401,58402,58405,58406,58409],{},"La première méthode concerne la modification du fichier des ",[362,58399,58400],{},"alias"," pour\nune configuration standard de ",[362,58403,58404],{},"postfix",". Nous allons commencer par\nmodifier le fichier ",[184,58407,58408],{},"\u002Fetc\u002Faliases"," en ajoutant la ligne suivante (pour\nle projet 1) :",[2105,58411,58414],{"className":58412,"code":58413,"language":26023},[26021],"projet1: |\"\u002Fusr\u002Fbin\u002Frun_email2trac\"\n",[184,58415,58413],{"__ignoreMap":1646},[12,58417,58418,58419,58197],{},"Il est également possible de préciser le projet sur lequel il faut\ntravailler (projet définit dans ",[184,58420,58252],{},[2105,58422,58425],{"className":58423,"code":58424,"language":26023},[26021],"projet2: |\"\u002Fusr\u002Fbin\u002Frun_email2trac --project=projet2 --ticket_prefix=devel\"\n",[184,58426,58424],{"__ignoreMap":1646},[12,58428,58429,58430,58433],{},"Dans le fichier ",[184,58431,58432],{},"\u002Fetc\u002Fpostfix\u002Fmain.cf",", il faut avoir défini\nl'emplacement des alias de la manière suivante :",[2105,58435,58437],{"className":43018,"code":58436,"language":43020,"meta":1646,"style":1646},"alias_maps = hash:\u002Fetc\u002Faliases\n",[184,58438,58439],{"__ignoreMap":1646},[2113,58440,58441,58444,58446],{"class":2115,"line":2116},[2113,58442,58443],{"class":2326},"alias_maps",[2113,58445,2153],{"class":2119},[2113,58447,58448],{"class":2149}," hash:\u002Fetc\u002Faliases\n",[12,58450,58451,58452,58454],{},"Les commandes suivantes permettent de mettre à jour le fichier des alias\net demander à ",[362,58453,58384],{}," de recharger la configuration :",[2105,58456,58458],{"className":2317,"code":58457,"language":2319,"meta":1646,"style":1646},"postaliases \u002Fetc\u002Faliases\npostfix reload\n",[184,58459,58460,58468],{"__ignoreMap":1646},[2113,58461,58462,58465],{"class":2115,"line":2116},[2113,58463,58464],{"class":2133},"postaliases",[2113,58466,58467],{"class":2149}," \u002Fetc\u002Faliases\n",[2113,58469,58470,58472],{"class":2115,"line":1647},[2113,58471,58404],{"class":2133},[2113,58473,58474],{"class":2149}," reload\n",[12,58476,58477,58478,33746,58484,58490,58491,58494],{},"Une fois ceci terminé, les mails envoyé à ",[362,58479,58480],{},[49,58481,58483],{"href":58482},"mailto:projet1@domain.org","projet1@domain.org",[362,58485,58486],{},[49,58487,58489],{"href":58488},"mailto:projet2@domaine.org","projet2@domaine.org"," seront automatiquement redirigés vers ",[362,58492,58493],{},"email2trac","\nqui s'occupera de créer le ticket dans votre système de suivi.",[27533,58496,58498],{"id":58497},"transport","Transport",[12,58500,58501,58502,58504],{},"Cette configuration concerne les personnes qui ont une configuration\nplus complexe de ",[362,58503,58404],{}," par exemple en gérant des domaines virtuels.\nIl est alors possible de définir une redirection des tickets pour un\nsous domaine.",[12,58506,58507,58508,58511,58512,58518,58519,58525],{},"Nous avons donc créé le sous-domaine ",[362,58509,58510],{},"tickets.domaine.org"," et voulons\nque les mails envoyés à ",[362,58513,58514],{},[49,58515,58517],{"href":58516},"mailto:projet1@tickets.domaine.org","projet1@tickets.domaine.org"," est\n",[362,58520,58521],{},[49,58522,58524],{"href":58523},"mailto:projet2@tickets.domaine.org","projet2@tickets.domaine.org"," soient redirigés vers email2trac.",[12,58527,22776,58528,58531],{},[184,58529,58530],{},"\u002Fetc\u002Fpostfix\u002Ftransport"," avec les\nlignes ci-dessous :",[2105,58533,58536],{"className":58534,"code":58535,"language":26023},[26021],"projet1@tickets.domaine.org  projet1:\nprojet2@tickets.domaine.org  projet2:\n",[184,58537,58535],{"__ignoreMap":1646},[12,58539,58540,58541,58543,58544,58546,58547,179],{},"Puis dans le fichier ",[184,58542,58432],{}," nous allons définir que pour\nle sous domaine ",[362,58545,58510],{},", il faut lire et utiliser le\nfichier des transports. (Attention il ne faut pas ajouter ce\nsous-domaine dans ",[184,58548,58549],{},"mydestination",[2105,58551,58553],{"className":43018,"code":58552,"language":43020,"meta":1646,"style":1646},"relay_domains = tickets.domaine.org\ntransport_maps = hash:\u002Fetc\u002Fpostfix\u002Ftransport\n",[184,58554,58555,58565],{"__ignoreMap":1646},[2113,58556,58557,58560,58562],{"class":2115,"line":2116},[2113,58558,58559],{"class":2326},"relay_domains",[2113,58561,2153],{"class":2119},[2113,58563,58564],{"class":2149}," tickets.domaine.org\n",[2113,58566,58567,58570,58572],{"class":2115,"line":1647},[2113,58568,58569],{"class":2326},"transport_maps",[2113,58571,2153],{"class":2119},[2113,58573,58574],{"class":2149}," hash:\u002Fetc\u002Fpostfix\u002Ftransport\n",[12,58576,58577,58578,58581,58582,58585,58586,58589],{},"Ensuite, il nous faut définir dans le fichier ",[184,58579,58580],{},"\u002Fetc\u002Fpostfix\u002Fmaster.cf","\nnotre nouveau mode de transport. Il n'est pas obligé d'utiliser\n",[184,58583,58584],{},"run_email2trac"," qui fait un changement d'utilisateur, car dans le\nfichier ",[184,58587,58588],{},"master.cf"," on peut directement définir l'utilisateur de\ndestination.",[2105,58591,58594],{"className":58592,"code":58593,"language":26023},[26021],"projet1      unix  -       n       n       -       -       pipe\n    flags=FR user=www-data argv=\u002Fusr\u002Fbin\u002Femail2trac\n    ${nexthop} ${user}\nprojet2      unix  -       n       n       -       -       pipe\n    flags=FR user=www-data argv=\u002Fusr\u002Fbin\u002Femail2trac\n    ${nexthop} ${user}\\r\n",[184,58595,58593],{"__ignoreMap":1646},[12,58597,58598,58599,44506],{},"Enfin il faut redémarrer ",[362,58600,58404],{},[2105,58602,58604],{"className":2317,"code":58603,"language":2319,"meta":1646,"style":1646},"postmap \u002Fetc\u002Fpostfix\u002Ftransport\npostfix reload\n",[184,58605,58606,58614],{"__ignoreMap":1646},[2113,58607,58608,58611],{"class":2115,"line":2116},[2113,58609,58610],{"class":2133},"postmap",[2113,58612,58613],{"class":2149}," \u002Fetc\u002Fpostfix\u002Ftransport\n",[2113,58615,58616,58618],{"class":2115,"line":1647},[2113,58617,58404],{"class":2133},[2113,58619,58474],{"class":2149},[12,58621,58622,58623,179],{},"Et voilà vous pouvez envoyer vos mails à\n",[362,58624,58625],{},[49,58626,58628],{"href":58627},"mailto:projet1@tickets.shadoware.org","projet1@tickets.shadoware.org",[133,58630,58632],{"id":58631},"tester","Tester",[12,58634,58635],{},"Pour tester il est possible d'envoyer un mail, comme ceci :",[2105,58637,58639],{"className":2317,"code":58638,"language":2319,"meta":1646,"style":1646},"mail projet1@tickets.shadoware.org \u003C ticket.txt\n",[184,58640,58641],{"__ignoreMap":1646},[2113,58642,58643,58646,58649,58652],{"class":2115,"line":2116},[2113,58644,58645],{"class":2133},"mail",[2113,58647,58648],{"class":2149}," projet1@tickets.shadoware.org",[2113,58650,58651],{"class":2119}," \u003C ",[2113,58653,58654],{"class":2149},"ticket.txt\n",[12,58656,58657],{},"En cas de problème, vous pouvez vérifier que email2trac est bien\nconfiguré avec la commande :",[2105,58659,58661],{"className":2317,"code":58660,"language":2319,"meta":1646,"style":1646},"email2trac --project=projet1 \u003C ticket.txt\n",[184,58662,58663],{"__ignoreMap":1646},[2113,58664,58665,58667,58670,58672],{"class":2115,"line":2116},[2113,58666,58493],{"class":2133},[2113,58668,58669],{"class":2166}," --project=projet1",[2113,58671,58651],{"class":2119},[2113,58673,58654],{"class":2149},[23219,58675,58677,58680],{"className":58676,"dataFootnotes":1646},[23222],[128,58678,23227],{"className":58679,"id":17406},[23226],[367,58681,58682,58690],{},[370,58683,58684,45630,58687],{"id":23232},[49,58685,58390],{"href":58388,"rel":58686},[347],[49,58688,23240],{"href":23236,"ariaLabel":23237,"className":58689,"dataFootnoteBackref":1646},[23239],[370,58691,58692,45630,58696],{"id":23243},[49,58693,58694],{"href":58694,"rel":58695},"http:\u002F\u002Ftrac.edgewall.org\u002F",[347],[49,58697,23240],{"href":23251,"ariaLabel":23252,"className":58698,"dataFootnoteBackref":1646},[23239],[3358,58700,58701],{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}",{"title":1646,"searchDepth":1647,"depth":1647,"links":58703},[58704,58705,58706,58707,58708],{"id":38897,"depth":1652,"text":38898},{"id":58245,"depth":1652,"text":58246},{"id":58371,"depth":1652,"text":58372},{"id":58631,"depth":1652,"text":58632},{"id":17406,"depth":1647,"text":23227},"2008-10-22","Email2Trac est un logiciel permettant de créer un ticket sous Trac\nen envoyant un mail au système de suivi de ticket.",{"type":9,"value":58712},[58713,58719,58721],[12,58714,58715,58095,58717,58099],{},[362,58716,58088],{},[362,58718,58098],{},[12,58720,58102],{},[12,58722,58105],{},{},"\u002Fpost\u002Femail2trac",{"title":58088,"description":58710},"posts\u002FLogiciels\u002F2008-10-22-email2trac",[38764,24897],"llLWYwDr2WQzswEZDA9vDYeLWcGqHIhabMKl6VUmCmo",{"id":58730,"title":58731,"author":7,"body":58732,"category":24866,"categorySlug":24867,"date":58861,"description":58862,"excerpt":58863,"extension":1764,"location":1765,"meta":58878,"navigation":1767,"path":58879,"published":1767,"seo":58880,"slug":58881,"stem":58882,"tags":58883,"timeToRead":2116,"__hash__":58884},"posts\u002Fposts\u002FLogiciels\u002F2008-10-19-nouvelles-fonctionnalites-de-qt-designer-45.md","Nouvelles fonctionnalités de Qt Designer 4.5",{"type":9,"value":58733,"toc":58858},[58734,58748,58751,58757,58763,58766,58772,58775,58817,58823,58826,58831,58834,58840],[12,58735,58736,58737,58742,58743,179],{},"Sur ",[362,58738,58739,58741],{},[56390,58740,57740],{},"Nokia Labs Blogs",", Friedelann Kleint nous\nprésente aujourd'hui les nouvelles fonctionnalités de Qt Designer 4.5",[17400,58744,58745],{},[49,58746,3686],{"href":17404,"ariaDescribedBy":58747,"dataFootnoteRef":1646,"id":17407},[17406],[12,58749,58750],{},"Au menu, on peut trouver l'ajout de filtre sur l'éditeur de propriétés\nainsi que sur l'inspecteur d'objet (Ce qui peut permettre de\nsélectionner un composant et d'en modifier les propriétés plus\nrapidement).",[12,58752,58753],{},[121,58754],{"alt":58755,"src":58756},"objectinspector","\u002FLogiciels\u002Fnouvelles-fonctionnalites-de-qt-designer-45\u002Fobjectinspector.png",[12,58758,58759],{},[121,58760],{"alt":58761,"src":58762},"propertyeditorfilter","\u002FLogiciels\u002Fnouvelles-fonctionnalites-de-qt-designer-45\u002Fpropertyeditorfilter.png",[12,58764,58765],{},"L'arrangement de la boite à outil est également différent :",[12,58767,58768],{},[121,58769],{"alt":58770,"src":58771},"widgetbox_iconmode","\u002FLogiciels\u002Fnouvelles-fonctionnalites-de-qt-designer-45\u002Fwidgetbox_iconmode.png",[12,58773,58774],{},"De plus il est maintenant possible de :",[506,58776,58777,58788,58798,58805,58812,58815],{},[370,58778,58779,58780,58783,58784,58787],{},"Sélectionner l'objet ancêtre d'un ",[362,58781,58782],{},"widget"," en sélectionnant ",[362,58785,58786],{},"Select\nancestors"," du menu contextuel.",[370,58789,58790,58791,58794,58795,58797],{},"Changer le ",[362,58792,58793],{},"Layout"," sans casser et reconstruire le ",[362,58796,58793],{}," de la\nfeuille",[370,58799,58800,58801,58804],{},"Ajouter facilement une nouvelle ligne au layout (L'action ",[362,58802,58803],{},"Add form\nlayout row"," ouvrant la boite de dialogue ci-dessous)",[370,58806,58807,58808,58811],{},"Créer des Assistants (",[362,58809,58810],{},"Wizard",") linéaires",[370,58813,58814],{},"Désactiver la traduction de certaine chaîne.",[370,58816,3699],{},[12,58818,58819],{},[121,58820],{"alt":58821,"src":58822},"formlayout","\u002FLogiciels\u002Fnouvelles-fonctionnalites-de-qt-designer-45\u002Fformlayout.png",[12,58824,58825],{},"Enfin, pour terminer, comme le dit Friedemann",[1886,58827,58828],{},[12,58829,58830],{},"The general focus of the 4.5 release is on stability and performance.",[12,58832,58833],{},"Soit : Le but principal de la version 4.5 est la stabilité et les\nperformances.",[12,58835,58836,58837,179],{},"Pour plus de précision, je vous conseille d'aller voir la page d'origine\net\u002Fou de télécharger la ",[362,58838,58839],{},"snapshot",[23219,58841,58843,58846],{"className":58842,"dataFootnotes":1646},[23222],[128,58844,23227],{"className":58845,"id":17406},[23226],[367,58847,58848],{},[370,58849,58850,58851,45630,58855],{"id":23232},"Référence : ",[49,58852,58853],{"href":58853,"rel":58854},"http:\u002F\u002Flabs.trolltech.com\u002Fblogs\u002F2008\u002F10\u002F16\u002Fnew-features-of-qt-designer-45\u002F",[347],[49,58856,23240],{"href":23236,"ariaLabel":23237,"className":58857,"dataFootnoteBackref":1646},[23239],{"title":1646,"searchDepth":1647,"depth":1647,"links":58859},[58860],{"id":17406,"depth":1647,"text":23227},"2008-10-19","Sur TrolltechNokia Labs Blogs, Friedelann Kleint nous\nprésente aujourd'hui les nouvelles fonctionnalités de Qt Designer 4.51.",{"type":9,"value":58864},[58865,58876],[12,58866,58736,58867,58742,58871,179],{},[362,58868,58869,58741],{},[56390,58870,57740],{},[17400,58872,58873],{},[49,58874,3686],{"href":17404,"ariaDescribedBy":58875,"dataFootnoteRef":1646,"id":17407},[17406],[12,58877,58750],{},{},"\u002Fpost\u002Fnouvelles-fonctionnalites-de-qt-designer-45",{"title":58731,"description":58862},"nouvelles-fonctionnalites-de-qt-designer-45","posts\u002FLogiciels\u002F2008-10-19-nouvelles-fonctionnalites-de-qt-designer-45",[38761],"nm-5nCtyIY7ytRcB2NS0B1KwbYlYkEMuv_OrrHl_gZI",{"id":58886,"title":58887,"author":7,"body":58888,"category":24866,"categorySlug":24867,"date":58976,"description":58977,"excerpt":57478,"extension":1764,"location":1765,"meta":58978,"navigation":1767,"path":58979,"published":1767,"seo":58980,"slug":58981,"stem":58982,"tags":58983,"timeToRead":2116,"__hash__":58984},"posts\u002Fposts\u002FLogiciels\u002F2008-10-06-qt-442-puis-qt-443-sont-sorties.md","QT 4.4.2 puis QT 4.4.3 sont sorties",{"type":9,"value":58889,"toc":58973},[58890,58895,58912,58933,58936,58950],[12,58891,58892,58893,179],{},"En une semaine sont sortie les versions 4.4.2 et 4.4.3 de ",[362,58894,43910],{},[12,58896,58897,58898,58901,58902,58906,58907,58909,58910,179],{},"La version 4.4.2 corrige de nombreux bugs (dont des fuites mémoires sur\nla partie ",[362,58899,58900],{},"XmlPatterns","). Vous pouvez trouver la listes des changements\n",[49,58903,58905],{"href":58904},"\u002FLogiciels\u002Fqt-442-puis-qt-443-sont-sorties\u002Fchanges-4.4.2","à cette adresse",", ou dans l'archive ",[362,58908,43910],{}," se trouvant sur le site\n",[362,58911,57740],{},[12,58913,58914,58915,58920,58921,46701,58923,58925,58926,58928,58929,3938],{},"La version 4.4.3",[17400,58916,58917],{},[49,58918,3686],{"href":17404,"ariaDescribedBy":58919,"dataFootnoteRef":1646,"id":17407},[17406]," modifie chaque entête de fichiers, les icônes,\n... pour refléter l'achat de ",[362,58922,57740],{},[362,58924,57331],{},". Par la même\noccasion le site de ",[362,58927,57740],{}," a été complètement refait\n(",[49,58930,58931],{"href":58931,"rel":58932},"http:\u002F\u002Ftrolltech.com",[347],[12,58934,58935],{},"Donc si vous avez la version 4.4.2, il n'est pas nécessaire de récupérer\nla 4.4.3 (sauf si vous souhaiter absolument la dernière version au nom\nde Nokia), par contre si vous êtes encore à la version 4.4.0 ou 4.4.1,\nil peut être intéressant d'installer la dernière version (suivant les\nfonctionnalités que vous utilisez).",[12,58937,58938,58939,58941,58942,58944,58945,41797],{},"Enfin, la question, sans réponse, est, est-ce que le site de ",[362,58940,57740],{},"\net de ",[362,58943,57331],{}," vont fusionner ou vont-il rester séparer",[17400,58946,58947],{},[49,58948,2547],{"href":17977,"ariaDescribedBy":58949,"dataFootnoteRef":1646,"id":17979},[17406],[23219,58951,58953,58956],{"className":58952,"dataFootnotes":1646},[23222],[128,58954,23227],{"className":58955,"id":17406},[23226],[367,58957,58958,58967],{},[370,58959,58960,45630,58964],{"id":23232},[49,58961,58962],{"href":58962,"rel":58963},"http:\u002F\u002Ftrolltech.com\u002Fabout\u002Fnews\u002Fqt-4.4.3-released",[347],[49,58965,23240],{"href":23236,"ariaLabel":23237,"className":58966,"dataFootnoteBackref":1646},[23239],[370,58968,58969,58970],{"id":23243},"ce dernier est tout de même plus pratique, je trouve ",[49,58971,23240],{"href":23251,"ariaLabel":23252,"className":58972,"dataFootnoteBackref":1646},[23239],{"title":1646,"searchDepth":1647,"depth":1647,"links":58974},[58975],{"id":17406,"depth":1647,"text":23227},"2008-10-06","En une semaine sont sortie les versions 4.4.2 et 4.4.3 de Qt.",{},"\u002Fpost\u002Fqt-442-puis-qt-443-sont-sorties",{"title":58887,"description":58977},"qt-442-puis-qt-443-sont-sorties","posts\u002FLogiciels\u002F2008-10-06-qt-442-puis-qt-443-sont-sorties",[38761],"TNEywWz-ZtPa5b6pfMVBEL9D98bBfXfM5LASdSKmk7E",{"id":58986,"title":58987,"author":7,"body":58988,"category":2057,"categorySlug":2058,"date":59999,"description":60000,"excerpt":60001,"extension":1764,"location":1765,"meta":60013,"navigation":1767,"path":60014,"published":1767,"seo":60015,"slug":60016,"stem":60017,"tags":60018,"timeToRead":2235,"__hash__":60019},"posts\u002Fposts\u002FProgrammation\u002F2008-09-08-paquet-debian-et-qt.md","Paquet Debian et Qt",{"type":9,"value":58989,"toc":59993},[58990,58998,59004,59008,59017,59040,59044,59048,59057,59063,59066,59169,59175,59188,59192,59201,59236,59239,59276,59279,59311,59315,59318,59324,59330,59340,59348,59443,59458,59462,59474,59480,59486,59500,59780,59783,59787,59791,59796,59809,59814,59826,59829,59842,59845,59849,59852,59858,59862,59865,59871,59890,59894,59901,59907,59911,59917,59921,59924,59961,59990],[12,58991,58992,58993,58995,58996,3938],{},"A titre personnel je fabrique quelques programmes en Qt. Comme j'utilise\nun système ",[362,58994,52264],{},", j'ai cherché à fabriquer des paquets pour mon\nsystème (plus pour le plaisir qu'autre chose, car la plus grande partie\nde mes utilisateurs sont sous ",[362,58997,52271],{},[12,58999,59000,59001,59003],{},"Ce billet explique la création de paquet ",[362,59002,52264],{}," pour des\napplications Qt 4 utilisant QMake",[133,59005,59007],{"id":59006},"installation-des-paquets","Installation des paquets",[12,59009,59010,59011,59013,59014,179],{},"Pour la création des paquets pour des programmes utilisant ",[184,59012,43014],{},", il\nfaut ",[184,59015,59016],{},"cdbs, dh_make, dpkg-buildpackage",[2105,59018,59020],{"className":2317,"code":59019,"language":2319,"meta":1646,"style":1646},"aptitude install cdbs dh-make fakeroot devscripts\n",[184,59021,59022],{"__ignoreMap":1646},[2113,59023,59024,59026,59028,59031,59034,59037],{"class":2115,"line":2116},[2113,59025,52598],{"class":2133},[2113,59027,52601],{"class":2149},[2113,59029,59030],{"class":2149}," cdbs",[2113,59032,59033],{"class":2149}," dh-make",[2113,59035,59036],{"class":2149}," fakeroot",[2113,59038,59039],{"class":2149}," devscripts\n",[133,59041,59043],{"id":59042},"création-du-paquet","Création du paquet",[27533,59045,59047],{"id":59046},"préparation","Préparation",[12,59049,59050,59051,59054,59055,179],{},"Vous pouvez donc choisir un de vos programmes que vous voulez\nempaqueter. Pour cela vous allez commencer par nettoyer l'arborescence\nde votre projet pour retirer les fichiers de sauvegarde ainsi que les\nfichiers de construction (",[184,59052,59053],{},"*~ *.o",", ...). Ce petit nettoyage va\npermettre d'avoir un paquet source ",[362,59056,44450],{},[12,59058,59059,59060,179],{},"Il faut que vous placiez les sources dans un dossier du nom de\n",[184,59061,59062],{},"\u003Cprogramme>-\u003Cversion>",[12,59064,59065],{},"Voici un exemple de fichier projet :",[2105,59067,59070],{"className":59068,"code":59069,"language":43014,"meta":1646,"style":1646},"language-qmake shiki shiki-themes one-dark-pro","TARGET = qgenconfig\nTEMPLATE = app\n\nCONFIG += exceptions warn_on qt\nQT += xml\n\nHEADERS += src\u002Floadconfigimpl.h \\\\\n    src\u002Fsetupfile.h \\\\\n    src\u002Fconfigfile.h \\\\\n    src\u002Fp_configfile.h\nSOURCES += src\u002Floadconfigimpl.cpp \\\\\n    src\u002Fsetupfile.cpp \\\\\n    src\u002Fmain.cpp \\\\\n    src\u002Fconfigfile.cpp\nFORMS = ui\u002Floadconfig.ui\n\nDESTDIR = bin\nMOC_DIR = build\nOBJECTS_DIR = build\nUI_DIR = build\n",[184,59071,59072,59077,59082,59086,59091,59096,59100,59105,59110,59115,59120,59125,59130,59135,59140,59145,59149,59154,59159,59164],{"__ignoreMap":1646},[2113,59073,59074],{"class":2115,"line":2116},[2113,59075,59076],{},"TARGET = qgenconfig\n",[2113,59078,59079],{"class":2115,"line":1647},[2113,59080,59081],{},"TEMPLATE = app\n",[2113,59083,59084],{"class":2115,"line":1652},[2113,59085,2125],{"emptyLinePlaceholder":1767},[2113,59087,59088],{"class":2115,"line":2185},[2113,59089,59090],{},"CONFIG += exceptions warn_on qt\n",[2113,59092,59093],{"class":2115,"line":2230},[2113,59094,59095],{},"QT += xml\n",[2113,59097,59098],{"class":2115,"line":2235},[2113,59099,2125],{"emptyLinePlaceholder":1767},[2113,59101,59102],{"class":2115,"line":2241},[2113,59103,59104],{},"HEADERS += src\u002Floadconfigimpl.h \\\\\n",[2113,59106,59107],{"class":2115,"line":2246},[2113,59108,59109],{},"    src\u002Fsetupfile.h \\\\\n",[2113,59111,59112],{"class":2115,"line":2464},[2113,59113,59114],{},"    src\u002Fconfigfile.h \\\\\n",[2113,59116,59117],{"class":2115,"line":2085},[2113,59118,59119],{},"    src\u002Fp_configfile.h\n",[2113,59121,59122],{"class":2115,"line":2514},[2113,59123,59124],{},"SOURCES += src\u002Floadconfigimpl.cpp \\\\\n",[2113,59126,59127],{"class":2115,"line":2533},[2113,59128,59129],{},"    src\u002Fsetupfile.cpp \\\\\n",[2113,59131,59132],{"class":2115,"line":2556},[2113,59133,59134],{},"    src\u002Fmain.cpp \\\\\n",[2113,59136,59137],{"class":2115,"line":2569},[2113,59138,59139],{},"    src\u002Fconfigfile.cpp\n",[2113,59141,59142],{"class":2115,"line":2575},[2113,59143,59144],{},"FORMS = ui\u002Floadconfig.ui\n",[2113,59146,59147],{"class":2115,"line":2596},[2113,59148,2125],{"emptyLinePlaceholder":1767},[2113,59150,59151],{"class":2115,"line":3192},[2113,59152,59153],{},"DESTDIR = bin\n",[2113,59155,59156],{"class":2115,"line":3213},[2113,59157,59158],{},"MOC_DIR = build\n",[2113,59160,59161],{"class":2115,"line":3236},[2113,59162,59163],{},"OBJECTS_DIR = build\n",[2113,59165,59166],{"class":2115,"line":3248},[2113,59167,59168],{},"UI_DIR = build\n",[12,59170,59171,59172,59174],{},"Pour générer le ",[184,59173,44430],{},", vous pouvez faire comme d'habitude :",[2105,59176,59178],{"className":2317,"code":59177,"language":2319,"meta":1646,"style":1646},"qmake-qt4 qgenconfig.pro\n",[184,59179,59180],{"__ignoreMap":1646},[2113,59181,59182,59185],{"class":2115,"line":2116},[2113,59183,59184],{"class":2133},"qmake-qt4",[2113,59186,59187],{"class":2149}," qgenconfig.pro\n",[27533,59189,59191],{"id":59190},"création-des-scripts","Création des scripts",[12,59193,59194,59195,59197,59198,179],{},"Pour créer les paquets, il faut un ensemble de script dans le dossier\n",[184,59196,38764],{},". Pour se faciliter la vie, il est possible de créer les scripts\navec ",[184,59199,59200],{},"dh_make",[2105,59202,59204],{"className":2317,"code":59203,"language":2319,"meta":1646,"style":1646},"DEBFULLNAME=\"Ulrich Van Den Hekke\"\ndh_make -e ulrich.vdh@shadoware.org -n -s -c gpl\n",[184,59205,59206,59216],{"__ignoreMap":1646},[2113,59207,59208,59211,59213],{"class":2115,"line":2116},[2113,59209,59210],{"class":2330},"DEBFULLNAME",[2113,59212,2335],{"class":2334},[2113,59214,59215],{"class":2149},"\"Ulrich Van Den Hekke\"\n",[2113,59217,59218,59220,59223,59226,59228,59230,59233],{"class":2115,"line":1647},[2113,59219,59200],{"class":2133},[2113,59221,59222],{"class":2166}," -e",[2113,59224,59225],{"class":2149}," ulrich.vdh@shadoware.org",[2113,59227,25042],{"class":2166},[2113,59229,37395],{"class":2166},[2113,59231,59232],{"class":2166}," -c",[2113,59234,59235],{"class":2149}," gpl\n",[12,59237,59238],{},"Voici la description de la liste des paramètres :",[506,59240,59241,59246,59252,59261,59267],{},[370,59242,59243,59245],{},[184,59244,59210],{}," indique le nom du mainteneur.",[370,59247,59248,59251],{},[184,59249,59250],{},"-e"," Permet de préciser l'email (suivis de l'email).",[370,59253,59254,59257,59258,59260],{},[184,59255,59256],{},"-n"," Produit un paquet natif pour ",[362,59259,52264],{}," (quand l'auteur fait\nses paquets lui-même).",[370,59262,59263,59266],{},[184,59264,59265],{},"-s"," Indique que le paquet ne va contenir qu'un simple exécutable\n(si vous voulez faire plusieurs paquets, il suffit de ne pas mettre\ncette option).",[370,59268,59269,59272,59273,3938],{},[184,59270,59271],{},"-c"," Indique la licence d'utilisation (ici ",[184,59274,59275],{},"gpl",[12,59277,59278],{},"Vous pouvez ensuite supprimer les fichiers exemples (mais pas\nnécessairement si vous voulez les étudier).",[2105,59280,59282],{"className":2317,"code":59281,"language":2319,"meta":1646,"style":1646},"rm debian\u002F*.ex  debian\u002F*.EX  debian\u002Fdocs  debian\u002Fdirs\n",[184,59283,59284],{"__ignoreMap":1646},[2113,59285,59286,59289,59292,59294,59297,59300,59302,59305,59308],{"class":2115,"line":2116},[2113,59287,59288],{"class":2133},"rm",[2113,59290,59291],{"class":2149}," debian\u002F",[2113,59293,2676],{"class":2414},[2113,59295,59296],{"class":2149},".ex",[2113,59298,59299],{"class":2149},"  debian\u002F",[2113,59301,2676],{"class":2414},[2113,59303,59304],{"class":2149},".EX",[2113,59306,59307],{"class":2149},"  debian\u002Fdocs",[2113,59309,59310],{"class":2149},"  debian\u002Fdirs\n",[34199,59312,59314],{"id":59313},"paquet-simple","Paquet simple",[12,59316,59317],{},"Nous allons commencer par créer un paquet simple. Cela signifie qu'il\nn'y aura qu'un seul exécutable dans le paquet (pas de librairie, ...).",[12,59319,58429,59320,59323],{},[184,59321,59322],{},"control",", il faut que vous renseignez les informations\ndu paquet que vous allez créer.",[2105,59325,59328],{"className":59326,"code":59327,"language":26023},[26021],"Source: qgenconfig\nSection: devel\nPriority: extra\nMaintainer: Ulrich Van Den Hekke \u003Culrich.vdh@shadoware.org>\nBuild-Depends: debhelper (>= 7)\nStandards-Version: 3.7.3\nHomepage: http:\u002F\u002Fprojects.shadoware.org\u002Fqgenconfig\n\nPackage: qgenconfig\nArchitecture: any\nDepends: ${shlibs:Depends}, ${misc:Depends}\nDescription: Create a config class in Qt after loading a XML file.\n    Load an XML description file of a settings object and create a class\n    that can be used to load and save information in a QSettings class. To\n    load information structure are used.\n",[184,59329,59327],{"__ignoreMap":1646},[12,59331,59332,59333,59336,59337,59339],{},"Dans le paquet ci-dessus, on créait un paquet binaire et un paquet\nsource. Les variables de dépendance (dans ",[184,59334,59335],{},"Depends",") sont remplacées\nautomatiquement à la génération du paquet. La description est celle\nécrite dans ",[184,59338,52598],{},". La première ligne est la description courte, les\nsuivantes (précédées d'un espace) sont la description longue.",[12,59341,59342,59343,59345,59346,44506],{},"Ensuite on va remplacer le fichier ",[184,59344,58207],{}," en utilisant ",[184,59347,58185],{},[2105,59349,59351],{"className":2317,"code":59350,"language":2319,"meta":1646,"style":1646},"#!\u002Fusr\u002Fbin\u002Fmake -f\ninclude \u002Fusr\u002Fshare\u002Fcdbs\u002F1\u002Frules\u002Fdebhelper.mk\ninclude \u002Fusr\u002Fshare\u002Fcdbs\u002F1\u002Fclass\u002Fqmake.mk\nQMAKE=qmake-qt4\n\ninstall\u002Fqgenconfig::\n    mkdir $(DEB_DESTDIR)\u002Fusr\u002F\n    mkdir $(DEB_DESTDIR)\u002Fusr\u002Fbin\u002F\n    cp $(CURDIR)\u002Fbin\u002Fqgenconfig $(DEB_DESTDIR)\u002Fusr\u002Fbin\u002F\n",[184,59352,59353,59358,59365,59372,59382,59386,59391,59407,59420],{"__ignoreMap":1646},[2113,59354,59355],{"class":2115,"line":2116},[2113,59356,59357],{"class":2396},"#!\u002Fusr\u002Fbin\u002Fmake -f\n",[2113,59359,59360,59362],{"class":2115,"line":1647},[2113,59361,44928],{"class":2133},[2113,59363,59364],{"class":2149}," \u002Fusr\u002Fshare\u002Fcdbs\u002F1\u002Frules\u002Fdebhelper.mk\n",[2113,59366,59367,59369],{"class":2115,"line":1652},[2113,59368,44928],{"class":2133},[2113,59370,59371],{"class":2149}," \u002Fusr\u002Fshare\u002Fcdbs\u002F1\u002Fclass\u002Fqmake.mk\n",[2113,59373,59374,59377,59379],{"class":2115,"line":2185},[2113,59375,59376],{"class":2330},"QMAKE",[2113,59378,2335],{"class":2334},[2113,59380,59381],{"class":2149},"qmake-qt4\n",[2113,59383,59384],{"class":2115,"line":2230},[2113,59385,2125],{"emptyLinePlaceholder":1767},[2113,59387,59388],{"class":2115,"line":2235},[2113,59389,59390],{"class":2133},"install\u002Fqgenconfig::\n",[2113,59392,59393,59396,59399,59402,59404],{"class":2115,"line":2241},[2113,59394,59395],{"class":2133},"    mkdir",[2113,59397,59398],{"class":2119}," $(",[2113,59400,59401],{"class":2133},"DEB_DESTDIR",[2113,59403,17832],{"class":2119},[2113,59405,59406],{"class":2149},"\u002Fusr\u002F\n",[2113,59408,59409,59411,59413,59415,59417],{"class":2115,"line":2246},[2113,59410,59395],{"class":2133},[2113,59412,59398],{"class":2119},[2113,59414,59401],{"class":2133},[2113,59416,17832],{"class":2119},[2113,59418,59419],{"class":2149},"\u002Fusr\u002Fbin\u002F\n",[2113,59421,59422,59425,59427,59430,59432,59435,59437,59439,59441],{"class":2115,"line":2464},[2113,59423,59424],{"class":2133},"    cp",[2113,59426,59398],{"class":2119},[2113,59428,59429],{"class":2133},"CURDIR",[2113,59431,17832],{"class":2119},[2113,59433,59434],{"class":2149},"\u002Fbin\u002Fqgenconfig",[2113,59436,59398],{"class":2119},[2113,59438,59401],{"class":2133},[2113,59440,17832],{"class":2119},[2113,59442,59419],{"class":2149},[12,59444,59445,59446,59449,59454,59455,3938],{},"En premier lieu on définit les variables pour la compilation, puis\nensuite on définit les opérations d'installations dans la cible\n",[184,59447,59448],{},"install\u002Fqgenconfig",[17400,59450,59451],{},[49,59452,3686],{"href":17404,"ariaDescribedBy":59453,"dataFootnoteRef":1646,"id":17407},[17406],".Dans les opérations, vous allez copier\nvotre exécutable dans le dossier de génération de debian (dossier\n",[184,59456,59457],{},"debian\u002F\u003Ccible>",[34199,59459,59461],{"id":59460},"paquet-multiple","Paquet multiple",[12,59463,59464,59465,59467,59468,59470,59471,24994],{},"Créer plusieurs paquets à partir d'une même source avec ",[184,59466,58185],{}," est assez\nsimple. Il suffit de rajouter des cibles, correspondantes au fichier\n",[184,59469,59322],{},", dans le fichier ",[184,59472,59473],{},"rules",[12,59475,59476,59477,59479],{},"Dans le fichier de ",[184,59478,59322],{}," on va avoir plusieurs paquets binaires pour\nun seul paquet source.",[2105,59481,59484],{"className":59482,"code":59483,"language":26023},[26021],"Source: xinx\nSection: devel\nPriority: extra\nMaintainer: Ulrich Van Den Hekke \u003Cxinx@shadoware.org>\nBuild-Depends: debhelper (>= 7)\nStandards-Version: 3.7.3\nHomepage: http:\u002F\u002Fxinx.shadoware.org\u002F\n\nPackage: xinx\nSection: devel\nArchitecture: any\nDepends: ${shlibs:Depends}, ${misc:Depends}, xinx-scripts (>= 0.7.2.0)\nRecommends: xinx-plugins-services, xinx-plugins-cvs, xinx-plugins-svn\nSuggests: xinx-doc\nDescription: XSL\u002FJS\u002FHTML editor for Generix\n    XINX is an editor of XSL stylesheet, JavaScript, Cascading Style Sheet.\n    This editor is used with egx.\n\nPackage: xinx-doc\nSection: doc\nArchitecture: all\nDepends: xinx (>= 0.7.2.0)\nDescription: Documentation for xinx\n    Technical Documentation of XINX\n",[184,59485,59483],{"__ignoreMap":1646},[12,59487,59488,59489,59491,59492,59494,59499],{},"Nous allons définir dans le fichier ",[184,59490,59473],{}," les différentes cibles que\nnous avons dans le fichier ",[184,59493,59322],{},[17400,59495,59496],{},[49,59497,2547],{"href":17977,"ariaDescribedBy":59498,"dataFootnoteRef":1646,"id":17979},[17406],"^.",[2105,59501,59503],{"className":2317,"code":59502,"language":2319,"meta":1646,"style":1646},"#!\u002Fusr\u002Fbin\u002Fmake -f\ninclude \u002Fusr\u002Fshare\u002Fcdbs\u002F1\u002Frules\u002Fdebhelper.mk\ninclude \u002Fusr\u002Fshare\u002Fcdbs\u002F1\u002Fclass\u002Fqmake.mk\nQMAKE=qmake-qt4 project.pro\n\ninstall\u002Fxinx::\n    mkdir $(CURDIR)\u002Fdebian\u002Fxinx\u002Fusr\u002F\n    mkdir $(CURDIR)\u002Fdebian\u002Fxinx\u002Fusr\u002Fbin\u002F\n    cp $(CURDIR)\u002Fxinx\u002Fxinx $(CURDIR)\u002Fdebian\u002Fxinx\u002Fusr\u002Fbin\u002F\n    cp $(CURDIR)\u002Fxinxprojectwizard\u002Fxinxprojectwizard $(CURDIR)\u002Fdebian\u002Fxinx\u002Fusr\u002Fbin\u002F\n\n    mkdir $(CURDIR)\u002Fdebian\u002Fxinx\u002Fusr\u002Flib\u002F\n    cp -a $(CURDIR)\u002Flibxinx\u002Flibsharedxinx* $(CURDIR)\u002Fdebian\u002Fxinx\u002Fusr\u002Flib\u002F\n    cp -a $(CURDIR)\u002Fcomponents\u002Flibxinxcmp* $(CURDIR)\u002Fdebian\u002Fxinx\u002Fusr\u002Flib\u002F\n\ninstall\u002Fxinx-doc::\n    mkdir $(CURDIR)\u002Fdebian\u002Fxinx-doc\u002Fusr\u002F\n    mkdir $(CURDIR)\u002Fdebian\u002Fxinx-doc\u002Fusr\u002Fshare\u002F\n    mkdir $(CURDIR)\u002Fdebian\u002Fxinx-doc\u002Fusr\u002Fshare\u002Fdoc\u002F\n    mkdir $(CURDIR)\u002Fdebian\u002Fxinx-doc\u002Fusr\u002Fshare\u002Fdoc\u002Fxinx-doc\u002F\n    cp -a $(CURDIR)\u002Fdoc\u002F* $(CURDIR)\u002Fdebian\u002Fxinx-doc\u002Fusr\u002Fshare\u002Fdoc\u002Fxinx-doc\u002F\n    rm -rf $(CURDIR)\u002Fdebian\u002Fxinx-doc\u002Fusr\u002Fshare\u002Fdoc\u002Fxinx-doc\u002Fhtml\u002F.svn\n",[184,59504,59505,59509,59515,59521,59532,59536,59541,59554,59567,59588,59609,59613,59626,59652,59677,59681,59686,59699,59712,59725,59738,59763],{"__ignoreMap":1646},[2113,59506,59507],{"class":2115,"line":2116},[2113,59508,59357],{"class":2396},[2113,59510,59511,59513],{"class":2115,"line":1647},[2113,59512,44928],{"class":2133},[2113,59514,59364],{"class":2149},[2113,59516,59517,59519],{"class":2115,"line":1652},[2113,59518,44928],{"class":2133},[2113,59520,59371],{"class":2149},[2113,59522,59523,59525,59527,59529],{"class":2115,"line":2185},[2113,59524,59376],{"class":2330},[2113,59526,2335],{"class":2334},[2113,59528,59184],{"class":2149},[2113,59530,59531],{"class":2133}," project.pro\n",[2113,59533,59534],{"class":2115,"line":2230},[2113,59535,2125],{"emptyLinePlaceholder":1767},[2113,59537,59538],{"class":2115,"line":2235},[2113,59539,59540],{"class":2133},"install\u002Fxinx::\n",[2113,59542,59543,59545,59547,59549,59551],{"class":2115,"line":2241},[2113,59544,59395],{"class":2133},[2113,59546,59398],{"class":2119},[2113,59548,59429],{"class":2133},[2113,59550,17832],{"class":2119},[2113,59552,59553],{"class":2149},"\u002Fdebian\u002Fxinx\u002Fusr\u002F\n",[2113,59555,59556,59558,59560,59562,59564],{"class":2115,"line":2246},[2113,59557,59395],{"class":2133},[2113,59559,59398],{"class":2119},[2113,59561,59429],{"class":2133},[2113,59563,17832],{"class":2119},[2113,59565,59566],{"class":2149},"\u002Fdebian\u002Fxinx\u002Fusr\u002Fbin\u002F\n",[2113,59568,59569,59571,59573,59575,59577,59580,59582,59584,59586],{"class":2115,"line":2464},[2113,59570,59424],{"class":2133},[2113,59572,59398],{"class":2119},[2113,59574,59429],{"class":2133},[2113,59576,17832],{"class":2119},[2113,59578,59579],{"class":2149},"\u002Fxinx\u002Fxinx",[2113,59581,59398],{"class":2119},[2113,59583,59429],{"class":2133},[2113,59585,17832],{"class":2119},[2113,59587,59566],{"class":2149},[2113,59589,59590,59592,59594,59596,59598,59601,59603,59605,59607],{"class":2115,"line":2085},[2113,59591,59424],{"class":2133},[2113,59593,59398],{"class":2119},[2113,59595,59429],{"class":2133},[2113,59597,17832],{"class":2119},[2113,59599,59600],{"class":2149},"\u002Fxinxprojectwizard\u002Fxinxprojectwizard",[2113,59602,59398],{"class":2119},[2113,59604,59429],{"class":2133},[2113,59606,17832],{"class":2119},[2113,59608,59566],{"class":2149},[2113,59610,59611],{"class":2115,"line":2514},[2113,59612,2125],{"emptyLinePlaceholder":1767},[2113,59614,59615,59617,59619,59621,59623],{"class":2115,"line":2533},[2113,59616,59395],{"class":2133},[2113,59618,59398],{"class":2119},[2113,59620,59429],{"class":2133},[2113,59622,17832],{"class":2119},[2113,59624,59625],{"class":2149},"\u002Fdebian\u002Fxinx\u002Fusr\u002Flib\u002F\n",[2113,59627,59628,59630,59633,59635,59637,59639,59642,59644,59646,59648,59650],{"class":2115,"line":2556},[2113,59629,59424],{"class":2133},[2113,59631,59632],{"class":2166}," -a",[2113,59634,59398],{"class":2119},[2113,59636,59429],{"class":2133},[2113,59638,17832],{"class":2119},[2113,59640,59641],{"class":2149},"\u002Flibxinx\u002Flibsharedxinx",[2113,59643,2676],{"class":2414},[2113,59645,59398],{"class":2119},[2113,59647,59429],{"class":2133},[2113,59649,17832],{"class":2119},[2113,59651,59625],{"class":2149},[2113,59653,59654,59656,59658,59660,59662,59664,59667,59669,59671,59673,59675],{"class":2115,"line":2569},[2113,59655,59424],{"class":2133},[2113,59657,59632],{"class":2166},[2113,59659,59398],{"class":2119},[2113,59661,59429],{"class":2133},[2113,59663,17832],{"class":2119},[2113,59665,59666],{"class":2149},"\u002Fcomponents\u002Flibxinxcmp",[2113,59668,2676],{"class":2414},[2113,59670,59398],{"class":2119},[2113,59672,59429],{"class":2133},[2113,59674,17832],{"class":2119},[2113,59676,59625],{"class":2149},[2113,59678,59679],{"class":2115,"line":2575},[2113,59680,2125],{"emptyLinePlaceholder":1767},[2113,59682,59683],{"class":2115,"line":2596},[2113,59684,59685],{"class":2133},"install\u002Fxinx-doc::\n",[2113,59687,59688,59690,59692,59694,59696],{"class":2115,"line":3192},[2113,59689,59395],{"class":2133},[2113,59691,59398],{"class":2119},[2113,59693,59429],{"class":2133},[2113,59695,17832],{"class":2119},[2113,59697,59698],{"class":2149},"\u002Fdebian\u002Fxinx-doc\u002Fusr\u002F\n",[2113,59700,59701,59703,59705,59707,59709],{"class":2115,"line":3213},[2113,59702,59395],{"class":2133},[2113,59704,59398],{"class":2119},[2113,59706,59429],{"class":2133},[2113,59708,17832],{"class":2119},[2113,59710,59711],{"class":2149},"\u002Fdebian\u002Fxinx-doc\u002Fusr\u002Fshare\u002F\n",[2113,59713,59714,59716,59718,59720,59722],{"class":2115,"line":3236},[2113,59715,59395],{"class":2133},[2113,59717,59398],{"class":2119},[2113,59719,59429],{"class":2133},[2113,59721,17832],{"class":2119},[2113,59723,59724],{"class":2149},"\u002Fdebian\u002Fxinx-doc\u002Fusr\u002Fshare\u002Fdoc\u002F\n",[2113,59726,59727,59729,59731,59733,59735],{"class":2115,"line":3248},[2113,59728,59395],{"class":2133},[2113,59730,59398],{"class":2119},[2113,59732,59429],{"class":2133},[2113,59734,17832],{"class":2119},[2113,59736,59737],{"class":2149},"\u002Fdebian\u002Fxinx-doc\u002Fusr\u002Fshare\u002Fdoc\u002Fxinx-doc\u002F\n",[2113,59739,59740,59742,59744,59746,59748,59750,59753,59755,59757,59759,59761],{"class":2115,"line":4899},[2113,59741,59424],{"class":2133},[2113,59743,59632],{"class":2166},[2113,59745,59398],{"class":2119},[2113,59747,59429],{"class":2133},[2113,59749,17832],{"class":2119},[2113,59751,59752],{"class":2149},"\u002Fdoc\u002F",[2113,59754,2676],{"class":2414},[2113,59756,59398],{"class":2119},[2113,59758,59429],{"class":2133},[2113,59760,17832],{"class":2119},[2113,59762,59737],{"class":2149},[2113,59764,59765,59768,59771,59773,59775,59777],{"class":2115,"line":1777},[2113,59766,59767],{"class":2133},"    rm",[2113,59769,59770],{"class":2166}," -rf",[2113,59772,59398],{"class":2119},[2113,59774,59429],{"class":2133},[2113,59776,17832],{"class":2119},[2113,59778,59779],{"class":2149},"\u002Fdebian\u002Fxinx-doc\u002Fusr\u002Fshare\u002Fdoc\u002Fxinx-doc\u002Fhtml\u002F.svn\n",[12,59781,59782],{},"Ici on génère deux paquets, le paquet binaire et la documentation.",[27533,59784,59786],{"id":59785},"génération-du-paquet","Génération du paquet",[34199,59788,59790],{"id":59789},"mise-à-jour-des-informations-du-paquets","Mise à jour des informations du paquets",[506,59792,59793],{},[370,59794,59795],{},"Pour modifier le changelog sur la version courante",[2105,59797,59799],{"className":2317,"code":59798,"language":2319,"meta":1646,"style":1646},"dch -a\n",[184,59800,59801],{"__ignoreMap":1646},[2113,59802,59803,59806],{"class":2115,"line":2116},[2113,59804,59805],{"class":2133},"dch",[2113,59807,59808],{"class":2166}," -a\n",[506,59810,59811],{},[370,59812,59813],{},"Pour créer une nouvelle version",[2105,59815,59817],{"className":2317,"code":59816,"language":2319,"meta":1646,"style":1646},"dch -i\n",[184,59818,59819],{"__ignoreMap":1646},[2113,59820,59821,59823],{"class":2115,"line":2116},[2113,59822,59805],{"class":2133},[2113,59824,59825],{"class":2166}," -i\n",[34199,59827,59043],{"id":59828},"création-du-paquet-1",[2105,59830,59832],{"className":2317,"code":59831,"language":2319,"meta":1646,"style":1646},"dpkg-buildpackage -rfakeroot\n",[184,59833,59834],{"__ignoreMap":1646},[2113,59835,59836,59839],{"class":2115,"line":2116},[2113,59837,59838],{"class":2133},"dpkg-buildpackage",[2113,59840,59841],{"class":2166}," -rfakeroot\n",[12,59843,59844],{},"L'application va signer le paquet s'il trouve la clé assignée à\nl'utilisateur.",[133,59846,59848],{"id":59847},"création-dun-dépôt","Création d'un dépôt",[12,59850,59851],{},"Après avoir créé des paquets, il peut-être intéressant de créer un dépôt\npour installer ces paquets. voici la marche à suivre pour créer le\ndépôt.",[12,59853,59854,59855,3938],{},"Sur votre serveur internet (nous ne parlerons pas ici de la mise à\ndisposition sur Internet), vous allez devoir créer un dossier contenant\nvotre référentiel (exemple : ",[184,59856,59857],{},"\u002Fwww\u002Fsites\u002Fapt",[27533,59859,59861],{"id":59860},"création-de-larborescence","Création de l'arborescence",[12,59863,59864],{},"Vous allez devoir créer une arborescence comme suite :",[2105,59866,59869],{"className":59867,"code":59868,"language":26023},[26021],"dists\n    +- main\n        |- binary-i386\n        |- binary-amd64\n        +- source\n",[184,59870,59868],{"__ignoreMap":1646},[12,59872,59873,59874,59877,59878,932,59881,59884,59885,16994,59888,179],{},"Parmi les fichiers générés vous allez devoir mettre le fichier ",[184,59875,59876],{},".deb","\ndans le dossier binaire, et les fichiers ",[184,59879,59880],{},".changes",[184,59882,59883],{},".dsc",", et\n",[184,59886,59887],{},".tar.gz",[184,59889,44470],{},[27533,59891,59893],{"id":59892},"création-dun-fichier-apt-ftparchiveconf","Création d'un fichier apt-ftparchive.conf",[12,59895,59896,59897,59900],{},"Nous allons commencer par créer un fichier ",[184,59898,59899],{},"apt-ftparchive.conf"," dans\nlequel nous allons définir les fichiers à créer.",[2105,59902,59905],{"className":59903,"code":59904,"language":26023},[26021],"Dir {\n    ArchiveDir \".\";\n    CacheDir \".\";\n};\n\nTree \"dists\u002Funstable\" {\n    Sections \"main\";\n    Architectures \"amd64 source\";\n};\n\nBinDirectory \"dists\u002Funstable\u002Fmain\u002Fbinary-amd64\" {\n    Packages \"dists\u002Funstable\u002Fmain\u002Fbinary-amd64\u002FPackages\";\n    Contents \"dists\u002Funstable\u002FContents-amd64\";\n    SrcPackages \"dists\u002Funstable\u002Fmain\u002Fsource\u002FSources\";\n};\n",[184,59906,59904],{"__ignoreMap":1646},[27533,59908,59910],{"id":59909},"création-du-fichier-apt-unstable-releaseconf","Création du fichier apt-unstable-release.conf",[2105,59912,59915],{"className":59913,"code":59914,"language":26023},[26021],"APT::FTPArchive::Release::Origin    \"shadoware.org\";\nAPT::FTPArchive::Release::Label     \"shadoware.org\";\nAPT::FTPArchive::Release::Suite     \"unstable\";\nAPT::FTPArchive::Release::Codename   \"unstable\";\nAPT::FTPArchive::Release::Architectures  \"amd64 source\";\nAPT::FTPArchive::Release::Components \"main\";\nAPT::FTPArchive::Release::Description    \"Paquets du site Shadoware.Org\";\n",[184,59916,59914],{"__ignoreMap":1646},[27533,59918,59920],{"id":59919},"création-du-fichier-update-archivesh","Création du fichier update-archive.sh",[12,59922,59923],{},"Ce fichier est celui que l'on doit exécuter pour la génération du\ncontenu de l'arborescence :",[2105,59925,59927],{"className":2317,"code":59926,"language":2319,"meta":1646,"style":1646},"apt-ftparchive generate apt-ftparchive.conf\napt-ftparchive -c apt-unstable-release.conf release dists\u002Funstable > dists\u002Funstable\u002FRelease\n",[184,59928,59929,59940],{"__ignoreMap":1646},[2113,59930,59931,59934,59937],{"class":2115,"line":2116},[2113,59932,59933],{"class":2133},"apt-ftparchive",[2113,59935,59936],{"class":2149}," generate",[2113,59938,59939],{"class":2149}," apt-ftparchive.conf\n",[2113,59941,59942,59944,59946,59949,59952,59955,59958],{"class":2115,"line":1647},[2113,59943,59933],{"class":2133},[2113,59945,59232],{"class":2166},[2113,59947,59948],{"class":2149}," apt-unstable-release.conf",[2113,59950,59951],{"class":2149}," release",[2113,59953,59954],{"class":2149}," dists\u002Funstable",[2113,59956,59957],{"class":2119}," > ",[2113,59959,59960],{"class":2149},"dists\u002Funstable\u002FRelease\n",[23219,59962,59964,59967],{"className":59963,"dataFootnotes":1646},[23222],[128,59965,23227],{"className":59966,"id":17406},[23226],[367,59968,59969,59977],{},[370,59970,59971,59972,57685,59974],{"id":23232},"D'autres cibles sont disponibles (cf la doc de ",[184,59973,58185],{},[49,59975,23240],{"href":23236,"ariaLabel":23237,"className":59976,"dataFootnoteBackref":1646},[23239],[370,59978,59979,59980,59983,59984,43549,59987],{"id":23243},"A cause d'un bogue, ou d'une mauvaise utilisation, la variable ",[184,59981,59982],{},"$(DEB_DESTDIR)"," ne me ramenait pas la bonne valeur, je l'ai donc remplacée par ",[184,59985,59986],{},"$(CURDIR)\u002Fdebian",[49,59988,23240],{"href":23251,"ariaLabel":23252,"className":59989,"dataFootnoteBackref":1646},[23239],[3358,59991,59992],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}",{"title":1646,"searchDepth":1647,"depth":1647,"links":59994},[59995,59996,59997,59998],{"id":59006,"depth":1652,"text":59007},{"id":59042,"depth":1652,"text":59043},{"id":59847,"depth":1652,"text":59848},{"id":17406,"depth":1647,"text":23227},"2008-09-08","A titre personnel je fabrique quelques programmes en Qt. Comme j'utilise\nun système Gnu\u002FDebian, j'ai cherché à fabriquer des paquets pour mon\nsystème (plus pour le plaisir qu'autre chose, car la plus grande partie\nde mes utilisateurs sont sous MS\u002FWindows).",{"type":9,"value":60002},[60003,60009],[12,60004,58992,60005,58995,60007,3938],{},[362,60006,52264],{},[362,60008,52271],{},[12,60010,59000,60011,59003],{},[362,60012,52264],{},{},"\u002Fpost\u002Fpaquet-debian-et-qt",{"title":58987,"description":60000},"paquet-debian-et-qt","posts\u002FProgrammation\u002F2008-09-08-paquet-debian-et-qt",[38764,38761],"UfskBB2vEveCnHyHdgHEScfMRoTQzydi1tuF-O_x8Ik",{"id":60021,"title":60022,"author":7,"body":60023,"category":24866,"categorySlug":24867,"date":60043,"description":26097,"excerpt":57478,"extension":1764,"location":1765,"meta":60044,"navigation":1767,"path":60045,"published":1767,"seo":60046,"slug":60047,"stem":60048,"tags":60049,"timeToRead":2116,"__hash__":60050},"posts\u002Fposts\u002FLogiciels\u002F2008-02-18-mises-a-jour-gnu-debian-recentes.md","Mises à jour Gnu\u002FDebian récentes",{"type":9,"value":60024,"toc":60041},[60025,60027,60030,60038],[12,60026,26097],{},[12,60028,60029],{},"La distribution GNU\u002FDebian propose une nouvelle mise à jour pour sa\nversion etch. Les points importants que je remarque, sont",[506,60031,60032,60035],{},[370,60033,60034],{},"la correction de la faille sur le noyau Linux pour toutes les\nmachines serveurs : Veillez à bien mettre à jour votre noyau car\nmême si vous ne proposez pas de compte aux utilisateurs, l'escalade\nde privilèges peut intervenir dans une faille de sécurité de votre\nsite Internet (cf de l'injection de code).",[370,60036,60037],{},"la mise à jour de KDE, qui désormais permet à Konqueror de charger\nle plugin flash non libre.",[12,60039,60040],{},"Bien sûr, cette mise à jour propose également d'autres corrections de\nBug et mise à jour de sécurité, ainsi que la suppression de certains\npaquets (soit trop miné\u002Fbuggé, soit propriétaire). On peut par ailleurs\nretrouver le plugin flash dans les backports",{"title":1646,"searchDepth":1647,"depth":1647,"links":60042},[],"2008-02-18",{},"\u002Fpost\u002Fmises-a-jour-gnu-debian-recentes",{"title":60022,"description":26097},"mises-a-jour-gnu-debian-recentes","posts\u002FLogiciels\u002F2008-02-18-mises-a-jour-gnu-debian-recentes",[38764],"GM6W6GPMbuROhvLrd5-UfzUhAflxM9pIgnr1TX720ek",{"id":60052,"title":60053,"author":7,"body":60054,"category":2057,"categorySlug":2058,"date":60129,"description":60130,"excerpt":60131,"extension":1764,"location":1765,"meta":60152,"navigation":1767,"path":60153,"published":1767,"seo":60154,"slug":60155,"stem":60156,"tags":60157,"timeToRead":2116,"__hash__":60158},"posts\u002Fposts\u002FProgrammation\u002F2008-01-21-memo-qt.md","Mémo Qt",{"type":9,"value":60055,"toc":60125},[60056,60064,60067,60085,60089,60091,60096,60098,60101,60107,60109,60115,60119],[12,60057,60058,60059,179],{},"Ceci est un petit mémo pour me permettre de ne pas oublier quelques\nastuces lors de la programmation avec Qt. En parlant de cela, je me suis\ncommandé le livre suivant : ",[49,60060,60063],{"href":60061,"rel":60062},"http:\u002F\u002Fwww.amazon.fr\u002FGUI-Programming-Qt4-Jasmin-Blanchette\u002Fdp\u002F0132354160\u002Fref=sr_11_1?ie=UTF8&qid=1199972515&sr=11-1",[347],"C++ GUI Programming with Qt4",[133,60065,53019],{"id":60066},"qabstractitemmodel",[506,60068,60069,60072],{},[370,60070,60071],{},"Lors de l'insertion (avec beginInsertRows()), les éléments insérés\nne doivent pas contenir de sous éléments (rowCount() == 0) sous\npeine de causer des plantages.",[370,60073,60074,60075,792,60080],{},"Un model de donnée interne, pouvant être utilisé avec\nQAbstractItemModel pour afficher une arborescence et étant capable\nde se rafraîchir à la lecture d'un fichier, se trouve dans les\nfichiers ",[49,60076,60079],{"href":60077,"rel":60078},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FXINX\u002Fxinx\u002Ffiles\u002F53bd629daf731398437a8c2f328e7b4015a013ca\u002Flibxinx\u002Ffilecontentstructure.cpp",[347],"filecontentstructure.cpp",[49,60081,60084],{"href":60082,"rel":60083},"http:\u002F\u002Fhg.shadoware.org\u002FSoftware\u002FXINX\u002Fxinx\u002Ffiles\u002F53bd629daf731398437a8c2f328e7b4015a013ca\u002Flibxinx\u002Ffilecontentstructure.h",[347],"filecontentstructure.h",[133,60086,60088],{"id":60087},"cmake-et-qt","CMake et Qt",[27533,60090,45513],{"id":45512},[2105,60092,60094],{"className":60093,"code":45516,"language":26023},[26021],[184,60095,45516],{"__ignoreMap":1646},[27533,60097,45332],{"id":45331},[34199,60099,60100],{"id":60100},"dynamique",[2105,60102,60105],{"className":60103,"code":60104,"language":26023},[26021],"add_definitions(${QT_DEFINITIONS})\nadd_definitions(-DQT_PLUGIN)\nadd_definitions(-DQT_NO_DEBUG)\nadd_definitions(-DQT_SHARED)\nadd_library(webplugin SHARED ${webplugin_SRCS} ${webplugin_MOC_SRCS})\n",[184,60106,60104],{"__ignoreMap":1646},[34199,60108,7668],{"id":7668},[2105,60110,60113],{"className":60111,"code":60112,"language":26023},[26021],"?\n",[184,60114,60112],{"__ignoreMap":1646},[27533,60116,60118],{"id":60117},"compilation-de-fichier-rc-pour-windows","Compilation de fichier RC pour Windows",[2105,60120,60123],{"className":60121,"code":60122,"language":26023},[26021],"if(WIN32)\n    add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}\u002Fxinx_ico.obj\n           COMMAND windres.exe -I${CMAKE_CURRENT_SOURCE_DIR} -o ${CMAKE_CURRENT_BINARY_DIR}\u002Fxinx_ico.obj -i${CMAKE_CURRENT_SOURCE_DIR}\u002F${xinx_RCS}\n    )\n    set(xinx_RESS ${CMAKE_CURRENT_BINARY_DIR}\u002Fxinx_ico.obj)\nendif(WIN32)\n",[184,60124,60122],{"__ignoreMap":1646},{"title":1646,"searchDepth":1647,"depth":1647,"links":60126},[60127,60128],{"id":60066,"depth":1652,"text":53019},{"id":60087,"depth":1652,"text":60088},"2008-01-21","Ceci est un petit mémo pour me permettre de ne pas oublier quelques\nastuces lors de la programmation avec Qt. En parlant de cela, je me suis\ncommandé le livre suivant : C++ GUI Programming with Qt4.",{"type":9,"value":60132},[60133,60138,60140],[12,60134,60058,60135,179],{},[49,60136,60063],{"href":60061,"rel":60137},[347],[133,60139,53019],{"id":60066},[506,60141,60142,60144],{},[370,60143,60071],{},[370,60145,60074,60146,792,60149],{},[49,60147,60079],{"href":60077,"rel":60148},[347],[49,60150,60084],{"href":60082,"rel":60151},[347],{},"\u002Fpost\u002Fmemo-qt",{"title":60053,"description":60130},"memo-qt","posts\u002FProgrammation\u002F2008-01-21-memo-qt",[38761],"LurBRat5GKyW_n9EoVty1Vi5WkYd8uVKKIfb9AwdIE4",{"id":60160,"title":60161,"author":7,"body":60162,"category":43667,"categorySlug":43863,"date":60296,"description":1646,"excerpt":60297,"extension":1764,"location":1765,"meta":60323,"navigation":1767,"path":60324,"published":1767,"seo":60325,"slug":60326,"stem":60327,"tags":60328,"timeToRead":1652,"__hash__":60329},"posts\u002Fposts\u002FKMDAlert\u002F2007-08-17-kmdalert-logiciel-de-surveillance-raid.md","KMDAlert - Logiciel de surveillance de périphériques RAID",{"type":9,"value":60163,"toc":60288},[60164,60166,60172,60178,60184,60195,60198,60201,60204,60206,60220,60223,60226,60229,60231,60234,60257,60259,60261,60266,60268,60281,60283,60285],[128,60165,43625],{"id":43624},[12,60167,60168,60169,60171],{},"Possédant chez moi des disques durs en RAID logiciel ",[362,60170,57765],{},", j'ai\ntoujours ressenti le besoin d'avoir une application graphique minimale\npour m'avertir des changements d'états du RAID. (Par exemple un disque\ndur est défectueux, le RAID est dégradé, le RAID est en reconstruction).\nEt, par exemple, dans le cas de la reconstruction être averti à la fin\nde la reconstruction.",[12,60173,60174,60175,60177],{},"KMDAlert est un petit logiciel ",[362,60176,57765],{}," que j'ai écrit, et permettant\nd'afficher des informations sur les volumes RAID (gérés à l'aide du\nmodule noyau md-mod) dans la boîte à miniature (SystemTray).",[12,60179,60180,60181,58197],{},"L'icône change suivant l'état du RAID (",[48221,60182,60183],{},"ATTENTION : Les icônes\nchoisies sont moches, si un graphiste veut me proposer d'autres icônes,\nje reste ouvert aux propositions",[506,60185,60186,60189,60192],{},[370,60187,60188],{},"dégradé",[370,60190,60191],{},"en cours de synchronisation",[370,60193,60194],{},"ou tout simplement Ok.",[12,60196,60197],{},"Des messages de notifications informent aussi l'utilisateur du\nchangement d'état d'un disque sur le RAID (par exemple le disque est en\néchec, supprimé, ajouté, la synchronisation démarre ou est terminée).",[12,60199,60200],{},"Le logiciel est actuellement uniquement disponible pour KDE 3. Je\nn'ai pas encore pris le temps de la ré-écrire.",[12,60202,60203],{},"Bien que cette application a été écrite dans mon cas particulier (un\nRAID 1) je me suis dit qu'elle devrait fonctionner pour les autres types\nde RAID et intéresser d'autres personnes.",[128,60205,43661],{"id":43660},[12,60207,60208,60209,60211,60212,43676,60214,932,60216,43683,60218,43686],{},"Pour fonctionner KMDAlert nécessite que le dossier ",[184,60210,43671],{}," soit monté et\nque ",[184,60213,43675],{},[184,60215,43679],{},[184,60217,43682],{},[184,60219,43667],{},[12,60221,60222],{},"L'application a été développée sur Gnu\u002FDebian Etch donc sur un KDE 3.5\navec un noyau 2.6.18.",[12,60224,60225],{},"KMDAlert ne nécessite pas de Inotify. KMDAlert lit le contenu de\nplusieurs fichiers dans le dossier \u002Fsys toutes les 6 secondes pour être\ninformé de la modification de l'état du périphérique. En effet sur le\nnoyau 2.6.18 que je possède, Inotify n'est pas averti des modifications\nfaites sur le système de fichier \u002Fsys. Les dates de ces fichiers ne sont\npas non plus modifiées. Si quelqu'un a une autre méthode à me proposer,\nje reste ouvert à toutes propositions.",[12,60227,60228],{},"KMDAlert est une application écrite en utilisant les librairies de KDE,\nmais devrait tout de même fonctionner sous Gnome. Les messages de\nnotification utilisent donc le système de notification de KDE (bien que\nje n'utilise pas toutes ses possibilités).",[128,60230,43731],{"id":43730},[12,60232,60233],{},"La compilation et l'installation de KMDAlert se fait à l'aide du trio\nhabituel :",[2105,60235,60237],{"className":2317,"code":60236,"language":2319,"meta":1646,"style":1646},".\u002Fconfigure --prefix=\u002Fusr\u002Flocal\u002F\nmake\nmake install\n",[184,60238,60239,60247,60251],{"__ignoreMap":1646},[2113,60240,60241,60244],{"class":2115,"line":2116},[2113,60242,60243],{"class":2133},".\u002Fconfigure",[2113,60245,60246],{"class":2166}," --prefix=\u002Fusr\u002Flocal\u002F\n",[2113,60248,60249],{"class":2115,"line":1647},[2113,60250,45575],{"class":2133},[2113,60252,60253,60255],{"class":2115,"line":1652},[2113,60254,17103],{"class":2133},[2113,60256,38944],{"class":2149},[12,60258,43808],{},[128,60260,1075],{"id":43811},[12,60262,43814,60263],{},[49,60264,43819],{"href":43817,"rel":60265},[347],[128,60267,43823],{"id":43822},[506,60269,60270,60275],{},[370,60271,43828,60272,179],{},[49,60273,43833],{"href":43831,"rel":60274},[347],[370,60276,60277,60278,179],{},"Le dépot officiel pour télécharger les sources et participer :\n",[49,60279,43841],{"href":43839,"rel":60280},[347],[128,60282,43848],{"id":43847},[12,60284,43851],{},[3358,60286,60287],{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":1646,"searchDepth":1647,"depth":1647,"links":60289},[60290,60291,60292,60293,60294,60295],{"id":43624,"depth":1647,"text":43625},{"id":43660,"depth":1647,"text":43661},{"id":43730,"depth":1647,"text":43731},{"id":43811,"depth":1647,"text":1075},{"id":43822,"depth":1647,"text":43823},{"id":43847,"depth":1647,"text":43848},"2007-08-17",{"type":9,"value":60298},[60299,60301,60305,60309,60313,60321],[128,60300,43625],{"id":43624},[12,60302,60168,60303,60171],{},[362,60304,57765],{},[12,60306,60174,60307,60177],{},[362,60308,57765],{},[12,60310,60180,60311,58197],{},[48221,60312,60183],{},[506,60314,60315,60317,60319],{},[370,60316,60188],{},[370,60318,60191],{},[370,60320,60194],{},[12,60322,60197],{},{},"\u002Fpost\u002Fkmdalert-logiciel-de-surveillance-raid",{"title":60161,"description":1646},"kmdalert-logiciel-de-surveillance-raid","posts\u002FKMDAlert\u002F2007-08-17-kmdalert-logiciel-de-surveillance-raid",[43895,38761,43863,43896],"PNNDW_nxCgCKf7lh8X7JuMHGPhrQnNwu5riYqVB4k-U",{"id":60331,"title":60332,"author":7,"body":60333,"category":61333,"categorySlug":61334,"date":61335,"description":61336,"excerpt":61337,"extension":1764,"location":1765,"meta":61383,"navigation":1767,"path":61384,"published":1767,"seo":61385,"slug":61386,"stem":61387,"tags":61388,"timeToRead":2514,"__hash__":61389},"posts\u002Fposts\u002FSysAdmin\u002F2007-08-04-debian-etch-cle-usb.md","Debian etch - Clé USB",{"type":9,"value":60334,"toc":61325},[60335,60342,60351,60354,60381,60390,60404,60408,60412,60418,60452,60454,60466,60472,60486,60490,60494,60497,60513,60532,60545,60551,60554,60636,60640,60648,60846,60850,60857,60872,60876,60879,60883,60890,60896,60914,60925,60930,60948,60952,60965,60971,60977,60981,60987,60993,60999,61003,61016,61051,61065,61069,61113,61127,61133,61137,61140,61190,61194,61209,61227,61237,61243,61249,61255,61259,61271,61277,61281,61284,61290,61322],[12,60336,60337,60338,60341],{},"Il peut être parfois utile de se déplacer ici et là avec une\ndistribution Linux ",[362,60339,60340],{},"clé en main"," sans se trimbaler toujours avec un\nordinateur portable ou un CD.",[12,60343,60344,60345,60350],{},"La clé USB devient alors très utile atteignant aujourd'hui des tailles\nde l'ordre de 2G pour pas trop cher",[17400,60346,60347],{},[49,60348,3686],{"href":17404,"ariaDescribedBy":60349,"dataFootnoteRef":1646,"id":17407},[17406],". De petite taille, elle\npeut être utilisée dans des endroits ayant des ordinateurs avec un\nsystème d'exploitation propriétaire (ou un autre système libre, ou même\npas sans système) mais permettant de démarrer sur un périphérique\nexterne (une clé USB par exemple).",[12,60352,60353],{},"L'utilité peut-être variable :",[506,60355,60356,60359,60372,60375,60378],{},[370,60357,60358],{},"Surfer avec une plus grande tranquillité",[370,60360,60361,60362,932,60365,932,60368,60371],{},"Utiliser ses logiciels favoris ( ",[184,60363,60364],{},"Amarok",[184,60366,60367],{},"KMyMoney",[184,60369,60370],{},"KDevelop",",\n... )",[370,60373,60374],{},"Utiliser une interface graphique, bien sûr, meilleure.",[370,60376,60377],{},"Utiliser dans un système embarqué.",[370,60379,60380],{},"Tester les ordinateurs portables dans les magasins",[12,60382,60383,60384,60389],{},"J'ai trouvé différents sites, tel que ",[49,60385,60388],{"href":60386,"rel":60387},"http:\u002F\u002Fwww.breizh-ardente.fr\u002Fdotclear\u002Findex.php?2006\u002F12\u002F03\u002F57-usbuntu-ou-comment-installer-ubuntu-sur-un-disque-clef-usb-en-mode-persistant",[347],"Ubuntu en mode persistant","\npermettant d'installer une distribution Ubuntu sur une clé USB ou un\ndisque dur USB.L'inconvénient de cette méthode est qu'il n'est pas\npossible d'installer uniquement ce que l'on souhaite partant\nobligatoirement d'une base LiveCD.",[12,60391,60392,60393,792,60398,60403],{},"Ensuite j'ai trouvé d'autres sites, tels que ",[49,60394,60397],{"href":60395,"rel":60396},"http:\u002F\u002Fferaga.com\u002Fnode\u002F25",[347],"Howto Install a Debian\nGNU\u002FLinux system onto a USB flash thumb drive",[49,60399,60402],{"href":60400,"rel":60401},"http:\u002F\u002Fwww.debian-administration.org\u002Farticles\u002F179",[347],"Installing Debian\nonto USB flash media with everything encrypted."," dont je me suis\ninspiré pour écrire ce tutoriel.",[133,60405,60407],{"id":60406},"préparation-de-la-clé","Préparation de la clé",[27533,60409,60411],{"id":60410},"modules","Modules",[12,60413,60414,60415,60417],{},"Afin de pouvoir accéder à la clé USB, il faut bien évidemment avoir\nchargé sur sa machine ",[362,60416,57765],{}," les modules adéquates :",[2105,60419,60421],{"className":2317,"code":60420,"language":2319,"meta":1646,"style":1646},"modprobe ehci_hcd\nmodprobe ohci_hcd\nmodprobe usbhid\nmodprobe usb_storage\n",[184,60422,60423,60431,60438,60445],{"__ignoreMap":1646},[2113,60424,60425,60428],{"class":2115,"line":2116},[2113,60426,60427],{"class":2133},"modprobe",[2113,60429,60430],{"class":2149}," ehci_hcd\n",[2113,60432,60433,60435],{"class":2115,"line":1647},[2113,60434,60427],{"class":2133},[2113,60436,60437],{"class":2149}," ohci_hcd\n",[2113,60439,60440,60442],{"class":2115,"line":1652},[2113,60441,60427],{"class":2133},[2113,60443,60444],{"class":2149}," usbhid\n",[2113,60446,60447,60449],{"class":2115,"line":2185},[2113,60448,60427],{"class":2133},[2113,60450,60451],{"class":2149}," usb_storage\n",[27533,60453,24866],{"id":24867},[12,60455,60456,60457,2495,60459,60462,60463],{},"Nous allons considérer que nous sommes sur un système à base de\n",[362,60458,52264],{},[362,60460,60461],{},"Ubuntu"," devrait pouvoir faire l'affaire). Si vous avez un\nautre système, il vous faut trouver un moyen d'installer le programme\n",[184,60464,60465],{},"debootstrap",[12,60467,60468,60469,60471],{},"Sur un système ",[362,60470,52264],{},", il vous suffit de taper la commande, si le\nprogramme n'est pas déjà installé :",[2105,60473,60475],{"className":2317,"code":60474,"language":2319,"meta":1646,"style":1646},"aptitude install debootstrap\n",[184,60476,60477],{"__ignoreMap":1646},[2113,60478,60479,60481,60483],{"class":2115,"line":2116},[2113,60480,52598],{"class":2133},[2113,60482,52601],{"class":2149},[2113,60484,60485],{"class":2149}," debootstrap\n",[133,60487,60489],{"id":60488},"installation-du-système-de-base","Installation du système de base",[27533,60491,60493],{"id":60492},"partitionnement-de-la-clé-usb","Partitionnement de la clé USB",[12,60495,60496],{},"En premier lieu il faut effectuer le partitionnement de la clé USB.\nSelon sa taille et aussi de la quantité de logiciel que vous voulez\ninstaller il faudra prévoir plus ou moins de place.",[12,60498,60499,60500,932,60503,932,60506,932,60509,60512],{},"Pour le partitionnement vous pouvez utiliser votre utilitaire favori\n(",[184,60501,60502],{},"parted",[184,60504,60505],{},"qtparted",[184,60507,60508],{},"fdisk",[184,60510,60511],{},"cfdisk",", ...). Le partitionnement que\nj'ai effectué pour ma propre clé est le suivant :",[367,60514,60515,60521,60526],{},[370,60516,60517,60518],{},"50Mo pour la partition ",[155,60519,60520],{},"\u002Fboot",[370,60522,60523,60524],{},"1,5Go pour la partition ",[155,60525,2520],{},[370,60527,60528,60529],{},"le reste pour la partition ",[155,60530,60531],{},"\u002Fhome",[12,60533,60534,60535,60537,60538,60540,60541,60544],{},"La partition ",[155,60536,60531],{}," a été séparée afin de pouvoir mettre la partie\n",[155,60539,2520],{}," de la clé USB en ",[362,60542,60543],{},"lecture seule"," (étape que je n'ai pas encore\neffectuée).",[2105,60546,60549],{"className":60547,"code":60548,"language":26023},[26021],"Disk \u002Fdev\u002Fusb\u002Fk2g: 2024 MB, 2024275968 bytes\n63 heads, 62 sectors\u002Ftrack, 1012 cylinders\nUnits = cylinders of 3906 * 512 = 1999872 bytes\n\n        Device Boot      Start         End      Blocks   Id  System\n\u002Fdev\u002Fsda1                   1          26       50747   83  Linux\n\u002Fdev\u002Fsda2                  27         827     1564353   83  Linux\n\u002Fdev\u002Fsda3                 828        1012      361305   83  Linux\n",[184,60550,60548],{"__ignoreMap":1646},[12,60552,60553],{},"Une fois les partitions créées vous pouvez les formater pour les\nutiliser.",[2105,60555,60557],{"className":2317,"code":60556,"language":2319,"meta":1646,"style":1646},"mke2fs \u002Fdev\u002Fsda1 -L live-boot\nmke2fs \u002Fdev\u002Fsda2 -L live-root\nmke2fs \u002Fdev\u002Fsda3 -L live-home\nmount \u002Fdev\u002Fsda2 \u002Fmnt\nmkdir \u002Fmnt\u002Fboot\nmkdir \u002Fmnt\u002Fhome\nmount \u002Fdev\u002Fsda1 \u002Fmnt\u002Fboot\nmount \u002Fdev\u002Fsda3 \u002Fmnt\u002Fhome\n",[184,60558,60559,60573,60585,60597,60606,60613,60620,60628],{"__ignoreMap":1646},[2113,60560,60561,60564,60567,60570],{"class":2115,"line":2116},[2113,60562,60563],{"class":2133},"mke2fs",[2113,60565,60566],{"class":2149}," \u002Fdev\u002Fsda1",[2113,60568,60569],{"class":2166}," -L",[2113,60571,60572],{"class":2149}," live-boot\n",[2113,60574,60575,60577,60580,60582],{"class":2115,"line":1647},[2113,60576,60563],{"class":2133},[2113,60578,60579],{"class":2149}," \u002Fdev\u002Fsda2",[2113,60581,60569],{"class":2166},[2113,60583,60584],{"class":2149}," live-root\n",[2113,60586,60587,60589,60592,60594],{"class":2115,"line":1652},[2113,60588,60563],{"class":2133},[2113,60590,60591],{"class":2149}," \u002Fdev\u002Fsda3",[2113,60593,60569],{"class":2166},[2113,60595,60596],{"class":2149}," live-home\n",[2113,60598,60599,60601,60603],{"class":2115,"line":2185},[2113,60600,979],{"class":2133},[2113,60602,60579],{"class":2149},[2113,60604,60605],{"class":2149}," \u002Fmnt\n",[2113,60607,60608,60610],{"class":2115,"line":2230},[2113,60609,17072],{"class":2133},[2113,60611,60612],{"class":2149}," \u002Fmnt\u002Fboot\n",[2113,60614,60615,60617],{"class":2115,"line":2235},[2113,60616,17072],{"class":2133},[2113,60618,60619],{"class":2149}," \u002Fmnt\u002Fhome\n",[2113,60621,60622,60624,60626],{"class":2115,"line":2241},[2113,60623,979],{"class":2133},[2113,60625,60566],{"class":2149},[2113,60627,60612],{"class":2149},[2113,60629,60630,60632,60634],{"class":2115,"line":2246},[2113,60631,979],{"class":2133},[2113,60633,60591],{"class":2149},[2113,60635,60619],{"class":2149},[27533,60637,60639],{"id":60638},"installation-à-laide-de-debootstrap","Installation à l'aide de debootstrap",[12,60641,60642,60643,60645,60646,44506],{},"Une fois les partitions montées on peut installer le système\n",[362,60644,52264],{}," sur la clé USB en utilisant l'utilitaire ",[184,60647,60465],{},[2105,60649,60651],{"className":2317,"code":60650,"language":2319,"meta":1646,"style":1646},"> debootstrap --arch i386 etch \u002Fmnt\u002F ftp:\u002F\u002Fftp2.fr.debian.org\u002Fdebian\u002F\nI: Retrieving Release\nI: Retrieving Packages\nI: Validating Packages\nI:  Resolving dependencies of required packages...\nI: Resolving dependencies of base packages...\nI: Found additional base dependencies: libdb4.2 libgnutls12 libreadline5\n    libsigc++-2.0-0c2a openbsd-inetd readline-common\nI: Checking component main on http:\u002F\u002Fftp.debian.org\u002Fdebian...\nI: Retrieving adduser\n...\nI: Configuring gnupg...\nI: Configuring sysklogd...\nI: Configuring klogd...\nI: Configuring netbase...\nI: Configuring openbsd-inetd...\nI: Base system installed successfully.\n",[184,60652,60653,60658,60669,60678,60687,60704,60720,60744,60755,60772,60781,60785,60794,60803,60812,60821,60830],{"__ignoreMap":1646},[2113,60654,60655],{"class":2115,"line":2116},[2113,60656,60657],{"class":2119},"> debootstrap --arch i386 etch \u002Fmnt\u002F ftp:\u002F\u002Fftp2.fr.debian.org\u002Fdebian\u002F\n",[2113,60659,60660,60663,60666],{"class":2115,"line":1647},[2113,60661,60662],{"class":2133},"I:",[2113,60664,60665],{"class":2149}," Retrieving",[2113,60667,60668],{"class":2149}," Release\n",[2113,60670,60671,60673,60675],{"class":2115,"line":1652},[2113,60672,60662],{"class":2133},[2113,60674,60665],{"class":2149},[2113,60676,60677],{"class":2149}," Packages\n",[2113,60679,60680,60682,60685],{"class":2115,"line":2185},[2113,60681,60662],{"class":2133},[2113,60683,60684],{"class":2149}," Validating",[2113,60686,60677],{"class":2149},[2113,60688,60689,60691,60694,60696,60698,60701],{"class":2115,"line":2230},[2113,60690,60662],{"class":2133},[2113,60692,60693],{"class":2149},"  Resolving",[2113,60695,42496],{"class":2149},[2113,60697,2281],{"class":2149},[2113,60699,60700],{"class":2149}," required",[2113,60702,60703],{"class":2149}," packages...\n",[2113,60705,60706,60708,60711,60713,60715,60718],{"class":2115,"line":2235},[2113,60707,60662],{"class":2133},[2113,60709,60710],{"class":2149}," Resolving",[2113,60712,42496],{"class":2149},[2113,60714,2281],{"class":2149},[2113,60716,60717],{"class":2149}," base",[2113,60719,60703],{"class":2149},[2113,60721,60722,60724,60727,60730,60732,60735,60738,60741],{"class":2115,"line":2241},[2113,60723,60662],{"class":2133},[2113,60725,60726],{"class":2149}," Found",[2113,60728,60729],{"class":2149}," additional",[2113,60731,60717],{"class":2149},[2113,60733,60734],{"class":2149}," dependencies:",[2113,60736,60737],{"class":2149}," libdb4.2",[2113,60739,60740],{"class":2149}," libgnutls12",[2113,60742,60743],{"class":2149}," libreadline5\n",[2113,60745,60746,60749,60752],{"class":2115,"line":2246},[2113,60747,60748],{"class":2133},"    libsigc++-2.0-0c2a",[2113,60750,60751],{"class":2149}," openbsd-inetd",[2113,60753,60754],{"class":2149}," readline-common\n",[2113,60756,60757,60759,60762,60765,60767,60769],{"class":2115,"line":2464},[2113,60758,60662],{"class":2133},[2113,60760,60761],{"class":2149}," Checking",[2113,60763,60764],{"class":2149}," component",[2113,60766,3909],{"class":2149},[2113,60768,17791],{"class":2149},[2113,60770,60771],{"class":2149}," http:\u002F\u002Fftp.debian.org\u002Fdebian...\n",[2113,60773,60774,60776,60778],{"class":2115,"line":2085},[2113,60775,60662],{"class":2133},[2113,60777,60665],{"class":2149},[2113,60779,60780],{"class":2149}," adduser\n",[2113,60782,60783],{"class":2115,"line":2514},[2113,60784,4902],{"class":2334},[2113,60786,60787,60789,60791],{"class":2115,"line":2533},[2113,60788,60662],{"class":2133},[2113,60790,42448],{"class":2149},[2113,60792,60793],{"class":2149}," gnupg...\n",[2113,60795,60796,60798,60800],{"class":2115,"line":2556},[2113,60797,60662],{"class":2133},[2113,60799,42448],{"class":2149},[2113,60801,60802],{"class":2149}," sysklogd...\n",[2113,60804,60805,60807,60809],{"class":2115,"line":2569},[2113,60806,60662],{"class":2133},[2113,60808,42448],{"class":2149},[2113,60810,60811],{"class":2149}," klogd...\n",[2113,60813,60814,60816,60818],{"class":2115,"line":2575},[2113,60815,60662],{"class":2133},[2113,60817,42448],{"class":2149},[2113,60819,60820],{"class":2149}," netbase...\n",[2113,60822,60823,60825,60827],{"class":2115,"line":2596},[2113,60824,60662],{"class":2133},[2113,60826,42448],{"class":2149},[2113,60828,60829],{"class":2149}," openbsd-inetd...\n",[2113,60831,60832,60834,60837,60840,60843],{"class":2115,"line":3192},[2113,60833,60662],{"class":2133},[2113,60835,60836],{"class":2149}," Base",[2113,60838,60839],{"class":2149}," system",[2113,60841,60842],{"class":2149}," installed",[2113,60844,60845],{"class":2149}," successfully.\n",[27533,60847,60849],{"id":60848},"dans-un-chroot","Dans un Chroot",[12,60851,60852,60853,60856],{},"Les opérations suivantes sont à faire sur l'environnement de\ndestination, il faut donc utiliser la commande ",[184,60854,60855],{},"chroot"," pour s'y placer.",[2105,60858,60860],{"className":2317,"code":60859,"language":2319,"meta":1646,"style":1646},"chroot \u002Fmnt '\u002Fbin\u002Fbash -i'\n",[184,60861,60862],{"__ignoreMap":1646},[2113,60863,60864,60866,60869],{"class":2115,"line":2116},[2113,60865,60855],{"class":2133},[2113,60867,60868],{"class":2149}," \u002Fmnt",[2113,60870,60871],{"class":2149}," '\u002Fbin\u002Fbash -i'\n",[27533,60873,60875],{"id":60874},"noyaux","Noyaux",[12,60877,60878],{},"Une fois le système de base installé, il faut le configurer et y ajouter\nde quoi démarrer la clé USB.",[34199,60880,60882],{"id":60881},"ajustement-et-systèmes-de-fichiers-etcfstab","Ajustement et systèmes de fichiers : \u002Fetc\u002Ffstab",[12,60884,60885,60886,60889],{},"Cela dépend de votre configuration, et du partitionnement que vous avez\nfait. Si vous avez fait le même partitionnement que moi vous devriez\navoir un ",[155,60887,60888],{},"fstab"," ressemblant à celui là :",[2105,60891,60894],{"className":60892,"code":60893,"language":26023},[26021],"#\u002Fetc\u002Ffstab: static file system informations.\n#\nLABEL=live-root    \u002F           ext2    defaults,errors=remount-ro,noatime  0   1\nLABEL=live-boot    \u002Fboot           ext2    defaults,noatime,ro         0   2\nLABEL=live-home    \u002Fhome           ext2    defaults,noatime            0   2\nproc       \u002Fproc           proc    defaults                0   0\n#tmpfs     \u002Fetc\u002Fnetwork\u002Frun    tmpfs   defaults,noatime            0   0\ntmpfs      \u002Ftmp            tmpfs   defaults,noatime            0   0\ntmpfs      \u002Fvar\u002Flock       tmpfs   defaults,noatime            0   0\ntmpfs      \u002Fvar\u002Flog        tmpfs   defaults,noatime            0   0\ntmpfs      \u002Fvar\u002Frun        tmpfs   defaults,noatime            0   0\ntmpfs      \u002Fvar\u002Ftmp        tmpfs   defaults,noatime            0   0\n",[184,60895,60893],{"__ignoreMap":1646},[12,60897,60898,60899,33255,60902,60904,60905,60910,60911,179],{},"J'utilise le label de la partition au lieu d'utiliser le ",[362,60900,60901],{},"block device",[362,60903,60901],{}," peut gêner si la clé USB n'est pas\ntoujours détectée dans le même ordre",[17400,60906,60907],{},[49,60908,2547],{"href":17977,"ariaDescribedBy":60909,"dataFootnoteRef":1646,"id":17979},[17406],".Les dossiers temporaires\nsont tous reportés dans un système de fichier virtuel (en mémoire) à\nl'aide de ",[184,60912,60913],{},"tmpfs",[12,60915,60916,60917,60920,60921,60924],{},"Une fois le fichier ",[155,60918,60919],{},"\u002Fetc\u002Ffstab"," configuré, il ne reste plus qu'à\nmonter les partitions. Pour monter les partitions un simple ",[184,60922,60923],{},"mount -a","\nle fait. Il est aussi possible de monter les partitions une à une.",[12,60926,60927,44506],{},[48221,60928,60929],{},"Il est possible que la commande suivante faite hors du chroot\npuisse résoudre certain problème si l'application utilise udev,\n...",[2105,60931,60933],{"className":2317,"code":60932,"language":2319,"meta":1646,"style":1646},"mount --bind \u002Fdev\u002F \u002Fmnt\u002Fdev\u002F\n",[184,60934,60935],{"__ignoreMap":1646},[2113,60936,60937,60939,60942,60945],{"class":2115,"line":2116},[2113,60938,979],{"class":2133},[2113,60940,60941],{"class":2166}," --bind",[2113,60943,60944],{"class":2149}," \u002Fdev\u002F",[2113,60946,60947],{"class":2149}," \u002Fmnt\u002Fdev\u002F\n",[34199,60949,60951],{"id":60950},"le-réseau-etchostname-etchosts","Le réseau : \u002Fetc\u002Fhostname \u002Fetc\u002Fhosts",[12,60953,4080,60954,60957,60958,60961,60962,60964],{},[155,60955,60956],{},"\u002Fetc\u002Fhosts"," doit contenir une ligne indiquant l'adresse du\n",[362,60959,60960],{},"loopback"," et le nom de la machine. Ceci afin que ",[362,60963,46036],{}," par exemple\npuisse démarrer :",[2105,60966,60969],{"className":60967,"code":60968,"language":26023},[26021],"127.0.0.1       localhost.localdomain   localhost  liveding\n",[184,60970,60968],{"__ignoreMap":1646},[12,60972,58429,60973,60976],{},[155,60974,60975],{},"\u002Fetc\u002Fhostname"," il faut également mettre le nom de la\nmachine.",[34199,60978,60980],{"id":60979},"apt-etcaptsourceslist","Apt : \u002Fetc\u002Fapt\u002Fsources.list",[12,60982,4080,60983,60986],{},[155,60984,60985],{},"sources.list"," contient les lignes par défaut suivantes :",[2105,60988,60991],{"className":60989,"code":60990,"language":26023},[26021],"# etch\ndeb ftp:\u002F\u002Fftp2.fr.debian.org\u002Fdebian\u002F etch main contrib non-free\ndeb-src ftp:\u002F\u002Fftp2.fr.debian.org\u002Fdebian\u002F etch main contrib\ndeb http:\u002F\u002Fsecurity.debian.org\u002F etch\u002Fupdates main\ndeb-src http:\u002F\u002Fsecurity.debian.org\u002F etch\u002Fupdates main\n\n# etch unofficial\ndeb http:\u002F\u002Fftp.debian-unofficial.org\u002Fdebian etch main contrib non-free restricted\n",[184,60992,60990],{"__ignoreMap":1646},[12,60994,60995,60996,60998],{},"Ces lignes permettent de télécharger les paquets par défaut de\n",[362,60997,52264],{}," ainsi que les paquets de sécurité, et les paquets non\nofficiels permettant d'activer certaine fonctionnalité.",[34199,61000,61002],{"id":61001},"initrd-etckernel-imgconf","Initrd : \u002Fetc\u002Fkernel-img.conf",[12,61004,58429,61005,61008,61009,61011,61012,61015],{},[155,61006,61007],{},"\u002Fetc\u002Fkernel-img.conf"," on enregistre les informations\nsuivantes (afin que ",[362,61010,52264],{}," crée un fichier ",[184,61013,61014],{},"initrd"," lors de\nl'installation du noyau) :",[2105,61017,61019],{"className":43018,"code":61018,"language":43020,"meta":1646,"style":1646},"# Kernel Image management overrides\n# See kernel-img.conf(5) for details\ndo_symlinks = Yes\ndo_initrd = yes\n",[184,61020,61021,61026,61031,61041],{"__ignoreMap":1646},[2113,61022,61023],{"class":2115,"line":2116},[2113,61024,61025],{"class":2396},"# Kernel Image management overrides\n",[2113,61027,61028],{"class":2115,"line":1647},[2113,61029,61030],{"class":2396},"# See kernel-img.conf(5) for details\n",[2113,61032,61033,61036,61038],{"class":2115,"line":1652},[2113,61034,61035],{"class":2326},"do_symlinks",[2113,61037,2153],{"class":2119},[2113,61039,61040],{"class":2149}," Yes\n",[2113,61042,61043,61046,61048],{"class":2115,"line":2185},[2113,61044,61045],{"class":2326},"do_initrd",[2113,61047,2153],{"class":2119},[2113,61049,61050],{"class":2149}," yes\n",[12,61052,4080,61053,61055,61056,61061,61062,61064],{},[184,61054,61014],{}," permet de charger les modules nécessaires",[17400,61057,61058],{},[49,61059,3696],{"href":41827,"ariaDescribedBy":61060,"dataFootnoteRef":1646,"id":41829},[17406],"\nau démarrage de ",[362,61063,57765],{}," sur une clé USB.",[34199,61066,61068],{"id":61067},"installation-de-paquet","Installation de paquet",[2105,61070,61072],{"className":2317,"code":61071,"language":2319,"meta":1646,"style":1646},"aptitude install initrd-tools linux-image-2.6.16-1-686\naptitude install grub\naptitude install localepurge\naptitude clean# localepurge\n",[184,61073,61074,61086,61095,61104],{"__ignoreMap":1646},[2113,61075,61076,61078,61080,61083],{"class":2115,"line":2116},[2113,61077,52598],{"class":2133},[2113,61079,52601],{"class":2149},[2113,61081,61082],{"class":2149}," initrd-tools",[2113,61084,61085],{"class":2149}," linux-image-2.6.16-1-686\n",[2113,61087,61088,61090,61092],{"class":2115,"line":1647},[2113,61089,52598],{"class":2133},[2113,61091,52601],{"class":2149},[2113,61093,61094],{"class":2149}," grub\n",[2113,61096,61097,61099,61101],{"class":2115,"line":1652},[2113,61098,52598],{"class":2133},[2113,61100,52601],{"class":2149},[2113,61102,61103],{"class":2149}," localepurge\n",[2113,61105,61106,61108,61111],{"class":2115,"line":2185},[2113,61107,52598],{"class":2133},[2113,61109,61110],{"class":2149}," clean#",[2113,61112,61103],{"class":2149},[12,61114,61115,61116,61118,61119,61122,61123,61126],{},"On installe une image du noyau, ainsi que les outils pour générer\nl'image ",[184,61117,61014],{},". On installe également ",[184,61120,61121],{},"grub"," qui servira à démarrer\nsur la clé USB. Le paquet ",[184,61124,61125],{},"localepurge"," permet quant à lui de nettoyer\nles locales qui prennent un peu trop de place sur la clé USB.\nGénéralement je ne conserve que les locales françaises et anglaises.",[12,61128,61129,61130,61132],{},"Il est aussi possible d'installer quelques paquets comme un serveur\ngraphique, même voir ",[362,61131,46036],{},", ou encore des outils d'administration. On\npeut aussi installer des outils qui pourront permettre de réparer une\nmachine.",[34199,61134,61136],{"id":61135},"quelques-ajustements","Quelques ajustements",[12,61138,61139],{},"Afin de ne pas écrire trop souvent sur la clé USB, on remplace quelques\nfichiers.",[2105,61141,61143],{"className":2317,"code":61142,"language":2319,"meta":1646,"style":1646},"rm -f \u002Fetc\u002Fblkid.tab*\nln -s \u002Fdev\u002Fnull \u002Fetc\u002Fblkid.tab\nrm -f \u002Fetc\u002Fmtab\nln -s \u002Fproc\u002Fmounts \u002Fetc\u002Fmtab\n",[184,61144,61145,61157,61170,61179],{"__ignoreMap":1646},[2113,61146,61147,61149,61152,61155],{"class":2115,"line":2116},[2113,61148,59288],{"class":2133},[2113,61150,61151],{"class":2166}," -f",[2113,61153,61154],{"class":2149}," \u002Fetc\u002Fblkid.tab",[2113,61156,3233],{"class":2414},[2113,61158,61159,61162,61164,61167],{"class":2115,"line":1647},[2113,61160,61161],{"class":2133},"ln",[2113,61163,37395],{"class":2166},[2113,61165,61166],{"class":2149}," \u002Fdev\u002Fnull",[2113,61168,61169],{"class":2149}," \u002Fetc\u002Fblkid.tab\n",[2113,61171,61172,61174,61176],{"class":2115,"line":1652},[2113,61173,59288],{"class":2133},[2113,61175,61151],{"class":2166},[2113,61177,61178],{"class":2149}," \u002Fetc\u002Fmtab\n",[2113,61180,61181,61183,61185,61188],{"class":2115,"line":2185},[2113,61182,61161],{"class":2133},[2113,61184,37395],{"class":2166},[2113,61186,61187],{"class":2149}," \u002Fproc\u002Fmounts",[2113,61189,61178],{"class":2149},[133,61191,61193],{"id":61192},"configuration-de-grub","Configuration de grub",[12,61195,61196,61197,61199,61200,61202,61203,61205,61206,44506],{},"Afin que la machine puisse démarrer il faut installer ",[184,61198,61121],{}," sur le\nsecteur de démarrage de la clé USB.Pour cela, on peut quitter le\n",[184,61201,60855],{},". Pour installer ",[184,61204,61121],{}," on peut utiliser ",[184,61207,61208],{},"grub-install",[2105,61210,61212],{"className":2317,"code":61211,"language":2319,"meta":1646,"style":1646},"grub-install recheck root-directory=\u002Fmnt\u002F \u002Fdev\u002Fsda\n",[184,61213,61214],{"__ignoreMap":1646},[2113,61215,61216,61218,61221,61224],{"class":2115,"line":2116},[2113,61217,61208],{"class":2133},[2113,61219,61220],{"class":2149}," recheck",[2113,61222,61223],{"class":2149}," root-directory=\u002Fmnt\u002F",[2113,61225,61226],{"class":2149}," \u002Fdev\u002Fsda\n",[12,61228,61229,61230,61232,61233,61236],{},"Normalement cela devrait fonctionner ;) mais il est possible, si cela ne\nmarche pas d'essayer de l'installer à l'aide de la commande ",[184,61231,61121],{},"\ndirectement (attention le ",[362,61234,61235],{},"hd1"," est à adapter selon votre\nconfiguration) :",[2105,61238,61241],{"className":61239,"code":61240,"language":26023},[26021],"# grub\ngrub> root (hd1,0)\ngrub> setup (hd1)\n",[184,61242,61240],{"__ignoreMap":1646},[12,61244,61245,61246],{},"Il faut également renseigner le fichier ",[155,61247,61248],{},"\u002Fmnt\u002Fboot\u002Fgrub\u002Fmenu.lst",[2105,61250,61253],{"className":61251,"code":61252,"language":26023},[26021],"# menu.lst - See: grub(8), info grub, update-grub(8)\n#            grub-install(8), grub-floppy(8),\n#            grub-md5-crypt, \u002Fusr\u002Fshare\u002Fdoc\u002Fgrub\n#            and \u002Fusr\u002Fshare\u002Fdoc\u002Fgrub-doc\u002F.\n\n## default num\n# Set the default entry to the entry number NUM. Numbering starts from 0, and\n# the entry number 0 is the default if the command is not used.\n#\n# You can specify 'saved' instead of a number. In this case, the default entry\n# is the entry saved with the command 'savedefault'.\n# WARNING: If you are using dmraid do not change this entry to 'saved' or your\n# array will desync and will not let you boot your system.\ndefault        0\n\n## timeout sec\n# Set a timeout, in SEC seconds, before automatically booting the default entry\n# (normally the first entry defined).\ntimeout        5\n\n# Pretty colours\ncolor cyan\u002Fblue white\u002Fblue\n\n## password '--md5' passwd\n# If used in the first section of a menu file, disable all interactive editing\n# control (menu entry editor and command-line)  and entries protected by the\n# command 'lock'\n# e.g. password topsecret\n#      password --md5 $1$gLhU0\u002F$aW78kHK1QfV3P2b2znUoe\u002F\n# password topsecret\n\n#\n# examples\n#\n# title        Windows 95\u002F98\u002FNT\u002F2000\n# root     (hd0,0)\n# makeactive\n# chainloader  +1\n#\n# title        Linux\n# root     (hd0,1)\n# kernel   \u002Fvmlinuz root=\u002Fdev\u002Fhda2 ro\n#\n\n#\n# Put static boot stanzas before and\u002For after AUTOMAGIC KERNEL LIST\n\n### BEGIN AUTOMAGIC KERNELS LIST\n## lines between the AUTOMAGIC KERNELS LIST markers will be modified\n## by the debian update-grub script except for the default options below\n\n## DO NOT UNCOMMENT THEM, Just edit them to your needs\n\n## ## Start Default Options ##\n## default kernel options\n## default kernel options for automagic boot options\n## If you want special options for specific kernels use kopt_x_y_z\n## where x.y.z is kernel version. Minor versions can be omitted.\n## e.g. kopt=root=\u002Fdev\u002Fhda1 ro\n##      kopt_2_6_8=root=\u002Fdev\u002Fhdc1 ro\n##      kopt_2_6_8_2_686=root=\u002Fdev\u002Fhdc2 ro\n# kopt=root=LABEL=live-root rootdelay=5 ro quiet\n\n## default grub root device\n## e.g. groot=(hd0,0)\n# groot=(hd0,0)\n\n## should update-grub create alternative automagic boot options\n## e.g. alternative=true\n##      alternative=false\n# alternative=true\n\n## should update-grub lock alternative automagic boot options\n## e.g. lockalternative=true\n##      lockalternative=false\n# lockalternative=false\n\n## additional options to use with the default boot option, but not with the\n## alternatives\n## e.g. defoptions=vga=791 resume=\u002Fdev\u002Fhda5\n# defoptions=vga=791\n\n## should update-grub lock old automagic boot options\n## e.g. lockold=false\n##      lockold=true\n# lockold=false\n\n## Xen hypervisor options to use with the default Xen boot option\n# xenhopt=\n\n## Xen Linux kernel options to use with the default Xen boot option\n# xenkopt=console=tty0\n\n## altoption boot targets option\n## multiple altoptions lines are allowed\n## e.g. altoptions=(extra menu suffix) extra boot options\n##      altoptions=(single-user) single\n# altoptions=(single-user mode) single\n\n## controls how many kernels should be put into the menu.lst\n## only counts the first occurence of a kernel, not the\n## alternative kernel options\n## e.g. howmany=all\n##      howmany=7\n# howmany=all\n\n## should update-grub create memtest86 boot option\n## e.g. memtest86=true\n##      memtest86=false\n# memtest86=true\n\n## should update-grub adjust the value of the default booted system\n## can be true or false\n# updatedefaultentry=false\n\n## ## End Default Options ##\n\ntitle      Debian GNU\u002FLinux, kernel 2.6.18-4-686\nroot       (hd0,0)\nkernel     \u002Fvmlinuz-2.6.18-4-686 root=LABEL=live-root rootdelay=5 ro quiet vga=791\ninitrd     \u002Finitrd.img-2.6.18-4-686\nsavedefault\n\ntitle      Debian GNU\u002FLinux, kernel 2.6.18-4-686 (single-user mode)\nroot       (hd0,0)\nkernel     \u002Fvmlinuz-2.6.18-4-686 root=LABEL=live-root rootdelay=5 ro quiet single\ninitrd     \u002Finitrd.img-2.6.18-4-686\nsavedefault\n\n### END DEBIAN AUTOMAGIC KERNELS LIST\n",[184,61254,61252],{"__ignoreMap":1646},[133,61256,61258],{"id":61257},"ajout-dun-utilisateur","Ajout d'un utilisateur",[12,61260,61261,61262,61267,61268,61270],{},"Enfin il ne faut pas oublier d'installer un compte\nutilisateur",[17400,61263,61264],{},[49,61265,706],{"href":41857,"ariaDescribedBy":61266,"dataFootnoteRef":1646,"id":41859},[17406],", ainsi que changer le mot de passe ",[362,61269,58192],{}," à l'aide\ndes commandes :",[2105,61272,61275],{"className":61273,"code":61274,"language":26023},[26021],"chroot \u002Fmnt\npasswd\nadduser toto\n",[184,61276,61274],{"__ignoreMap":1646},[133,61278,61280],{"id":61279},"redémarrage-et-test","Redémarrage et test",[12,61282,61283],{},"Il ne reste plus qu'à démonter toutes les partitions et redémarrer la\nmachine.",[2105,61285,61288],{"className":61286,"code":61287,"language":26023},[26021],"umount -a\nexit\nreboot\n",[184,61289,61287],{"__ignoreMap":1646},[23219,61291,61293,61296],{"className":61292,"dataFootnotes":1646},[23222],[128,61294,23227],{"className":61295,"id":17406},[23226],[367,61297,61298,61304,61310,61316],{},[370,61299,61300,61301],{"id":23232},"J'ai par ailleurs acheté récemment une clé USB de 2Go à cet effet pour à peine 15 euros ",[49,61302,23240],{"href":23236,"ariaLabel":23237,"className":61303,"dataFootnoteBackref":1646},[23239],[370,61305,61306,61307],{"id":23243},"Ce qui peut arriver quand on change de machine par exemple. ",[49,61308,23240],{"href":23251,"ariaLabel":23252,"className":61309,"dataFootnoteBackref":1646},[23239],[370,61311,61312,61313],{"id":43495},"i.e les modules USB, ... ",[49,61314,23240],{"href":43499,"ariaLabel":35010,"className":61315,"dataFootnoteBackref":1646},[23239],[370,61317,61318,61319],{"id":43503},"Il ne faudrait pas travailler en administrateur, hein ! ",[49,61320,23240],{"href":43507,"ariaLabel":43508,"className":61321,"dataFootnoteBackref":1646},[23239],[3358,61323,61324],{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}",{"title":1646,"searchDepth":1647,"depth":1647,"links":61326},[61327,61328,61329,61330,61331,61332],{"id":60406,"depth":1652,"text":60407},{"id":60488,"depth":1652,"text":60489},{"id":61192,"depth":1652,"text":61193},{"id":61257,"depth":1652,"text":61258},{"id":61279,"depth":1652,"text":61280},{"id":17406,"depth":1647,"text":23227},"SysAdmin","sysadmin","2007-08-04","Il peut être parfois utile de se déplacer ici et là avec une\ndistribution Linux clé en main sans se trimbaler toujours avec un\nordinateur portable ou un CD.",{"type":9,"value":61338},[61339,61343,61350,61352,61370,61375],[12,61340,60337,61341,60341],{},[362,61342,60340],{},[12,61344,60344,61345,60350],{},[17400,61346,61347],{},[49,61348,3686],{"href":17404,"ariaDescribedBy":61349,"dataFootnoteRef":1646,"id":17407},[17406],[12,61351,60353],{},[506,61353,61354,61356,61364,61366,61368],{},[370,61355,60358],{},[370,61357,60361,61358,932,61360,932,61362,60371],{},[184,61359,60364],{},[184,61361,60367],{},[184,61363,60370],{},[370,61365,60374],{},[370,61367,60377],{},[370,61369,60380],{},[12,61371,60383,61372,60389],{},[49,61373,60388],{"href":60386,"rel":61374},[347],[12,61376,60392,61377,792,61380,60403],{},[49,61378,60397],{"href":60395,"rel":61379},[347],[49,61381,60402],{"href":60400,"rel":61382},[347],{},"\u002Fpost\u002Fdebian-etch-cle-usb",{"title":60332,"description":61336},"debian-etch-cle-usb","posts\u002FSysAdmin\u002F2007-08-04-debian-etch-cle-usb",[38764],"P1wXAJpGAtElk0Xu1quXBbbh85GWI8n-HVkQxXWB6m0",{"id":61391,"title":61392,"author":7,"body":61393,"category":61333,"categorySlug":61334,"date":64212,"description":64213,"excerpt":64214,"extension":1764,"location":1765,"meta":64234,"navigation":1767,"path":64235,"published":1767,"seo":64236,"slug":61697,"stem":64237,"tags":64238,"timeToRead":2241,"__hash__":64239},"posts\u002Fposts\u002FSysAdmin\u002F2006-10-16-sauvegarde-automatique.md","Sauvegarde automatique 0.9",{"type":9,"value":61394,"toc":64207},[61395,61400,61407,61418,61421,61424,61428,61695,61699,62697,62701,64204],[12,61396,61397,61398,179],{},"Ce billet concerne une application, de mon cru, écrite en ",[184,61399,2319],{},[12,61401,61402,61403,61406],{},"Elle utilise l'application ",[184,61404,61405],{},"dar"," pour effectuer des sauvegardes et les\ncopier sur un périphérique USB ou\u002Fet sur le réseau.Tous les patchs,\ncorrections, améliorations sont les bienvenues sur cette application\nmaison.",[12,61408,61409,61410,61413,61414,61417],{},"Pour fonctionner l'application se configure à l'aide d'un fichier du\ndossier ",[184,61411,61412],{},"\u002Fetc"," qui permet de déterminer une liste de dossier à\nsauvegarder (",[184,61415,61416],{},"\u002Fetc\u002Fautobackup","). Dans ce fichier, on y défini le dossier\nà sauvegarde, le dossier de destination, le nombre de sauvegarde\nincrémentale (ou dit d'une autre facon, au bout de combien fois faut-il\ncréer une nouvelle sauvegarde compléte?).",[12,61419,61420],{},"Il permet aussi de définir des points de copie où l'on place les\ndifférentes sauvegardes. (Ex: un périphérique USB, un dossier SSH).Ces\npoints sont, bien sûr, utilisés seulement s'ils existent. Le fichier est\ncopié sur le périphérique USB, s'il est branché, sur la machine distante\nsi elle est allumée, ....",[12,61422,61423],{},"Il ne reste plus qu'à configurer ces petits scripts et les mettre dans\nun cron",[133,61425,61427],{"id":61426},"contenu-exemple-du-fichier-de-configuration","Contenu exemple du fichier de configuration",[2105,61429,61431],{"className":2317,"code":61430,"language":2319,"meta":1646,"style":1646},"# This config file make relation for backup\n\n# Options\nBACKUP_OPT=\"-v -s 4613734400 -D -z9 -m 150\"\nBACKUP_EXCLUDE_EXT=\"-Z *.avi -Z *.bz2 -Z *.gif -Z *.gz -Z *.jpg -Z *.mov -Z *.mpg -Z *.pbm -Z *.pdf -Z *.png -Z *.pnm -Z *.Z -Z *.zip\"\nBACKUP_EXCLUDE_PATH=\"-P phoenix\u002F.bogofilter -P phoenix\u002F.fonts -P phoenix\u002F.kde\u002Fcache-maxiding -P phoenix\u002F.kde\u002Fsocket-maxiding -P phoenix\u002F.kde\u002Ftmp-maxiding -P phoenix\u002F.spamassassin -P phoenix\u002F.thumbnails -P phoenix\u002F.turboprint -P phoenix\u002F.unison -P phoenix\u002F.wesnoth -P phoenix\u002F.widelands -P phoenix\u002F.wine* -P phoenix\u002Fusr -P phoenix\u002Fvirtuel -P .Trash-* -P phoenix\u002Ftmp\"\n\n# Copy Backup\nCOPYTO=\"usbdrive network\"\n\n# Copy to usb drive\nusbdrive_type=\"usb\"\nusbdrive_dev=\"\u002Fdev\u002Fusb\u002Fdisk1\"\nusbdrive_mountpoint=\"\u002Fmedia\u002Fdisque\u002F\"\nusbdrive_pathname=\"backup\u002F\"\n\n# Copy to network\nnetwork_type=\"net\"\nnetwork_host=\"miniding\"\nnetwork_user=\"phoenix\"\n# no network_pass : use ssh key for backup\nnetwork_dir=\"\u002Fhome\u002Fphoenix\u002Fbackup\"\n\n# Home\nhome_name=maxiding_home\nhome_source=\u002Fhome\u002F\nhome_destination=\u002Fgravure\u002Fdar\u002Fhome\u002F\nhome_inc_max=10\n\n# Etc\netc_name=maxiding_etc\netc_source=\u002Fetc\u002F\netc_destination=\u002Fgravure\u002Fdar\u002Fetc\u002F\netc_inc_max=10\n",[184,61432,61433,61438,61442,61447,61457,61467,61477,61481,61486,61496,61500,61505,61515,61525,61535,61545,61549,61554,61564,61574,61584,61589,61599,61603,61608,61618,61628,61638,61647,61651,61656,61666,61676,61686],{"__ignoreMap":1646},[2113,61434,61435],{"class":2115,"line":2116},[2113,61436,61437],{"class":2396},"# This config file make relation for backup\n",[2113,61439,61440],{"class":2115,"line":1647},[2113,61441,2125],{"emptyLinePlaceholder":1767},[2113,61443,61444],{"class":2115,"line":1652},[2113,61445,61446],{"class":2396},"# Options\n",[2113,61448,61449,61452,61454],{"class":2115,"line":2185},[2113,61450,61451],{"class":2330},"BACKUP_OPT",[2113,61453,2335],{"class":2334},[2113,61455,61456],{"class":2149},"\"-v -s 4613734400 -D -z9 -m 150\"\n",[2113,61458,61459,61462,61464],{"class":2115,"line":2230},[2113,61460,61461],{"class":2330},"BACKUP_EXCLUDE_EXT",[2113,61463,2335],{"class":2334},[2113,61465,61466],{"class":2149},"\"-Z *.avi -Z *.bz2 -Z *.gif -Z *.gz -Z *.jpg -Z *.mov -Z *.mpg -Z *.pbm -Z *.pdf -Z *.png -Z *.pnm -Z *.Z -Z *.zip\"\n",[2113,61468,61469,61472,61474],{"class":2115,"line":2235},[2113,61470,61471],{"class":2330},"BACKUP_EXCLUDE_PATH",[2113,61473,2335],{"class":2334},[2113,61475,61476],{"class":2149},"\"-P phoenix\u002F.bogofilter -P phoenix\u002F.fonts -P phoenix\u002F.kde\u002Fcache-maxiding -P phoenix\u002F.kde\u002Fsocket-maxiding -P phoenix\u002F.kde\u002Ftmp-maxiding -P phoenix\u002F.spamassassin -P phoenix\u002F.thumbnails -P phoenix\u002F.turboprint -P phoenix\u002F.unison -P phoenix\u002F.wesnoth -P phoenix\u002F.widelands -P phoenix\u002F.wine* -P phoenix\u002Fusr -P phoenix\u002Fvirtuel -P .Trash-* -P phoenix\u002Ftmp\"\n",[2113,61478,61479],{"class":2115,"line":2241},[2113,61480,2125],{"emptyLinePlaceholder":1767},[2113,61482,61483],{"class":2115,"line":2246},[2113,61484,61485],{"class":2396},"# Copy Backup\n",[2113,61487,61488,61491,61493],{"class":2115,"line":2464},[2113,61489,61490],{"class":2330},"COPYTO",[2113,61492,2335],{"class":2334},[2113,61494,61495],{"class":2149},"\"usbdrive network\"\n",[2113,61497,61498],{"class":2115,"line":2085},[2113,61499,2125],{"emptyLinePlaceholder":1767},[2113,61501,61502],{"class":2115,"line":2514},[2113,61503,61504],{"class":2396},"# Copy to usb drive\n",[2113,61506,61507,61510,61512],{"class":2115,"line":2533},[2113,61508,61509],{"class":2330},"usbdrive_type",[2113,61511,2335],{"class":2334},[2113,61513,61514],{"class":2149},"\"usb\"\n",[2113,61516,61517,61520,61522],{"class":2115,"line":2556},[2113,61518,61519],{"class":2330},"usbdrive_dev",[2113,61521,2335],{"class":2334},[2113,61523,61524],{"class":2149},"\"\u002Fdev\u002Fusb\u002Fdisk1\"\n",[2113,61526,61527,61530,61532],{"class":2115,"line":2569},[2113,61528,61529],{"class":2330},"usbdrive_mountpoint",[2113,61531,2335],{"class":2334},[2113,61533,61534],{"class":2149},"\"\u002Fmedia\u002Fdisque\u002F\"\n",[2113,61536,61537,61540,61542],{"class":2115,"line":2575},[2113,61538,61539],{"class":2330},"usbdrive_pathname",[2113,61541,2335],{"class":2334},[2113,61543,61544],{"class":2149},"\"backup\u002F\"\n",[2113,61546,61547],{"class":2115,"line":2596},[2113,61548,2125],{"emptyLinePlaceholder":1767},[2113,61550,61551],{"class":2115,"line":3192},[2113,61552,61553],{"class":2396},"# Copy to network\n",[2113,61555,61556,61559,61561],{"class":2115,"line":3213},[2113,61557,61558],{"class":2330},"network_type",[2113,61560,2335],{"class":2334},[2113,61562,61563],{"class":2149},"\"net\"\n",[2113,61565,61566,61569,61571],{"class":2115,"line":3236},[2113,61567,61568],{"class":2330},"network_host",[2113,61570,2335],{"class":2334},[2113,61572,61573],{"class":2149},"\"miniding\"\n",[2113,61575,61576,61579,61581],{"class":2115,"line":3248},[2113,61577,61578],{"class":2330},"network_user",[2113,61580,2335],{"class":2334},[2113,61582,61583],{"class":2149},"\"phoenix\"\n",[2113,61585,61586],{"class":2115,"line":4899},[2113,61587,61588],{"class":2396},"# no network_pass : use ssh key for backup\n",[2113,61590,61591,61594,61596],{"class":2115,"line":1777},[2113,61592,61593],{"class":2330},"network_dir",[2113,61595,2335],{"class":2334},[2113,61597,61598],{"class":2149},"\"\u002Fhome\u002Fphoenix\u002Fbackup\"\n",[2113,61600,61601],{"class":2115,"line":4931},[2113,61602,2125],{"emptyLinePlaceholder":1767},[2113,61604,61605],{"class":2115,"line":4961},[2113,61606,61607],{"class":2396},"# Home\n",[2113,61609,61610,61613,61615],{"class":2115,"line":4976},[2113,61611,61612],{"class":2330},"home_name",[2113,61614,2335],{"class":2334},[2113,61616,61617],{"class":2149},"maxiding_home\n",[2113,61619,61620,61623,61625],{"class":2115,"line":4993},[2113,61621,61622],{"class":2330},"home_source",[2113,61624,2335],{"class":2334},[2113,61626,61627],{"class":2149},"\u002Fhome\u002F\n",[2113,61629,61630,61633,61635],{"class":2115,"line":5013},[2113,61631,61632],{"class":2330},"home_destination",[2113,61634,2335],{"class":2334},[2113,61636,61637],{"class":2149},"\u002Fgravure\u002Fdar\u002Fhome\u002F\n",[2113,61639,61640,61643,61645],{"class":2115,"line":5018},[2113,61641,61642],{"class":2330},"home_inc_max",[2113,61644,2335],{"class":2334},[2113,61646,35445],{"class":2149},[2113,61648,61649],{"class":2115,"line":5042},[2113,61650,2125],{"emptyLinePlaceholder":1767},[2113,61652,61653],{"class":2115,"line":5057},[2113,61654,61655],{"class":2396},"# Etc\n",[2113,61657,61658,61661,61663],{"class":2115,"line":5062},[2113,61659,61660],{"class":2330},"etc_name",[2113,61662,2335],{"class":2334},[2113,61664,61665],{"class":2149},"maxiding_etc\n",[2113,61667,61668,61671,61673],{"class":2115,"line":5098},[2113,61669,61670],{"class":2330},"etc_source",[2113,61672,2335],{"class":2334},[2113,61674,61675],{"class":2149},"\u002Fetc\u002F\n",[2113,61677,61678,61681,61683],{"class":2115,"line":5117},[2113,61679,61680],{"class":2330},"etc_destination",[2113,61682,2335],{"class":2334},[2113,61684,61685],{"class":2149},"\u002Fgravure\u002Fdar\u002Fetc\u002F\n",[2113,61687,61688,61691,61693],{"class":2115,"line":5142},[2113,61689,61690],{"class":2330},"etc_inc_max",[2113,61692,2335],{"class":2334},[2113,61694,35445],{"class":2149},[133,61696,61698],{"id":61697},"sauvegarde-automatique","Sauvegarde automatique",[2105,61700,61702],{"className":2317,"code":61701,"language":2319,"meta":1646,"style":1646},"#!\u002Fbin\u002Fsh\n\n#############################################################################\n##   Copyright (C) 2006 by Ulrich Van Den Hekke                            ##\n##   ulrich.vdh@free.fr                                                    ##\n##                                                                         ##\n##   This program is free software; you can redistribute it and\u002For modify  ##\n##   it under the terms of the GNU General Public License as published by  ##\n##   the Free Software Foundation; either version 2 of the License, or     ##\n##   (at your option) any later version.                                   ##\n##                                                                         ##\n# \u002Fvar\n##   This program is distributed in the hope that it will be useful,       ##\n##   but WITHOUT ANY WARRANTY; without even the implied warranty of        ##\n##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         ##\n##   GNU General Public License for more details.                          ##\n##                                                                         ##\n##   You should have received a copy of the GNU General Public License     ##\n##   along with this program; if not, write to the                         ##\n##   Free Software Foundation, Inc.,                                       ##\n##   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             ##\n#############################################################################\n\nif [ -e \u002Fetc\u002Fautobackup ] ; then\n        . \u002Fetc\u002Fautobackup\nelse\necho \"Please configure the autobackup config file\"\nexit 1\nfi\n\nif [ $# -ne 1 ] ; then\necho \"Please specify a project to backup\"\nexit 2\nfi\n\n# Define name of variable to use\nvar_name=\"\\$$1_name\"\nvar_source=\"\\$$1_source\"\nvar_destination=\"\\$$1_destination\"\nvar_inc_max=\"\\$$1_inc_max\"\n\n# Define content of variable for the project\nBACKUP_SRC=`eval echo $var_source`\nBACKUP_DST=`eval echo $var_destination`\nBACKUP_NAME=`eval echo $var_name`\nBACKUP_INCREMENTAL_MAX=`eval echo $var_inc_max`\n\nif [ -z \"$BACKUP_NAME\" ] ; then\necho \"Project undefined\"\nexit 3\nfi\n\nBACKUP_BIN=dar\nDATE_BACKUP=`date +%Y%m%d`\nBACKUP_ARCHIVE=\"${BACKUP_NAME}_${DATE_BACKUP}\"\n\n# From config file\n# COUNT_INCREMENTAL_BACKUP\n# LAST_BACKUP\n# LAST_COMPLETE_BACKUP\n\n# Some options\n# -v verbose\n# -c archive name\n# -R root place\n# -s slice size\n# -p slice pause\n# -D backup empty dir\n# -z9 compression gzip level 9\n# -m for minimal size\n# -Z exclude from compression\n# -P exclude file\n\nfunction start_backup() {\n        echo \"Backup of $BACKUP_SRC in progress ...\"\n\n        $BACKUP_BIN -c ${BACKUP_DST}${BACKUP_ARCHIVE} -R $BACKUP_SRC $BACKUP_OPT $BACKUP_EXCLUDE_EXT $BACKUP_EXCLUDE_PATH > ${BACKUP_DST}${BACKUP_NAME}.log\n}\n\nfunction load_backup_config() {\n        echo -n \"Load config file ... \"\n\n        if [ -e ${BACKUP_DST}${BACKUP_NAME}.config ] ; then\n                . ${BACKUP_DST}${BACKUP_NAME}.config\n                echo \"loaded.\"\n        else\n                COUNT_INCREMENTAL_BACKUP=$BACKUP_INCREMENTAL_MAX\n                echo \"not found\"\n        fi\n}\n\nfunction save_backup_config() {\n        echo \"Save backup config file ...\"\n\n        echo \"COUNT_INCREMENTAL_BACKUP=$COUNT_INCREMENTAL_BACKUP\" > ${BACKUP_DST}${BACKUP_NAME}.config\n        echo \"LAST_BACKUP=$DATE_BACKUP\"                          >> ${BACKUP_DST}${BACKUP_NAME}.config\n        echo \"LAST_COMPLETE_BACKUP=$LAST_COMPLETE_BACKUP\"        >> ${BACKUP_DST}${BACKUP_NAME}.config\n}\n\nfunction define_incremental() {\n        echo \"There is $COUNT_INCREMENTAL_BACKUP on $BACKUP_INCREMENTAL_MAX\"\n        if [ $COUNT_INCREMENTAL_BACKUP -lt $BACKUP_INCREMENTAL_MAX  ] ; then\n                echo \"Backup is incremental from $LAST_COMPLETE_BACKUP\"\n                COUNT_INCREMENTAL_BACKUP=`expr $COUNT_INCREMENTAL_BACKUP + 1`\n                BACKUP_OPT=\"-A ${BACKUP_DST}${BACKUP_NAME}_${LAST_COMPLETE_BACKUP} ${BACKUP_OPT}\"\n        else\n                echo \"Backup is complete\"\n                COUNT_INCREMENTAL_BACKUP=1\n                LAST_COMPLETE_BACKUP=$DATE_BACKUP\n        fi\n}\n\nload_backup_config;\ndefine_incremental;\nstart_backup;\nsave_backup_config;\n",[184,61703,61704,61709,61713,61718,61723,61728,61733,61738,61743,61748,61753,61757,61762,61767,61772,61777,61782,61786,61791,61796,61801,61806,61810,61814,61828,61835,61840,61848,61856,61861,61865,61884,61891,61898,61902,61906,61911,61928,61944,61960,61976,61980,61985,62006,62024,62042,62060,62064,62084,62091,62098,62102,62106,62116,62131,62151,62155,62160,62165,62170,62175,62179,62184,62189,62194,62199,62204,62209,62214,62219,62224,62229,62234,62238,62247,62260,62264,62311,62315,62319,62328,62337,62341,62362,62379,62387,62392,62402,62409,62414,62418,62422,62431,62438,62442,62466,62491,62516,62520,62524,62533,62550,62569,62580,62600,62628,62632,62639,62647,62657,62661,62665,62669,62676,62683,62690],{"__ignoreMap":1646},[2113,61705,61706],{"class":2115,"line":2116},[2113,61707,61708],{"class":2396},"#!\u002Fbin\u002Fsh\n",[2113,61710,61711],{"class":2115,"line":1647},[2113,61712,2125],{"emptyLinePlaceholder":1767},[2113,61714,61715],{"class":2115,"line":1652},[2113,61716,61717],{"class":2396},"#############################################################################\n",[2113,61719,61720],{"class":2115,"line":2185},[2113,61721,61722],{"class":2396},"##   Copyright (C) 2006 by Ulrich Van Den Hekke                            ##\n",[2113,61724,61725],{"class":2115,"line":2230},[2113,61726,61727],{"class":2396},"##   ulrich.vdh@free.fr                                                    ##\n",[2113,61729,61730],{"class":2115,"line":2235},[2113,61731,61732],{"class":2396},"##                                                                         ##\n",[2113,61734,61735],{"class":2115,"line":2241},[2113,61736,61737],{"class":2396},"##   This program is free software; you can redistribute it and\u002For modify  ##\n",[2113,61739,61740],{"class":2115,"line":2246},[2113,61741,61742],{"class":2396},"##   it under the terms of the GNU General Public License as published by  ##\n",[2113,61744,61745],{"class":2115,"line":2464},[2113,61746,61747],{"class":2396},"##   the Free Software Foundation; either version 2 of the License, or     ##\n",[2113,61749,61750],{"class":2115,"line":2085},[2113,61751,61752],{"class":2396},"##   (at your option) any later version.                                   ##\n",[2113,61754,61755],{"class":2115,"line":2514},[2113,61756,61732],{"class":2396},[2113,61758,61759],{"class":2115,"line":2533},[2113,61760,61761],{"class":2396},"# \u002Fvar\n",[2113,61763,61764],{"class":2115,"line":2556},[2113,61765,61766],{"class":2396},"##   This program is distributed in the hope that it will be useful,       ##\n",[2113,61768,61769],{"class":2115,"line":2569},[2113,61770,61771],{"class":2396},"##   but WITHOUT ANY WARRANTY; without even the implied warranty of        ##\n",[2113,61773,61774],{"class":2115,"line":2575},[2113,61775,61776],{"class":2396},"##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         ##\n",[2113,61778,61779],{"class":2115,"line":2596},[2113,61780,61781],{"class":2396},"##   GNU General Public License for more details.                          ##\n",[2113,61783,61784],{"class":2115,"line":3192},[2113,61785,61732],{"class":2396},[2113,61787,61788],{"class":2115,"line":3213},[2113,61789,61790],{"class":2396},"##   You should have received a copy of the GNU General Public License     ##\n",[2113,61792,61793],{"class":2115,"line":3236},[2113,61794,61795],{"class":2396},"##   along with this program; if not, write to the                         ##\n",[2113,61797,61798],{"class":2115,"line":3248},[2113,61799,61800],{"class":2396},"##   Free Software Foundation, Inc.,                                       ##\n",[2113,61802,61803],{"class":2115,"line":4899},[2113,61804,61805],{"class":2396},"##   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             ##\n",[2113,61807,61808],{"class":2115,"line":1777},[2113,61809,61717],{"class":2396},[2113,61811,61812],{"class":2115,"line":4931},[2113,61813,2125],{"emptyLinePlaceholder":1767},[2113,61815,61816,61818,61821,61823,61826],{"class":2115,"line":4961},[2113,61817,4109],{"class":2326},[2113,61819,61820],{"class":2119}," [ ",[2113,61822,59250],{"class":2334},[2113,61824,61825],{"class":2119}," \u002Fetc\u002Fautobackup ] ; ",[2113,61827,7421],{"class":2326},[2113,61829,61830,61832],{"class":2115,"line":4976},[2113,61831,10331],{"class":2334},[2113,61833,61834],{"class":2149}," \u002Fetc\u002Fautobackup\n",[2113,61836,61837],{"class":2115,"line":4993},[2113,61838,61839],{"class":2326},"else\n",[2113,61841,61842,61845],{"class":2115,"line":5013},[2113,61843,61844],{"class":2334},"echo",[2113,61846,61847],{"class":2149}," \"Please configure the autobackup config file\"\n",[2113,61849,61850,61853],{"class":2115,"line":5018},[2113,61851,61852],{"class":2334},"exit",[2113,61854,61855],{"class":2166}," 1\n",[2113,61857,61858],{"class":2115,"line":5042},[2113,61859,61860],{"class":2326},"fi\n",[2113,61862,61863],{"class":2115,"line":5057},[2113,61864,2125],{"emptyLinePlaceholder":1767},[2113,61866,61867,61869,61871,61874,61877,61879,61882],{"class":2115,"line":5062},[2113,61868,4109],{"class":2326},[2113,61870,61820],{"class":2119},[2113,61872,61873],{"class":2414},"$#",[2113,61875,61876],{"class":2334}," -ne",[2113,61878,2850],{"class":2166},[2113,61880,61881],{"class":2119}," ] ; ",[2113,61883,7421],{"class":2326},[2113,61885,61886,61888],{"class":2115,"line":5098},[2113,61887,61844],{"class":2334},[2113,61889,61890],{"class":2149}," \"Please specify a project to backup\"\n",[2113,61892,61893,61895],{"class":2115,"line":5117},[2113,61894,61852],{"class":2334},[2113,61896,61897],{"class":2166}," 2\n",[2113,61899,61900],{"class":2115,"line":5142},[2113,61901,61860],{"class":2326},[2113,61903,61904],{"class":2115,"line":5148},[2113,61905,2125],{"emptyLinePlaceholder":1767},[2113,61907,61908],{"class":2115,"line":5163},[2113,61909,61910],{"class":2396},"# Define name of variable to use\n",[2113,61912,61913,61916,61918,61920,61923,61926],{"class":2115,"line":5169},[2113,61914,61915],{"class":2330},"var_name",[2113,61917,2335],{"class":2334},[2113,61919,7406],{"class":2149},[2113,61921,61922],{"class":2334},"\\$",[2113,61924,61925],{"class":2330},"$1_name",[2113,61927,7434],{"class":2149},[2113,61929,61930,61933,61935,61937,61939,61942],{"class":2115,"line":5175},[2113,61931,61932],{"class":2330},"var_source",[2113,61934,2335],{"class":2334},[2113,61936,7406],{"class":2149},[2113,61938,61922],{"class":2334},[2113,61940,61941],{"class":2330},"$1_source",[2113,61943,7434],{"class":2149},[2113,61945,61946,61949,61951,61953,61955,61958],{"class":2115,"line":5180},[2113,61947,61948],{"class":2330},"var_destination",[2113,61950,2335],{"class":2334},[2113,61952,7406],{"class":2149},[2113,61954,61922],{"class":2334},[2113,61956,61957],{"class":2330},"$1_destination",[2113,61959,7434],{"class":2149},[2113,61961,61962,61965,61967,61969,61971,61974],{"class":2115,"line":5199},[2113,61963,61964],{"class":2330},"var_inc_max",[2113,61966,2335],{"class":2334},[2113,61968,7406],{"class":2149},[2113,61970,61922],{"class":2334},[2113,61972,61973],{"class":2330},"$1_inc_max",[2113,61975,7434],{"class":2149},[2113,61977,61978],{"class":2115,"line":5204},[2113,61979,2125],{"emptyLinePlaceholder":1767},[2113,61981,61982],{"class":2115,"line":5209},[2113,61983,61984],{"class":2396},"# Define content of variable for the project\n",[2113,61986,61987,61990,61992,61994,61997,62000,62003],{"class":2115,"line":5232},[2113,61988,61989],{"class":2330},"BACKUP_SRC",[2113,61991,2335],{"class":2334},[2113,61993,20427],{"class":2149},[2113,61995,61996],{"class":2334},"eval",[2113,61998,61999],{"class":2149}," echo ",[2113,62001,62002],{"class":2330},"$var_source",[2113,62004,62005],{"class":2149},"`\n",[2113,62007,62008,62011,62013,62015,62017,62019,62022],{"class":2115,"line":5237},[2113,62009,62010],{"class":2330},"BACKUP_DST",[2113,62012,2335],{"class":2334},[2113,62014,20427],{"class":2149},[2113,62016,61996],{"class":2334},[2113,62018,61999],{"class":2149},[2113,62020,62021],{"class":2330},"$var_destination",[2113,62023,62005],{"class":2149},[2113,62025,62026,62029,62031,62033,62035,62037,62040],{"class":2115,"line":5242},[2113,62027,62028],{"class":2330},"BACKUP_NAME",[2113,62030,2335],{"class":2334},[2113,62032,20427],{"class":2149},[2113,62034,61996],{"class":2334},[2113,62036,61999],{"class":2149},[2113,62038,62039],{"class":2330},"$var_name",[2113,62041,62005],{"class":2149},[2113,62043,62044,62047,62049,62051,62053,62055,62058],{"class":2115,"line":5267},[2113,62045,62046],{"class":2330},"BACKUP_INCREMENTAL_MAX",[2113,62048,2335],{"class":2334},[2113,62050,20427],{"class":2149},[2113,62052,61996],{"class":2334},[2113,62054,61999],{"class":2149},[2113,62056,62057],{"class":2330},"$var_inc_max",[2113,62059,62005],{"class":2149},[2113,62061,62062],{"class":2115,"line":5282},[2113,62063,2125],{"emptyLinePlaceholder":1767},[2113,62065,62066,62068,62070,62073,62075,62078,62080,62082],{"class":2115,"line":5295},[2113,62067,4109],{"class":2326},[2113,62069,61820],{"class":2119},[2113,62071,62072],{"class":2334},"-z",[2113,62074,7400],{"class":2149},[2113,62076,62077],{"class":2330},"$BACKUP_NAME",[2113,62079,7406],{"class":2149},[2113,62081,61881],{"class":2119},[2113,62083,7421],{"class":2326},[2113,62085,62086,62088],{"class":2115,"line":5310},[2113,62087,61844],{"class":2334},[2113,62089,62090],{"class":2149}," \"Project undefined\"\n",[2113,62092,62093,62095],{"class":2115,"line":5315},[2113,62094,61852],{"class":2334},[2113,62096,62097],{"class":2166}," 3\n",[2113,62099,62100],{"class":2115,"line":5320},[2113,62101,61860],{"class":2326},[2113,62103,62104],{"class":2115,"line":8625},[2113,62105,2125],{"emptyLinePlaceholder":1767},[2113,62107,62108,62111,62113],{"class":2115,"line":8631},[2113,62109,62110],{"class":2330},"BACKUP_BIN",[2113,62112,2335],{"class":2334},[2113,62114,62115],{"class":2149},"dar\n",[2113,62117,62118,62121,62123,62125,62128],{"class":2115,"line":8637},[2113,62119,62120],{"class":2330},"DATE_BACKUP",[2113,62122,2335],{"class":2334},[2113,62124,20427],{"class":2149},[2113,62126,62127],{"class":2133},"date",[2113,62129,62130],{"class":2149}," +%Y%m%d`\n",[2113,62132,62133,62136,62138,62141,62143,62146,62148],{"class":2115,"line":8652},[2113,62134,62135],{"class":2330},"BACKUP_ARCHIVE",[2113,62137,2335],{"class":2334},[2113,62139,62140],{"class":2149},"\"${",[2113,62142,62028],{"class":2330},[2113,62144,62145],{"class":2149},"}_${",[2113,62147,62120],{"class":2330},[2113,62149,62150],{"class":2149},"}\"\n",[2113,62152,62153],{"class":2115,"line":8674},[2113,62154,2125],{"emptyLinePlaceholder":1767},[2113,62156,62157],{"class":2115,"line":8706},[2113,62158,62159],{"class":2396},"# From config file\n",[2113,62161,62162],{"class":2115,"line":8724},[2113,62163,62164],{"class":2396},"# COUNT_INCREMENTAL_BACKUP\n",[2113,62166,62167],{"class":2115,"line":8729},[2113,62168,62169],{"class":2396},"# LAST_BACKUP\n",[2113,62171,62172],{"class":2115,"line":8734},[2113,62173,62174],{"class":2396},"# LAST_COMPLETE_BACKUP\n",[2113,62176,62177],{"class":2115,"line":8740},[2113,62178,2125],{"emptyLinePlaceholder":1767},[2113,62180,62181],{"class":2115,"line":8772},[2113,62182,62183],{"class":2396},"# Some options\n",[2113,62185,62186],{"class":2115,"line":8778},[2113,62187,62188],{"class":2396},"# -v verbose\n",[2113,62190,62191],{"class":2115,"line":8789},[2113,62192,62193],{"class":2396},"# -c archive name\n",[2113,62195,62196],{"class":2115,"line":8795},[2113,62197,62198],{"class":2396},"# -R root place\n",[2113,62200,62201],{"class":2115,"line":8817},[2113,62202,62203],{"class":2396},"# -s slice size\n",[2113,62205,62206],{"class":2115,"line":8822},[2113,62207,62208],{"class":2396},"# -p slice pause\n",[2113,62210,62211],{"class":2115,"line":8857},[2113,62212,62213],{"class":2396},"# -D backup empty dir\n",[2113,62215,62216],{"class":2115,"line":8896},[2113,62217,62218],{"class":2396},"# -z9 compression gzip level 9\n",[2113,62220,62221],{"class":2115,"line":8901},[2113,62222,62223],{"class":2396},"# -m for minimal size\n",[2113,62225,62226],{"class":2115,"line":8907},[2113,62227,62228],{"class":2396},"# -Z exclude from compression\n",[2113,62230,62231],{"class":2115,"line":8913},[2113,62232,62233],{"class":2396},"# -P exclude file\n",[2113,62235,62236],{"class":2115,"line":8931},[2113,62237,2125],{"emptyLinePlaceholder":1767},[2113,62239,62240,62242,62245],{"class":2115,"line":8936},[2113,62241,7865],{"class":2326},[2113,62243,62244],{"class":2133}," start_backup",[2113,62246,3912],{"class":2119},[2113,62248,62249,62251,62254,62257],{"class":2115,"line":8942},[2113,62250,7426],{"class":2334},[2113,62252,62253],{"class":2149}," \"Backup of ",[2113,62255,62256],{"class":2330},"$BACKUP_SRC",[2113,62258,62259],{"class":2149}," in progress ...\"\n",[2113,62261,62262],{"class":2115,"line":8950},[2113,62263,2125],{"emptyLinePlaceholder":1767},[2113,62265,62266,62269,62271,62273,62275,62278,62280,62282,62285,62288,62291,62294,62297,62300,62302,62304,62306,62308],{"class":2115,"line":8968},[2113,62267,62268],{"class":2330},"        $BACKUP_BIN",[2113,62270,59232],{"class":2166},[2113,62272,45208],{"class":2119},[2113,62274,62010],{"class":2330},[2113,62276,62277],{"class":2119},"}${",[2113,62279,62135],{"class":2330},[2113,62281,4224],{"class":2119},[2113,62283,62284],{"class":2166},"-R",[2113,62286,62287],{"class":2330}," $BACKUP_SRC",[2113,62289,62290],{"class":2330}," $BACKUP_OPT",[2113,62292,62293],{"class":2330}," $BACKUP_EXCLUDE_EXT",[2113,62295,62296],{"class":2330}," $BACKUP_EXCLUDE_PATH",[2113,62298,62299],{"class":2119}," > ${",[2113,62301,62010],{"class":2330},[2113,62303,62277],{"class":2119},[2113,62305,62028],{"class":2330},[2113,62307,15888],{"class":2119},[2113,62309,62310],{"class":2149},".log\n",[2113,62312,62313],{"class":2115,"line":8983},[2113,62314,2599],{"class":2119},[2113,62316,62317],{"class":2115,"line":8988},[2113,62318,2125],{"emptyLinePlaceholder":1767},[2113,62320,62321,62323,62326],{"class":2115,"line":9011},[2113,62322,7865],{"class":2326},[2113,62324,62325],{"class":2133}," load_backup_config",[2113,62327,3912],{"class":2119},[2113,62329,62330,62332,62334],{"class":2115,"line":9016},[2113,62331,7426],{"class":2334},[2113,62333,25042],{"class":2166},[2113,62335,62336],{"class":2149}," \"Load config file ... \"\n",[2113,62338,62339],{"class":2115,"line":9022},[2113,62340,2125],{"emptyLinePlaceholder":1767},[2113,62342,62343,62345,62347,62349,62351,62353,62355,62357,62360],{"class":2115,"line":9045},[2113,62344,4518],{"class":2326},[2113,62346,61820],{"class":2119},[2113,62348,59250],{"class":2334},[2113,62350,45208],{"class":2119},[2113,62352,62010],{"class":2330},[2113,62354,62277],{"class":2119},[2113,62356,62028],{"class":2330},[2113,62358,62359],{"class":2119},"}.config ] ; ",[2113,62361,7421],{"class":2326},[2113,62363,62364,62366,62368,62370,62372,62374,62376],{"class":2115,"line":9076},[2113,62365,32886],{"class":2334},[2113,62367,45208],{"class":2119},[2113,62369,62010],{"class":2330},[2113,62371,62277],{"class":2119},[2113,62373,62028],{"class":2330},[2113,62375,15888],{"class":2119},[2113,62377,62378],{"class":2149},".config\n",[2113,62380,62381,62384],{"class":2115,"line":9082},[2113,62382,62383],{"class":2334},"                echo",[2113,62385,62386],{"class":2149}," \"loaded.\"\n",[2113,62388,62389],{"class":2115,"line":9105},[2113,62390,62391],{"class":2326},"        else\n",[2113,62393,62394,62397,62399],{"class":2115,"line":9138},[2113,62395,62396],{"class":2330},"                COUNT_INCREMENTAL_BACKUP",[2113,62398,2335],{"class":2334},[2113,62400,62401],{"class":2330},"$BACKUP_INCREMENTAL_MAX\n",[2113,62403,62404,62406],{"class":2115,"line":9149},[2113,62405,62383],{"class":2334},[2113,62407,62408],{"class":2149}," \"not found\"\n",[2113,62410,62411],{"class":2115,"line":9157},[2113,62412,62413],{"class":2326},"        fi\n",[2113,62415,62416],{"class":2115,"line":9182},[2113,62417,2599],{"class":2119},[2113,62419,62420],{"class":2115,"line":9188},[2113,62421,2125],{"emptyLinePlaceholder":1767},[2113,62423,62424,62426,62429],{"class":2115,"line":9193},[2113,62425,7865],{"class":2326},[2113,62427,62428],{"class":2133}," save_backup_config",[2113,62430,3912],{"class":2119},[2113,62432,62433,62435],{"class":2115,"line":9198},[2113,62434,7426],{"class":2334},[2113,62436,62437],{"class":2149}," \"Save backup config file ...\"\n",[2113,62439,62440],{"class":2115,"line":9216},[2113,62441,2125],{"emptyLinePlaceholder":1767},[2113,62443,62444,62446,62449,62452,62454,62456,62458,62460,62462,62464],{"class":2115,"line":9238},[2113,62445,7426],{"class":2334},[2113,62447,62448],{"class":2149}," \"COUNT_INCREMENTAL_BACKUP=",[2113,62450,62451],{"class":2330},"$COUNT_INCREMENTAL_BACKUP",[2113,62453,7406],{"class":2149},[2113,62455,62299],{"class":2119},[2113,62457,62010],{"class":2330},[2113,62459,62277],{"class":2119},[2113,62461,62028],{"class":2330},[2113,62463,15888],{"class":2119},[2113,62465,62378],{"class":2149},[2113,62467,62468,62470,62473,62476,62478,62481,62483,62485,62487,62489],{"class":2115,"line":9243},[2113,62469,7426],{"class":2334},[2113,62471,62472],{"class":2149}," \"LAST_BACKUP=",[2113,62474,62475],{"class":2330},"$DATE_BACKUP",[2113,62477,7406],{"class":2149},[2113,62479,62480],{"class":2119},"                          >> ${",[2113,62482,62010],{"class":2330},[2113,62484,62277],{"class":2119},[2113,62486,62028],{"class":2330},[2113,62488,15888],{"class":2119},[2113,62490,62378],{"class":2149},[2113,62492,62493,62495,62498,62501,62503,62506,62508,62510,62512,62514],{"class":2115,"line":10782},[2113,62494,7426],{"class":2334},[2113,62496,62497],{"class":2149}," \"LAST_COMPLETE_BACKUP=",[2113,62499,62500],{"class":2330},"$LAST_COMPLETE_BACKUP",[2113,62502,7406],{"class":2149},[2113,62504,62505],{"class":2119},"        >> ${",[2113,62507,62010],{"class":2330},[2113,62509,62277],{"class":2119},[2113,62511,62028],{"class":2330},[2113,62513,15888],{"class":2119},[2113,62515,62378],{"class":2149},[2113,62517,62518],{"class":2115,"line":10787},[2113,62519,2599],{"class":2119},[2113,62521,62522],{"class":2115,"line":10813},[2113,62523,2125],{"emptyLinePlaceholder":1767},[2113,62525,62526,62528,62531],{"class":2115,"line":10834},[2113,62527,7865],{"class":2326},[2113,62529,62530],{"class":2133}," define_incremental",[2113,62532,3912],{"class":2119},[2113,62534,62535,62537,62540,62542,62545,62548],{"class":2115,"line":10843},[2113,62536,7426],{"class":2334},[2113,62538,62539],{"class":2149}," \"There is ",[2113,62541,62451],{"class":2330},[2113,62543,62544],{"class":2149}," on ",[2113,62546,62547],{"class":2330},"$BACKUP_INCREMENTAL_MAX",[2113,62549,7434],{"class":2149},[2113,62551,62552,62554,62556,62558,62561,62564,62567],{"class":2115,"line":10852},[2113,62553,4518],{"class":2326},[2113,62555,61820],{"class":2119},[2113,62557,62451],{"class":2330},[2113,62559,62560],{"class":2334}," -lt",[2113,62562,62563],{"class":2330}," $BACKUP_INCREMENTAL_MAX",[2113,62565,62566],{"class":2119},"  ] ; ",[2113,62568,7421],{"class":2326},[2113,62570,62571,62573,62576,62578],{"class":2115,"line":10858},[2113,62572,62383],{"class":2334},[2113,62574,62575],{"class":2149}," \"Backup is incremental from ",[2113,62577,62500],{"class":2330},[2113,62579,7434],{"class":2149},[2113,62581,62582,62584,62586,62588,62591,62594,62596,62598],{"class":2115,"line":10880},[2113,62583,62396],{"class":2330},[2113,62585,2335],{"class":2334},[2113,62587,20427],{"class":2149},[2113,62589,62590],{"class":2133},"expr",[2113,62592,62593],{"class":2330}," $COUNT_INCREMENTAL_BACKUP",[2113,62595,7222],{"class":2149},[2113,62597,3686],{"class":2166},[2113,62599,62005],{"class":2149},[2113,62601,62602,62605,62607,62610,62612,62614,62616,62618,62621,62624,62626],{"class":2115,"line":10885},[2113,62603,62604],{"class":2330},"                BACKUP_OPT",[2113,62606,2335],{"class":2334},[2113,62608,62609],{"class":2149},"\"-A ${",[2113,62611,62010],{"class":2330},[2113,62613,62277],{"class":2149},[2113,62615,62028],{"class":2330},[2113,62617,62145],{"class":2149},[2113,62619,62620],{"class":2330},"LAST_COMPLETE_BACKUP",[2113,62622,62623],{"class":2149},"} ${",[2113,62625,61451],{"class":2330},[2113,62627,62150],{"class":2149},[2113,62629,62630],{"class":2115,"line":10890},[2113,62631,62391],{"class":2326},[2113,62633,62634,62636],{"class":2115,"line":10912},[2113,62635,62383],{"class":2334},[2113,62637,62638],{"class":2149}," \"Backup is complete\"\n",[2113,62640,62641,62643,62645],{"class":2115,"line":10918},[2113,62642,62396],{"class":2330},[2113,62644,2335],{"class":2334},[2113,62646,35509],{"class":2149},[2113,62648,62649,62652,62654],{"class":2115,"line":10925},[2113,62650,62651],{"class":2330},"                LAST_COMPLETE_BACKUP",[2113,62653,2335],{"class":2334},[2113,62655,62656],{"class":2330},"$DATE_BACKUP\n",[2113,62658,62659],{"class":2115,"line":10933},[2113,62660,62413],{"class":2326},[2113,62662,62663],{"class":2115,"line":10958},[2113,62664,2599],{"class":2119},[2113,62666,62667],{"class":2115,"line":10964},[2113,62668,2125],{"emptyLinePlaceholder":1767},[2113,62670,62671,62674],{"class":2115,"line":10970},[2113,62672,62673],{"class":2133},"load_backup_config",[2113,62675,2487],{"class":2119},[2113,62677,62678,62681],{"class":2115,"line":10977},[2113,62679,62680],{"class":2133},"define_incremental",[2113,62682,2487],{"class":2119},[2113,62684,62685,62688],{"class":2115,"line":10985},[2113,62686,62687],{"class":2133},"start_backup",[2113,62689,2487],{"class":2119},[2113,62691,62692,62695],{"class":2115,"line":11010},[2113,62693,62694],{"class":2133},"save_backup_config",[2113,62696,2487],{"class":2119},[133,62698,62700],{"id":62699},"copie-automatique","Copie automatique",[2105,62702,62704],{"className":2317,"code":62703,"language":2319,"meta":1646,"style":1646},"#!\u002Fbin\u002Fsh\n\n#############################################################################\n##   Copyright (C) 2006 by Ulrich Van Den Hekke                            ##\n##   ulrich.vdh@free.fr                                                    ##\n##                                                                         ##\n##   This program is free software; you can redistribute it and\u002For modify  ##\n##   it under the terms of the GNU General Public License as published by  ##\n##   the Free Software Foundation; either version 2 of the License, or     ##\n##   (at your option) any later version.                                   ##\n##                                                                         ##\n##   This program is distributed in the hope that it will be useful,       ##\n##   but WITHOUT ANY WARRANTY; without even the implied warranty of        ##\n##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         ##\n##   GNU General Public License for more details.                          ##\n##                                                                         ##\n##   You should have received a copy of the GNU General Public License     ##\n##   along with this program; if not, write to the                         ##\n##   Free Software Foundation, Inc.,                                       ##\n##   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             ##\n#############################################################################\n\nif [ -e \u002Fetc\u002Fautobackup ] ; then\n        . \u002Fetc\u002Fautobackup\nelse\necho \"Please configure the autobackup config file\"\nexit 1\nfi\n\nif [ $# -ne 1 ] ; then\necho \"Please specify a project to backup\"\nexit 2\nfi\n\n# Define name of variable to use\nvar_name=\"\\$$1_name\"\nvar_source=\"\\$$1_source\"\nvar_destination=\"\\$$1_destination\"\nvar_inc_max=\"\\$$1_inc_max\"\n\n# Define content of variable for the project\nBACKUP_SRC=`eval echo $var_source`\nBACKUP_DST=`eval echo $var_destination`\nBACKUP_NAME=`eval echo $var_name`\nBACKUP_INCREMENTAL_MAX=`eval echo $var_inc_max`\n\nif [ -z \"$BACKUP_NAME\" ] ; then\necho \"Project undefined\"\nexit 3\nfi\n\n# From config file\n# COUNT_INCREMENTAL_BACKUP\n# LAST_BACKUP\n# LAST_COMPLETE_BACKUP\n\nfunction load_backup_config() {\n        echo -n \"Load config file ... \"\n\n        if [ -e ${BACKUP_DST}${BACKUP_NAME}.config ] ; then\n                . ${BACKUP_DST}${BACKUP_NAME}.config\n\n                BACKUP_ARCHIVE=\"${BACKUP_NAME}_${LAST_BACKUP}\"\n                echo \"loaded.\"\n        else\n                echo \"not found\"\n                exit 4\n        fi\n}\n\nfunction save_by_network() {\n        echo -n \"Save on computer $HOST as $USER on $DIR\"\n\n        ping -c 3 -W 10 $HOST > \u002Fdev\u002Fnull\n\n        if [ $? -ne 0 ] ; then\n                echo \"failed\"\n        else\n                DESTINATION=\"${HOST}:${DIR}\"\n                if [ -n $USER ] ; then\n                        DESTINATION=\"${USER}@${DESTINATION}\"\n                fi\n                scp ${BACKUP_DST}${BACKUP_ARCHIVE}* $DESTINATION\n\n                if [ $? -ne 0 ] ; then\n                        echo \"failed\"\n                else\n                        echo \"ok\"\n                fi\n        fi\n}\n\nfunction save_by_usb() {\n        echo -n \"Save on drive $DEV \"\n\n        if [ -e $DEV ] ; then\n                TOUMOUNT=1\n                LINECOUNT=`mount | grep $DEV | cut -f 1 -d\\  | wc -l `\n                if [ $LINECOUNT -gt 0 ] ; then\n                        MOUNTPOINT=`mount | grep $DEV | cut -f 3 -d\\ `\n                        TOUMOUNT=0\n                else\n                        mount $MOUNTPOINT\n\n                        if [ $? -ne 0 ] ; then\n                                echo \"mount failed \"\n                        fi\n                fi\n                if [ -d ${MOUNTPOINT}${PATHNAME} ] ; then\n                        for FILE in `ls -1 ${BACKUP_DST}${BACKUP_ARCHIVE}*` ; do\n                                FILENAME=`basename $FILE`\n                                if [ -e ${MOUNTPOINT}${PATHNAME}${FILENAME} ] ; then\n                                        echo \"exist \"\n                                else\n                                        cp ${FILE} ${MOUNTPOINT}${PATHNAME}\n                                fi\n                        done\n\n                        echo \"ok\"\n                else\n                        echo \"failed\"\n                fi\n                if [ $TOUMOUNT -eq 1 ] ; then\n                        umount $MOUNTPOINT\n                fi\n\n        else\n                echo \"device not found\"\n        fi\n}\n\nfunction copy_to() {\n        echo \"Start make copy of backup $BACKUP_NAME\"\n\n        for NAME in $COPYTO ; do\n                var_type=\"\\$${NAME}_type\"\n                TYPE=`eval echo $var_type`\n\n                echo \"--> $NAME as $TYPE\"\n                case $TYPE in\n                        \"usb\")\n                                var_dev=\"\\$${NAME}_dev\"\n                                DEV=`eval echo $var_dev`\n\n                                var_mountpoint=\"\\$${NAME}_mountpoint\"\n                                MOUNTPOINT=`eval echo $var_mountpoint`\n\n                                var_pathname=\"\\$${NAME}_pathname\"\n                                PATHNAME=`eval echo $var_pathname`\n\n                                save_by_usb;\n                        ;;\n\n                        \"net\")\n                                var_host=\"\\$${NAME}_host\"\n                                HOST=`eval echo $var_host`\n\n                                var_user=\"\\$${NAME}_user\"\n                                USER=`eval echo $var_user`\n\n                                var_dir=\"\\$${NAME}_dir\"\n                                DIR=`eval echo $var_dir`\n\n                                save_by_network;\n                        ;;\n                esac\n        done\n}\n\ncopy_to;\n",[184,62705,62706,62710,62714,62718,62722,62726,62730,62734,62738,62742,62746,62750,62754,62758,62762,62766,62770,62774,62778,62782,62786,62790,62794,62806,62812,62816,62822,62828,62832,62836,62852,62858,62864,62868,62872,62876,62890,62904,62918,62932,62936,62940,62956,62972,62988,63004,63008,63026,63032,63038,63042,63046,63050,63054,63058,63062,63066,63074,63082,63086,63106,63122,63126,63144,63150,63154,63160,63168,63172,63176,63180,63189,63214,63218,63241,63245,63262,63269,63273,63293,63308,63328,63333,63353,63357,63373,63380,63385,63392,63396,63400,63404,63408,63417,63432,63436,63451,63460,63503,63521,63552,63561,63565,63573,63577,63593,63601,63606,63610,63634,63669,63686,63712,63720,63725,63745,63750,63755,63759,63765,63769,63775,63779,63797,63804,63808,63812,63816,63823,63827,63831,63835,63844,63855,63859,63875,63894,63912,63916,63933,63944,63951,63969,63979,63983,63997,64007,64011,64025,64035,64039,64046,64051,64056,64064,64083,64094,64099,64114,64125,64130,64145,64156,64161,64169,64174,64180,64186,64191,64196],{"__ignoreMap":1646},[2113,62707,62708],{"class":2115,"line":2116},[2113,62709,61708],{"class":2396},[2113,62711,62712],{"class":2115,"line":1647},[2113,62713,2125],{"emptyLinePlaceholder":1767},[2113,62715,62716],{"class":2115,"line":1652},[2113,62717,61717],{"class":2396},[2113,62719,62720],{"class":2115,"line":2185},[2113,62721,61722],{"class":2396},[2113,62723,62724],{"class":2115,"line":2230},[2113,62725,61727],{"class":2396},[2113,62727,62728],{"class":2115,"line":2235},[2113,62729,61732],{"class":2396},[2113,62731,62732],{"class":2115,"line":2241},[2113,62733,61737],{"class":2396},[2113,62735,62736],{"class":2115,"line":2246},[2113,62737,61742],{"class":2396},[2113,62739,62740],{"class":2115,"line":2464},[2113,62741,61747],{"class":2396},[2113,62743,62744],{"class":2115,"line":2085},[2113,62745,61752],{"class":2396},[2113,62747,62748],{"class":2115,"line":2514},[2113,62749,61732],{"class":2396},[2113,62751,62752],{"class":2115,"line":2533},[2113,62753,61766],{"class":2396},[2113,62755,62756],{"class":2115,"line":2556},[2113,62757,61771],{"class":2396},[2113,62759,62760],{"class":2115,"line":2569},[2113,62761,61776],{"class":2396},[2113,62763,62764],{"class":2115,"line":2575},[2113,62765,61781],{"class":2396},[2113,62767,62768],{"class":2115,"line":2596},[2113,62769,61732],{"class":2396},[2113,62771,62772],{"class":2115,"line":3192},[2113,62773,61790],{"class":2396},[2113,62775,62776],{"class":2115,"line":3213},[2113,62777,61795],{"class":2396},[2113,62779,62780],{"class":2115,"line":3236},[2113,62781,61800],{"class":2396},[2113,62783,62784],{"class":2115,"line":3248},[2113,62785,61805],{"class":2396},[2113,62787,62788],{"class":2115,"line":4899},[2113,62789,61717],{"class":2396},[2113,62791,62792],{"class":2115,"line":1777},[2113,62793,2125],{"emptyLinePlaceholder":1767},[2113,62795,62796,62798,62800,62802,62804],{"class":2115,"line":4931},[2113,62797,4109],{"class":2326},[2113,62799,61820],{"class":2119},[2113,62801,59250],{"class":2334},[2113,62803,61825],{"class":2119},[2113,62805,7421],{"class":2326},[2113,62807,62808,62810],{"class":2115,"line":4961},[2113,62809,10331],{"class":2334},[2113,62811,61834],{"class":2149},[2113,62813,62814],{"class":2115,"line":4976},[2113,62815,61839],{"class":2326},[2113,62817,62818,62820],{"class":2115,"line":4993},[2113,62819,61844],{"class":2334},[2113,62821,61847],{"class":2149},[2113,62823,62824,62826],{"class":2115,"line":5013},[2113,62825,61852],{"class":2334},[2113,62827,61855],{"class":2166},[2113,62829,62830],{"class":2115,"line":5018},[2113,62831,61860],{"class":2326},[2113,62833,62834],{"class":2115,"line":5042},[2113,62835,2125],{"emptyLinePlaceholder":1767},[2113,62837,62838,62840,62842,62844,62846,62848,62850],{"class":2115,"line":5057},[2113,62839,4109],{"class":2326},[2113,62841,61820],{"class":2119},[2113,62843,61873],{"class":2414},[2113,62845,61876],{"class":2334},[2113,62847,2850],{"class":2166},[2113,62849,61881],{"class":2119},[2113,62851,7421],{"class":2326},[2113,62853,62854,62856],{"class":2115,"line":5062},[2113,62855,61844],{"class":2334},[2113,62857,61890],{"class":2149},[2113,62859,62860,62862],{"class":2115,"line":5098},[2113,62861,61852],{"class":2334},[2113,62863,61897],{"class":2166},[2113,62865,62866],{"class":2115,"line":5117},[2113,62867,61860],{"class":2326},[2113,62869,62870],{"class":2115,"line":5142},[2113,62871,2125],{"emptyLinePlaceholder":1767},[2113,62873,62874],{"class":2115,"line":5148},[2113,62875,61910],{"class":2396},[2113,62877,62878,62880,62882,62884,62886,62888],{"class":2115,"line":5163},[2113,62879,61915],{"class":2330},[2113,62881,2335],{"class":2334},[2113,62883,7406],{"class":2149},[2113,62885,61922],{"class":2334},[2113,62887,61925],{"class":2330},[2113,62889,7434],{"class":2149},[2113,62891,62892,62894,62896,62898,62900,62902],{"class":2115,"line":5169},[2113,62893,61932],{"class":2330},[2113,62895,2335],{"class":2334},[2113,62897,7406],{"class":2149},[2113,62899,61922],{"class":2334},[2113,62901,61941],{"class":2330},[2113,62903,7434],{"class":2149},[2113,62905,62906,62908,62910,62912,62914,62916],{"class":2115,"line":5175},[2113,62907,61948],{"class":2330},[2113,62909,2335],{"class":2334},[2113,62911,7406],{"class":2149},[2113,62913,61922],{"class":2334},[2113,62915,61957],{"class":2330},[2113,62917,7434],{"class":2149},[2113,62919,62920,62922,62924,62926,62928,62930],{"class":2115,"line":5180},[2113,62921,61964],{"class":2330},[2113,62923,2335],{"class":2334},[2113,62925,7406],{"class":2149},[2113,62927,61922],{"class":2334},[2113,62929,61973],{"class":2330},[2113,62931,7434],{"class":2149},[2113,62933,62934],{"class":2115,"line":5199},[2113,62935,2125],{"emptyLinePlaceholder":1767},[2113,62937,62938],{"class":2115,"line":5204},[2113,62939,61984],{"class":2396},[2113,62941,62942,62944,62946,62948,62950,62952,62954],{"class":2115,"line":5209},[2113,62943,61989],{"class":2330},[2113,62945,2335],{"class":2334},[2113,62947,20427],{"class":2149},[2113,62949,61996],{"class":2334},[2113,62951,61999],{"class":2149},[2113,62953,62002],{"class":2330},[2113,62955,62005],{"class":2149},[2113,62957,62958,62960,62962,62964,62966,62968,62970],{"class":2115,"line":5232},[2113,62959,62010],{"class":2330},[2113,62961,2335],{"class":2334},[2113,62963,20427],{"class":2149},[2113,62965,61996],{"class":2334},[2113,62967,61999],{"class":2149},[2113,62969,62021],{"class":2330},[2113,62971,62005],{"class":2149},[2113,62973,62974,62976,62978,62980,62982,62984,62986],{"class":2115,"line":5237},[2113,62975,62028],{"class":2330},[2113,62977,2335],{"class":2334},[2113,62979,20427],{"class":2149},[2113,62981,61996],{"class":2334},[2113,62983,61999],{"class":2149},[2113,62985,62039],{"class":2330},[2113,62987,62005],{"class":2149},[2113,62989,62990,62992,62994,62996,62998,63000,63002],{"class":2115,"line":5242},[2113,62991,62046],{"class":2330},[2113,62993,2335],{"class":2334},[2113,62995,20427],{"class":2149},[2113,62997,61996],{"class":2334},[2113,62999,61999],{"class":2149},[2113,63001,62057],{"class":2330},[2113,63003,62005],{"class":2149},[2113,63005,63006],{"class":2115,"line":5267},[2113,63007,2125],{"emptyLinePlaceholder":1767},[2113,63009,63010,63012,63014,63016,63018,63020,63022,63024],{"class":2115,"line":5282},[2113,63011,4109],{"class":2326},[2113,63013,61820],{"class":2119},[2113,63015,62072],{"class":2334},[2113,63017,7400],{"class":2149},[2113,63019,62077],{"class":2330},[2113,63021,7406],{"class":2149},[2113,63023,61881],{"class":2119},[2113,63025,7421],{"class":2326},[2113,63027,63028,63030],{"class":2115,"line":5295},[2113,63029,61844],{"class":2334},[2113,63031,62090],{"class":2149},[2113,63033,63034,63036],{"class":2115,"line":5310},[2113,63035,61852],{"class":2334},[2113,63037,62097],{"class":2166},[2113,63039,63040],{"class":2115,"line":5315},[2113,63041,61860],{"class":2326},[2113,63043,63044],{"class":2115,"line":5320},[2113,63045,2125],{"emptyLinePlaceholder":1767},[2113,63047,63048],{"class":2115,"line":8625},[2113,63049,62159],{"class":2396},[2113,63051,63052],{"class":2115,"line":8631},[2113,63053,62164],{"class":2396},[2113,63055,63056],{"class":2115,"line":8637},[2113,63057,62169],{"class":2396},[2113,63059,63060],{"class":2115,"line":8652},[2113,63061,62174],{"class":2396},[2113,63063,63064],{"class":2115,"line":8674},[2113,63065,2125],{"emptyLinePlaceholder":1767},[2113,63067,63068,63070,63072],{"class":2115,"line":8706},[2113,63069,7865],{"class":2326},[2113,63071,62325],{"class":2133},[2113,63073,3912],{"class":2119},[2113,63075,63076,63078,63080],{"class":2115,"line":8724},[2113,63077,7426],{"class":2334},[2113,63079,25042],{"class":2166},[2113,63081,62336],{"class":2149},[2113,63083,63084],{"class":2115,"line":8729},[2113,63085,2125],{"emptyLinePlaceholder":1767},[2113,63087,63088,63090,63092,63094,63096,63098,63100,63102,63104],{"class":2115,"line":8734},[2113,63089,4518],{"class":2326},[2113,63091,61820],{"class":2119},[2113,63093,59250],{"class":2334},[2113,63095,45208],{"class":2119},[2113,63097,62010],{"class":2330},[2113,63099,62277],{"class":2119},[2113,63101,62028],{"class":2330},[2113,63103,62359],{"class":2119},[2113,63105,7421],{"class":2326},[2113,63107,63108,63110,63112,63114,63116,63118,63120],{"class":2115,"line":8740},[2113,63109,32886],{"class":2334},[2113,63111,45208],{"class":2119},[2113,63113,62010],{"class":2330},[2113,63115,62277],{"class":2119},[2113,63117,62028],{"class":2330},[2113,63119,15888],{"class":2119},[2113,63121,62378],{"class":2149},[2113,63123,63124],{"class":2115,"line":8772},[2113,63125,2125],{"emptyLinePlaceholder":1767},[2113,63127,63128,63131,63133,63135,63137,63139,63142],{"class":2115,"line":8778},[2113,63129,63130],{"class":2330},"                BACKUP_ARCHIVE",[2113,63132,2335],{"class":2334},[2113,63134,62140],{"class":2149},[2113,63136,62028],{"class":2330},[2113,63138,62145],{"class":2149},[2113,63140,63141],{"class":2330},"LAST_BACKUP",[2113,63143,62150],{"class":2149},[2113,63145,63146,63148],{"class":2115,"line":8789},[2113,63147,62383],{"class":2334},[2113,63149,62386],{"class":2149},[2113,63151,63152],{"class":2115,"line":8795},[2113,63153,62391],{"class":2326},[2113,63155,63156,63158],{"class":2115,"line":8817},[2113,63157,62383],{"class":2334},[2113,63159,62408],{"class":2149},[2113,63161,63162,63165],{"class":2115,"line":8822},[2113,63163,63164],{"class":2334},"                exit",[2113,63166,63167],{"class":2166}," 4\n",[2113,63169,63170],{"class":2115,"line":8857},[2113,63171,62413],{"class":2326},[2113,63173,63174],{"class":2115,"line":8896},[2113,63175,2599],{"class":2119},[2113,63177,63178],{"class":2115,"line":8901},[2113,63179,2125],{"emptyLinePlaceholder":1767},[2113,63181,63182,63184,63187],{"class":2115,"line":8907},[2113,63183,7865],{"class":2326},[2113,63185,63186],{"class":2133}," save_by_network",[2113,63188,3912],{"class":2119},[2113,63190,63191,63193,63195,63198,63201,63204,63207,63209,63212],{"class":2115,"line":8913},[2113,63192,7426],{"class":2334},[2113,63194,25042],{"class":2166},[2113,63196,63197],{"class":2149}," \"Save on computer ",[2113,63199,63200],{"class":2330},"$HOST",[2113,63202,63203],{"class":2149}," as ",[2113,63205,63206],{"class":2330},"$USER",[2113,63208,62544],{"class":2149},[2113,63210,63211],{"class":2330},"$DIR",[2113,63213,7434],{"class":2149},[2113,63215,63216],{"class":2115,"line":8931},[2113,63217,2125],{"emptyLinePlaceholder":1767},[2113,63219,63220,63223,63225,63227,63230,63233,63236,63238],{"class":2115,"line":8936},[2113,63221,63222],{"class":2133},"        ping",[2113,63224,59232],{"class":2166},[2113,63226,16106],{"class":2166},[2113,63228,63229],{"class":2166}," -W",[2113,63231,63232],{"class":2166}," 10",[2113,63234,63235],{"class":2330}," $HOST",[2113,63237,59957],{"class":2119},[2113,63239,63240],{"class":2149},"\u002Fdev\u002Fnull\n",[2113,63242,63243],{"class":2115,"line":8942},[2113,63244,2125],{"emptyLinePlaceholder":1767},[2113,63246,63247,63249,63251,63254,63256,63258,63260],{"class":2115,"line":8950},[2113,63248,4518],{"class":2326},[2113,63250,61820],{"class":2119},[2113,63252,63253],{"class":2414},"$?",[2113,63255,61876],{"class":2334},[2113,63257,3208],{"class":2166},[2113,63259,61881],{"class":2119},[2113,63261,7421],{"class":2326},[2113,63263,63264,63266],{"class":2115,"line":8968},[2113,63265,62383],{"class":2334},[2113,63267,63268],{"class":2149}," \"failed\"\n",[2113,63270,63271],{"class":2115,"line":8983},[2113,63272,62391],{"class":2326},[2113,63274,63275,63278,63280,63282,63285,63288,63291],{"class":2115,"line":8988},[2113,63276,63277],{"class":2330},"                DESTINATION",[2113,63279,2335],{"class":2334},[2113,63281,62140],{"class":2149},[2113,63283,63284],{"class":2330},"HOST",[2113,63286,63287],{"class":2149},"}:${",[2113,63289,63290],{"class":2330},"DIR",[2113,63292,62150],{"class":2149},[2113,63294,63295,63297,63299,63301,63304,63306],{"class":2115,"line":9011},[2113,63296,5065],{"class":2326},[2113,63298,61820],{"class":2119},[2113,63300,59256],{"class":2334},[2113,63302,63303],{"class":2330}," $USER",[2113,63305,61881],{"class":2119},[2113,63307,7421],{"class":2326},[2113,63309,63310,63313,63315,63317,63320,63323,63326],{"class":2115,"line":9016},[2113,63311,63312],{"class":2330},"                        DESTINATION",[2113,63314,2335],{"class":2334},[2113,63316,62140],{"class":2149},[2113,63318,63319],{"class":2330},"USER",[2113,63321,63322],{"class":2149},"}@${",[2113,63324,63325],{"class":2330},"DESTINATION",[2113,63327,62150],{"class":2149},[2113,63329,63330],{"class":2115,"line":9022},[2113,63331,63332],{"class":2326},"                fi\n",[2113,63334,63335,63338,63340,63342,63344,63346,63348,63350],{"class":2115,"line":9045},[2113,63336,63337],{"class":2133},"                scp",[2113,63339,45208],{"class":2119},[2113,63341,62010],{"class":2330},[2113,63343,62277],{"class":2119},[2113,63345,62135],{"class":2330},[2113,63347,15888],{"class":2119},[2113,63349,2676],{"class":2414},[2113,63351,63352],{"class":2330}," $DESTINATION\n",[2113,63354,63355],{"class":2115,"line":9076},[2113,63356,2125],{"emptyLinePlaceholder":1767},[2113,63358,63359,63361,63363,63365,63367,63369,63371],{"class":2115,"line":9082},[2113,63360,5065],{"class":2326},[2113,63362,61820],{"class":2119},[2113,63364,63253],{"class":2414},[2113,63366,61876],{"class":2334},[2113,63368,3208],{"class":2166},[2113,63370,61881],{"class":2119},[2113,63372,7421],{"class":2326},[2113,63374,63375,63378],{"class":2115,"line":9105},[2113,63376,63377],{"class":2334},"                        echo",[2113,63379,63268],{"class":2149},[2113,63381,63382],{"class":2115,"line":9138},[2113,63383,63384],{"class":2326},"                else\n",[2113,63386,63387,63389],{"class":2115,"line":9149},[2113,63388,63377],{"class":2334},[2113,63390,63391],{"class":2149}," \"ok\"\n",[2113,63393,63394],{"class":2115,"line":9157},[2113,63395,63332],{"class":2326},[2113,63397,63398],{"class":2115,"line":9182},[2113,63399,62413],{"class":2326},[2113,63401,63402],{"class":2115,"line":9188},[2113,63403,2599],{"class":2119},[2113,63405,63406],{"class":2115,"line":9193},[2113,63407,2125],{"emptyLinePlaceholder":1767},[2113,63409,63410,63412,63415],{"class":2115,"line":9198},[2113,63411,7865],{"class":2326},[2113,63413,63414],{"class":2133}," save_by_usb",[2113,63416,3912],{"class":2119},[2113,63418,63419,63421,63423,63426,63429],{"class":2115,"line":9216},[2113,63420,7426],{"class":2334},[2113,63422,25042],{"class":2166},[2113,63424,63425],{"class":2149}," \"Save on drive ",[2113,63427,63428],{"class":2330},"$DEV",[2113,63430,63431],{"class":2149}," \"\n",[2113,63433,63434],{"class":2115,"line":9238},[2113,63435,2125],{"emptyLinePlaceholder":1767},[2113,63437,63438,63440,63442,63444,63447,63449],{"class":2115,"line":9243},[2113,63439,4518],{"class":2326},[2113,63441,61820],{"class":2119},[2113,63443,59250],{"class":2334},[2113,63445,63446],{"class":2330}," $DEV",[2113,63448,61881],{"class":2119},[2113,63450,7421],{"class":2326},[2113,63452,63453,63456,63458],{"class":2115,"line":10782},[2113,63454,63455],{"class":2330},"                TOUMOUNT",[2113,63457,2335],{"class":2334},[2113,63459,35509],{"class":2149},[2113,63461,63462,63465,63467,63469,63471,63474,63477,63479,63481,63484,63486,63488,63490,63493,63495,63498,63500],{"class":2115,"line":10787},[2113,63463,63464],{"class":2330},"                LINECOUNT",[2113,63466,2335],{"class":2334},[2113,63468,20427],{"class":2149},[2113,63470,979],{"class":2133},[2113,63472,63473],{"class":2119}," |",[2113,63475,63476],{"class":2133}," grep",[2113,63478,63446],{"class":2330},[2113,63480,63473],{"class":2119},[2113,63482,63483],{"class":2133}," cut",[2113,63485,61151],{"class":2166},[2113,63487,2850],{"class":2166},[2113,63489,7373],{"class":2166},[2113,63491,63492],{"class":2334},"\\ ",[2113,63494,63473],{"class":2119},[2113,63496,63497],{"class":2133}," wc",[2113,63499,25019],{"class":2166},[2113,63501,63502],{"class":2149}," `\n",[2113,63504,63505,63507,63509,63512,63515,63517,63519],{"class":2115,"line":10813},[2113,63506,5065],{"class":2326},[2113,63508,61820],{"class":2119},[2113,63510,63511],{"class":2330},"$LINECOUNT",[2113,63513,63514],{"class":2334}," -gt",[2113,63516,3208],{"class":2166},[2113,63518,61881],{"class":2119},[2113,63520,7421],{"class":2326},[2113,63522,63523,63526,63528,63530,63532,63534,63536,63538,63540,63542,63544,63546,63548,63550],{"class":2115,"line":10834},[2113,63524,63525],{"class":2330},"                        MOUNTPOINT",[2113,63527,2335],{"class":2334},[2113,63529,20427],{"class":2149},[2113,63531,979],{"class":2133},[2113,63533,63473],{"class":2119},[2113,63535,63476],{"class":2133},[2113,63537,63446],{"class":2330},[2113,63539,63473],{"class":2119},[2113,63541,63483],{"class":2133},[2113,63543,61151],{"class":2166},[2113,63545,16106],{"class":2166},[2113,63547,7373],{"class":2166},[2113,63549,63492],{"class":2334},[2113,63551,62005],{"class":2149},[2113,63553,63554,63557,63559],{"class":2115,"line":10843},[2113,63555,63556],{"class":2330},"                        TOUMOUNT",[2113,63558,2335],{"class":2334},[2113,63560,18077],{"class":2149},[2113,63562,63563],{"class":2115,"line":10852},[2113,63564,63384],{"class":2326},[2113,63566,63567,63570],{"class":2115,"line":10858},[2113,63568,63569],{"class":2133},"                        mount",[2113,63571,63572],{"class":2330}," $MOUNTPOINT\n",[2113,63574,63575],{"class":2115,"line":10880},[2113,63576,2125],{"emptyLinePlaceholder":1767},[2113,63578,63579,63581,63583,63585,63587,63589,63591],{"class":2115,"line":10885},[2113,63580,6849],{"class":2326},[2113,63582,61820],{"class":2119},[2113,63584,63253],{"class":2414},[2113,63586,61876],{"class":2334},[2113,63588,3208],{"class":2166},[2113,63590,61881],{"class":2119},[2113,63592,7421],{"class":2326},[2113,63594,63595,63598],{"class":2115,"line":10890},[2113,63596,63597],{"class":2334},"                                echo",[2113,63599,63600],{"class":2149}," \"mount failed \"\n",[2113,63602,63603],{"class":2115,"line":10912},[2113,63604,63605],{"class":2326},"                        fi\n",[2113,63607,63608],{"class":2115,"line":10918},[2113,63609,63332],{"class":2326},[2113,63611,63612,63614,63616,63619,63621,63624,63626,63629,63632],{"class":2115,"line":10925},[2113,63613,5065],{"class":2326},[2113,63615,61820],{"class":2119},[2113,63617,63618],{"class":2334},"-d",[2113,63620,45208],{"class":2119},[2113,63622,63623],{"class":2330},"MOUNTPOINT",[2113,63625,62277],{"class":2119},[2113,63627,63628],{"class":2330},"PATHNAME",[2113,63630,63631],{"class":2119},"} ] ; ",[2113,63633,7421],{"class":2326},[2113,63635,63636,63639,63642,63644,63646,63648,63651,63653,63655,63657,63659,63661,63663,63665,63667],{"class":2115,"line":10933},[2113,63637,63638],{"class":2326},"                        for",[2113,63640,63641],{"class":2330}," FILE",[2113,63643,10067],{"class":2326},[2113,63645,17813],{"class":2149},[2113,63647,50517],{"class":2133},[2113,63649,63650],{"class":2166}," -1",[2113,63652,45208],{"class":2149},[2113,63654,62010],{"class":2330},[2113,63656,62277],{"class":2149},[2113,63658,62135],{"class":2330},[2113,63660,15888],{"class":2149},[2113,63662,2676],{"class":2414},[2113,63664,20427],{"class":2149},[2113,63666,50522],{"class":2119},[2113,63668,7384],{"class":2326},[2113,63670,63671,63674,63676,63678,63681,63684],{"class":2115,"line":10958},[2113,63672,63673],{"class":2330},"                                FILENAME",[2113,63675,2335],{"class":2334},[2113,63677,20427],{"class":2149},[2113,63679,63680],{"class":2133},"basename",[2113,63682,63683],{"class":2330}," $FILE",[2113,63685,62005],{"class":2149},[2113,63687,63688,63691,63693,63695,63697,63699,63701,63703,63705,63708,63710],{"class":2115,"line":10964},[2113,63689,63690],{"class":2326},"                                if",[2113,63692,61820],{"class":2119},[2113,63694,59250],{"class":2334},[2113,63696,45208],{"class":2119},[2113,63698,63623],{"class":2330},[2113,63700,62277],{"class":2119},[2113,63702,63628],{"class":2330},[2113,63704,62277],{"class":2119},[2113,63706,63707],{"class":2330},"FILENAME",[2113,63709,63631],{"class":2119},[2113,63711,7421],{"class":2326},[2113,63713,63714,63717],{"class":2115,"line":10970},[2113,63715,63716],{"class":2334},"                                        echo",[2113,63718,63719],{"class":2149}," \"exist \"\n",[2113,63721,63722],{"class":2115,"line":10977},[2113,63723,63724],{"class":2326},"                                else\n",[2113,63726,63727,63730,63732,63735,63737,63739,63741,63743],{"class":2115,"line":10985},[2113,63728,63729],{"class":2133},"                                        cp",[2113,63731,45208],{"class":2119},[2113,63733,63734],{"class":2330},"FILE",[2113,63736,62623],{"class":2119},[2113,63738,63623],{"class":2330},[2113,63740,62277],{"class":2119},[2113,63742,63628],{"class":2330},[2113,63744,2599],{"class":2119},[2113,63746,63747],{"class":2115,"line":11010},[2113,63748,63749],{"class":2326},"                                fi\n",[2113,63751,63752],{"class":2115,"line":11015},[2113,63753,63754],{"class":2326},"                        done\n",[2113,63756,63757],{"class":2115,"line":11020},[2113,63758,2125],{"emptyLinePlaceholder":1767},[2113,63760,63761,63763],{"class":2115,"line":11026},[2113,63762,63377],{"class":2334},[2113,63764,63391],{"class":2149},[2113,63766,63767],{"class":2115,"line":11049},[2113,63768,63384],{"class":2326},[2113,63770,63771,63773],{"class":2115,"line":11062},[2113,63772,63377],{"class":2334},[2113,63774,63268],{"class":2149},[2113,63776,63777],{"class":2115,"line":11074},[2113,63778,63332],{"class":2326},[2113,63780,63781,63783,63785,63788,63791,63793,63795],{"class":2115,"line":11079},[2113,63782,5065],{"class":2326},[2113,63784,61820],{"class":2119},[2113,63786,63787],{"class":2330},"$TOUMOUNT",[2113,63789,63790],{"class":2334}," -eq",[2113,63792,2850],{"class":2166},[2113,63794,61881],{"class":2119},[2113,63796,7421],{"class":2326},[2113,63798,63799,63802],{"class":2115,"line":11084},[2113,63800,63801],{"class":2133},"                        umount",[2113,63803,63572],{"class":2330},[2113,63805,63806],{"class":2115,"line":11090},[2113,63807,63332],{"class":2326},[2113,63809,63810],{"class":2115,"line":11124},[2113,63811,2125],{"emptyLinePlaceholder":1767},[2113,63813,63814],{"class":2115,"line":11150},[2113,63815,62391],{"class":2326},[2113,63817,63818,63820],{"class":2115,"line":11169},[2113,63819,62383],{"class":2334},[2113,63821,63822],{"class":2149}," \"device not found\"\n",[2113,63824,63825],{"class":2115,"line":11197},[2113,63826,62413],{"class":2326},[2113,63828,63829],{"class":2115,"line":11202},[2113,63830,2599],{"class":2119},[2113,63832,63833],{"class":2115,"line":11207},[2113,63834,2125],{"emptyLinePlaceholder":1767},[2113,63836,63837,63839,63842],{"class":2115,"line":11213},[2113,63838,7865],{"class":2326},[2113,63840,63841],{"class":2133}," copy_to",[2113,63843,3912],{"class":2119},[2113,63845,63846,63848,63851,63853],{"class":2115,"line":11239},[2113,63847,7426],{"class":2334},[2113,63849,63850],{"class":2149}," \"Start make copy of backup ",[2113,63852,62077],{"class":2330},[2113,63854,7434],{"class":2149},[2113,63856,63857],{"class":2115,"line":11260},[2113,63858,2125],{"emptyLinePlaceholder":1767},[2113,63860,63861,63863,63866,63868,63871,63873],{"class":2115,"line":11269},[2113,63862,14355],{"class":2326},[2113,63864,63865],{"class":2330}," NAME",[2113,63867,10067],{"class":2326},[2113,63869,63870],{"class":2330}," $COPYTO",[2113,63872,50522],{"class":2119},[2113,63874,7384],{"class":2326},[2113,63876,63877,63880,63882,63884,63886,63888,63891],{"class":2115,"line":11278},[2113,63878,63879],{"class":2330},"                var_type",[2113,63881,2335],{"class":2334},[2113,63883,7406],{"class":2149},[2113,63885,61922],{"class":2334},[2113,63887,15877],{"class":2149},[2113,63889,63890],{"class":2330},"NAME",[2113,63892,63893],{"class":2149},"}_type\"\n",[2113,63895,63896,63899,63901,63903,63905,63907,63910],{"class":2115,"line":11283},[2113,63897,63898],{"class":2330},"                TYPE",[2113,63900,2335],{"class":2334},[2113,63902,20427],{"class":2149},[2113,63904,61996],{"class":2334},[2113,63906,61999],{"class":2149},[2113,63908,63909],{"class":2330},"$var_type",[2113,63911,62005],{"class":2149},[2113,63913,63914],{"class":2115,"line":11290},[2113,63915,2125],{"emptyLinePlaceholder":1767},[2113,63917,63918,63920,63923,63926,63928,63931],{"class":2115,"line":11298},[2113,63919,62383],{"class":2334},[2113,63921,63922],{"class":2149}," \"--> ",[2113,63924,63925],{"class":2330},"$NAME",[2113,63927,63203],{"class":2149},[2113,63929,63930],{"class":2330},"$TYPE",[2113,63932,7434],{"class":2149},[2113,63934,63935,63938,63941],{"class":2115,"line":11309},[2113,63936,63937],{"class":2326},"                case",[2113,63939,63940],{"class":2330}," $TYPE",[2113,63942,63943],{"class":2326}," in\n",[2113,63945,63946,63949],{"class":2115,"line":11314},[2113,63947,63948],{"class":2149},"                        \"usb\"",[2113,63950,4660],{"class":2119},[2113,63952,63953,63956,63958,63960,63962,63964,63966],{"class":2115,"line":11319},[2113,63954,63955],{"class":2330},"                                var_dev",[2113,63957,2335],{"class":2334},[2113,63959,7406],{"class":2149},[2113,63961,61922],{"class":2334},[2113,63963,15877],{"class":2149},[2113,63965,63890],{"class":2330},[2113,63967,63968],{"class":2149},"}_dev\"\n",[2113,63970,63971,63974,63976],{"class":2115,"line":11325},[2113,63972,63973],{"class":2330},"                                DEV",[2113,63975,2335],{"class":2334},[2113,63977,63978],{"class":2119},"`eval echo $var_dev`\n",[2113,63980,63981],{"class":2115,"line":11356},[2113,63982,2125],{"emptyLinePlaceholder":1767},[2113,63984,63985,63988,63990,63992,63994],{"class":2115,"line":11363},[2113,63986,63987],{"class":2149},"                                var_mountpoint=\"",[2113,63989,61922],{"class":2334},[2113,63991,15877],{"class":2149},[2113,63993,63890],{"class":2330},[2113,63995,63996],{"class":2149},"}_mountpoint\"\n",[2113,63998,63999,64002,64004],{"class":2115,"line":11371},[2113,64000,64001],{"class":2330},"                                MOUNTPOINT",[2113,64003,2335],{"class":2334},[2113,64005,64006],{"class":2119},"`eval echo $var_mountpoint`\n",[2113,64008,64009],{"class":2115,"line":11391},[2113,64010,2125],{"emptyLinePlaceholder":1767},[2113,64012,64013,64016,64018,64020,64022],{"class":2115,"line":11396},[2113,64014,64015],{"class":2149},"                                var_pathname=\"",[2113,64017,61922],{"class":2334},[2113,64019,15877],{"class":2149},[2113,64021,63890],{"class":2330},[2113,64023,64024],{"class":2149},"}_pathname\"\n",[2113,64026,64027,64030,64032],{"class":2115,"line":11403},[2113,64028,64029],{"class":2330},"                                PATHNAME",[2113,64031,2335],{"class":2334},[2113,64033,64034],{"class":2119},"`eval echo $var_pathname`\n",[2113,64036,64037],{"class":2115,"line":11411},[2113,64038,2125],{"emptyLinePlaceholder":1767},[2113,64040,64041,64044],{"class":2115,"line":11440},[2113,64042,64043],{"class":2149},"                                save_by_usb",[2113,64045,2487],{"class":2119},[2113,64047,64048],{"class":2115,"line":11445},[2113,64049,64050],{"class":2119},"                        ;;\n",[2113,64052,64054],{"class":2115,"line":64053},153,[2113,64055,2125],{"emptyLinePlaceholder":1767},[2113,64057,64059,64062],{"class":2115,"line":64058},154,[2113,64060,64061],{"class":2149},"                        \"net\"",[2113,64063,4660],{"class":2119},[2113,64065,64067,64070,64072,64074,64076,64078,64080],{"class":2115,"line":64066},155,[2113,64068,64069],{"class":2330},"                                var_host",[2113,64071,2335],{"class":2334},[2113,64073,7406],{"class":2149},[2113,64075,61922],{"class":2334},[2113,64077,15877],{"class":2149},[2113,64079,63890],{"class":2330},[2113,64081,64082],{"class":2149},"}_host\"\n",[2113,64084,64086,64089,64091],{"class":2115,"line":64085},156,[2113,64087,64088],{"class":2330},"                                HOST",[2113,64090,2335],{"class":2334},[2113,64092,64093],{"class":2119},"`eval echo $var_host`\n",[2113,64095,64097],{"class":2115,"line":64096},157,[2113,64098,2125],{"emptyLinePlaceholder":1767},[2113,64100,64102,64105,64107,64109,64111],{"class":2115,"line":64101},158,[2113,64103,64104],{"class":2149},"                                var_user=\"",[2113,64106,61922],{"class":2334},[2113,64108,15877],{"class":2149},[2113,64110,63890],{"class":2330},[2113,64112,64113],{"class":2149},"}_user\"\n",[2113,64115,64117,64120,64122],{"class":2115,"line":64116},159,[2113,64118,64119],{"class":2330},"                                USER",[2113,64121,2335],{"class":2334},[2113,64123,64124],{"class":2119},"`eval echo $var_user`\n",[2113,64126,64128],{"class":2115,"line":64127},160,[2113,64129,2125],{"emptyLinePlaceholder":1767},[2113,64131,64133,64136,64138,64140,64142],{"class":2115,"line":64132},161,[2113,64134,64135],{"class":2149},"                                var_dir=\"",[2113,64137,61922],{"class":2334},[2113,64139,15877],{"class":2149},[2113,64141,63890],{"class":2330},[2113,64143,64144],{"class":2149},"}_dir\"\n",[2113,64146,64148,64151,64153],{"class":2115,"line":64147},162,[2113,64149,64150],{"class":2330},"                                DIR",[2113,64152,2335],{"class":2334},[2113,64154,64155],{"class":2119},"`eval echo $var_dir`\n",[2113,64157,64159],{"class":2115,"line":64158},163,[2113,64160,2125],{"emptyLinePlaceholder":1767},[2113,64162,64164,64167],{"class":2115,"line":64163},164,[2113,64165,64166],{"class":2149},"                                save_by_network",[2113,64168,2487],{"class":2119},[2113,64170,64172],{"class":2115,"line":64171},165,[2113,64173,64050],{"class":2119},[2113,64175,64177],{"class":2115,"line":64176},166,[2113,64178,64179],{"class":2326},"                esac\n",[2113,64181,64183],{"class":2115,"line":64182},167,[2113,64184,64185],{"class":2326},"        done\n",[2113,64187,64189],{"class":2115,"line":64188},168,[2113,64190,2599],{"class":2119},[2113,64192,64194],{"class":2115,"line":64193},169,[2113,64195,2125],{"emptyLinePlaceholder":1767},[2113,64197,64199,64202],{"class":2115,"line":64198},170,[2113,64200,64201],{"class":2133},"copy_to",[2113,64203,2487],{"class":2119},[3358,64205,64206],{},"html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}",{"title":1646,"searchDepth":1647,"depth":1647,"links":64208},[64209,64210,64211],{"id":61426,"depth":1652,"text":61427},{"id":61697,"depth":1652,"text":61698},{"id":62699,"depth":1652,"text":62700},"2006-10-16","Ce billet concerne une application, de mon cru, écrite en bash.",{"type":9,"value":64215},[64216,64220,64224,64230,64232],[12,64217,61397,64218,179],{},[184,64219,2319],{},[12,64221,61402,64222,61406],{},[184,64223,61405],{},[12,64225,61409,64226,61413,64228,61417],{},[184,64227,61412],{},[184,64229,61416],{},[12,64231,61420],{},[12,64233,61423],{},{},"\u002Fpost\u002Fsauvegarde-automatique",{"title":61392,"description":64213},"posts\u002FSysAdmin\u002F2006-10-16-sauvegarde-automatique",[18167],"tiOqKpPKnPayGrhb8Ovj19GWXvi-3rDyhtkkPwFVK5Q",{"id":64241,"title":64242,"author":7,"body":64243,"category":61333,"categorySlug":61334,"date":64897,"description":64247,"excerpt":64898,"extension":1764,"location":1765,"meta":64906,"navigation":1767,"path":64907,"published":1767,"seo":64908,"slug":64909,"stem":64910,"tags":64911,"timeToRead":2235,"__hash__":64912},"posts\u002Fposts\u002FSysAdmin\u002F2006-08-24-chiffrer-une-partition-d-un-disque.md","Chiffrer une partition d'un disque",{"type":9,"value":64244,"toc":64887},[64245,64248,64251,64254,64258,64294,64303,64307,64319,64325,64329,64332,64338,64342,64358,64362,64368,64371,64374,64380,64383,64387,64397,64403,64406,64412,64416,64422,64425,64431,64437,64443,64449,64455,64460,64466,64470,64475,64481,64486,64492,64495,64501,64504,64510,64514,64517,64522,64528,64533,64539,64542,64548,64552,64560,64851,64884],[12,64246,64247],{},"On peut se demander à quoi sert le chiffrage des partitions d'un disque\nsous Linux. Après tout, avec la gestion des droits on peut empêcher un\nutilisateur d'accéder à certain endroit du disque.",[12,64249,64250],{},"Maintenant, imaginez que vous travaillez pour une entreprise et que vous\npossédez des données confidentielles (les documents du dernier prototype\nde 'chez_pas_quoi', ...). En cas de vol de vos machines, un intrus\npeut lire le contenu du disque dur, avec un autre système\nd'exploitation, ou avec un autre Linux dont il aura les droits\nd'administrateur.",[12,64252,64253],{},"Heureusement, les données confidentielles auront été cachées dans une\npartition chiffrée du disque dur, et donc non accessible sans la clé de\ncryptage que vous aurez judicieusement choisie.",[133,64255,64257],{"id":64256},"les-préparations","Les préparations",[506,64259,64260,64263,64285],{},[370,64261,64262],{},"Il faut d'abord choisir une clé de chiffrage difficile à trouver.\nSouvent une suite de chiffres et de lettres minuscules\u002Fmajuscules\nmélangées n'ayant aucune signification, constitue une véritable clé,\ndifficilement cassable. Sont à proscrire les noms, les prénoms, les\ndates, ou tout autre événement lié à votre vie ou à la vie de\nl'entreprise.",[370,64264,64265,64266,932,64269,932,64272,932,64275,64278,64279],{},"Une partition vide. Cette partition doit être complètement vide de\ndonnées afin de pouvoir la reformater dans un autre système de\nfichier. Il vous faudra donc utiliser un logiciel tel que\n",[184,64267,64268],{},"Gnu\u002FParted",[184,64270,64271],{},"QtParted",[184,64273,64274],{},"Fdisk",[184,64276,64277],{},"LVM"," pour créer votre partition,\nou utiliser un fichier à l'aide de l'interface Loop. Dans les\nexemples ci-dessous nous utiliserons un volume logique issu de LVM.\nIl aura été créé à l'aide de la commande",[2105,64280,64283],{"className":64281,"code":64282,"language":26023},[26021],"lvcreate -L10G -n secure-close debian\n",[184,64284,64282],{"__ignoreMap":1646},[370,64286,64287,64288],{},"Posséder un noyau 2.6 compiler avec le support Device-Manager, et\nDM-Crypt, ainsi que les utilitaires dmsetup et cryptsetup que vous\ninstallez sous Gnu\u002FDebian Sarge à l'aide de la commande :",[2105,64289,64292],{"className":64290,"code":64291,"language":26023},[26021],"apt-get install dmsetup cryptsetup openssl\n",[184,64293,64291],{"__ignoreMap":1646},[133,64295,64297,64298],{"id":64296},"préparation-de-la-partition1","Préparation de la partition",[17400,64299,64300],{},[49,64301,3686],{"href":17977,"ariaDescribedBy":64302,"dataFootnoteRef":1646,"id":17979},[17406],[27533,64304,64306],{"id":64305},"cas-dune-partition","Cas d'une partition",[12,64308,64309,64310,64313,64314,2384],{},"Si vous stockez des données chiffrées sur une partition contenant à\nl'origine des données non cryptées ou des blancs, il sera très facile de\nrepérer les données chiffrées et les données claires, de voir la\nquantité de données chiffrées, voire même de les décrypter plus\nfacilement. Nous allons donc ",[49741,64311,64312],{},"nettoyer"," la partition en y écrivant\ndes données aléatoires ",[17400,64315,64316],{},[49,64317,2547],{"href":17404,"ariaDescribedBy":64318,"dataFootnoteRef":1646,"id":17407},[17406],[2105,64320,64323],{"className":64321,"code":64322,"language":26023},[26021],"dd if=\u002Fdev\u002Furandom of=\u002Fdev\u002Fdebian\u002Fsecure-close\n",[184,64324,64322],{"__ignoreMap":1646},[27533,64326,64328],{"id":64327},"cas-dun-fichier","Cas d'un fichier",[12,64330,64331],{},"Vous pouvez aussi décider d'enregistrer vos données chiffrées dans un\nfichier (parce que vous n'avez plus d'espace libre pour une nouvelle\npartition par exemple). Pour cela, vous pouvez utiliser les commandes\nsuivantes. Il vous faudra alors utiliser le périphérique \u002Fdev\u002Floop0 dans\nla suite de l'article.",[2105,64333,64336],{"className":64334,"code":64335,"language":26023},[26021],"dd if=\u002Fdev\u002Furandom of=\u002Fhome\u002Fsecure_close.img bs=1M count=10000\nlosetup \u002Fdev\u002Floop0 \u002Fhome\u002Fsecure_close.img\n",[184,64337,64335],{"__ignoreMap":1646},[133,64339,64341],{"id":64340},"chiffrement-de-la-partition-et-création-du-système-de-fichier","Chiffrement de la partition et création du système de fichier",[12,64343,64344,64345,932,64348,932,64351,4706,64354,64357],{},"Nous allons maintenant ouvrir notre partition en tant que système\nchiffré et formater son contenu. Vous pourrez formater la partition avec\nle système de fichier de votre choix (",[184,64346,64347],{},"ReiserFS",[184,64349,64350],{},"Ext3FS",[184,64352,64353],{},"Ext2FS",[184,64355,64356],{},"XFS",", ....)",[27533,64359,64361],{"id":64360},"sous-gnudebian-sarge","Sous Gnu\u002FDebian Sarge",[2105,64363,64366],{"className":64364,"code":64365,"language":26023},[26021],"openssl rand -base64 64 | head -n 1 | openssl aes-256-ecb > \u002Fhome\u002Fsecure_close.key\n",[184,64367,64365],{"__ignoreMap":1646},[12,64369,64370],{},"Cette ligne de commande va créer une clé de chiffrage dans le fichier\nsecure_close.key et le protéger à l'aide de votre mot de passe. Cela\nvous permettra de changer votre mot de passe, sans reformater la\npartition. Cette commande vous demandera alors votre mot de passe, il ne\nfaut maintenant plus l'oublier.",[12,64372,64373],{},"Maintenant nous allons chiffrer la partition à proprement parlé :",[2105,64375,64378],{"className":64376,"code":64377,"language":26023},[26021],"openssl enc -d -aes-256-ecb -in \u002Fhome\u002Fsecure_close.key | cryptsetup create secure-open \u002Fdev\u002Fdebian\u002Fsecure-close\n",[184,64379,64377],{"__ignoreMap":1646},[12,64381,64382],{},"Vous pourrez alors retrouver votre partition déchiffrée dans\n\u002Fdev\u002Fmapper\u002Fsecure-open. Il ne vous reste plus qu'a créer le système de\nfichier, monter la partition et copier les fichiers à protéger. (Vous\npouvez utiliser aussi un utilitaire d'effacement spécialisé pour écraser\nles fichiers avec un contenu aléatoire avant de réellement le\nsupprimer.)",[27533,64384,64386],{"id":64385},"sous-debian-etchsid","Sous Debian etch\u002Fsid",[12,64388,64389,64390,64393,64394,64396],{},"Sous Debian etch\u002Fsid, il est possible d'utiliser ",[362,64391,64392],{},"Luks"," pour le\nchiffrage. Cela permet de stocker la clé au début de la partition, et de\npouvoir la modifier ultérieurement sans reformater. Cela permet de ne\nplus avoir de fichier séparé avec la clé.Pour formater une partition\nau format ",[362,64395,64392],{}," et ainsi l'initialiser avec le mot de passe de\nprotection, il faut utiliser la commande suivante (Attention à ne pas\noublier le mot de passe) :",[2105,64398,64401],{"className":64399,"code":64400,"language":26023},[26021],"cryptsetup luksFormat \u002Fdev\u002Fdebian\u002Fsecure-close\n",[184,64402,64400],{"__ignoreMap":1646},[12,64404,64405],{},"Après cela, on se retrouve avec une partition chiffré vierge. Il faut\ndonc l'ouvrir pour pouvoir ensuite y installer un système de fichier et\ndes fichiers. Pour cela, on utilise la commande suivante qui va associer\nà notre partition chiffrée une partition déchiffrée (virtuelle)",[2105,64407,64410],{"className":64408,"code":64409,"language":26023},[26021],"cryptsetup luksOpen \u002Fdev\u002Fdebian\u002Fsecure-close secure-open\n",[184,64411,64409],{"__ignoreMap":1646},[27533,64413,64415],{"id":64414},"préparation-et-protection-des-données","Préparation et protection des données",[2105,64417,64420],{"className":64418,"code":64419,"language":26023},[26021],"mke2fs -j \u002Fdev\u002Fmapper\u002Fsecure-open\nmount -t ext3 \u002Fdev\u002Fmapper\u002Fsecure-open \u002Fmnt\ncp -a dossiers_confidentiels\u002F* \u002Fmnt\u002F\n",[184,64421,64419],{"__ignoreMap":1646},[12,64423,64424],{},"Une fois que vous avez fini, vous pouvez démonter la partition, et\nsupprimer l'accès au contenu déchiffré.",[2105,64426,64429],{"className":64427,"code":64428,"language":26023},[26021],"umount \u002Fmnt\n",[184,64430,64428],{"__ignoreMap":1646},[12,64432,64433,64434,64436],{},"Si vous n'utilisez pas ",[362,64435,64392],{}," (sous Gnu\u002FDebian Sarge) :",[2105,64438,64441],{"className":64439,"code":64440,"language":26023},[26021],"cryptsetup remove secure-open\n",[184,64442,64440],{"__ignoreMap":1646},[12,64444,64445,64446,64448],{},"Si vous utilisez ",[362,64447,64392],{},", la commande est",[2105,64450,64453],{"className":64451,"code":64452,"language":26023},[26021],"cryptsetup luksClose secure-open\n",[184,64454,64452],{"__ignoreMap":1646},[12,64456,64457,64458,2384],{},"Et si vous avez créé un périphérique ",[184,64459,29720],{},[2105,64461,64464],{"className":64462,"code":64463,"language":26023},[26021],"losetup -d \u002Fdev\u002Floop0\n",[184,64465,64463],{"__ignoreMap":1646},[133,64467,64469],{"id":64468},"utilisation-courante","Utilisation courante",[12,64471,64472,64473,2384],{},"Dans une utilisation courante vous allez monter la partition quand vous\nvoudrez y accéder, et la démonter quand vous ne l'utiliserez plus.Le\nmontage de la partition se fait à l'aide des commandes ci-dessous, si\nvous n'utilisez pas ",[362,64474,64392],{},[2105,64476,64479],{"className":64477,"code":64478,"language":26023},[26021],"openssl enc -d -aes-256-ecb -in \u002Fhome\u002Fsecure_close.key | cryptsetup create secure-open \u002Fdev\u002Fdebian\u002Fsecure-close\nmount -t ext3 \u002Fdev\u002Fmapper\u002Fsecure-open \u002Fmnt\n",[184,64480,64478],{"__ignoreMap":1646},[12,64482,64483,64484],{},"et à partir de la commande suivante si vous utilisez ",[362,64485,64392],{},[2105,64487,64490],{"className":64488,"code":64489,"language":26023},[26021],"cryptsetup luksOpen \u002Fdev\u002Fdebian\u002Fsecure-close secure-open\nmount -t ext3 \u002Fdev\u002Fmapper\u002Fsecure-open \u002Fmnt\n",[184,64491,64489],{"__ignoreMap":1646},[12,64493,64494],{},"Le démontage de la partition est identique :",[2105,64496,64499],{"className":64497,"code":64498,"language":26023},[26021],"umount \u002Fmntcryptsetup remove secure-open\n",[184,64500,64498],{"__ignoreMap":1646},[12,64502,64503],{},"ou",[2105,64505,64508],{"className":64506,"code":64507,"language":26023},[26021],"umount \u002Fmntcryptsetup luksClose secure-open\n",[184,64509,64507],{"__ignoreMap":1646},[133,64511,64513],{"id":64512},"changement-de-mot-de-passe","Changement de mot de passe",[12,64515,64516],{},"Quand vous souhaitez changer de mot de passe, vous pouvez utiliser la\ncommande suivante :",[12,64518,64519,64520],{},"Sans ",[362,64521,64392],{},[2105,64523,64526],{"className":64524,"code":64525,"language":26023},[26021],"echo `openssl enc -d -aes-256-ecb -in \u002Fhome\u002Fsecure_close.key` | openssl aes-256-ecb > \u002Fhome\u002Fsecure_close.key\n",[184,64527,64525],{"__ignoreMap":1646},[12,64529,64530,64531],{},"Avec ",[362,64532,64392],{},[2105,64534,64537],{"className":64535,"code":64536,"language":26023},[26021],"cryptsetup luksDump \u002Fdev\u002Fdebian\u002Fsecure-close\n",[184,64538,64536],{"__ignoreMap":1646},[12,64540,64541],{},"pour voir quels sont les slots utilisés.puis",[2105,64543,64546],{"className":64544,"code":64545,"language":26023},[26021],"cryptsetup luksAddKey \u002Fdev\u002Fdebian\u002Fsecure-closecryptsetup luksDelKey [ancien slot]\n",[184,64547,64545],{"__ignoreMap":1646},[133,64549,64551],{"id":64550},"un-script-pour-le-montage-et-le-démontage-automatique","Un script pour le montage et le démontage automatique",[12,64553,64554,64555,2384],{},"Voici un script que vous pouvez utiliser en root ou à l'aide de la\ncommande sudo pour monter et démonter vos partitions chiffrer",[17400,64556,64557],{},[49,64558,3696],{"href":41827,"ariaDescribedBy":64559,"dataFootnoteRef":1646,"id":41829},[17406],[2105,64561,64563],{"className":2317,"code":64562,"language":2319,"meta":1646,"style":1646},"#!\u002Fbin\u002Fsh\n\nif [ -e \u002Fdev\u002Fmapper\u002Fsecure-open ] ; then\n        umount \u002Fhome\u002Fsecure 2> \u002Fdev\u002Fnull\n        if [ $? != 0 ] ; then\n                kdialog --title KSecure --error \"Impossible de démonter le périphérique\"\n        fi\n        if [ -e \u002Fdev\u002Fmapper\u002Fsecure-open ] ; then\n                \u002Fsbin\u002Fcryptsetup remove secure-open\n                kdialog --title KSecure --msgbox \"Suppression du périphérique accomplie\"\n        fi\n    else\n        password=`kdialog --title KSecure --password \"Clé de déchiffrement\"`\n        if [ -z $password ] ; then\n                password=\"nopassword\"\n        fi\n        echo \"$password\" | \\\n        openssl enc -d -aes-256-ecb -in ~\u002Fcompte\u002Fsecure.cle -kfile \u002Fdev\u002Fstdin | \\\n        \u002Fsbin\u002Fcryptsetup create secure-open \u002Fdev\u002Fdebian\u002Fsecure-close 2> \u002Fdev\u002Fnull\n        mount \u002Fhome\u002Fsecure 2> \u002Fdev\u002Fnull\n        if [ $? -ne  0 ] ; then\n                kdialog --title KSecure --error \"Clé incorrecte\"\n                \u002Fsbin\u002Fcryptsetup remove secure-open\n        fi\nfi\n",[184,64564,64565,64569,64573,64586,64599,64615,64632,64636,64648,64659,64673,64677,64681,64704,64719,64729,64733,64748,64777,64794,64805,64822,64835,64843,64847],{"__ignoreMap":1646},[2113,64566,64567],{"class":2115,"line":2116},[2113,64568,61708],{"class":2396},[2113,64570,64571],{"class":2115,"line":1647},[2113,64572,2125],{"emptyLinePlaceholder":1767},[2113,64574,64575,64577,64579,64581,64584],{"class":2115,"line":1652},[2113,64576,4109],{"class":2326},[2113,64578,61820],{"class":2119},[2113,64580,59250],{"class":2334},[2113,64582,64583],{"class":2119}," \u002Fdev\u002Fmapper\u002Fsecure-open ] ; ",[2113,64585,7421],{"class":2326},[2113,64587,64588,64591,64594,64597],{"class":2115,"line":2185},[2113,64589,64590],{"class":2133},"        umount",[2113,64592,64593],{"class":2149}," \u002Fhome\u002Fsecure",[2113,64595,64596],{"class":2119}," 2> ",[2113,64598,63240],{"class":2149},[2113,64600,64601,64603,64605,64607,64609,64611,64613],{"class":2115,"line":2230},[2113,64602,4518],{"class":2326},[2113,64604,61820],{"class":2119},[2113,64606,63253],{"class":2414},[2113,64608,5638],{"class":2334},[2113,64610,3208],{"class":2166},[2113,64612,61881],{"class":2119},[2113,64614,7421],{"class":2326},[2113,64616,64617,64620,64623,64626,64629],{"class":2115,"line":2235},[2113,64618,64619],{"class":2133},"                kdialog",[2113,64621,64622],{"class":2166}," --title",[2113,64624,64625],{"class":2149}," KSecure",[2113,64627,64628],{"class":2166}," --error",[2113,64630,64631],{"class":2149}," \"Impossible de démonter le périphérique\"\n",[2113,64633,64634],{"class":2115,"line":2241},[2113,64635,62413],{"class":2326},[2113,64637,64638,64640,64642,64644,64646],{"class":2115,"line":2246},[2113,64639,4518],{"class":2326},[2113,64641,61820],{"class":2119},[2113,64643,59250],{"class":2334},[2113,64645,64583],{"class":2119},[2113,64647,7421],{"class":2326},[2113,64649,64650,64653,64656],{"class":2115,"line":2464},[2113,64651,64652],{"class":2133},"                \u002Fsbin\u002Fcryptsetup",[2113,64654,64655],{"class":2149}," remove",[2113,64657,64658],{"class":2149}," secure-open\n",[2113,64660,64661,64663,64665,64667,64670],{"class":2115,"line":2085},[2113,64662,64619],{"class":2133},[2113,64664,64622],{"class":2166},[2113,64666,64625],{"class":2149},[2113,64668,64669],{"class":2166}," --msgbox",[2113,64671,64672],{"class":2149}," \"Suppression du périphérique accomplie\"\n",[2113,64674,64675],{"class":2115,"line":2514},[2113,64676,62413],{"class":2326},[2113,64678,64679],{"class":2115,"line":2533},[2113,64680,7439],{"class":2326},[2113,64682,64683,64686,64688,64690,64693,64695,64698,64701],{"class":2115,"line":2556},[2113,64684,64685],{"class":2330},"        password",[2113,64687,2335],{"class":2334},[2113,64689,20427],{"class":2149},[2113,64691,64692],{"class":2133},"kdialog",[2113,64694,64622],{"class":2166},[2113,64696,64697],{"class":2149}," KSecure ",[2113,64699,64700],{"class":2166},"--password",[2113,64702,64703],{"class":2149}," \"Clé de déchiffrement\"`\n",[2113,64705,64706,64708,64710,64712,64715,64717],{"class":2115,"line":2569},[2113,64707,4518],{"class":2326},[2113,64709,61820],{"class":2119},[2113,64711,62072],{"class":2334},[2113,64713,64714],{"class":2330}," $password",[2113,64716,61881],{"class":2119},[2113,64718,7421],{"class":2326},[2113,64720,64721,64724,64726],{"class":2115,"line":2575},[2113,64722,64723],{"class":2330},"                password",[2113,64725,2335],{"class":2334},[2113,64727,64728],{"class":2149},"\"nopassword\"\n",[2113,64730,64731],{"class":2115,"line":2596},[2113,64732,62413],{"class":2326},[2113,64734,64735,64737,64739,64742,64744,64746],{"class":2115,"line":3192},[2113,64736,7426],{"class":2334},[2113,64738,7400],{"class":2149},[2113,64740,64741],{"class":2330},"$password",[2113,64743,7406],{"class":2149},[2113,64745,7357],{"class":2119},[2113,64747,25005],{"class":2334},[2113,64749,64750,64753,64756,64758,64761,64764,64767,64770,64773,64775],{"class":2115,"line":3213},[2113,64751,64752],{"class":2133},"        openssl",[2113,64754,64755],{"class":2149}," enc",[2113,64757,7373],{"class":2166},[2113,64759,64760],{"class":2166}," -aes-256-ecb",[2113,64762,64763],{"class":2166}," -in",[2113,64765,64766],{"class":2149}," ~\u002Fcompte\u002Fsecure.cle",[2113,64768,64769],{"class":2166}," -kfile",[2113,64771,64772],{"class":2149}," \u002Fdev\u002Fstdin",[2113,64774,7357],{"class":2119},[2113,64776,25005],{"class":2334},[2113,64778,64779,64782,64784,64787,64790,64792],{"class":2115,"line":3236},[2113,64780,64781],{"class":2133},"        \u002Fsbin\u002Fcryptsetup",[2113,64783,47182],{"class":2149},[2113,64785,64786],{"class":2149}," secure-open",[2113,64788,64789],{"class":2149}," \u002Fdev\u002Fdebian\u002Fsecure-close",[2113,64791,64596],{"class":2119},[2113,64793,63240],{"class":2149},[2113,64795,64796,64799,64801,64803],{"class":2115,"line":3248},[2113,64797,64798],{"class":2133},"        mount",[2113,64800,64593],{"class":2149},[2113,64802,64596],{"class":2119},[2113,64804,63240],{"class":2149},[2113,64806,64807,64809,64811,64813,64815,64818,64820],{"class":2115,"line":4899},[2113,64808,4518],{"class":2326},[2113,64810,61820],{"class":2119},[2113,64812,63253],{"class":2414},[2113,64814,61876],{"class":2334},[2113,64816,64817],{"class":2166},"  0",[2113,64819,61881],{"class":2119},[2113,64821,7421],{"class":2326},[2113,64823,64824,64826,64828,64830,64832],{"class":2115,"line":1777},[2113,64825,64619],{"class":2133},[2113,64827,64622],{"class":2166},[2113,64829,64625],{"class":2149},[2113,64831,64628],{"class":2166},[2113,64833,64834],{"class":2149}," \"Clé incorrecte\"\n",[2113,64836,64837,64839,64841],{"class":2115,"line":4931},[2113,64838,64652],{"class":2133},[2113,64840,64655],{"class":2149},[2113,64842,64658],{"class":2149},[2113,64844,64845],{"class":2115,"line":4961},[2113,64846,62413],{"class":2326},[2113,64848,64849],{"class":2115,"line":4976},[2113,64850,61860],{"class":2326},[23219,64852,64854,64857],{"className":64853,"dataFootnotes":1646},[23222],[128,64855,23227],{"className":64856,"id":17406},[23226],[367,64858,64859,64869,64875],{},[370,64860,64861,64862,43549,64866],{"id":23243},"Les différentes commandes sont inspirés du site ",[49,64863,64864],{"href":64864,"rel":64865},"http:\u002F\u002Fdocisland.blogspot.com\u002F",[347],[49,64867,23240],{"href":23251,"ariaLabel":23237,"className":64868,"dataFootnoteBackref":1646},[23239],[370,64870,64871,64872],{"id":23232},"Attention, cela détruira le contenu de la partition que vous assainissez. ",[49,64873,23240],{"href":23236,"ariaLabel":23252,"className":64874,"dataFootnoteBackref":1646},[23239],[370,64876,64877,64878,64880,64881],{"id":43495},"Vous pourrez trouver un script pour le format ",[362,64879,64392],{}," dans les commentaires ou dans la partie Script du site. ",[49,64882,23240],{"href":43499,"ariaLabel":35010,"className":64883,"dataFootnoteBackref":1646},[23239],[3358,64885,64886],{},"html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":1646,"searchDepth":1647,"depth":1647,"links":64888},[64889,64890,64892,64893,64894,64895,64896],{"id":64256,"depth":1652,"text":64257},{"id":64296,"depth":1652,"text":64891},"Préparation de la partition1",{"id":64340,"depth":1652,"text":64341},{"id":64468,"depth":1652,"text":64469},{"id":64512,"depth":1652,"text":64513},{"id":64550,"depth":1652,"text":64551},{"id":17406,"depth":1647,"text":23227},"2006-08-24",{"type":9,"value":64899},[64900,64902,64904],[12,64901,64247],{},[12,64903,64250],{},[12,64905,64253],{},{},"\u002Fpost\u002Fchiffrer-une-partition-d-un-disque",{"title":64242,"description":64247},"chiffrer-une-partition-d-un-disque","posts\u002FSysAdmin\u002F2006-08-24-chiffrer-une-partition-d-un-disque",[38764,18167],"MUqfUdlUG9slFHhhsbdUUZar_gs50vY5m24eDEqwL4Q",{"id":64914,"title":64915,"author":7,"body":64916,"category":61333,"categorySlug":61334,"date":66985,"description":66986,"excerpt":66987,"extension":1764,"location":1765,"meta":66997,"navigation":1767,"path":66998,"published":1767,"seo":66999,"slug":67000,"stem":67001,"tags":67002,"timeToRead":2235,"__hash__":67003},"posts\u002Fposts\u002FSysAdmin\u002F2006-08-20-script-linux.md","Script Linux",{"type":9,"value":64917,"toc":66975},[64918,64929,64932,64936,64946,65016,65029,65033,65036,65094,65098,65117,65425,65429,65432,65548,65552,65559,65845,65849,65862,65991,65995,66014,66339,66343,66355,66972],[12,64919,64920,64921,64924,64925,64928],{},"Ce billet est consacré au script Linux. J'ai écrit divers script pour\n\"automatiser\" certaines commandes, ",[362,64922,64923],{},"même si je dois exécuter ces\nscripts manuellement",". Je pourrais, quand bien même, appeler ces scripts\ndans un ",[184,64926,64927],{},"cron"," si je le souhaitais.",[12,64930,64931],{},"Vous trouverez, donc, dans la suite, différents scripts, différentes\nidées, à améliorer, à récupérer.",[133,64933,64935],{"id":64934},"liste-des-paquets","Liste des paquets",[12,64937,64938,64939,64942,64943,179],{},"Ce script permet de lister les paquets installés sur un système\nGNU\\Debian. Cela me permet de récupérer une liste de paquet dans un\nfichier, liste que je peux réutiliser dans un logiciel comme synaptic\npour installer les mêmes paquets sur une autre distribution.Couplé à une\nsauvegarde du dossier home et du dossier etc, le script permet de\npouvoir réinstaller un système à l'identique (à condition que l'on ne se\ntrouve pas sur ",[362,64940,64941],{},"unstable"," et moyennant le dossier ",[184,64944,64945],{},"\u002Fvar",[2105,64947,64949],{"className":2317,"code":64948,"language":2319,"meta":1646,"style":1646},"liste=`dpkg -l | tail +6 | cut -d\\  -f3`\nfor i in $liste ; do\n    echo \"$i        install\"\ndone\n",[184,64950,64951,64985,65000,65012],{"__ignoreMap":1646},[2113,64952,64953,64956,64958,64960,64962,64964,64966,64969,64972,64974,64976,64978,64980,64983],{"class":2115,"line":2116},[2113,64954,64955],{"class":2330},"liste",[2113,64957,2335],{"class":2334},[2113,64959,20427],{"class":2149},[2113,64961,58215],{"class":2133},[2113,64963,25019],{"class":2166},[2113,64965,63473],{"class":2119},[2113,64967,64968],{"class":2133}," tail",[2113,64970,64971],{"class":2149}," +6 ",[2113,64973,6756],{"class":2119},[2113,64975,63483],{"class":2133},[2113,64977,7373],{"class":2166},[2113,64979,63492],{"class":2334},[2113,64981,64982],{"class":2166}," -f3",[2113,64984,62005],{"class":2149},[2113,64986,64987,64989,64991,64993,64996,64998],{"class":2115,"line":1647},[2113,64988,8677],{"class":2326},[2113,64990,7910],{"class":2330},[2113,64992,10067],{"class":2326},[2113,64994,64995],{"class":2330}," $liste",[2113,64997,50522],{"class":2119},[2113,64999,7384],{"class":2326},[2113,65001,65002,65005,65007,65009],{"class":2115,"line":1652},[2113,65003,65004],{"class":2334},"    echo",[2113,65006,7400],{"class":2149},[2113,65008,50532],{"class":2330},[2113,65010,65011],{"class":2149},"        install\"\n",[2113,65013,65014],{"class":2115,"line":2185},[2113,65015,7474],{"class":2326},[12,65017,65018],{},[48221,65019,65020,65021,65024,65025,65028],{},"La commande ",[184,65022,65023],{},"dpkg --get-selections"," peut très bien remplacer le\nscript ci-dessus. Merci à ",[362,65026,65027],{},"mx"," pour sa remarque.",[133,65030,65032],{"id":65031},"purge-des-paquets","Purge des paquets",[12,65034,65035],{},"Permet de purger les paquets supprimés d'une distribution GNU\\Debian.\nCela permet de supprimer les quelques fichiers inutiles lors de la\nsuppression de paquet GNU\\Debian. Néanmoins, parfois on ne veut pas\nfaire les purges.",[2105,65037,65039],{"className":2317,"code":65038,"language":2319,"meta":1646,"style":1646},"for i in `dpkg -l | grep -e '^rc.*' | cut -f3 -d\\ ` ; do dpkg -P $i ; done\n",[184,65040,65041],{"__ignoreMap":1646},[2113,65042,65043,65045,65047,65049,65051,65053,65055,65057,65059,65061,65064,65066,65068,65070,65072,65074,65076,65078,65081,65084,65087,65090,65092],{"class":2115,"line":2116},[2113,65044,8677],{"class":2326},[2113,65046,7910],{"class":2330},[2113,65048,10067],{"class":2326},[2113,65050,17813],{"class":2149},[2113,65052,58215],{"class":2133},[2113,65054,25019],{"class":2166},[2113,65056,63473],{"class":2119},[2113,65058,63476],{"class":2133},[2113,65060,59222],{"class":2166},[2113,65062,65063],{"class":2149}," '^rc.*' ",[2113,65065,6756],{"class":2119},[2113,65067,63483],{"class":2133},[2113,65069,64982],{"class":2166},[2113,65071,7373],{"class":2166},[2113,65073,63492],{"class":2334},[2113,65075,20427],{"class":2149},[2113,65077,50522],{"class":2119},[2113,65079,65080],{"class":2326},"do",[2113,65082,65083],{"class":2133}," dpkg",[2113,65085,65086],{"class":2166}," -P",[2113,65088,65089],{"class":2330}," $i",[2113,65091,50522],{"class":2119},[2113,65093,7474],{"class":2326},[133,65095,65097],{"id":65096},"sécurisation-des-données","Sécurisation des données",[12,65099,65100,65101,65104,65105,65108,65109,65112,65113,65116],{},"Permet de monter une partition démontée et démonter une partition\nmontée. Ce script est basé sur KDialog pour demander les informations,\nil nécessite donc KDE (mais je pense qu'il est possible de le réécrire\npour Gnome).On lui passe en paramètre le nom d'un périphérique. Il\nsuppose que la partition démontée est un volume logique (sous LVM) dont\nle groupe est GNU\\Debian et dont le nom est ",[184,65102,65103],{},"$DISK-close",". Il crée à\nla suite le ",[362,65106,65107],{},"device-mapper"," (en supposant qu'il a été créé à l'aide de\n",[362,65110,65111],{},"LUKS",") du nom de ",[184,65114,65115],{},"$DISK-open",".S'il trouve ce \"device-mapper\", il le\ndémonte et le supprime.",[2105,65118,65120],{"className":2317,"code":65119,"language":2319,"meta":1646,"style":1646},"#!\u002Fbin\u002Fsh\n\nDISK=$1\nDISKOPEN=\"$DISK-open\"\nDISKCLOSE=\"$DISK-close\"\n\nif [ -e \u002Fdev\u002Fmapper\u002F$DISKOPEN ] ; then\n        umount \u002Fdev\u002Fmapper\u002F$DISKOPEN 2> \u002Fdev\u002Fnull\n        if [ $? != 0 ] ; then\n                kdialog --title KSecure --error \"Impossible de démonter le péripherique $DISK\"\n        fi\n                if [ -e \u002Fdev\u002Fmapper\u002F$DISKOPEN ] ; then\n                \u002Fsbin\u002Fcryptsetup luksClose $DISKOPEN\n                                kdialog --title KSecure --msgbox \"Suppression du péripherique $DISK accomplie\"\n                fi\nelse\n        password=`kdialog --title KSecure --password \"Cle de déchiffrement de $DISK\"`\n        if [ -z $password ] ; then\n                password=\"nopassword\"\n        fi\n        echo \"$password\" | \\\n                \u002Fsbin\u002Fcryptsetup luksOpen \u002Fdev\u002Fdebian\u002F$DISKCLOSE $DISKOPEN 2> \u002Fdev\u002Fnull\n        mount \u002Fdev\u002Fmapper\u002F$DISKOPEN 2> \u002Fdev\u002Fnull\n                if [ $? -ne  0 ] ; then\n                                kdialog --title KSecure --error \"Clé incorrecte\"\n                fi\nfi\n",[184,65121,65122,65126,65130,65140,65155,65169,65173,65191,65203,65219,65236,65240,65256,65266,65285,65289,65293,65317,65331,65339,65343,65357,65377,65389,65405,65417,65421],{"__ignoreMap":1646},[2113,65123,65124],{"class":2115,"line":2116},[2113,65125,61708],{"class":2396},[2113,65127,65128],{"class":2115,"line":1647},[2113,65129,2125],{"emptyLinePlaceholder":1767},[2113,65131,65132,65135,65137],{"class":2115,"line":1652},[2113,65133,65134],{"class":2330},"DISK",[2113,65136,2335],{"class":2334},[2113,65138,65139],{"class":2429},"$1\n",[2113,65141,65142,65145,65147,65149,65152],{"class":2115,"line":2185},[2113,65143,65144],{"class":2330},"DISKOPEN",[2113,65146,2335],{"class":2334},[2113,65148,7406],{"class":2149},[2113,65150,65151],{"class":2330},"$DISK",[2113,65153,65154],{"class":2149},"-open\"\n",[2113,65156,65157,65160,65162,65164,65166],{"class":2115,"line":2230},[2113,65158,65159],{"class":2330},"DISKCLOSE",[2113,65161,2335],{"class":2334},[2113,65163,7406],{"class":2149},[2113,65165,65151],{"class":2330},[2113,65167,65168],{"class":2149},"-close\"\n",[2113,65170,65171],{"class":2115,"line":2235},[2113,65172,2125],{"emptyLinePlaceholder":1767},[2113,65174,65175,65177,65179,65181,65184,65187,65189],{"class":2115,"line":2241},[2113,65176,4109],{"class":2326},[2113,65178,61820],{"class":2119},[2113,65180,59250],{"class":2334},[2113,65182,65183],{"class":2119}," \u002Fdev\u002Fmapper\u002F",[2113,65185,65186],{"class":2330},"$DISKOPEN",[2113,65188,61881],{"class":2119},[2113,65190,7421],{"class":2326},[2113,65192,65193,65195,65197,65199,65201],{"class":2115,"line":2246},[2113,65194,64590],{"class":2133},[2113,65196,65183],{"class":2149},[2113,65198,65186],{"class":2330},[2113,65200,64596],{"class":2119},[2113,65202,63240],{"class":2149},[2113,65204,65205,65207,65209,65211,65213,65215,65217],{"class":2115,"line":2464},[2113,65206,4518],{"class":2326},[2113,65208,61820],{"class":2119},[2113,65210,63253],{"class":2414},[2113,65212,5638],{"class":2334},[2113,65214,3208],{"class":2166},[2113,65216,61881],{"class":2119},[2113,65218,7421],{"class":2326},[2113,65220,65221,65223,65225,65227,65229,65232,65234],{"class":2115,"line":2085},[2113,65222,64619],{"class":2133},[2113,65224,64622],{"class":2166},[2113,65226,64625],{"class":2149},[2113,65228,64628],{"class":2166},[2113,65230,65231],{"class":2149}," \"Impossible de démonter le péripherique ",[2113,65233,65151],{"class":2330},[2113,65235,7434],{"class":2149},[2113,65237,65238],{"class":2115,"line":2514},[2113,65239,62413],{"class":2326},[2113,65241,65242,65244,65246,65248,65250,65252,65254],{"class":2115,"line":2533},[2113,65243,5065],{"class":2326},[2113,65245,61820],{"class":2119},[2113,65247,59250],{"class":2334},[2113,65249,65183],{"class":2119},[2113,65251,65186],{"class":2330},[2113,65253,61881],{"class":2119},[2113,65255,7421],{"class":2326},[2113,65257,65258,65260,65263],{"class":2115,"line":2556},[2113,65259,64652],{"class":2133},[2113,65261,65262],{"class":2149}," luksClose",[2113,65264,65265],{"class":2330}," $DISKOPEN\n",[2113,65267,65268,65271,65273,65275,65277,65280,65282],{"class":2115,"line":2569},[2113,65269,65270],{"class":2133},"                                kdialog",[2113,65272,64622],{"class":2166},[2113,65274,64625],{"class":2149},[2113,65276,64669],{"class":2166},[2113,65278,65279],{"class":2149}," \"Suppression du péripherique ",[2113,65281,65151],{"class":2330},[2113,65283,65284],{"class":2149}," accomplie\"\n",[2113,65286,65287],{"class":2115,"line":2575},[2113,65288,63332],{"class":2326},[2113,65290,65291],{"class":2115,"line":2596},[2113,65292,61839],{"class":2326},[2113,65294,65295,65297,65299,65301,65303,65305,65307,65309,65312,65314],{"class":2115,"line":3192},[2113,65296,64685],{"class":2330},[2113,65298,2335],{"class":2334},[2113,65300,20427],{"class":2149},[2113,65302,64692],{"class":2133},[2113,65304,64622],{"class":2166},[2113,65306,64697],{"class":2149},[2113,65308,64700],{"class":2166},[2113,65310,65311],{"class":2149}," \"Cle de déchiffrement de ",[2113,65313,65151],{"class":2330},[2113,65315,65316],{"class":2149},"\"`\n",[2113,65318,65319,65321,65323,65325,65327,65329],{"class":2115,"line":3213},[2113,65320,4518],{"class":2326},[2113,65322,61820],{"class":2119},[2113,65324,62072],{"class":2334},[2113,65326,64714],{"class":2330},[2113,65328,61881],{"class":2119},[2113,65330,7421],{"class":2326},[2113,65332,65333,65335,65337],{"class":2115,"line":3236},[2113,65334,64723],{"class":2330},[2113,65336,2335],{"class":2334},[2113,65338,64728],{"class":2149},[2113,65340,65341],{"class":2115,"line":3248},[2113,65342,62413],{"class":2326},[2113,65344,65345,65347,65349,65351,65353,65355],{"class":2115,"line":4899},[2113,65346,7426],{"class":2334},[2113,65348,7400],{"class":2149},[2113,65350,64741],{"class":2330},[2113,65352,7406],{"class":2149},[2113,65354,7357],{"class":2119},[2113,65356,25005],{"class":2334},[2113,65358,65359,65361,65364,65367,65370,65373,65375],{"class":2115,"line":1777},[2113,65360,64652],{"class":2133},[2113,65362,65363],{"class":2149}," luksOpen",[2113,65365,65366],{"class":2149}," \u002Fdev\u002Fdebian\u002F",[2113,65368,65369],{"class":2330},"$DISKCLOSE",[2113,65371,65372],{"class":2330}," $DISKOPEN",[2113,65374,64596],{"class":2119},[2113,65376,63240],{"class":2149},[2113,65378,65379,65381,65383,65385,65387],{"class":2115,"line":4931},[2113,65380,64798],{"class":2133},[2113,65382,65183],{"class":2149},[2113,65384,65186],{"class":2330},[2113,65386,64596],{"class":2119},[2113,65388,63240],{"class":2149},[2113,65390,65391,65393,65395,65397,65399,65401,65403],{"class":2115,"line":4961},[2113,65392,5065],{"class":2326},[2113,65394,61820],{"class":2119},[2113,65396,63253],{"class":2414},[2113,65398,61876],{"class":2334},[2113,65400,64817],{"class":2166},[2113,65402,61881],{"class":2119},[2113,65404,7421],{"class":2326},[2113,65406,65407,65409,65411,65413,65415],{"class":2115,"line":4976},[2113,65408,65270],{"class":2133},[2113,65410,64622],{"class":2166},[2113,65412,64625],{"class":2149},[2113,65414,64628],{"class":2166},[2113,65416,64834],{"class":2149},[2113,65418,65419],{"class":2115,"line":4993},[2113,65420,63332],{"class":2326},[2113,65422,65423],{"class":2115,"line":5013},[2113,65424,61860],{"class":2326},[133,65426,65428],{"id":65427},"apprentissage-du-spam","Apprentissage du spam",[12,65430,65431],{},"Permet d'appeler bogofliter et spamassassin sur une liste de message\nIndésirable au format mbox. Il permet aussi de faire l'apprentissage des\nmessages désirables.",[2105,65433,65435],{"className":2317,"code":65434,"language":2319,"meta":1646,"style":1646},"#!\u002Fbin\u002Fbash\n\nsa-learn -L --no-sync --spam --mbox \u002Fhome\u002Fphoenix\u002FMail\u002FIndésirable\\ \\(Spam\\)\nsa-learn -L --no-sync --ham --mbox \u002Fhome\u002Fphoenix\u002FMail\u002FDésirable\\ \\(Non\\ spam\\)\n\nbogofilter -N -s \u003C \u002Fhome\u002Fphoenix\u002FMail\u002FIndésirable\\ \\(Spam\\)\nbogofilter -S -n \u003C \u002Fhome\u002Fphoenix\u002FMail\u002FDésirable\\ \\(Non\\ spam\\)\n",[184,65436,65437,65441,65445,65472,65499,65503,65524],{"__ignoreMap":1646},[2113,65438,65439],{"class":2115,"line":2116},[2113,65440,7300],{"class":2396},[2113,65442,65443],{"class":2115,"line":1647},[2113,65444,2125],{"emptyLinePlaceholder":1767},[2113,65446,65447,65450,65452,65455,65458,65461,65464,65467,65469],{"class":2115,"line":1652},[2113,65448,65449],{"class":2133},"sa-learn",[2113,65451,60569],{"class":2166},[2113,65453,65454],{"class":2166}," --no-sync",[2113,65456,65457],{"class":2166}," --spam",[2113,65459,65460],{"class":2166}," --mbox",[2113,65462,65463],{"class":2149}," \u002Fhome\u002Fphoenix\u002FMail\u002FIndésirable",[2113,65465,65466],{"class":2334},"\\ \\(",[2113,65468,58336],{"class":2149},[2113,65470,65471],{"class":2334},"\\)\n",[2113,65473,65474,65476,65478,65480,65483,65485,65488,65490,65493,65495,65497],{"class":2115,"line":2185},[2113,65475,65449],{"class":2133},[2113,65477,60569],{"class":2166},[2113,65479,65454],{"class":2166},[2113,65481,65482],{"class":2166}," --ham",[2113,65484,65460],{"class":2166},[2113,65486,65487],{"class":2149}," \u002Fhome\u002Fphoenix\u002FMail\u002FDésirable",[2113,65489,65466],{"class":2334},[2113,65491,65492],{"class":2149},"Non",[2113,65494,63492],{"class":2334},[2113,65496,58335],{"class":2149},[2113,65498,65471],{"class":2334},[2113,65500,65501],{"class":2115,"line":2230},[2113,65502,2125],{"emptyLinePlaceholder":1767},[2113,65504,65505,65508,65511,65513,65515,65518,65520,65522],{"class":2115,"line":2235},[2113,65506,65507],{"class":2133},"bogofilter",[2113,65509,65510],{"class":2166}," -N",[2113,65512,37395],{"class":2166},[2113,65514,58651],{"class":2119},[2113,65516,65517],{"class":2149},"\u002Fhome\u002Fphoenix\u002FMail\u002FIndésirable",[2113,65519,65466],{"class":2334},[2113,65521,58336],{"class":2149},[2113,65523,65471],{"class":2334},[2113,65525,65526,65528,65531,65533,65535,65538,65540,65542,65544,65546],{"class":2115,"line":2241},[2113,65527,65507],{"class":2133},[2113,65529,65530],{"class":2166}," -S",[2113,65532,25042],{"class":2166},[2113,65534,58651],{"class":2119},[2113,65536,65537],{"class":2149},"\u002Fhome\u002Fphoenix\u002FMail\u002FDésirable",[2113,65539,65466],{"class":2334},[2113,65541,65492],{"class":2149},[2113,65543,63492],{"class":2334},[2113,65545,58335],{"class":2149},[2113,65547,65471],{"class":2334},[133,65549,65551],{"id":65550},"création-de-lien","Création de lien",[12,65553,65554,65555,65558],{},"Permet à partir d'une liste de fichier identique générée par ",[362,65556,65557],{},"fdupes"," de\ncréer des liens physiques entre les fichiers pour économiser de la\nplace. Attention : si un des fichiers physiques est modifié, l'autre le\nsera aussi.",[2105,65560,65562],{"className":2317,"code":65561,"language":2319,"meta":1646,"style":1646},"#!\u002Fbin\u002Fsh\n\necho -n \"|\"\nread -r line\nwhile [ $? -eq 0 ] ; do\n        line=`echo $line | sed -e \"s\u002F\\\\\\\\\\\\ \u002F@\u002Fg\"`\n        index=0\n        principal=\"\"\n        for chemin in $line ; do\n                if [ $index -eq 0 ] ; then\n                        principal=$chemin\n                else\n                        a=`echo $principal | sed -e \"s\u002F@\u002F\\\\ \u002Fg\"`\n                        b=`echo $chemin | sed -e \"s\u002F@\u002F\\\\ \u002Fg\"`\n                        ln -f \"$a\" \"$b\"\n                fi\n\n                index=`expr $index + 1`\n        done\n\n        echo -n \"=\"\n\n        read -r line\ndone\necho -n \"|\"\n",[184,65563,65564,65568,65572,65581,65590,65606,65636,65645,65654,65669,65686,65696,65700,65728,65754,65775,65779,65783,65803,65807,65811,65820,65824,65833,65837],{"__ignoreMap":1646},[2113,65565,65566],{"class":2115,"line":2116},[2113,65567,61708],{"class":2396},[2113,65569,65570],{"class":2115,"line":1647},[2113,65571,2125],{"emptyLinePlaceholder":1767},[2113,65573,65574,65576,65578],{"class":2115,"line":1652},[2113,65575,61844],{"class":2334},[2113,65577,25042],{"class":2166},[2113,65579,65580],{"class":2149}," \"|\"\n",[2113,65582,65583,65585,65587],{"class":2115,"line":2185},[2113,65584,4567],{"class":2334},[2113,65586,7370],{"class":2166},[2113,65588,65589],{"class":2149}," line\n",[2113,65591,65592,65594,65596,65598,65600,65602,65604],{"class":2115,"line":2230},[2113,65593,7360],{"class":2326},[2113,65595,61820],{"class":2119},[2113,65597,63253],{"class":2414},[2113,65599,63790],{"class":2334},[2113,65601,3208],{"class":2166},[2113,65603,61881],{"class":2119},[2113,65605,7384],{"class":2326},[2113,65607,65608,65611,65613,65615,65617,65620,65622,65625,65627,65630,65633],{"class":2115,"line":2235},[2113,65609,65610],{"class":2330},"        line",[2113,65612,2335],{"class":2334},[2113,65614,20427],{"class":2149},[2113,65616,61844],{"class":2334},[2113,65618,65619],{"class":2330}," $line",[2113,65621,63473],{"class":2119},[2113,65623,65624],{"class":2133}," sed",[2113,65626,59222],{"class":2166},[2113,65628,65629],{"class":2149}," \"s\u002F",[2113,65631,65632],{"class":2334},"\\\\\\\\\\\\",[2113,65634,65635],{"class":2149}," \u002F@\u002Fg\"`\n",[2113,65637,65638,65641,65643],{"class":2115,"line":2241},[2113,65639,65640],{"class":2330},"        index",[2113,65642,2335],{"class":2334},[2113,65644,18077],{"class":2149},[2113,65646,65647,65650,65652],{"class":2115,"line":2246},[2113,65648,65649],{"class":2330},"        principal",[2113,65651,2335],{"class":2334},[2113,65653,19809],{"class":2149},[2113,65655,65656,65658,65661,65663,65665,65667],{"class":2115,"line":2464},[2113,65657,14355],{"class":2326},[2113,65659,65660],{"class":2330}," chemin",[2113,65662,10067],{"class":2326},[2113,65664,65619],{"class":2330},[2113,65666,50522],{"class":2119},[2113,65668,7384],{"class":2326},[2113,65670,65671,65673,65675,65678,65680,65682,65684],{"class":2115,"line":2085},[2113,65672,5065],{"class":2326},[2113,65674,61820],{"class":2119},[2113,65676,65677],{"class":2330},"$index",[2113,65679,63790],{"class":2334},[2113,65681,3208],{"class":2166},[2113,65683,61881],{"class":2119},[2113,65685,7421],{"class":2326},[2113,65687,65688,65691,65693],{"class":2115,"line":2514},[2113,65689,65690],{"class":2330},"                        principal",[2113,65692,2335],{"class":2334},[2113,65694,65695],{"class":2330},"$chemin\n",[2113,65697,65698],{"class":2115,"line":2533},[2113,65699,63384],{"class":2326},[2113,65701,65702,65705,65707,65709,65711,65714,65716,65718,65720,65723,65725],{"class":2115,"line":2556},[2113,65703,65704],{"class":2330},"                        a",[2113,65706,2335],{"class":2334},[2113,65708,20427],{"class":2149},[2113,65710,61844],{"class":2334},[2113,65712,65713],{"class":2330}," $principal",[2113,65715,63473],{"class":2119},[2113,65717,65624],{"class":2133},[2113,65719,59222],{"class":2166},[2113,65721,65722],{"class":2149}," \"s\u002F@\u002F",[2113,65724,57012],{"class":2334},[2113,65726,65727],{"class":2149}," \u002Fg\"`\n",[2113,65729,65730,65733,65735,65737,65739,65742,65744,65746,65748,65750,65752],{"class":2115,"line":2569},[2113,65731,65732],{"class":2330},"                        b",[2113,65734,2335],{"class":2334},[2113,65736,20427],{"class":2149},[2113,65738,61844],{"class":2334},[2113,65740,65741],{"class":2330}," $chemin",[2113,65743,63473],{"class":2119},[2113,65745,65624],{"class":2133},[2113,65747,59222],{"class":2166},[2113,65749,65722],{"class":2149},[2113,65751,57012],{"class":2334},[2113,65753,65727],{"class":2149},[2113,65755,65756,65759,65761,65763,65766,65768,65770,65773],{"class":2115,"line":2575},[2113,65757,65758],{"class":2133},"                        ln",[2113,65760,61151],{"class":2166},[2113,65762,7400],{"class":2149},[2113,65764,65765],{"class":2330},"$a",[2113,65767,7406],{"class":2149},[2113,65769,7400],{"class":2149},[2113,65771,65772],{"class":2330},"$b",[2113,65774,7434],{"class":2149},[2113,65776,65777],{"class":2115,"line":2596},[2113,65778,63332],{"class":2326},[2113,65780,65781],{"class":2115,"line":3192},[2113,65782,2125],{"emptyLinePlaceholder":1767},[2113,65784,65785,65788,65790,65792,65794,65797,65799,65801],{"class":2115,"line":3213},[2113,65786,65787],{"class":2330},"                index",[2113,65789,2335],{"class":2334},[2113,65791,20427],{"class":2149},[2113,65793,62590],{"class":2133},[2113,65795,65796],{"class":2330}," $index",[2113,65798,7222],{"class":2149},[2113,65800,3686],{"class":2166},[2113,65802,62005],{"class":2149},[2113,65804,65805],{"class":2115,"line":3236},[2113,65806,64185],{"class":2326},[2113,65808,65809],{"class":2115,"line":3248},[2113,65810,2125],{"emptyLinePlaceholder":1767},[2113,65812,65813,65815,65817],{"class":2115,"line":4899},[2113,65814,7426],{"class":2334},[2113,65816,25042],{"class":2166},[2113,65818,65819],{"class":2149}," \"=\"\n",[2113,65821,65822],{"class":2115,"line":1777},[2113,65823,2125],{"emptyLinePlaceholder":1767},[2113,65825,65826,65829,65831],{"class":2115,"line":4931},[2113,65827,65828],{"class":2334},"        read",[2113,65830,7370],{"class":2166},[2113,65832,65589],{"class":2149},[2113,65834,65835],{"class":2115,"line":4961},[2113,65836,7474],{"class":2326},[2113,65838,65839,65841,65843],{"class":2115,"line":4976},[2113,65840,61844],{"class":2334},[2113,65842,25042],{"class":2166},[2113,65844,65580],{"class":2149},[133,65846,65848],{"id":65847},"renumérotation","Renumérotation",[12,65850,65851,65852,65855,65856,65858,65859],{},"Permet de renuméroté une liste d'image d'un dossier, du style\n",[184,65853,65854],{},"1 10 11 12 13 2 3 4..."," en une suite précédé d'un ",[184,65857,3095],{}," :\n",[184,65860,65861],{},"01 02 03 04 05 06 ...",[2105,65863,65865],{"className":2317,"code":65864,"language":2319,"meta":1646,"style":1646},"#!\u002Fbin\u002Fsh\n\nfor DIR in `ls` ; do\n\n    echo -n \"Change directory $DIR \"\n\n    cd $DIR\n\n    CMD=`ls | sed -e 's\u002F\\(.*\\)_\\([1-9]\\)\\.jpg\u002Fmv \\1_\\2.jpg \\1_0\\2.jpg ;\u002Fg' | grep \"mv \"`\n    eval $CMD\n\n    cd ..\n\n    echo \"Ok\"\n\ndone\n",[184,65866,65867,65871,65875,65894,65898,65911,65915,65923,65927,65954,65962,65966,65972,65976,65983,65987],{"__ignoreMap":1646},[2113,65868,65869],{"class":2115,"line":2116},[2113,65870,61708],{"class":2396},[2113,65872,65873],{"class":2115,"line":1647},[2113,65874,2125],{"emptyLinePlaceholder":1767},[2113,65876,65877,65879,65882,65884,65886,65888,65890,65892],{"class":2115,"line":1652},[2113,65878,8677],{"class":2326},[2113,65880,65881],{"class":2330}," DIR",[2113,65883,10067],{"class":2326},[2113,65885,17813],{"class":2149},[2113,65887,50517],{"class":2133},[2113,65889,20427],{"class":2149},[2113,65891,50522],{"class":2119},[2113,65893,7384],{"class":2326},[2113,65895,65896],{"class":2115,"line":2185},[2113,65897,2125],{"emptyLinePlaceholder":1767},[2113,65899,65900,65902,65904,65907,65909],{"class":2115,"line":2230},[2113,65901,65004],{"class":2334},[2113,65903,25042],{"class":2166},[2113,65905,65906],{"class":2149}," \"Change directory ",[2113,65908,63211],{"class":2330},[2113,65910,63431],{"class":2149},[2113,65912,65913],{"class":2115,"line":2235},[2113,65914,2125],{"emptyLinePlaceholder":1767},[2113,65916,65917,65920],{"class":2115,"line":2241},[2113,65918,65919],{"class":2334},"    cd",[2113,65921,65922],{"class":2330}," $DIR\n",[2113,65924,65925],{"class":2115,"line":2246},[2113,65926,2125],{"emptyLinePlaceholder":1767},[2113,65928,65929,65932,65934,65936,65938,65940,65942,65944,65947,65949,65951],{"class":2115,"line":2464},[2113,65930,65931],{"class":2330},"    CMD",[2113,65933,2335],{"class":2334},[2113,65935,20427],{"class":2149},[2113,65937,50517],{"class":2133},[2113,65939,63473],{"class":2119},[2113,65941,65624],{"class":2133},[2113,65943,59222],{"class":2166},[2113,65945,65946],{"class":2149}," 's\u002F\\(.*\\)_\\([1-9]\\)\\.jpg\u002Fmv \\1_\\2.jpg \\1_0\\2.jpg ;\u002Fg' ",[2113,65948,6756],{"class":2119},[2113,65950,63476],{"class":2133},[2113,65952,65953],{"class":2149}," \"mv \"`\n",[2113,65955,65956,65959],{"class":2115,"line":2085},[2113,65957,65958],{"class":2334},"    eval",[2113,65960,65961],{"class":2330}," $CMD\n",[2113,65963,65964],{"class":2115,"line":2514},[2113,65965,2125],{"emptyLinePlaceholder":1767},[2113,65967,65968,65970],{"class":2115,"line":2533},[2113,65969,65919],{"class":2334},[2113,65971,50879],{"class":2149},[2113,65973,65974],{"class":2115,"line":2556},[2113,65975,2125],{"emptyLinePlaceholder":1767},[2113,65977,65978,65980],{"class":2115,"line":2569},[2113,65979,65004],{"class":2334},[2113,65981,65982],{"class":2149}," \"Ok\"\n",[2113,65984,65985],{"class":2115,"line":2575},[2113,65986,2125],{"emptyLinePlaceholder":1767},[2113,65988,65989],{"class":2115,"line":2596},[2113,65990,7474],{"class":2326},[133,65992,65994],{"id":65993},"génération-de-playlist","Génération de playlist",[12,65996,65997,65998,66001,66002,66004,66005,66008,66009,179],{},"Permet de générer une playlist sur un dossier pour utilisation avec le\nlogiciel kpf (par exemple) de KDE. Il parcourt l'ensemble des dossiers\net crée un fichier ",[362,65999,66000],{},"m3u"," à chaque fois qu'il trouve un fichier\nmultimédia. Il remplace le début du dossier par un lien ",[362,66003,18039],{},".Je\nrajoute qu'il existe un très bon site contenant de la musique\n",[155,66006,66007],{},"libre"," : ",[49,66010,66013],{"href":66011,"rel":66012},"http:\u002F\u002Fwww.jamendo.org\u002Ffr\u002F",[347],"Jamendo",[2105,66015,66017],{"className":2317,"code":66016,"language":2319,"meta":1646,"style":1646},"#!\u002Fbin\u002Fsh\n\nOLD=`pwd`\n\ncd \"$1\"\nif [ $? -ne 0 ] ; then\n    exit\nfi\n\nTHIS=`pwd`\nBASE=`basename \"$THIS\"`\n\necho \"Ouverture de $BASE\"\n\nfor path in * ; do\n    if [ -d \"$THIS\u002F$path\" ] ; then\n        $0 \"$THIS\u002F$path\"\n    fi\ndone\n\necho \"Traitement de $BASE\"\nrm $BASE.m3u 2> \u002Fdev\u002Fnull\n\nfor path in *.mp3 *.ogg *.wma *.avi ; do\n    echo $path | grep \\* > \u002Fdev\u002Fnull\n    if [ $? -eq 1 ] ; then\n        echo $THIS\u002F$path | sed -e \"s\u002F\\\u002Fmedia\\\u002Fmusic\u002Fhttp\\:\\\u002F\\\u002F192\\.168\\.101\\.7\\:8001\u002Fg\" >> $BASE.m3u\n    fi\ndone\n\necho \"Sortie de $BASE\"\n",[184,66018,66019,66023,66027,66041,66045,66056,66072,66077,66081,66085,66098,66116,66120,66132,66136,66150,66173,66188,66192,66196,66200,66211,66225,66229,66253,66272,66288,66316,66320,66324,66328],{"__ignoreMap":1646},[2113,66020,66021],{"class":2115,"line":2116},[2113,66022,61708],{"class":2396},[2113,66024,66025],{"class":2115,"line":1647},[2113,66026,2125],{"emptyLinePlaceholder":1767},[2113,66028,66029,66032,66034,66036,66039],{"class":2115,"line":1652},[2113,66030,66031],{"class":2330},"OLD",[2113,66033,2335],{"class":2334},[2113,66035,20427],{"class":2149},[2113,66037,66038],{"class":2334},"pwd",[2113,66040,62005],{"class":2149},[2113,66042,66043],{"class":2115,"line":2185},[2113,66044,2125],{"emptyLinePlaceholder":1767},[2113,66046,66047,66049,66051,66054],{"class":2115,"line":2230},[2113,66048,17064],{"class":2334},[2113,66050,7400],{"class":2149},[2113,66052,66053],{"class":2429},"$1",[2113,66055,7434],{"class":2149},[2113,66057,66058,66060,66062,66064,66066,66068,66070],{"class":2115,"line":2235},[2113,66059,4109],{"class":2326},[2113,66061,61820],{"class":2119},[2113,66063,63253],{"class":2414},[2113,66065,61876],{"class":2334},[2113,66067,3208],{"class":2166},[2113,66069,61881],{"class":2119},[2113,66071,7421],{"class":2326},[2113,66073,66074],{"class":2115,"line":2241},[2113,66075,66076],{"class":2334},"    exit\n",[2113,66078,66079],{"class":2115,"line":2246},[2113,66080,61860],{"class":2326},[2113,66082,66083],{"class":2115,"line":2464},[2113,66084,2125],{"emptyLinePlaceholder":1767},[2113,66086,66087,66090,66092,66094,66096],{"class":2115,"line":2085},[2113,66088,66089],{"class":2330},"THIS",[2113,66091,2335],{"class":2334},[2113,66093,20427],{"class":2149},[2113,66095,66038],{"class":2334},[2113,66097,62005],{"class":2149},[2113,66099,66100,66103,66105,66107,66109,66111,66114],{"class":2115,"line":2514},[2113,66101,66102],{"class":2330},"BASE",[2113,66104,2335],{"class":2334},[2113,66106,20427],{"class":2149},[2113,66108,63680],{"class":2133},[2113,66110,7400],{"class":2149},[2113,66112,66113],{"class":2330},"$THIS",[2113,66115,65316],{"class":2149},[2113,66117,66118],{"class":2115,"line":2533},[2113,66119,2125],{"emptyLinePlaceholder":1767},[2113,66121,66122,66124,66127,66130],{"class":2115,"line":2556},[2113,66123,61844],{"class":2334},[2113,66125,66126],{"class":2149}," \"Ouverture de ",[2113,66128,66129],{"class":2330},"$BASE",[2113,66131,7434],{"class":2149},[2113,66133,66134],{"class":2115,"line":2569},[2113,66135,2125],{"emptyLinePlaceholder":1767},[2113,66137,66138,66140,66142,66144,66146,66148],{"class":2115,"line":2575},[2113,66139,8677],{"class":2326},[2113,66141,6584],{"class":2330},[2113,66143,10067],{"class":2326},[2113,66145,2671],{"class":2149},[2113,66147,50522],{"class":2119},[2113,66149,7384],{"class":2326},[2113,66151,66152,66154,66156,66158,66160,66162,66164,66167,66169,66171],{"class":2115,"line":2596},[2113,66153,7394],{"class":2326},[2113,66155,61820],{"class":2119},[2113,66157,63618],{"class":2334},[2113,66159,7400],{"class":2149},[2113,66161,66113],{"class":2330},[2113,66163,2520],{"class":2149},[2113,66165,66166],{"class":2330},"$path",[2113,66168,7406],{"class":2149},[2113,66170,61881],{"class":2119},[2113,66172,7421],{"class":2326},[2113,66174,66175,66178,66180,66182,66184,66186],{"class":2115,"line":3192},[2113,66176,66177],{"class":2429},"        $0",[2113,66179,7400],{"class":2149},[2113,66181,66113],{"class":2330},[2113,66183,2520],{"class":2149},[2113,66185,66166],{"class":2330},[2113,66187,7434],{"class":2149},[2113,66189,66190],{"class":2115,"line":3213},[2113,66191,7469],{"class":2326},[2113,66193,66194],{"class":2115,"line":3236},[2113,66195,7474],{"class":2326},[2113,66197,66198],{"class":2115,"line":3248},[2113,66199,2125],{"emptyLinePlaceholder":1767},[2113,66201,66202,66204,66207,66209],{"class":2115,"line":4899},[2113,66203,61844],{"class":2334},[2113,66205,66206],{"class":2149}," \"Traitement de ",[2113,66208,66129],{"class":2330},[2113,66210,7434],{"class":2149},[2113,66212,66213,66215,66218,66221,66223],{"class":2115,"line":1777},[2113,66214,59288],{"class":2133},[2113,66216,66217],{"class":2330}," $BASE",[2113,66219,66220],{"class":2149},".m3u",[2113,66222,64596],{"class":2119},[2113,66224,63240],{"class":2149},[2113,66226,66227],{"class":2115,"line":4931},[2113,66228,2125],{"emptyLinePlaceholder":1767},[2113,66230,66231,66233,66235,66237,66240,66243,66246,66249,66251],{"class":2115,"line":4961},[2113,66232,8677],{"class":2326},[2113,66234,6584],{"class":2330},[2113,66236,10067],{"class":2326},[2113,66238,66239],{"class":2149}," *.mp3",[2113,66241,66242],{"class":2149}," *.ogg",[2113,66244,66245],{"class":2149}," *.wma",[2113,66247,66248],{"class":2149}," *.avi",[2113,66250,50522],{"class":2119},[2113,66252,7384],{"class":2326},[2113,66254,66255,66257,66260,66262,66265,66268,66270],{"class":2115,"line":4976},[2113,66256,65004],{"class":2334},[2113,66258,66259],{"class":2330}," $path",[2113,66261,7357],{"class":2119},[2113,66263,66264],{"class":2133},"grep",[2113,66266,66267],{"class":2334}," \\*",[2113,66269,59957],{"class":2119},[2113,66271,63240],{"class":2149},[2113,66273,66274,66276,66278,66280,66282,66284,66286],{"class":2115,"line":4993},[2113,66275,7394],{"class":2326},[2113,66277,61820],{"class":2119},[2113,66279,63253],{"class":2414},[2113,66281,63790],{"class":2334},[2113,66283,2850],{"class":2166},[2113,66285,61881],{"class":2119},[2113,66287,7421],{"class":2326},[2113,66289,66290,66292,66295,66297,66299,66301,66304,66306,66309,66311,66313],{"class":2115,"line":5013},[2113,66291,7426],{"class":2334},[2113,66293,66294],{"class":2330}," $THIS",[2113,66296,2520],{"class":2149},[2113,66298,66166],{"class":2330},[2113,66300,7357],{"class":2119},[2113,66302,66303],{"class":2133},"sed",[2113,66305,59222],{"class":2166},[2113,66307,66308],{"class":2149}," \"s\u002F\\\u002Fmedia\\\u002Fmusic\u002Fhttp\\:\\\u002F\\\u002F192\\.168\\.101\\.7\\:8001\u002Fg\"",[2113,66310,7409],{"class":2119},[2113,66312,66129],{"class":2330},[2113,66314,66315],{"class":2149},".m3u\n",[2113,66317,66318],{"class":2115,"line":5018},[2113,66319,7469],{"class":2326},[2113,66321,66322],{"class":2115,"line":5042},[2113,66323,7474],{"class":2326},[2113,66325,66326],{"class":2115,"line":5057},[2113,66327,2125],{"emptyLinePlaceholder":1767},[2113,66329,66330,66332,66335,66337],{"class":2115,"line":5062},[2113,66331,61844],{"class":2334},[2113,66333,66334],{"class":2149}," \"Sortie de ",[2113,66336,66129],{"class":2330},[2113,66338,7434],{"class":2149},[133,66340,66342],{"id":66341},"script-pour-gérer-les-documents-latex","Script pour gérer les documents latex",[12,66344,66345,66346,66349,66350,66352,66353,179],{},"Ce script date de quelques temps quand je devais générer des documents\ndifférents à partir d'un même document ",[362,66347,66348],{},"LaTeX",".Il utilise différentes\ncommandes pour cela, et permet aussi de nettoyer le dossier. Une sorte\nde ",[362,66351,44430],{}," à la mode ",[362,66354,2319],{},[2105,66356,66358],{"className":2317,"code":66357,"language":2319,"meta":1646,"style":1646},"#!\u002Fbin\u002Fsh\n\nif [ $# != 2 ] ; then\n    echo \"Il faut le nom du fichier et de la sortie\"\n    exit 1;\nfi\n\nNAME=$1\nDEST=$2\n\ncase $DEST in\n    cleanPDF)\n        rm -f $NAME.pdf ;;\n    cleanTXT)\n        rm -f $NAME.txt ;;\n    cleanPS)\n        rm -f $NAME.ps  ;;\n    cleanDVI)\n        rm -f $NAME.dvi ;;\n    cleanHTML)\n        rm -f $NAME.html ;;\n    cleanTEX)\n        rm -f *.log *.toc *.htoc *.aux *.haux *~ ;;\n    clean)\n        glatex.sh $NAME cleanTEX\n        glatex.sh $NAME cleanDVI\n        glatex.sh $NAME cleanPS\n        glatex.sh $NAME cleanTXT\n        glatex.sh $NAME cleanPDF\n        glatex.sh $NAME cleanHTML ;;\n    mrproper)\n        glatex.sh $NAME clean ;;\n    _dvi)\n        pslatex $NAME.tex\n        pslatex $NAME.tex ;;\n    xdvi)\n        glatex.sh $NAME _dvi\n        xdvi $NAME.dvi ;;\n    dvi)\n        glatex.sh $NAME _dvi\n        glatex.sh $NAME cleanTEX ;;\n    _html)\n        hevea -francais -fix -noiso $NAME.tex ;;\n    html)\n        glatex.sh $NAME _html\n        glatex.sh $NAME cleanTEX ;;\n    _txt)\n        hevea -francais -fix -text $NAME.tex ;;\n    txt)\n        glatex.sh $NAME _txt\n        glatex.sh $NAME cleanTEX ;;\n    _ps)\n        glatex.sh $NAME _dvi\n        dvips -Ppdf $NAME.dvi ;;\n    ps)\n        glatex.sh $NAME _ps\n        glatex.sh $NAME cleanDVI\n        glatex.sh $NAME cleanTEX ;;\n    _pdf)\n        pdflatex $NAME.tex\n        pdflatex $NAME.tex ;;\n    pdf)\n        glatex.sh $NAME _pdf\n        glatex.sh $NAME cleanTEX ;;\nesac\n",[184,66359,66360,66364,66368,66384,66391,66399,66403,66407,66415,66425,66429,66439,66446,66462,66469,66482,66489,66503,66510,66523,66530,66542,66549,66586,66593,66603,66612,66621,66630,66639,66650,66657,66668,66675,66685,66696,66703,66712,66723,66730,66738,66749,66756,66776,66783,66792,66802,66809,66826,66833,66842,66852,66859,66867,66881,66888,66897,66905,66915,66922,66931,66941,66948,66957,66967],{"__ignoreMap":1646},[2113,66361,66362],{"class":2115,"line":2116},[2113,66363,61708],{"class":2396},[2113,66365,66366],{"class":2115,"line":1647},[2113,66367,2125],{"emptyLinePlaceholder":1767},[2113,66369,66370,66372,66374,66376,66378,66380,66382],{"class":2115,"line":1652},[2113,66371,4109],{"class":2326},[2113,66373,61820],{"class":2119},[2113,66375,61873],{"class":2414},[2113,66377,5638],{"class":2334},[2113,66379,2484],{"class":2166},[2113,66381,61881],{"class":2119},[2113,66383,7421],{"class":2326},[2113,66385,66386,66388],{"class":2115,"line":2185},[2113,66387,65004],{"class":2334},[2113,66389,66390],{"class":2149}," \"Il faut le nom du fichier et de la sortie\"\n",[2113,66392,66393,66395,66397],{"class":2115,"line":2230},[2113,66394,56296],{"class":2334},[2113,66396,2850],{"class":2166},[2113,66398,2487],{"class":2119},[2113,66400,66401],{"class":2115,"line":2235},[2113,66402,61860],{"class":2326},[2113,66404,66405],{"class":2115,"line":2241},[2113,66406,2125],{"emptyLinePlaceholder":1767},[2113,66408,66409,66411,66413],{"class":2115,"line":2246},[2113,66410,63890],{"class":2330},[2113,66412,2335],{"class":2334},[2113,66414,65139],{"class":2429},[2113,66416,66417,66420,66422],{"class":2115,"line":2464},[2113,66418,66419],{"class":2330},"DEST",[2113,66421,2335],{"class":2334},[2113,66423,66424],{"class":2429},"$2\n",[2113,66426,66427],{"class":2115,"line":2085},[2113,66428,2125],{"emptyLinePlaceholder":1767},[2113,66430,66431,66434,66437],{"class":2115,"line":2514},[2113,66432,66433],{"class":2326},"case",[2113,66435,66436],{"class":2330}," $DEST",[2113,66438,63943],{"class":2326},[2113,66440,66441,66444],{"class":2115,"line":2533},[2113,66442,66443],{"class":2330},"    cleanPDF",[2113,66445,4660],{"class":2119},[2113,66447,66448,66451,66453,66456,66459],{"class":2115,"line":2556},[2113,66449,66450],{"class":2133},"        rm",[2113,66452,61151],{"class":2166},[2113,66454,66455],{"class":2330}," $NAME",[2113,66457,66458],{"class":2149},".pdf",[2113,66460,66461],{"class":2119}," ;;\n",[2113,66463,66464,66467],{"class":2115,"line":2569},[2113,66465,66466],{"class":2330},"    cleanTXT",[2113,66468,4660],{"class":2119},[2113,66470,66471,66473,66475,66477,66480],{"class":2115,"line":2575},[2113,66472,66450],{"class":2133},[2113,66474,61151],{"class":2166},[2113,66476,66455],{"class":2330},[2113,66478,66479],{"class":2149},".txt",[2113,66481,66461],{"class":2119},[2113,66483,66484,66487],{"class":2115,"line":2596},[2113,66485,66486],{"class":2330},"    cleanPS",[2113,66488,4660],{"class":2119},[2113,66490,66491,66493,66495,66497,66500],{"class":2115,"line":3192},[2113,66492,66450],{"class":2133},[2113,66494,61151],{"class":2166},[2113,66496,66455],{"class":2330},[2113,66498,66499],{"class":2149},".ps",[2113,66501,66502],{"class":2119},"  ;;\n",[2113,66504,66505,66508],{"class":2115,"line":3213},[2113,66506,66507],{"class":2330},"    cleanDVI",[2113,66509,4660],{"class":2119},[2113,66511,66512,66514,66516,66518,66521],{"class":2115,"line":3236},[2113,66513,66450],{"class":2133},[2113,66515,61151],{"class":2166},[2113,66517,66455],{"class":2330},[2113,66519,66520],{"class":2149},".dvi",[2113,66522,66461],{"class":2119},[2113,66524,66525,66528],{"class":2115,"line":3248},[2113,66526,66527],{"class":2330},"    cleanHTML",[2113,66529,4660],{"class":2119},[2113,66531,66532,66534,66536,66538,66540],{"class":2115,"line":4899},[2113,66533,66450],{"class":2133},[2113,66535,61151],{"class":2166},[2113,66537,66455],{"class":2330},[2113,66539,41019],{"class":2149},[2113,66541,66461],{"class":2119},[2113,66543,66544,66547],{"class":2115,"line":1777},[2113,66545,66546],{"class":2330},"    cleanTEX",[2113,66548,4660],{"class":2119},[2113,66550,66551,66553,66555,66557,66560,66562,66565,66567,66570,66572,66575,66577,66580,66582,66584],{"class":2115,"line":4931},[2113,66552,66450],{"class":2133},[2113,66554,61151],{"class":2166},[2113,66556,2671],{"class":2414},[2113,66558,66559],{"class":2149},".log",[2113,66561,2671],{"class":2414},[2113,66563,66564],{"class":2149},".toc",[2113,66566,2671],{"class":2414},[2113,66568,66569],{"class":2149},".htoc",[2113,66571,2671],{"class":2414},[2113,66573,66574],{"class":2149},".aux",[2113,66576,2671],{"class":2414},[2113,66578,66579],{"class":2149},".haux",[2113,66581,2671],{"class":2414},[2113,66583,40569],{"class":2149},[2113,66585,66461],{"class":2119},[2113,66587,66588,66591],{"class":2115,"line":4961},[2113,66589,66590],{"class":2330},"    clean",[2113,66592,4660],{"class":2119},[2113,66594,66595,66598,66600],{"class":2115,"line":4976},[2113,66596,66597],{"class":2133},"        glatex.sh",[2113,66599,66455],{"class":2330},[2113,66601,66602],{"class":2149}," cleanTEX\n",[2113,66604,66605,66607,66609],{"class":2115,"line":4993},[2113,66606,66597],{"class":2133},[2113,66608,66455],{"class":2330},[2113,66610,66611],{"class":2149}," cleanDVI\n",[2113,66613,66614,66616,66618],{"class":2115,"line":5013},[2113,66615,66597],{"class":2133},[2113,66617,66455],{"class":2330},[2113,66619,66620],{"class":2149}," cleanPS\n",[2113,66622,66623,66625,66627],{"class":2115,"line":5018},[2113,66624,66597],{"class":2133},[2113,66626,66455],{"class":2330},[2113,66628,66629],{"class":2149}," cleanTXT\n",[2113,66631,66632,66634,66636],{"class":2115,"line":5042},[2113,66633,66597],{"class":2133},[2113,66635,66455],{"class":2330},[2113,66637,66638],{"class":2149}," cleanPDF\n",[2113,66640,66641,66643,66645,66648],{"class":2115,"line":5057},[2113,66642,66597],{"class":2133},[2113,66644,66455],{"class":2330},[2113,66646,66647],{"class":2149}," cleanHTML",[2113,66649,66461],{"class":2119},[2113,66651,66652,66655],{"class":2115,"line":5062},[2113,66653,66654],{"class":2330},"    mrproper",[2113,66656,4660],{"class":2119},[2113,66658,66659,66661,66663,66666],{"class":2115,"line":5098},[2113,66660,66597],{"class":2133},[2113,66662,66455],{"class":2330},[2113,66664,66665],{"class":2149}," clean",[2113,66667,66461],{"class":2119},[2113,66669,66670,66673],{"class":2115,"line":5117},[2113,66671,66672],{"class":2330},"    _dvi",[2113,66674,4660],{"class":2119},[2113,66676,66677,66680,66682],{"class":2115,"line":5142},[2113,66678,66679],{"class":2133},"        pslatex",[2113,66681,66455],{"class":2330},[2113,66683,66684],{"class":2149},".tex\n",[2113,66686,66687,66689,66691,66694],{"class":2115,"line":5148},[2113,66688,66679],{"class":2133},[2113,66690,66455],{"class":2330},[2113,66692,66693],{"class":2149},".tex",[2113,66695,66461],{"class":2119},[2113,66697,66698,66701],{"class":2115,"line":5163},[2113,66699,66700],{"class":2330},"    xdvi",[2113,66702,4660],{"class":2119},[2113,66704,66705,66707,66709],{"class":2115,"line":5169},[2113,66706,66597],{"class":2133},[2113,66708,66455],{"class":2330},[2113,66710,66711],{"class":2149}," _dvi\n",[2113,66713,66714,66717,66719,66721],{"class":2115,"line":5175},[2113,66715,66716],{"class":2133},"        xdvi",[2113,66718,66455],{"class":2330},[2113,66720,66520],{"class":2149},[2113,66722,66461],{"class":2119},[2113,66724,66725,66728],{"class":2115,"line":5180},[2113,66726,66727],{"class":2330},"    dvi",[2113,66729,4660],{"class":2119},[2113,66731,66732,66734,66736],{"class":2115,"line":5199},[2113,66733,66597],{"class":2133},[2113,66735,66455],{"class":2330},[2113,66737,66711],{"class":2149},[2113,66739,66740,66742,66744,66747],{"class":2115,"line":5204},[2113,66741,66597],{"class":2133},[2113,66743,66455],{"class":2330},[2113,66745,66746],{"class":2149}," cleanTEX",[2113,66748,66461],{"class":2119},[2113,66750,66751,66754],{"class":2115,"line":5209},[2113,66752,66753],{"class":2330},"    _html",[2113,66755,4660],{"class":2119},[2113,66757,66758,66761,66764,66767,66770,66772,66774],{"class":2115,"line":5232},[2113,66759,66760],{"class":2133},"        hevea",[2113,66762,66763],{"class":2166}," -francais",[2113,66765,66766],{"class":2166}," -fix",[2113,66768,66769],{"class":2166}," -noiso",[2113,66771,66455],{"class":2330},[2113,66773,66693],{"class":2149},[2113,66775,66461],{"class":2119},[2113,66777,66778,66781],{"class":2115,"line":5237},[2113,66779,66780],{"class":2330},"    html",[2113,66782,4660],{"class":2119},[2113,66784,66785,66787,66789],{"class":2115,"line":5242},[2113,66786,66597],{"class":2133},[2113,66788,66455],{"class":2330},[2113,66790,66791],{"class":2149}," _html\n",[2113,66793,66794,66796,66798,66800],{"class":2115,"line":5267},[2113,66795,66597],{"class":2133},[2113,66797,66455],{"class":2330},[2113,66799,66746],{"class":2149},[2113,66801,66461],{"class":2119},[2113,66803,66804,66807],{"class":2115,"line":5282},[2113,66805,66806],{"class":2330},"    _txt",[2113,66808,4660],{"class":2119},[2113,66810,66811,66813,66815,66817,66820,66822,66824],{"class":2115,"line":5295},[2113,66812,66760],{"class":2133},[2113,66814,66763],{"class":2166},[2113,66816,66766],{"class":2166},[2113,66818,66819],{"class":2166}," -text",[2113,66821,66455],{"class":2330},[2113,66823,66693],{"class":2149},[2113,66825,66461],{"class":2119},[2113,66827,66828,66831],{"class":2115,"line":5310},[2113,66829,66830],{"class":2330},"    txt",[2113,66832,4660],{"class":2119},[2113,66834,66835,66837,66839],{"class":2115,"line":5315},[2113,66836,66597],{"class":2133},[2113,66838,66455],{"class":2330},[2113,66840,66841],{"class":2149}," _txt\n",[2113,66843,66844,66846,66848,66850],{"class":2115,"line":5320},[2113,66845,66597],{"class":2133},[2113,66847,66455],{"class":2330},[2113,66849,66746],{"class":2149},[2113,66851,66461],{"class":2119},[2113,66853,66854,66857],{"class":2115,"line":8625},[2113,66855,66856],{"class":2330},"    _ps",[2113,66858,4660],{"class":2119},[2113,66860,66861,66863,66865],{"class":2115,"line":8631},[2113,66862,66597],{"class":2133},[2113,66864,66455],{"class":2330},[2113,66866,66711],{"class":2149},[2113,66868,66869,66872,66875,66877,66879],{"class":2115,"line":8637},[2113,66870,66871],{"class":2133},"        dvips",[2113,66873,66874],{"class":2166}," -Ppdf",[2113,66876,66455],{"class":2330},[2113,66878,66520],{"class":2149},[2113,66880,66461],{"class":2119},[2113,66882,66883,66886],{"class":2115,"line":8652},[2113,66884,66885],{"class":2330},"    ps",[2113,66887,4660],{"class":2119},[2113,66889,66890,66892,66894],{"class":2115,"line":8674},[2113,66891,66597],{"class":2133},[2113,66893,66455],{"class":2330},[2113,66895,66896],{"class":2149}," _ps\n",[2113,66898,66899,66901,66903],{"class":2115,"line":8706},[2113,66900,66597],{"class":2133},[2113,66902,66455],{"class":2330},[2113,66904,66611],{"class":2149},[2113,66906,66907,66909,66911,66913],{"class":2115,"line":8724},[2113,66908,66597],{"class":2133},[2113,66910,66455],{"class":2330},[2113,66912,66746],{"class":2149},[2113,66914,66461],{"class":2119},[2113,66916,66917,66920],{"class":2115,"line":8729},[2113,66918,66919],{"class":2330},"    _pdf",[2113,66921,4660],{"class":2119},[2113,66923,66924,66927,66929],{"class":2115,"line":8734},[2113,66925,66926],{"class":2133},"        pdflatex",[2113,66928,66455],{"class":2330},[2113,66930,66684],{"class":2149},[2113,66932,66933,66935,66937,66939],{"class":2115,"line":8740},[2113,66934,66926],{"class":2133},[2113,66936,66455],{"class":2330},[2113,66938,66693],{"class":2149},[2113,66940,66461],{"class":2119},[2113,66942,66943,66946],{"class":2115,"line":8772},[2113,66944,66945],{"class":2330},"    pdf",[2113,66947,4660],{"class":2119},[2113,66949,66950,66952,66954],{"class":2115,"line":8778},[2113,66951,66597],{"class":2133},[2113,66953,66455],{"class":2330},[2113,66955,66956],{"class":2149}," _pdf\n",[2113,66958,66959,66961,66963,66965],{"class":2115,"line":8789},[2113,66960,66597],{"class":2133},[2113,66962,66455],{"class":2330},[2113,66964,66746],{"class":2149},[2113,66966,66461],{"class":2119},[2113,66968,66969],{"class":2115,"line":8795},[2113,66970,66971],{"class":2326},"esac\n",[3358,66973,66974],{},"html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}",{"title":1646,"searchDepth":1647,"depth":1647,"links":66976},[66977,66978,66979,66980,66981,66982,66983,66984],{"id":64934,"depth":1652,"text":64935},{"id":65031,"depth":1652,"text":65032},{"id":65096,"depth":1652,"text":65097},{"id":65427,"depth":1652,"text":65428},{"id":65550,"depth":1652,"text":65551},{"id":65847,"depth":1652,"text":65848},{"id":65993,"depth":1652,"text":65994},{"id":66341,"depth":1652,"text":66342},"2006-08-20","Ce billet est consacré au script Linux. J'ai écrit divers script pour\n\"automatiser\" certaines commandes, même si je dois exécuter ces\nscripts manuellement. Je pourrais, quand bien même, appeler ces scripts\ndans un cron si je le souhaitais.",{"type":9,"value":66988},[66989,66995],[12,66990,64920,66991,64924,66993,64928],{},[362,66992,64923],{},[184,66994,64927],{},[12,66996,64931],{},{},"\u002Fpost\u002Fscript-linux",{"title":64915,"description":66986},"script-linux","posts\u002FSysAdmin\u002F2006-08-20-script-linux",[38764,18167],"qYYuAxXiRSKJnYilIVY9b-rBpo_YbAqe2w9ZkHlKm6k",1777582399249]