Skip to content

Instantly share code, notes, and snippets.

@yves-chevallier
Created June 8, 2020 08:12
Show Gist options
  • Save yves-chevallier/2584c91ccb4c618cbd47869aace46c84 to your computer and use it in GitHub Desktop.
Save yves-chevallier/2584c91ccb4c618cbd47869aace46c84 to your computer and use it in GitHub Desktop.
UON précisions binaire

Précisions sur le format binaire UON

Introduction

La plupart des langages de sérialisation de données permettent la génération d'un flux texte comme XML, JSON ou YAML qui permet une lecture facile mais qui n'est pas optimal du point de vue du médium de transmission : un encodage minimal serait préférable.

UON offre deux solutions pour minimiser la charge utile :

  1. Partager la charge utile entre un schéma de description et une charge de données
  2. Encoder UON en format binaire

Encodage binaire

Prenons la description UON suivante :

{
  model: "Prius",
  brand: "Toyota",
  year: 2016
}

La taille de la charge utile est de 54 caractères, y compris les espaces et les accolades. Néanmoins l'information utile n'est que

Prius
Toyota
2016

Soit 15 caractères. Néanmoins, ce dernier format à perdu toute substance et il n'est plus possible de savoir si Prius correspond au modèle ou à la marque.

Une première approche est d'encoder la charge utile en format binaire. On sait du point de vue UON que la description UON ci-dessus à la composition suivante :

  1. Dictionnaire comportant
  2. Clé "model"
  3. Valeur "Prius" de type str
  4. Clé "brand"
  5. Valeur "Toyota" de type str
  6. Clé "year"
  7. Valeur 2016 de type num

On sait d'après la spécification UON qu'une clé est de type !keyword et possède l'ID 0x12. De même les types !str et !num ont chacun des identifiants uniques 0x11 et 0x15. On peut donc réécrire la description en utilisant un format plus compact. Le format !num utilise l'encodage BCDFP (voir spécification).

0x02 !map      
0x12 !keyword "model\x00" (6d 6f 64 65 6c 00)
0x11 !str "Prius\x00" (50 72 69 75 73 00)
0x12 !keyword "brand\x00" (62 72 61 6e 64 00)
0x11 !str "Toyota\x00" (54 6f 79 6f 74 61 00)
0x12 !keyword "year\x00" (79 65 61 72 00)
0x15 !num 2016 (16 20 ff)

Maintenant on peut écrire la charge utile complète :

02 12 6d 6f 64 65 6c 00 11 50 72 69 75 73 00 12 
62 72 61 6e 64 00 11 54 6f 79 6f 74 61 00 12 79 
65 61 72 00 15 16 20 ff

On observe néanmoins que les noms des clés prennent de la place et qu'en sachant à priori quelles seront les valeurs de la charge utile on pourrait simplement écrire :

0x11 !str "Prius\x00" (50 72 69 75 73 00)
0x11 !str "Toyota\x00" (54 6f 79 6f 74 61 00)
0x15 !num 2016 (16 20 ff)
11 50 72 69 75 73 00 11 54 6f 79 6f 74 61 00 15
16 20 ff

Si l'on sait que l'année n'est qu'un entier ne pouvant prendre les valeurs de 1900 à 2100 (200 années), il est possible d'utiliser un !uint8 à la place de !num et d'y ajouter un offset de 1900. La valeur 2016 serait alors 116 (0x74). De la même manière, si l'on connaît à priori le nom de toutes les marques de véhicules qui pourraient être transmises, il est possible d'utiliser un type énuméré par exemple !enum(values: ["Opel", "Toyota", "Audi", "Seat", "Ford"]). Ici la marque serait 0x01. On peut donc réécrire la charge utile ainsi :

0x11 !str "Prius\x00" (50 72 69 75 73 00)
0x37 !uint8 (01)
0x15 !uint8 (74)
11 50 72 69 75 73 00 37 01 15 74

Notons que le format binaire est équivalant au format textué :

{
    "Prius",
    !uint8 1,
    !uint8 74
}

Ceci étant, réduire ainsi la charge utile n'est possible qu'en combinant cette dernière avec un schéma de description.

Schéma de description

Le schéma de description permet de décrire une charge utile en permettant à cette dernière d'être plus compacte. Dans les exemples ci-dessus, la charge utile à perdu beaucoup d'information et il est maintenant nécessaire de la décrire:

!schema {
    model: !str,
    brand: !enum(values: ["Opel", "Toyota", "Audi", "Seat", "Ford"]),
    year: !uint8(offset: 1900)
}

En connaissant ce schém, on sera capable de décoder :

{ "Prius", 1, 74}

ainsi que que l'équivalent binaire :

11 50 72 69 75 73 00 37 01 15 74

Ceci étant, il pourrait être utile de contraindre le schéma pour éviter des erreurs, ceci est possible avec un schéma de validation

Schéma de validation

Le schéma de validation est en tout point identique au schéma de description sauf qu'il contient des contraintes sur les valeurs :

!schema {
    model: !str(pattern: /[A-Za-z]{2,20}/),
    brand: !enum(values: ["Opel", "Toyota", "Audi", "Seat", "Ford"]),
    year: !uint8(offset: 1900) !year(required: false, min: 1900, max: 2099)
}

Dans ce dernier, le nom du modèle est contraint par une expression régulière à des noms d'un maximum de 20 caractères. L'année est maintenant optionnelle avec le required false. Le type de l'année est !year imposant une quantité de temps mais ce type est transtypé en un uint8 avec un offset de 1900.

Chargement en mémoire

Voici comment peut être traité les données en Python :

from uon import Schema
schema = """
!schema {
    model: !str(pattern: /[A-Za-z]{2,20}/),
    brand: !enum(values: ["Opel", "Toyota", "Audi", "Seat", "Ford"]),
    year: !uint8(offset: 1900) !year(required: false, min: 1900, max: 2099)
}
"""

>>> vehicule = Schema(schema)
>>> vehicule.from_binary(b"\x11\x50\x72\x69\x75\x73\x00\x37\x01\x15\x74")
>>> vehicule.brand
Toyota
>>> vehicule.year
2016
>>> vehicule.brand = "Audi"
>>> vehicule.brand.to_binary()
b"x02"
>>> vehicule.to_binary()
b"\x11\x50\x72\x69\x75\x73\x00\x37\x02\x15\x74"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment