Hello!

Inspiré(e) de prendre part à la discussion ? Ou de poser une question ou demander de l’aide ?

Alors bienvenues dans les grands sujets des forums de La Bulle : m’inscrire.

Cette partie du forum n’est pas compatible avec les bloqueurs publicitaires

Félicitations à vous, de préférer les accès payants plutôt que la gratuité par la publicité, c’est honnorable et cohérent de votre part. Malheureusement, l’accès payant par micropaiement (qui serait d’environ 1 cent pour 20 pages consultées) n’est pour l’instant pas encore mis en place, et l’accès gratuit sans publicité, est réservé aux membres actif(ve)s du forum. En attendant, si vous souhaitez poursuivre votre visite chez nous, vous pouvez ajouter le site à votre liste blanche, ou encore mieux, désactiver le bloqueur partout. Pour ajouter le site à votre liste blanche, pour Firefox (similaire pour les autres navigateurs), rendez‑vous en bas à gauche de la fenêtre de votre navigateur, et cliquez sur le menu comme dans l’exemple de l’image ci‑dessous, puis rechargez la page, en appuyant sur F5.

Décodeur/encodeur Unicode en Ada
Auteur Message
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22173
Dim 3 Mar 2013 22:14
Message Décodeur/encodeur Unicode en Ada
Note : le sujet est fermé aux commentaires, le temps qu’il est en cours de construction. Il sera ouvert aux commentaires une fois terminé.

Résumé


Le sujet présente une spécification et une implémentation Ada, pour une prise en charge de base d’Unicode. Il fait un rappel des notions de base d’Unicode et discute des choix de mise en œuvre en Ada, avec un décodeur et un encodeur sous forme de deux automates passifs (la raison est donnée au cours du sujet).

Ce qui est proposé ici ne signifie pas qu’il est ignoré que Ada 2012 propose ce qu’il faut pour les usages les plus courant d’Unicode en pratique; il s’agit seulement d’une approche alternative avec ses propres caractéristiques, et qui pourra être un sujet d’étude ou de curiosité comme un autre.

Le sujet n’est pas une véritable introduction à Ada, il suppose que Ada soit connu un petit peu au minimum, au moins de l’avoir déjà survolé, même rapidement.

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22173
Dim 3 Mar 2013 22:48
Message Re: Décodeur/encodeur Unicode en Ada

Rappel des notions de base d’Unicode


Unicode définit des types de données qui ne doivent pas être confondus (en pratique, ils le sont assez souvent) : code‑point, code‑points encodables ou non‑encodables, caractères, non‑caractères, caractères à usage privé, caractères assignés ou non‑assignés. Les code‑points sont de plus, organisés en plans (ce dernier aspect est marginal en pratique).

L’espace d’encodage est la série des entiers compris entre 0 et 10FFFFh (1 114 111, environ 1 million), bornes incluses. Un code‑point est n’importe quelle valeur appartenant à cet ensemble.

Les caractères, sont une interprétation de certains de ces code‑points. Les code‑points ne correspondent pas toujours à des caractères, certains code‑points ne correspondent et ne correspondront jamais à un caractère, mais un caractère a toujours un code‑point.

Sont appelés non‑caractères, les code‑points qui ne correspondent à aucun caractère, et qui sont réservés à un usage strictement interne à une application; il est possible de les appeler « caractères privés à usage interne » (à ne pas confondre avec les caractères à usage privé tout‑court). Leur usage est normalement rare. En font parti, ceux dont la valeur modulo 10000h est soit FFFEh ou FFFFh, ainsi que ceux qui se trouvent dans l’intervalle FDD0h à FDEFh, bornes incluses. Les non‑caractères ne correspondent pas à des code‑points invalides (la notion de code‑point invalide, n’existe pas), et signifie seulement qu’il est invalide de les interpréter comme des caractères (la notion de caractère invalide, elle, existe bien) quand ils n’ont pas une origine interne. Par exemple il est valide de trouver ces code‑points dans un fichier au format UTF‑8 après décodage, et il sera juste invalide de les interpréter comme des caractères (« normalement »). Un fichier UTF‑8 encodant de tels code‑points, sera et restera toujours valide. L’interprétation des non‑caractères ne sera jamais spécifiée par Unicode, et ne doit pas être spécifié par aucun standard indépendant d’Unicode (seulement éventuellement dans une spécification interne à une application).

Anecdote.

Les « Caractères » en Ada, c’est à dire les types prédéfinis Character, Wide_Character et Wide_Wide_Character, ne sont finalement pas des caractères, mais des code‑points. En effet et par exemple, la déclaration du paquet Standard, dans la référence Ada, présente ceci :

Source Ada : 

package Standard is

type Wide_Character is (nul, sohHex_0000FFFE, Hex_0000FFFF);

end Standard;

Ors, d’après le standard Unicode, FFFEh et FFFFh, sont des non‑caractères. On peut raisonnablement dire que les caractères d’Ada, sont plus des code‑points que des caractères au sens courant du mot.

Fin de l’anecdote.

Les caractères (les code‑points pouvant être interprétés comme des caractères), se distinguent entre les caractères à usage privé, les caractères assignés et les caractères non‑assignés.

Les caractères à usage privés sont ceux dont les code‑points sont dans l’intervalle 0F0000h à 10FFFFh. Ces caractères ne sont interprétables dans aucune langue ou langage et n’ont pas de signification, et ce sont pourtant bien des caractères et pas des non‑caractères. Il appartient à l’application qui les utilise de leur donner une signification comme caractères (ou de refuser de leur en donner une). S’ils proviennent d’un émetteur inconnu ou non‑concerné par les conventions propres à cette application, ces caractères ne devront pas être interprétés, car leur interprétation restera toujours privée et ne sera jamais dictée par le standard (mais leurs interprétation pourront être spécifiées par un standard indépendant d’Unicode).

Les caractères assignés, sont les caractères qui ne sont pas à usage privé et qui ont une signification et/ou un glyphe associé. Ces caractères sont toujours interprétables au moins dans certaines langues, langages, ou protocoles.

Les caractères non‑assignés, sont les caractères qui ne sont pas à usage privé, mais qui n’ont pas encore été assignés. Ils ne sont sémantiquement interprétables dans aucune langue pour le moment, mais peuvent à tout moment dans l’avenir, se voit assigner une interprétation. Contrairement aux caractères à usage privé, il n’est pas permis à une application d’assigner elle‑même une signification à ces caractères (cela rendrait l’application non‑conforme avec toutes éventuelles futures version d’Unicode qui assignerait ces caractères); ces caractères ne sont pas librement ré‑assignables, ceci afin de respecter une éventuelle assignation future qui leur serait donnée par le standard.

Les code‑points (qu’ils correspondent à des caractères ou pas) sont toujours valides en temps que code‑point, mais certains sont encodables et d’autres non‑encodables. Les code‑points non‑encodables sont ceux se trouvant dans l’intervalle D800h à DFFFh, bornes incluses. Par opposition, les autres sont encodables, et Unicode les appelle les scalaires. En pratique, tous les caractères, assignés ou pas, privés ou pas, internes ou pas, appartiennent à ce sous‑ensembles des code‑points que sont les scalaires, sinon ils ne pourraient pas être transmis, ce qui les rendraient inutiles.

Un code‑point non‑encodable peut toujours être stocké dans une application avec l’encodage décidé par l’application (le plus souvent simplement le type utilisé par la machine pour représenter les nombres), ces code‑points ne sont pas invalides comme données brutes dans une application, ils ne peuvent seulement pas êtres encodés dans un des formats standards d’Unicode ou alors le format d’encodage ne peut plus être qualifié de conforme à Unicode et il est alors un format d’encodage privé. On ne doit normalement pas les trouver dans une chaîne. Ces code‑points ne peuvent et ne doivent pas être encodés dans des fichiers UTF‑8 (il serait alors invalide ou partiellement invalide), sauf s’ils sont internes à une application où à un système. Les code‑points non‑encodables, sont en pratique, non‑interopérables, et cela est une conséquence de leur définition.

Anecdote.

Les code‑points non‑encodables, sont non‑encodables parce qu’ils ne pourraient pas être encodés avec le format d’encodage UTF‑16. Ils pourraient être encodés avec les formats d’encodage UTF‑8 et UTF‑32, mais à fin de garantir un ensemble de définition de l’encodage identique pour tous les trois formats d’encodage, il a été décidé par Unicode, de spécifier que ce qui ne peut techniquement pas être encodé en UTF‑16, ne devra pas non‑plus être encodé en UTF‑8 ou UTF‑32 (même si ce serait techniquement possible). C’est aussi pour une raison technique comparable, et dans laquelle encore UTF‑16 est impliqué, que l’espace d’encodage d’Unicode, est limité par une borne supérieure fixée à 10FFFFh.

Fin de l’anecdote.


Les code‑points sont organisés en plans de 10000h (65536) éléments chacun. Le numéro du plan d’appartenance d’un code‑point, est donc le numéro du code‑point, divisé par 10000h. La position d’un code‑point dans son plan, est le numéro du code‑point, modulo 10000h. On manipule rarement les plans et les positions en pratique, mais ces deux notions sont tout de même utilisées dans des définitions importantes, ainsi que dans les stratégies d’attributions des code‑points aux caractères, qui sont souvent regroupés par langues et familles d’usages, pour lesquels chaque plan à une « prédisposition » (les plans sont eux‑mêmes subdivisés plus précisément, mais d’une manière bien moins régulière).

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22173
Lun 4 Mar 2013 00:23
Message Re: Décodeur/encodeur Unicode en Ada

Rappel sur les formats UTF


Les formats UTF‑8/16/32, encodent des code‑points, et non‑pas des caractères. Ce n’est pas être pédant ou sourcilleux de le souligner, car cela signifie que des code‑points qui ne correspondent pas à des caractères, peuvent être encodés dans un flux UTF. L’oubliez, c’est prendre le risque de traiter comme non‑valide, un flux UTF pourtant valide [1]. L’interprétation des code‑points encodés, après décodage, n’est pas la responsabilité d’un décodeur UTF, c’est celle des applications clientes de ce décodeur. C’est ce qui consomme la sortie du décodeur, qui a la responsabilité de l’interprétation.

Est appelé format d’encodage, la définition de l’encodage sans se soucier de l’ordre des octets. Est appelé schéma d’encodage, la définition de l’encodage, à laquelle s’ajoute le soucis de l’ordre des octets. Pour UTF‑8, le schéma d’encodage est le même que le format d’encodage, mais pour le format UTF‑16, on aura les deux schémas UTF‑16LE et UTF‑16B, de même pour UTF‑32.

Les éléments constituant un flux UTF, sont appelés unité d’encodage. C’est l’octet pour UTF‑8, le mot de 16 bits pour UTF‑16 et le mot de 32 bits pour UTF‑32. L’unité d’encodage se comprends sans se soucier de l’ordre des octets, même si pour UTF‑8, cette question ne s’applique pas. Ceci même si ces mots de 16 ou 32 bits, peuvent être subdivisés pour les schémas concrets, LE et BE (Little‑Endian et Big‑Endian), de chaque formats. Quand l’ordre des octets est une question pertinente (ce qui n’est pas nécessairement le cas, même avec UTF16/32), on traite avec des octets, qui forment les unités. Pour être uniforme, j’appelle personnellement les octets, dans ce contexte, des sous‑unités.

La série des unités d’un flux UTF est appelée une séquence UTF. La série des unités encodant un code‑point seul dans le flux, est appelée une sous‑séquence. Une séquence ne contient que des sous‑séquence du même format et schéma d’encodage (il n’est pas permis par exemple, de mixer UTF‑8 et UTF‑16 dans une même séquence, ni de mixer UTF‑16LE et UTF‑16BE).

Une séquence bien‑formée, est une séquence dans laquelle l’encodage de chaque sous‑séquence est bien‑formé, elle est dite séquence mal‑formée dans le cas contraire. Une sous‑séquence bien‑formée est une sous‑séquence qui respecte le format d’encodage; une sous‑séquence mal‑formée est une sous‑séquence qui ne le respecte pas. L’interprétation des caractères ne joue aucun rôle dans le caractère bien‑formé ou mal‑formé d’une séquence ou d’une sous‑séquence.

Un flux ou séquence UTF‑8 est constituée d’autant de sous‑séquences d’encodage qu’il y a de code‑point encodés dans le flux. Chaque code‑point est encodé par une sous‑séquence, de longueur variable (longueur en nombre d’octets) et qui est fonction du code‑point; pour un code‑point donné, la longueur de cette sous‑séquence est toujours la même (en fait, l’encodage de chaque code‑point est toujours identique, et c’est même une condition pour que l’encodage soit qualifié de bien‑formé).


Références

[1] Unicode §3.9/D84 :
Citation : 
Ill-formed: A Unicode code unit sequence that purports to be in a Unicode encoding form is called ill-formed if and only if it does not follow the specification of that Unicode encoding form.

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22173
Lun 4 Mar 2013 04:02
Message Re: Décodeur/encodeur Unicode en Ada

Rappel sur le format UTF‑8


UTF‑8 est un format compacte, au moins en moyenne si on considère les fréquences d’occurrences des caractères encodés dans ce format (plus le code‑point est petit, plus la longueur de la chaîne d’octets l’encodant, est courte). Il est transparent pour le format ASCII 7 bits, car les code‑points sur 7 bits, s’encodent sur un octet portant 7 bits de données (vous comprendrez comment plus loin). Ce dernier point était la principale exigence faite à ce format. Ces deux caractéristiques l’ont fait être favorisé par Unicode et par la pratique.

UTF‑8 tronçonne la séquence de bits du code‑points à encoder, et écrit chacun de ces tronçons dans une unité d’encodage, dont chacune est spécifiquement taguée par des bits appropriés.

Chaque sous‑séquence d’encodage UTF‑8, commence par un octet de tête, suivi d’une éventuelle série d’octets de queue, qui peut être vide (la chaîne d’octets de queue). Les octets de queue ont tous le même format général. L’octet de tête indique le nombre totale d’octets dans la sous‑séquence. Les termes « octet de tête » et « octet de queue », ne sont pas les termes du standard, ils sont utilisés ici parce qu’ils parlent bien.

Chaque octet de queue, tous du même format général, transporte 6 bits de donnés au total. L’octet de tête est toujours présent, et il porte le reste des bits de données. Les octets de queue ne sont pas toujours présents : l’octet de tête peut être tout seul (c’est le cas pour les code‑points des caractères ASCII 7 bits). Pour connaitre le nombre d’octets de queue nécessaires pour encoder un code‑point, il faut diviser le nombre de bits du code‑point, par 6 (division entière classique, c’est à dire arrondie vers le bas), si le nombre de bits du code‑point est supérieur à 7, sinon, le nombre d’octets de queue est de zéro. Par exemple pour un code‑point nécessitant 8 bits de données, il y aura un octet de queue, et le reste (les deux bits de poids forts des 8 bits au total du code‑point) sera transporté par l’octet de tête.

Chaque octet de queue est du format “10nnnnnn” (en binaire), où les “nnnnnn” sont 6 bits du code‑point. Si l’octet de tête, a sont bit de poids fort à zéro, c’est à dire qu’il est de la forme “0nnnnnnn”, et alors il n’y a pas d’octets de queue, et le code‑point encodé est le décodage binaire de “nnnnnnn”. Si le bit de poids fort de l’octet de tête n’est pas à zéro, alors il est de la forme “1*x0n*y” : “1” répété x fois (et x est supérieur ou égale à deux), suivi d’un “0”, suivi de y bits “n”, le nombre total de bits (x + 1 + y) faisant 8. Le nombre de “1” du début, correspondant au nombre total d’octets dans la séquence, incluant l’octet de tête. Par exemple, “1110nnnn” est l’octet de tête d’une séquence de trois octets, dont les deux octets de queue qui le suivent, sont de la forme (toujours la même) “10nnnnnn”. Un octet de queue, “10nnnnnn”, ne peut pas être pris par erreur pour un octet de tête signifiant une séquence de un octet, car la séquence de un octet unique, c’est celle où l’octet de tête est seul, et où il est donc de la forme “0nnnnnnn” (c’est pour cela qu’il est dit plus haut, que x est supérieur ou égale à deux). Les bits du code‑points sont rangés dans les places dédiées à cet effet, en commençant par ranger les bits de poids fort dans l’octet de tête, puis en allant vers les bits de poids faible, qui seront rangés dans les octets de queue à la suite. Par exemple, le code‑point “aaabbbbbbccccccdddddd” (où les groupes de lettres représentent des groupes de bits), sera encodé comme “11110aaa”, “10bbbbbb”, “10cccccc”, “10dddddd”.

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22173
Mer 6 Mar 2013 15:35
Message Re: Décodeur/encodeur Unicode en Ada

Le traitement des erreurs d’encodage UTF‑8


Pour une meilleur interopérabilité, Unicode spécifie une recommandation pour le traitement des erreurs, pour les décodeurs traitant les erreurs. Ce qui n’est pas contre pas une recommandation, mais une obligation pour être conforme à Unicode, c’est de ne jamais interpréter une erreur et de ne jamais la filtrer ou l’ignorer silencieusement (ce qui serait une forme d’interprétation, que de dire qu’une sous‑séquence mal‑formée peut être ignorée).

Au début d’Unicode, et jusqu’au moins 2003, le traitement des erreurs d’encodage UTF‑8 par un décodeur, était laissé à la libre appréciation de chacun(e). Par la suite, les spécifications de l’encodage ont été resserrées, d’abord pour remédier à un problème de sécurité, et ensuite pour rendre plus interopérables et prévisibles, les décodeurs, jusque dans leur traitement des erreurs. La spécification actuelle d’Unicode est très précise à ce sujet, et ne laisse plus aucune place à l’interprétation personnelle, même si une part de celle‑ci n’a valeur « que » de recommandation, qui n’est pas requise pour la conformité à Unicode.

Le format d’encodage UTF‑8 (voir message précédent), pris dans sa forme générale, associe à chaque sous‑séquence bien‑formée, un seul code‑point qui pourra en être décodé, mais dans l’autre sens, celui de l’encodage, pour un même code‑point, il peut exister (plutôt pouvait exister) plusieurs encodages possibles. Le cas le plus simple à imaginer, est celui du zéro. Toutes les sous‑séquences qui suivent donnent le code‑point zéro après décodage : “00000000” ou “11000000 100000000” ou “11100000 100000000 100000000” ou “11110000 100000000 100000000 100000000”. La même chose est applicable à tout un ensemble de code‑points, dont ceux correspondants aux caractères ASCII 7 bits. Des pirates informatiques ont utilisé cette technique pour contourner des filtre de sécurité, qui avaient pour fonction d’empêcher certaines commandes d’entrer dans certains systèmes informatiques. Ces filtres filtraient des chaînes ASCII 7 bits, sans se soucier qu’il était possible de représenter les caractères ASCII 7 bits de plus d’une manière en UTF‑8, car ces filtre avaient été conçus pour ASCII 7 bits, et UTF‑8 n’est arrivé qu’ensuite, favorisé par sa compatibilité avec ASCII 7 bits. Pour remédier à cela, Unicode a spécifié que l’encodage valide pour un caractère, doit obligatoirement être la plus courte séquence possible, ce qui implique que la sous‑séquence encodant chaque code‑point soit unique [1]. Ainsi, pour le code‑point zéro, seul l’encodage “00000000” est valide, et les autres, donnés en exemple, bien que décodable en s’en tenant au format général de l’encodage UTF‑8, ne doivent pas être considérés comme bien‑formés et doivent être rejetés.

Certains code‑points ne sont pas encodables (voir le second message de ce sujet). Il serait possible de décoder un caractère selon le format général d’UTF‑8, tout en s’assurant que l’on décode la plus courte séquence possible (et de traiter comme une erreur une séquence inutilement trop longue), et de ne vérifier qu’après décodage du code‑point, que celui‑ci n’appartient pas à l’intervalle des code‑points non‑encodables. Unicode fait la recommandation de rejeter ces séquences au plus tôt, dès le premier octet qui trahit que la séquence produira inévitablement un code‑point en dehors de l’ensemble autorisé.

Tenant compte des contraintes les plus récentes pour un encodage UTF‑8 valide, de correspondre à la plus courte séquence possible et de détecter au plus tôt et même avant la fin de la séquence, la présence d’un code‑point dont l’encodage n’est pas permis, il est possible de dériver un format plus complexe que le format général, mais plus précis aussi, et qui garantie qu’une sous‑séquence mal‑formée, sera rejeté au plus tôt. D’après ce format, les octets de queue acceptable ne sont plus tous nécessairement du format général “10nnnnnn”, mais un sous‑ensemble des combinaisons rendues possible par ce format général. Une table exhaustive est fourni dans la référence Unicode, au chapitre §3.9 (“Unicode Encoding Forms”), table 3.7.

Table 3.7 du chapitre §3.9 de la version 6.2 de la spécification d’Unicode :

Séquences valides à 1 octets :
  • [00h à 7Fh] ( ⇒ code‑points de U+0000 à U+007F)

Séquences valides à 2 octets :
  • [C2h à DFh], [80h à BFh] ( ⇒ code‑points de U+0080..U+07FF)

Séquences valides à 3 octets :
  • [E0h], [A0h à BFh], [80h à BFh] ( ⇒ code‑points de U+0800 à U+0FFF)
  • [E1h à ECh], [80h à BFh], [80h à BFh] ( ⇒ code‑points de U+1000 à U+CFFF)
  • [1Dh], [80h à 9Fh], [80h à BFh] ( ⇒ code‑points de U+D000 à U+D7FF)
  • [EEh à EFh], [80h à BFh], [80h à BFh] ( ⇒ code‑points de U+E000 à U+FFFF)

Séquences valides à 4 octets :
  • [F0h], [90h à BFh], [80h à BFh], [80h à BFh] ( ⇒ code‑points de U+10000 à U+3FFFF)
  • [F1h à F3h], [80h à BFh], [80h à BFh], [80h à BFh] ( ⇒ code‑points de U+40000 à U+FFFFF)
  • [F4h], [80h à 8Fh], [80h à BFh], [80h à BFh] ( ⇒ code‑points de U+100000 à U+10FFFF)

Les octets de queue ne collant pas au format général et auxquels il faut prêter attention, sont soulignés (ils 4, et sont tous des octets de queue en seconde position dans la sous‑séquence). Excepté pour les séquences à un octet et les séquences à deux octets, des octets de têtes, aucun ne colle au format général, ceux‑ci ne sont pas soulignés, pour ne pas alourdir (la table de la référence d’Unicode fait de même). Tout ce qui ici, ne colle pas au format général, est tout de même inclus dans celui‑ci et est compatible avec celui‑ci, il s’agit en effet de restriction, de resserrements, par rapport au format général. Ce format plus précis et plus complexe, et un sous‑ensemble du format général.

L’application de la table ci‑dessus, pour détecter au plus tôt les sous‑séquences aboutissant à des code‑points en dehors du domaine des code‑points encodables, n’est pas obligatoire pour la conformité à Unicode, car l’application du format général suivis des autres tests nécessaires, aboutit au même résultat, mais l’application de ce format plus détaillé est important pour pouvoir suivre la recommandation pour le traitement des erreurs. Le rejet des séquences inutilement trop longues et des code‑points en dehors du domaine de l’encodable, sont par contre requis pour la conformité à Unicode; les sous‑séquences UTF‑8 ne respectant pas ces deux critères (en plus de respecter le format général de l’encodage), doivent obligatoirement être rejetées et signalées par le décodeur et ne pas être interprétée (ce qui implique de ne pas les ignorer silencieusement).

Une sous‑séquence mal‑formée, en plus de devoir être signalée au client du décodeur, peut se voir substituer le code‑point spécial FFFDh. Cette substitution n’est pas une obligation, mais une recommandation.

Il est recommandé que ce qui est substitué par FFFDh, soit la plus grande sous‑séquence initiale qui aurait put ressembler au début de la sous‑séquence bien‑formée d’un code‑point, sur la base de la table 3.7 du chapitre §3.9 et non‑pas sur la base du format général. La référence d’Unicode, au chapitre §3.9, sous le petit titre “Best Practices for Using U+FFFD”, en parle longuement, mais d’une manière difficile à comprendre, dont un résumé pourrait être que si un série d’octets commence comme une sous‑séquence bien‑formée (une sous‑séquence étant la partie d’un flux UTF‑8, correspondant à l’encodage d’un code‑point), et qu’il s’avère qu’elle échoue, alors ce qui doit être substitué par FFFDh, c’est toute la sous‑séquence initiale du code‑point, qui semblait à première vue commencer normalement. Si à partir de la position de décodage courante, il n’existe aucune sous‑séquence initiale bien‑formée, alors c’est l’octet courant qui doit être substitué par FFFDh (ce qui signifie que ce que l’on substitue par FFFDh, a toujours une longueur minimale de 1 octet) [2].

Après une substitution par FFFDh, si le décodeur doit (ceci dépendant de ses spécifications, car il peut tout autant lever une exception et ne pas reprendre) tenter de reprendre le décodage, il doit le faire à l’octet suivant immédiatement ce qui a été substitué par FFFDh [3].

Il n’est pas permis d’ignorer/filtrer silencieusement une sous‑séquence mal‑formée !


Références

[1] Unicode §3.9/D79 :
Citation : 
A Unicode encoding form assigns each Unicode scalar value to a unique code unit sequence.


[2] Unicode §3.9/Best Practices for Using U+FFFD :
Citation : 
Unconvertible offset: An offset in a code unit sequence for which no code unit subsequence starting at that offset is well-formed.

Maximal subpart of an ill-formed subsequence: The longest code unit subsequence starting at an unconvertible offset that is either:
a. the initial subsequence of a well-formed code unit sequence, or
b. a subsequence of length one.


[…]

Whenever an unconvertible offset is reached during conversion of a code unit sequence:
1. The maximal subpart at that offset should be replaced by a single U+FFFD.
2. The conversion should proceed at the offset immediately after the maximal subpart.


[3] Unicode §3.9/Best Practices for Using U+FFFD :
Citation : 
The conversion should proceed at the offset immediately after the maximal subpart.

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22173
Jeu 7 Mar 2013 00:00
Message Re: Décodeur/encodeur Unicode en Ada

Les éléments du domaine


Dans le domaine, les concepts suivant ont été identifiés :

  • (non‑)caractère
  • caractère (non‑)assigné
  • caractère à usage privé
  • code‑point
  • décodage
  • encodage
  • espace d’encodage
  • (format | schéma) d’encodage
  • plan
  • position
  • scalaire (le nom Unicode d’un code‑point encodable)
  • séquence (bien‑formée | mal‑formée)
  • sous‑séquence (bien‑formée | mal‑formée)
  • sous‑séquence initiale (bien‑formée | mal‑formée)
  • unité d’encodage
  • UTF‑8, UTF‑16(LE | BE), UTF‑32(LE | BE)

On peut distinguer deux grands groupes, que sont les caractères et code‑points d’un côté, et les séquences et encodages de l’autre côté.
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22173
Jeu 7 Mar 2013 00:31
Message Re: Décodeur/encodeur Unicode en Ada

Début de spécification Ada pour les code‑points et caractères


Les caractères et code‑points se définissent par des relations entres des ensembles, avec pour ensemble initial, l’espace d’encodage :

  • code‑points = espace d’encodage = [0-10FFFFh] (tout l’ensemble, ils sont identiques)
  • plan * position ⟼ code‑point
  • code‑point ⟼ plan * position
  • code‑points non‑encodable = [D800h-DFFFh]
  • scalaires = code‑points - code‑points non‑encodables
  • non‑caractères = code‑points - [FDD0h-FDEFh] - nnFFFEh - nnFFFFh
  • caractères = code‑points - non‑caractères
  • caractères assignés ⊂ caractères
  • caractères non‑assignes = caractères - caractères assignés
  • caractères à usage privés ⊂ caractères assignés

Note : en pratique, on devrait plutôt dire…
  • non‑caractères = scalaires - [FDD0h-FDEFh] - nnFFFEh - nnFFFFh
  • caractères = scalaires - non‑caractères
…mais Unicode définie les caractères à partir des code‑points, bien que en pratique et par la conséquence d’autres définitions, les caractères et non‑caractères, ne peuvent appartenir qu’à l’ensemble de ce que Unicode appelle les scalaires.

Ce premier regroupement de définitions, peut encore être divisé en deux groupes : les définitions à partir des code‑points (ou scalaires en pratique) et les définition à partir des caractères. Entre les deux, se trouve la définition des non‑caractères, qui bien que servant à définir les caractères, se défini à partir des code‑points, et pourrait donc entrer dans le groupe des code‑points, autant que dans celui des caractères.

Comme ce qui se défini à partir de l’ensemble des caractères, n’a pas besoin de référence directe au code‑points, quand on utilise les caractères, ont utilise pas nécessairement les code‑points, et il doit être possible d’utiliser les caractères, sans utiliser les code‑points, ce qui suggère de permettre deux “with” séparés, et donc deux paquets séparés.

En prenant le partie d’inclure la définition des non‑caractères, dans le paquet des caractères (justifié par le fait que la nécessité de cette définition ne vient qu’avec celle des caractères), une ébauche possible serait ce qui suit (où les « … » signifie des parties à compléter, omises ici par concision) :

Source Ada : 

package Code_Points is

type Codespace_Type is range …;

subtype Code_Point_Type is Codespace_Type;

subtype Non_Scalar_Type is Code_Point_Type range …;

subtype Scalar_Type is Code_Point_Type
with Static_Predicate => Scalar_Type not in Non_Scalar_Type;

type Plane_Type is range …;

type Position_Type is range …;

function Plane (CP : Code_Point_Type) return Plane_Type;

function Position (CP : Code_Point_Type) return Position_Type;

function CP
(Plane : Plane_Type;
Position : Position_Type)
return Code_Point_Type;

end Code_Points;


Source Ada : 

package Characters is

subtype Non_Character_Type is Code_Point_Type range …;

subtype Character_Type is Code_Point_Type
with Static_Predicate => Character_Type not in Non_Character_Type;

subtype Assigned_Character_Type is Character_Type
with Dynamic_Predicate => …;

subtype Private_Use_Character_Type is Assigned_Character_Type range …;

end Characters;


Quelques petites variations possibles :
  • Définir `Non_Character_Type` dans `Code_Points` au lieu de le définir dans `Characters`
  • Pour `Assigned_Character_Type`, utiliser un prédicat statique au lieu d’un prédicat dynamique (*); mais cela semble très difficile en pratique
  • Définir `Character_Type` à partir de `Scalar_Type` plutôt qu’à partir de `Code_Point_Type`, ceci collant moins avec la définition qu’en fait Unicode (sans être en contradiction avec), mais collant mieux aux contraintes effectives.

(*) un prédicat statique, ne peut utiliser qu’une expression statique, tandis qu’un prédicat dynamique, peut utiliser un appel de fonction pour définir l’expression de vérification du prédicat.

Comme les caractères assignés varient d’une version à l’autre d’Unicode, et qu’ils sont organisés en intervalles disparates, il est plus crédible d’utiliser un prédicat dynamique. Ce prédicat ne sera d’ailleurs pas défini ici, car trop complexe à mettre en œuvre pour une introduction (il nécessite de lire et interpréter la base de donnée Unicode).

Trois variations plus importantes peuvent être possibles. Une à propos de la définition de `Character_Type`, qui peut se définir soit comme sous‑type, comme un nouveau type ou comme un type opaque; puis deux autres variations sur le style et l’organisation, en décomposant un peu plus encore avec des paquets imbriqués et en appliquant un style plus centré sur les paquets en eux‑même pour le nommage de ce qu’ils définissent.

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
Administrateur
Avatar de l’utilisateur
  • Genre : Télétubbie
  • Messages : 22173
Ven 8 Mar 2013 02:10
Message Re: Décodeur/encodeur Unicode en Ada
Remarque préalable : les paquets imbriqués sont des paquets écrits directement à l’intérieur d’un paquet. Il sont différents des paquets enfants, qui sont attachés à un paquet parent (et existe distinctement « physiquement » à l’extérieur de celui‑ci, en étant cependant dans le contexte du parent).

nommage centré sur le contexte d’un domaine et paquets imbriqués


Une variation de style possible vient en notant que dans le paquet `Code_Points`, un type de donné porte le nom de `Code_Point_Type`, ce qui peut sembler redondant (cela dépendant de votre style concernant l’inclusion des paquets au moment de leurs utilisations). Ce style ou cette convention, est une parmi d’autres. Je lui préfère personnellement celle de nommer les éléments dans un paquet, d’après leur rôle dans un domaine, qui est lui, identifié par le nom du paquet, plutôt que de les nommer d’après leur usage final (qui reprend souvent le nom du domaine), sachant que Ada fourni une clause de renommage, qui permet à tout moment de donner à toutes choses, le nom que l’on souhaite (même si de manière détournée pour les types et les constantes).

Une variation d’organisation possible, et même si elle ne montre son intérêt immédiatement, est de définir deux paquets imbriqués dans `Code_Points`, pour individuellement, les deux notions assez autonomes même si incluses, que sont les plans et les positions, celles‑ci pouvant exister indépendamment l’une de l’autre, même si une relation les associe à un code‑point.

Ces deux variations sont illustrées par ces deux nouvelles versions des spécifications de paquets :

Source Ada : 

package Code_Points is

type Codespace_Type is range …;

subtype Instance is Codespace_Type;

subtype Scalar_Type is Instance
with Static_Predicate => Scalar_Type not in …;

package Planes is
First : constant := …;
Last : constant := …;
type Instance is range First .. Last;
function Mapping (Item : Code_Points.Instance) return Instance;
end Planes;

package Positions is
First : constant := …;
Last : constant := …;
type Instance is range First .. Last;
function Mapping (Item : Code_Points.Instance) return Instance;
end Positions;

function Mapping
(Plane : Planes.Instance;
Position : Positions.Instance)
return Instance;

end Code_Points;


Les noms des types `Plane_Type` et `Position_Type` sont tous deux devenus `Instance`. Les noms des deux fonctions `Plane` et `Position`, sont devenues tous deux `Mapping`. C’est que `Instance` est le rôle du type par rapport au domaine, autant pour le domaine `Planes` que `Positions`. Il en va de même avec `Mapping`, qui a le même rôle dans les deux cas pour son domane. En fait, `Instance` aurait put s’appeler `Type` et `Mapping` aurait put s’appeler `Function`, mais ces deux mots sont réservés en Ada, et il faut alors leur substituer un synonyme, et si on opte pour ce style, s’en tenir toujours à ce même synonyme, chaque fois qu’un même rôle est désigné.

Si la création des deux paquets imbriqués, peut sembler aller trop loin à première vue, l’intérêt de distinguer les sous‑domaines apparaitra avec un petit exemple plus loin, en complétant le paquet imbriqué `Planes`.

Source Ada : 

package Characters is

subtype Base_Type is Code_Points.Scalar_Type;

subtype Excluded_Type is Code_Points.Instance range …;

subtype Instance is Base_Type
with Static_Predicate => Instance not in Excluded_Type;

subtype Assigned_Type is Instance
with Dynamic_Predicate => …;

subtype Private_Use_Type is Assigned_Type range …;

end Characters;


Définir un paquet comme un domaine, et nommer ses éléments d’après leurs rôles dans le domaine plutôt que par leurs noms d’usage à l’extérieur, produit une différence assez notable, même sur un paquet aussi petit que celui‑ci.

  • `Non_Character_Type` est devenu `Excluded_Type`
  • `Character_Type` est devenu `Instance`
  • `Assigned_Character_Type` est devenu `Assigned_Type`
  • `Private_Use_Character_Type` est devenu `Private_Use_Type`
  • Le type à partir duquel `Instance` est défini, s’appelle `Base_Type`

Retour au paquet imbriqué `Code_Points.Planes`.

Il peut être complété comme suit, si on souhaite y ajouter quelques définitions standards issues de la référence Unicode :

Source Ada : 

package Planes is

First : constant := …;
Last : constant := …;
type Instance is range First .. Last;

function Mapping (Item : Code_Points.Instance) return Instance;

function Is_Private_Use (Item : Instance) return Boolean;

BMP : constant Instance := …;
SMP : constant Instance := …;
SIP : constant Instance := …;
SSP : constant Instance := …;

end Planes;


Ces définitions peuvent être ajoutées, sans avoir à donner des suffixes ou préfixes comme `_Plane` ou `Plane_` à leur noms. Utiliser des paquets inclus, si on y trouve pas d’objection avec ses propres exigences de style et d’organisation, peut se montrer utile assez rapidement, et ne se montre excessif que lorsqu’il est certain que le paquet en question ne sera pas complété par de futures ajouts de définitions ou qu’il ne distingue pas de véritable sous‑concepts pouvant être abordés avec un nombre limité de références au domaine englobant (dans le cas contraire, on a pas à faire à un concept assez autonome).

L’avantage de ce style (qui n’est cependant pas incontournable ni ne s’impose sans bien le sentir), et que les choses s’y lisent plus aisément pour elles‑mêmes. Dans le cas de cet exemple, les constantes s’y lisent immédiatement comme des constantes dont le type est celui du domaine, ce qui n’en fait que mieux ressortir leurs rôles par rapport au domaine. C’est une manière de construire les paquets, comme s’il s’agissait d’objets composites, avec des prédicats, des types, des constantes, des propriétés, portant des noms désignant leurs rôles par rapport à un domaine plus que des noms tentant de les rendre autonomes et distinguables dans n’importe quel autre contexte (un choix qui a évidemment des conséquences, qui est à faire en conscience). La manière d’écrire de la première ébauche, correspond mieux à une organisation aplatie, sans se priver cependant de l’expression des relations que permettent les paquets. Le style de la seconde version, nécessite d’aplatir explicitement, ou de créer un paquet plat recueillant toutes les définitions par renommages.

En pratique, si une référence à un élément d’un paquet est fréquente, il est toujours possible de lui donner un nom plus court dont l’écriture sera moins lourde autant qu’un nom plus expressif pour le nouveau contexte. Cette éventualité se présentera encore plus si l’on opte pour le même style que celui choisi ici. Il faut tout de même ne pas oublier que si cela se passe dans la spécification d’un paquet, alors cela introduit une définition dans le paquet, une définition publique… une chose qu’il faudra bien peser (on pourra le souhaiter, le tolérer, ou ni l’un ni l’autre).

Par exemple, le paquet `Characters`, au lieu de commencer comme :

Source Ada : 

package Characters is

subtype Excluded_Type is Code_Points.Instance range …;



end Characters;


… pourrait commencer comme ceci :

Source Ada : 

package Characters is

subtype Code_Point_Type is Code_Points.Instance;

subtype Excluded_Type is Code_Point_Type range …;



end Characters;


Remarque : la clause `renames` ne s’applique ni aux types, ni aux constantes. Même si cela exprime moins l’intention que ne le ferait `renames`, il est toujours nécessaire de « renommer » un type en redéfinissant un `subtype` du même sous‑type et de « renommer » une constante, en redéfinissant une constante de la même constante. La clause `renames` s’applique cependant bien aux paquets et aux sous‑programmes.

Image
Hibou57

« La perversion de la cité commence par la fraude des mots » [Platon]
Profil Site Internet
cron