Obsah
1. Využití serializačního formátu MessagePack v Pythonu
3. Některá omezení formátu MessagePack
4. Instalace balíčku msgpack i balíčku pro serializaci N-dimenzionálních polí
5. Prohlížení binárních souborů se serializovanými daty
6. Serializace primitivních datových typů do formátu MessagePack
8. Serializace pravdivostních hodnot True a False
9. Serializace celočíselných hodnot
10. Celočíselné hodnoty překračující 64bitový rozsah
11. Serializace hodnot s plovoucí řádovou čárkou
12. Složené datové typy ve formátu MessagePack
16. Serializace N-dimenzionálních polí
17. Serializace 1D polí z balíčku NumPy
18. Příloha: alternativní binární formáty pro serializaci dat
19. Repositář s demonstračními příklady
1. Využití serializačního formátu MessagePack v Pythonu
Se serializačním formátem nazvaným MessagePack jsme se již na stránkách Roota jednou setkali, a to konkrétně v souvislosti s programovacím jazykem Go. Připomeňme si ve stručnosti, že se v současnosti jedná o jeden z relativně velkého množství dostupných a používaných datových formátů určených pro serializaci a deserializaci dat různých typů s jejich případným přenosem do jiné aplikace či služby (poté se spíše setkáme s termíny marshalling a unmarshalling). Přenosem se přitom v tomto kontextu myslí jak lokální komunikace, tak i přenos do služby běžící na jiném počítači.
Již dříve jsme se ve stručnosti seznámili s využitím známého formátu JSON (ten se používá a někdy i zneužívá na mnoha místech) a nepřímo taktéž s formátem TOML používaným typicky pro konfigurační soubory (a mnohem méně často pro rozsáhlejší data). V případě JSONu se jedná o poměrně důležitý formát, protože JSON (a samozřejmě též XML) se v současnosti používá v mnoha webových službách a i když stále vznikají a jsou postupně adaptovány další formáty, ať již textové (YAML, edn) či binární (BSON, B-JSON, Smile, Protocol-Buffers), CBOR atd., je velmi pravděpodobné, že se JSON bude i nadále poměrně masivně využívat (a navíc pro něj existují užitečné nástroje typu jq). Nicméně pochopitelně existují situace, v nichž je vhodné textový a relativně neúsporný JSON nahradit právě nějakým binárním formátem.
I přesto, že se s výše uvedenými formáty JSON a XML setkáme prakticky ve všech oblastech moderního IT, nemusí se vždy jednat o to nejlepší možné řešení problému přenosu strukturovaných dat. Tyto formáty totiž data neukládají v kompaktní binární podobě a navíc je parsing numerických hodnot relativně zdlouhavý, což se projevuje zejména tehdy, pokud je nutné zpracovat skutečně obrovské množství dat (buď se tedy jedná o situaci, kdy je nutné zpracovat mnoho malých zpráv či událostí, nebo naopak rozsáhlé datové soubory). A právě v těchto situacích může být výhodnější sáhnout po nějakém vhodně navrženém binárním formátu, ideálně takovém formátu, který je popsán ve standardu a který je implementován pro více jazyků či ekosystémů. Takových úsporných formátů již dnes existuje velké množství, od staršího a dosti těžkopádného ASN.1 (Abstract Syntax Notation One) po formáty, které se snaží napodobit některé vlastnosti JSONu. Příkladem z této oblasti může být formát CBOR, jenž je mj. podporován knihovnou https://github.com/fxamacker/cbor, popř. formát BSON. A konečně, ve se především ve světě Go setkáme i s formátem nazvaným gob neboli Go Objects.
Jednou ze známých a relativně často nasazovaných „binárních alternativ“ k formátu JSON je i formát nazvaný MessagePack, s jehož základními vlastnostmi se seznámíme v navazujících kapitolách. Zaměříme se přitom na jeho použití v Pythonu, což s sebou nese určité specifické vlastnosti, které vyplývají z typového systému Pythonu (konkrétně z toho, že existuje celočíselný typ s neomezeným rozsahem či jediný numerický formát s plovoucí řádovou čárkou).
2. Formát MessagePack
Datový formát MessagePack je navržen takovým způsobem, aby byl „binárním protějškem“ známého a velmi často využívaného formátu JSON, ovšem s několika vylepšeními. Binární formát MessagePack umožňuje serializovat (ukládat) následující datové typy a pochopitelně i jejich kombinace, protože je vhodné si uvědomit, že mnohé datové typy jsou vlastně kontejnery pro hodnoty dalších typů (v MessagePacku se takto používají pole a taktéž mapy):
- Hodnotu nil odpovídající v JSONu hodnotě null. Tato hodnota je uložena v jediném bajtu, včetně typové informace.
- Pravdivostní hodnoty true a false. Opět je použit úsporný způsob uložení v jediném bajtu.
- Celá čísla (integer) s různou binární délkou. Malé hodnoty, s nimiž se setkáme nejčastěji, jsou uloženy v optimalizované (kratší) podobě. Jedná se o rozšíření oproti formátu JSON, který celá čísla nepodporuje.
- Čísla s plovoucí řádovou čárkou v jednoduché i dvojité přesnosti, a to (to je pro mnoho aplikací důležité) včetně všech speciálních hodnot, tedy kladného a záporného nekonečna i hodnoty „Not a Number“.
- Unicode řetězce, přičemž krátké řetězce jsou opět uloženy optimalizovaně.
- Sekvence bajtů, což je typ, který nám umožňuje například serializaci obrázků atd.
- Pole, jejichž prvky jsou prakticky jakéhokoli typu.
- Mapy, jejichž klíče i prvky jsou prakticky jakéhokoli typu (v tomto případě se jedná o rozšíření JSONu).
- Časová razítka. To je v praxi poměrně užitečný formát. JSON tuto možnost postrádá a proto se setkáme s mnoha různými způsoby uložení časových razítek (mnohé jsou navrženy špatně).
- Rozšíření (dvojice s typovou informací a hodnotou). Takto lze MessagePack rozšířit například o komplexní čísla, vektory atd. Podpora však musí existovat na obou komunikujících stranách!
Důležité přitom je, že způsob uložení dat určuje nejenom jejich hodnotu, ale i typ, takže přijímající strana získá například informaci „toto je hodnota False typu boolean“ nebo „toto je celé číslo s hodnotou 42“. Naproti tomu však nezískáme jméno příslušného atributu, takže obě komunikující strany musí mít (shodnou) informaci o tom, jaké datové struktury se přenáší a/nebo serializují, nebo je nutné použít mapu (což je nejčastější způsob).
Již z prvního bajtu každé serializované hodnoty lze určit její typ. Jak je to zařízeno je patrné z následující tabulky. Povšimněte si, že u mnoha typů je v prvním bajtu typ uložen jen v několika bitech a další bity tak lze využit i pro (částečné) uložení hodnoty:
Datový typ | hodnota prvního bajtu (bin) | hodnota prvního bajtu (hex) |
---|---|---|
positive fixint | 0×xxxxxx | 0×00 – 0×7f |
fixmap | 1000×xxx | 0×80 – 0×8f |
fixarray | 1001×xxx | 0×90 – 0×9f |
fixstr | 101×xxxx | 0×a0 – 0×bf |
nil (null, None) | 11000000 | 0×c0 |
(není použit) | 11000001 | 0×c1 |
false | 11000010 | 0×c2 |
true | 11000011 | 0×c3 |
bin 8 | 11000100 | 0×c4 |
bin 16 | 11000101 | 0×c5 |
bin 32 | 11000110 | 0×c6 |
ext 8 | 11000111 | 0×c7 |
ext 16 | 11001000 | 0×c8 |
ext 32 | 11001001 | 0×c9 |
float 32 | 11001010 | 0×ca |
float 64 | 11001011 | 0×cb |
uint 8 | 11001100 | 0×cc |
uint 16 | 11001101 | 0×cd |
uint 32 | 11001110 | 0×ce |
uint 64 | 11001111 | 0×cf |
int 8 | 11010000 | 0×d0 |
int 16 | 11010001 | 0×d1 |
int 32 | 11010010 | 0×d2 |
int 64 | 11010011 | 0×d3 |
fixext 1 | 11010100 | 0×d4 |
fixext 2 | 11010101 | 0×d5 |
fixext 4 | 11010110 | 0×d6 |
fixext 8 | 11010111 | 0×d7 |
fixext 16 | 11011000 | 0×d8 |
str 8 | 11011001 | 0×d9 |
str 16 | 11011010 | 0×da |
str 32 | 11011011 | 0×db |
array 16 | 11011100 | 0×dc |
array 32 | 11011101 | 0×dd |
map 16 | 11011110 | 0×de |
map 32 | 11011111 | 0×df |
negative fixint | 111×xxxx | 0×e0 – 0×ff |
3. Některá omezení formátu MessagePack
Možnosti binárního formátu MessagePack, kterým se v dnešním článku zabýváme, skutečně do značné míry odpovídají možnostem JSONu s několika rozšířeními, o nichž jsme se zmínili výše a s nimiž se ještě setkáme v demonstračních příkladech v praktické části článku. Ovšem musíme se zmínit i o některých principiálních omezeních, z nichž některé jsou společné i dalším často používaným serializačním formátům (nehledě na to, zda jsou textové či binární):
- celá čísla mohou nabývat hodnoty z rozsahu –263 až 264-1 (to není chyba – pro kladné hodnoty totiž v MessagePacku existuje typ bez znaménka, tedy unsigned int). Rozsah je zde tedy mnohem větší, než v případě JSONu, kde je nutné pracovat s typem double.
- maximální délka řetězců je rovna 4GB (což v praxi pravděpodobně nebude příliš velké omezení)
- maximální délka binárního bloku je taktéž rovna 4GB (což již v praxi někdy může vadit)
- maximální počet prvků v poli je roven 232-1
- maximální počet dvojic klíč-hodnota v mapě je taktéž roven 232-1
- nelze ukládat ukazatele a tím pádem ani přímo pracovat se stromy, obecnými grafy atd. Tento nedostatek se částečně dá nahradit mapami obsahujícími další hodnoty (pole, mapy). Totéž se týká JSONu a mnoha dalších přenositelných formátů.
- co je ze sémantického hlediska poněkud problematické – není podporován typ „množina“ (na to jsme si museli zvyknout i u JSONu).
- taktéž není podporován typ decimal, konkrétně numerické hodnoty s plovoucí desetinnou řádovou čárkou.
4. Instalace balíčku msgpack> i balíčku pro serializaci N-dimenzionálních polí
Pro práci s formátem MessagePack v Pythonu je určen balíček nazvaný msgpack. Ten již bývá nainstalován společně s Pythonem (protože ho využívají některé nástroje nad Pythonem postavené), ovšem pokud tomu tak není, je instalace balíčku msgpack snadná a přímočará:
$ pip install --user msgpack
Pro jednoduchý test, zda instalace balíčku proběhla v pořádku, použijeme tento skript:
import msgpack help(msgpack)
Měla by se zobrazit nápověda:
Help on package msgpack: NAME msgpack PACKAGE CONTENTS _cmsgpack exceptions ext fallback FUNCTIONS dump = pack(o, stream, **kwargs) Pack object `o` and write it to `stream` See :class:`Packer` for options. dumps = packb(o, **kwargs) Pack object `o` and return packed bytes See :class:`Packer` for options. load = unpack(stream, **kwargs) Unpack an object from `stream`. Raises `ExtraData` when `stream` contains extra bytes. See :class:`Unpacker` for options. pack(o, stream, **kwargs) Pack object `o` and write it to `stream` See :class:`Packer` for options. packb(o, **kwargs) Pack object `o` and return packed bytes See :class:`Packer` for options. unpack(stream, **kwargs) Unpack an object from `stream`. Raises `ExtraData` when `stream` contains extra bytes. See :class:`Unpacker` for options. DATA version = (1, 0, 6) VERSION 1.0.6 FILE /usr/lib64/python3.12/site-packages/msgpack/__init__.py
Povšimněte si, že k dispozici jen jen několik funkcí; samotné API je pojato minimalisticky (což je jen dobře):
Funkce | Stručný popis |
---|---|
pack | serializace hodnoty do otevřeného streamu |
packb | serializace hodnoty to sekvence bajtů |
unpack | deserializace hodnoty z otevřeného streamu |
unpackb | deserializace hodnoty ze sekvence bajtů |
dump | provádí tutéž operaci co pack() |
dumps | provádí tutéž operaci co packb() |
load | provádí tutéž operaci co unpack() |
loads | provádí tutéž operaci co unpackb() |
Dále si nainstalujeme přídavný balíček, který nám umožní serializaci a deserializaci N-dimenzionálních polí knihovny Numpy. Jedná se o velmi užitečný doplněk:
$ pip install --user msgpack-numpy
Tento balíček již typicky není v systému dostupný, takže se skutečně nainstaluje:
Collecting msgpack-numpy Downloading msgpack_numpy-0.4.8-py2.py3-none-any.whl.metadata (5.0 kB) Collecting numpy>=1.9.0 (from msgpack-numpy) Downloading numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (62 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.0/62.0 kB 955.9 kB/s eta 0:00:00 Requirement already satisfied: msgpack>=0.5.2 in /usr/lib64/python3.12/site-packages (from msgpack-numpy) (1.0.6) Downloading msgpack_numpy-0.4.8-py2.py3-none-any.whl (6.9 kB) Downloading numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (16.1 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 16.1/16.1 MB 8.0 MB/s eta 0:00:00 Installing collected packages: numpy, msgpack-numpy Successfully installed msgpack-numpy-0.4.8 numpy-2.2.0
5. Prohlížení binárních souborů se serializovanými daty
Pro prohlížení obsahu vytvořených binárních souborů se serializovanými daty je možné použít například nějakou formu hexadecimálního prohlížeče. Hexadecimálních prohlížečů a editorů existuje (pro Linux) relativně velké množství. První dva nástroje nazvané od a hexdump (zkráceně hd) pracují jako relativně jednoduché jednosměrné filtry (navíc bývají nainstalovány společně se základní sadou nástrojů), ovšem další nástroj pojmenovaný xxd již může být použit pro obousměrný převod (filtraci), tj. jak pro transformaci původního binárního souboru do čitelného tvaru (většinou s využitím šestnáctkové soustavy), tak i pro zpětný převod. Díky tomu je možné nástroj xxd použít například ve funkci pluginu do běžných textových editorů (v textovém editoru se v takovém případě edituje hexadecimální výpis, při uložení se data konvertují zpět do původní úsporné binární podoby).
Další nástroj pojmenovaný hexdiff dokáže porovnat obsah dvou binárních souborů a poslední zmíněný nástroj mcview je, na rozdíl od předchozí čtveřice, aplikací s interaktivním ovládáním a plnohodnotným textovým uživatelským prostředím (patří do sady nástrojů vytvořených okolo Midnight Commanderu a instaluje se společně s tímto nástrojem).
6. Serializace primitivních datových typů do formátu MessagePack
Již v úvodních dvou kapitolách jsme si řekli, že do formátu MessagePack je možné ukládat jak hodnoty jednoduchých (primitivních) datových typů, tak i hodnoty složených datových typů (což jsou různé typy kontejnerů – pole a mapy). Začneme primitivními datovými typy, protože binární formát MessagePack je navržen takovým způsobem, aby byl způsob jejich uložení v co největší míře efektivní – a to nejenom co se týká celkového objemu dat, ale i jednoduchosti nebo naopak složitosti zakódování a dekódování hodnot. Nativně jsou podporovány následující primitivní datové typy:
- Typ none s jedinou hodnotou nil (v Pythonu odpovídá typu NoneType)
- Typ boolean s hodnotami true a false (v Pythonu odpovídá typu bool)
- Typ unsigned integer s plně 64 bitovým rozsahem (ve standardním Pythonu nemá přímý protějšek)
- Typ signed integer s plně 64 bitovým rozsahem (částečně odpovídá typu int)
- Typ float/single/float32 s plovoucí řádovou čárkou (ve standardním Pythonu nemá přímý protějšek)
- Typ double/float64 s plovoucí řádovou čárkou (v Pythonu odpovídá typu float – pozor na odlišné pojmenování!)
7. Hodnota nil, resp. None
Začneme tím zdánlivě nejjednodušším možným příkladem, který je však v praxi poněkud problematický při předávání serializovaných hodnot mezi různými programovacími jazyky a proto je nutné se o něm zmínit. Konkrétně se budeme zabývat způsobem serializace Pythonovské hodnoty None. Ta je použita stejným způsobem jako hodnota null v JSONu, tedy pro indikaci chybějících dat. Přitom None má v Pythonu svůj vlastní datový typ pojmenovaný taktéž None, zatímco například nil v jazyce Go nemusí být nutně přiřazeno k datovému typu (z tohoto pohledu je v čase překladu beztypové, viz celý článek, který jsme na toto téma vydali: na rozdíl od samotného jazyka Go).
Zdrojový kód demonstračního příkladu, který serializuje hodnotu None, se skládá z několika operací:
- Vytvoření a otevření nového (binárního) souboru pro zápis
- Konstrukce objektu/struktury použité pro serializaci
- Vlastní serializace dat přímo do otevřeného binárního souboru
V Pythonu 3.x může takový program vypadat následovně:
import msgpack value = None with open("nil.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Pro zajímavost se podívejme, jak by podobný program vypadal při realizaci v programovacím jazyce Go, v němž se explicitně kontrolují případné chybové stavy atd.:
package main import ( "log" "os" "github.com/ugorji/go/codec" ) const filename = "/tmp/nil.bin" func main() { // vytvořit soubor s binárními daty fout, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { log.Fatal(err) } defer fout.Close() log.Print("Output file created") // handler var handler codec.MsgpackHandle // objekt realizující zakódování dat encoder := codec.NewEncoder(fout, &handler) log.Print("Encoder created") // zakódování dat err = encoder.Encode(nil) if err != nil { log.Fatal(err) } log.Print("Done") }
V obou případech, tedy nezávisle na použitém programovacím jazyku, by však měl mít výsledný soubor nil.bin naprosto totožný obsah. Výsledkem serializace totiž bude binární soubor obsahující jediný bajt s hodnotou 0×c0, o čemž se ostatně můžeme velmi snadno přesvědčit:
$ od -A x -t x1 -v nil.bin 000000 c0 000001
Obsah tohoto souboru pochopitelně plně odpovídá specifikaci.
8. Serializace pravdivostních hodnot True a False
V serializačním formátu MessagePack jsou plně podporovány i pravdivostní hodnoty True a False, což v praxi znamená, že není nutné (a ani ze sémantického pohledu rozumné) používat pro reprezentaci pravdivostních hodnot například hodnoty 0 a 1 či 0 a –1. Navíc jsou pravdivostní hodnoty uloženy relativně rozumným způsobem – v jediném bajtu (včetně informace o typu). O tom se budeme moci velmi snadno přesvědčit na dvojici příkladů, které uloží hodnotu True a ve druhém případě hodnotu False:
import msgpack value = True with open("true.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
import msgpack value = False with open("false.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Nyní se podívejme na to, jak jsou tyto dvě hodnoty uloženy do výsledného souboru:
$ od -A x -t x1 -v true.bin 000000 c3 000001 $ od -A x -t x1 -v false.bin 000000 c2 000001
Což opět plně odpovídá specifikaci.
9. Serializace celočíselných hodnot
V této kapitole si ukážeme způsob serializace celočíselných hodnot, což je již z pohledu programátora mnohem zajímavější problém. V tomto ohledu totiž museli tvůrci formátu MessagePack splnit dva protichůdné požadavky:
- reprezentovat co největší rozsah hodnot, ideálně 64bitové hodnoty
- na druhou stranu je použití 64bitů (8 bajtů) ve všech případech až trestuhodné plýtvání místem (a to mnohdy i oproti textovému JSONu v případě ukládání hodnot okolo nuly)
Výsledkem snahy o splnění obou požadavků je flexibilní způsob uložení celých čísel v jednom, dvou, třech, pěti či devíti bajtech – vždy v závislosti na konkrétní hodnotě a taktéž na tom, zda se jedná o hodnotu kladnou či zápornou. Specifikace uložení celých čísel ve skutečnosti není příliš složitá a můžeme si ji snadno otestovat na několika demonstračních příkladech.
Nejprve si otestujeme uložení velmi malého celého čísla v rozsahu 0 až 127:
import msgpack value = 42 with open("int_tiny.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Výsledkem je v tomto případě pouhý jeden bajt, který obsahuje jak informace o datovém typu, tak i vlastní hodnotu:
$ od -A x -t x1 -v int_tiny.bin 000000 2a 000001
Uložení čísla většího než 127, ale menšího než 256:
import msgpack value = 200 with open("int_small.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Výsledný soubor bude mít velikost dva bajty, opět přesně podle specifikace (první bajt obsahuje typ, druhý hodnotu):
$ od -A x -t x1 -v int_small.bin 000000 cc c8 000002
Celé číslo větší než 255, ale menší než 216 se uloží do tří bajtů:
import msgpack value = 1000 with open("int_large.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Výsledek:
$ od -A x -t x1 -v int_large.bin 000000 cd 03 e8 000003
kde 0×3e8 skutečně v desítkové soustavě odpovídá hodnotě 1000.
Serializace čísla většího než 216:
import msgpack value = 100000 with open("int_larger.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Opět je použit jeden bajt se specifikací typu, za kterým následuje čtveřice bajtů 0×0186a0 = 100000:
$ od -A x -t x1 -v int_larger.bin 000000 ce 00 01 86 a0 000005
A konečně hodnota 260:
import msgpack value = 2**60 with open("int_long.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Jedná se o 64bitovou hodnotu uloženou v devíti bajtech:
$ od -A x -t x1 -v long_int.bin 000000 cf 10 00 00 00 00 00 00 00 000009
Podobné příklady lze vytvořit i pro záporná čísla, například:
import msgpack value = -10 with open("negative_int.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
S výsledkem:
$ od -A x -t x1 -v negative_int.bin 000000 f6 000001
10. Celočíselné hodnoty překračující 64bitový rozsah
Celočíselné hodnoty, které přesahují 64bitový rozsah, nejsou v současné verzi MessagePacku podporovány, tedy za předpokladu, že si nenapíšete rozšíření pro vlastní datový typ (více o tomto tématu si řekneme dále). Zajímavé bude zjistit, co se tedy stane v případě, že budeme serializovat větší celočíselnou hodnotu. Otestování je snadné:
import msgpack value = 2**64 with open("too_long_int.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Výsledek nebude v tomto případě příliš potěšující – dojde k vyhození běhové výjimky typu OverflowError:
Traceback (most recent call last): File "msgpack/_packer.pyx", line 177, in msgpack._cmsgpack.Packer._pack OverflowError: Python int too large to convert to C unsigned long During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/ptisnovs/src/most-popular-python-libs/msgpack/serialize_too_long_int.py", line 6, in packed = msgpack.packb(value) ^^^^^^^^^^^^^^^^^^^^ File "/usr/lib64/python3.12/site-packages/msgpack/__init__.py", line 36, in packb return Packer(**kwargs).pack(o) ^^^^^^^^^^^^^^^^^^^^^^^^ File "msgpack/_packer.pyx", line 294, in msgpack._cmsgpack.Packer.pack File "msgpack/_packer.pyx", line 300, in msgpack._cmsgpack.Packer.pack File "msgpack/_packer.pyx", line 297, in msgpack._cmsgpack.Packer.pack File "msgpack/_packer.pyx", line 188, in msgpack._cmsgpack.Packer._pack OverflowError: Integer value out of range
11. Serializace hodnot s plovoucí řádovou čárkou
Ve formátu MessagePack jsou podle očekávání podporovány i hodnoty s plovoucí řádovou čárkou. Jedná se jak o hodnoty s jednoduchou přesností (single, float, float32), tak i o hodnoty s dvojitou přesností (double, float64). V Pythonu existuje jediný datový typ pro reprezentaci hodnoty s plovoucí řádovou čárkou, a ten odpovídá typu double/float64, takže se (při serializaci) většinou s hodnotou typu single nesetkáme:
import msgpack value = 3.14 with open("float.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Prozkoumejme výsledný soubor, který by měl mít délku devíti bajtů:
000000 cb 40 09 1e b8 51 eb 85 1f 000009
V mnoha serializačních formátech, zejména těch textově orientovaných (JSON, XML) se mnohdy poněkud zapomíná na hodnoty NaN a taktéž na kladné i záporné nekonečno. V případě MessagePacku jsou tyto speciální hodnoty plně podporovány, což je v poměrně mnoha oblastech důležité (samozřejmě otázkou je, jak tyto hodnoty vznikly, to však nemá řešit přenosový protokol).
Otestujme si nejdříve serializaci hodnoty NaN, tj. například na výsledek 0/0, sin(∞), √-1 atd.:
import math import msgpack value = math.nan with open("nan.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Výsledný soubor obsahuje devět bajtů – specifikaci formátu následovanou hodnotou NaN odpovídající IEEE 754, což si opět můžete ověřit na stránce https://baseconvert.com/ieee-754-floating-point po zadání „nan“ do vstupního políčka:
000000 cb 7f f8 00 00 00 00 00 00 000009
Podobně si můžeme ověřit, jak je uloženo kladné nebo záporné nekonečno. Začneme kladným nekonečnem:
import math import msgpack value = math.inf with open("inf.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Výsledek:
000000 cb 7f f0 00 00 00 00 00 00 000009
V případě záporného nekonečně se pouze změní jediný bit, takže druhý bajt bude namísto hodnoty 7f obsahovat hodnotu ff:
import math import msgpack value = -math.inf with open("negative_inf.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Výsledných devět bajtů:
000000 cb ff f0 00 00 00 00 00 00 000009
12. Složené datové typy ve formátu MessagePack
Po popisu způsobu uložení jednoduchých datových typů (což vlastně ve skutečnosti nebylo nic složitého) si v dnešním článku ukážeme, jakým způsobem je ve formátu MessagePack realizováno uložení složených datových typů. Do této kategorie se řadí především řetězce, sekvence bajtů, pole, ale v neposlední řadě i velmi důležité mapy, které lze použít například pro uložení atributů objektů (ostatně naprosto stejně je tato problematika řešená v JSONu). Opět uvidíme, že u některých výše zmíněných datových typů je dbáno na efektivitu výsledného binárního souboru, a to jak z hlediska celkového objemu dat, tak i složitosti kódování a dekódování těchto dat.
13. Krátké a dlouhé řetězce
Takřka nepostradatelným složeným datovým typem jsou řetězce. Interně se pro jejich uložení používá UTF-8. Neméně důležitá je však informace o tom, jak dlouhý řetězec je. Délka řetězce je uložena před vlastní znaky a to konkrétně tak, že pro krátké řetězce je délka uložena přímo v bajtu se specifikací typu (tedy neztratíme ani jediný bajt!) a pro delší řetězce je délka uložena v jednom, dvou či v celých čtyřech bajtech.
Velmi krátký řetězec, menší než 31 bajtů (nikoli znaků!), se serializuje takto:
import msgpack value = "Hello" with open("short_string.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
V tomto případě je délka řetězce uložena v prvním bajtu, přičemž první tři bity tohoto bajtu určují datový typ:
$ od -A x -t x1z -v short_string.bin 000000 a5 48 65 6c 6c 6f <.Hello> 000006
Vyzkoušejme si nyní poněkud delší řetězec:
import msgpack value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." with open("longer_string.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Takový řetězec po serializaci obsahuje v první bajtu konstantu 0×d9 a poté délku řetězce v jediném bajtu. Následují kódy jednotlivých znaků:
000000 d9 ea 4c 6f 72 65 6d 20 69 70 73 75 6d 20 64 6f >..Lorem ipsum do< 000010 6c 6f 72 20 73 69 74 20 61 6d 65 74 2c 20 63 6f >lor sit amet, co< 000020 6e 73 65 63 74 65 74 75 72 20 61 64 69 70 69 73 >nsectetur adipis< 000030 63 69 6e 67 20 65 6c 69 74 2c 20 73 65 64 20 64 >cing elit, sed d< 000040 6f 20 65 69 75 73 6d 6f 64 20 74 65 6d 70 6f 72 >o eiusmod tempor< 000050 20 69 6e 63 69 64 69 64 75 6e 74 20 75 74 20 6c > incididunt ut l< 000060 61 62 6f 72 65 20 65 74 20 64 6f 6c 6f 72 65 20 >abore et dolore < 000070 6d 61 67 6e 61 20 20 20 20 61 6c 69 71 75 61 2e >magna aliqua.< 000080 20 55 74 20 65 6e 69 6d 20 61 64 20 6d 69 6e 69 > Ut enim ad mini< 000090 6d 20 76 65 6e 69 61 6d 2c 20 71 75 69 73 20 6e >m veniam, quis n< 0000a0 6f 73 74 72 75 64 20 65 78 65 72 63 69 74 61 74 >ostrud exercitat< 0000b0 69 6f 6e 20 75 6c 6c 61 6d 63 6f 20 6c 61 62 6f >ion ullamco labo< 0000c0 72 69 73 20 6e 69 73 69 20 75 74 20 61 6c 69 71 >ris nisi ut aliq< 0000d0 75 69 70 20 65 78 20 65 61 20 63 6f 6d 6d 6f 64 >uip ex ea commod< 0000e0 6f 20 63 6f 6e 73 65 71 75 61 74 2e >o consequat.< 0000ec
Řetězec delší než 255 znaků, ale kratší než 65536 znaků:
import msgpack value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." with open("even_longer_string.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
V tomto případě je první bajt roven konstantě 0×da. Za ní následují dva bajty s délkou řetězce v bajtech, konkrétně celkem 0×1bd=445 bajtů. A poté již vlastní znaky tvořící řetězec:
000000 da 01 bd 4c 6f 72 65 6d 20 69 70 73 75 6d 20 64 >...Lorem ipsum d< 000010 6f 6c 6f 72 20 73 69 74 20 61 6d 65 74 2c 20 63 >olor sit amet, c< 000020 6f 6e 73 65 63 74 65 74 75 72 20 61 64 69 70 69 >onsectetur adipi< 000030 73 63 69 6e 67 20 65 6c 69 74 2c 20 73 65 64 20 >scing elit, sed < 000040 64 6f 20 65 69 75 73 6d 6f 64 20 74 65 6d 70 6f >do eiusmod tempo< 000050 72 20 69 6e 63 69 64 69 64 75 6e 74 20 75 74 20 >r incididunt ut < 000060 6c 61 62 6f 72 65 20 65 74 20 64 6f 6c 6f 72 65 >labore et dolore< 000070 20 6d 61 67 6e 61 20 61 6c 69 71 75 61 2e 20 55 > magna aliqua. U< 000080 74 20 65 6e 69 6d 20 61 64 20 6d 69 6e 69 6d 20 >t enim ad minim < 000090 76 65 6e 69 61 6d 2c 20 71 75 69 73 20 6e 6f 73 >veniam, quis nos< 0000a0 74 72 75 64 20 65 78 65 72 63 69 74 61 74 69 6f >trud exercitatio< 0000b0 6e 20 75 6c 6c 61 6d 63 6f 20 6c 61 62 6f 72 69 >n ullamco labori< 0000c0 73 20 6e 69 73 69 20 75 74 20 61 6c 69 71 75 69 >s nisi ut aliqui< 0000d0 70 20 65 78 20 65 61 20 63 6f 6d 6d 6f 64 6f 20 >p ex ea commodo < 0000e0 63 6f 6e 73 65 71 75 61 74 2e 20 44 75 69 73 20 >consequat. Duis < 0000f0 61 75 74 65 20 69 72 75 72 65 20 64 6f 6c 6f 72 >aute irure dolor< 000100 20 69 6e 20 72 65 70 72 65 68 65 6e 64 65 72 69 > in reprehenderi< 000110 74 20 69 6e 20 76 6f 6c 75 70 74 61 74 65 20 76 >t in voluptate v< 000120 65 6c 69 74 20 65 73 73 65 20 63 69 6c 6c 75 6d >elit esse cillum< 000130 20 64 6f 6c 6f 72 65 20 65 75 20 66 75 67 69 61 > dolore eu fugia< 000140 74 20 6e 75 6c 6c 61 20 70 61 72 69 61 74 75 72 >t nulla pariatur< 000150 2e 20 45 78 63 65 70 74 65 75 72 20 73 69 6e 74 >. Excepteur sint< 000160 20 6f 63 63 61 65 63 61 74 20 63 75 70 69 64 61 > occaecat cupida< 000170 74 61 74 20 6e 6f 6e 20 70 72 6f 69 64 65 6e 74 >tat non proident< 000180 2c 20 73 75 6e 74 20 69 6e 20 63 75 6c 70 61 20 >, sunt in culpa < 000190 71 75 69 20 6f 66 66 69 63 69 61 20 64 65 73 65 >qui officia dese< 0001a0 72 75 6e 74 20 6d 6f 6c 6c 69 74 20 61 6e 69 6d >runt mollit anim< 0001b0 20 69 64 20 65 73 74 20 6c 61 62 6f 72 75 6d 2e > id est laborum.< 0001c0
A konečně řetězec delší než 65535 bajtů
import msgpack value = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." value *= 300 print(len(value)) with open("huge_string.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Na začátku souboru je uložen bajt 0×db a následuje čtyřbajtová délka řetězce. Poté již následují jednotlivé znaky řetězce:
000000 db 00 01 0e b4 4c 6f 72 65 6d 20 69 70 73 75 6d >.....Lorem ipsum< 000010 20 64 6f 6c 6f 72 20 73 69 74 20 61 6d 65 74 2c > dolor sit amet,< 000020 20 63 6f 6e 73 65 63 74 65 74 75 72 20 61 64 69 > consectetur adi< 000030 70 69 73 63 69 6e 67 20 65 6c 69 74 2c 20 73 65 >piscing elit, se< 000040 64 20 64 6f 20 65 69 75 73 6d 6f 64 20 74 65 6d >d do eiusmod tem<
14. Malá a rozsáhlá pole
Pole, a to pole prvků libovolných typů, se do formátu MessagePack opět ukládá podle toho, kolik prvků takové pole obsahuje. Pole s prvky, jejichž počet nepřesáhne patnáct, obsahuje pouze jediný bajt navíc. Obsah tohoto bajtu určuje, že se jedná o pole a současně i ve spodních čtyřech bitech obsahuje počet prvků pole.
import msgpack value = [1, 2, 3, 4] with open("array.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Výše uvedené pole se čtyřmi prvky je uloženo v pouhých pěti bajtech a to z toho důvodu, že hodnoty prvků samy o sobě mají tak malou hodnotu, že každý z nich může být uložen v jediném bajtu. První bajt určuje jak typ (pole), tak i počet jeho prvků:
$ od -A x -t x1 -v short_array.bin 000000 94 01 02 03 04 000005
Naproti tomu druhé serializované pole již obsahuje prvky s relativně vyššími hodnotami:
import msgpack value = [100, 200, 300, 400] with open("array2.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Nyní bude soubor delší, protože již některé prvky nelze uložit do jediného bajtu:
$ od -A x -t x1 -v short_array2.bin 000000 94 64 d1 00 c8 d1 01 2c d1 01 90 00000b
V tomto případě první bajt obsahuje typ (pole) s jeho délkou. Následuje bajt s hodnotou 0×64=100, tedy první prvek (jediný bajt), další prvek je uložen ve třech bajtech (0×d1 = typ, 0×00c8=200 je hodnota) atd.
Pole delší než patnáct prvků se dále rozlišují podle toho, zda je celkový počet prvků menší než 216-1 nebo větší než tato hodnota. Podle počtu prvků se volí počet bajtů pro uložení délky pole – dva či čtyři bajty. My si dnes ukážeme pouze první typ, tj. pole menší než 216-1 prvků:
import msgpack value = [] for i in range(1000): value.append(i) with open("array3.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Nejprve je uveden typ (0×dc) a počet prvků 0×03e8=1000. Dále již následují hodnoty jednotlivých prvků. Pro prvních 128 prvků postačuje pro uložení použít jediný bajt:
000000 dc 03 e8 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 000010 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 000020 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 000030 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 000040 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 000050 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 000060 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 000070 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 000080 7d 7e 7f
Větší hodnoty jsou již uloženy složitějším způsobem, protože první bajt každé trojice obsahuje typ (0×d1):
000080 d1 00 80 d1 00 81 d1 00 82 d1 00 83 d1 000090 00 84 d1 00 85 d1 00 86 d1 00 87 d1 00 88 d1 00 0000a0 89 d1 00 8a d1 00 8b d1 00 8c d1 00 8d d1 00 8e 0000b0 d1 00 8f d1 00 90 d1 00 91 d1 00 92 d1 00 93 d1
15. Serializace map
Ve formátu JSON se prakticky vždy setkáme s mapami, resp. přesněji řečeno s asociativními poli. Tuto datovou strukturu lze použít i ve formátu MessagePack a to dokonce ještě ve vylepšené variantě, protože klíči mohou být hodnoty jakéhokoli typu, nejenom řetězce. Ukažme si ovšem základní použití s řetězci jako klíči:
import msgpack value = { "foo": 42, "bar": None, "baz": 3.14, } with open("dict.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Tato mapa se dvěma dvojicemi klíč+hodnota bude uložena v pouhých dvaceti čtyřech bajtech:
Mapa obsahuje tři dvojice, což je malý počet. Z tohoto důvodu je typ (mapa) i počet dvojic klíč-hodnota uložena v jediném bajtu 0×80+3=0×83. Následuje stejný obsah, jako v případě polí:
000000 83 a3 66 6f 6f 2a a3 62 61 72 c0 a3 62 61 7a cb >..foo*.bar..baz.< 000010 40 09 1e b8 51 eb 85 1f >@...Q...< 000018
Bajty | Význam |
---|---|
a3 66 6f 6f | řetězec „foo“ o délce tří bajtů |
2a | malé celé číslo 42 |
a3 62 61 72 | řetězec „bar“ o délce tří bajtů |
c0 | hodnota nil/None |
a3 62 61 7a | řetězec „baz“ o délce tří bajtů |
cb 40 09 1e b8 51 eb 85 1f | hodnota typu double = 3.14 (otestujte zde) |
16. Serializace N-dimenzionálních polí
V mnoha oblastech souvisejících s IT se setkáme s daty, která jsou uložena v N-rozměrných polích (ND array). Nejčastěji se s velkou pravděpodobností setkáme s jednorozměrnými poli (neboli vektory), protože například zvukové záznamy jsou vlastně tvořeny sekvencí hodnot zvukových vzorků (samplů). A pochopitelně prakticky každý IT systém pracuje s obrazovými daty (ty si můžeme představit buď jako matice nebo jako trojrozměrná pole, v případě, že barvové roviny tvoří třetí dimenzi). Relativně často se setkáme i s vícerozměrnými poli, například v oblasti statistiky, lineární algebry, datové analýzy, strojového učení, zpracování medicínských či astronomických dat apod. Současně se jedná o datové struktury a operace, u nichž má velký smysl využít SIMD instrukce, které jsou dostupné na všech moderních mikroprocesorových architekturách. A právě z tohoto důvodu jsme se na stránkách Roota již mnohokrát setkali s programovacími jazyky, popř. s knihovnami, které jsou určeny právě pro zpracování n-rozměrných polí.
Víme již, že práce s N-rozměrnými poli je poměrně dobře podporována jak ve specializovaných programovacích jazycích (APL, J, K, …), tak i například v Pythonu, pro nějž byla vytvořena populární knihovna Numpy. Taktéž jsme se setkali s balíčky pro práci s N-rozměrnými poli určenými pro programovací jazyk Go. Připomeňme si, že se jednalo především o balíčky Gonum Numerical Packages a taktéž o balíček narray. Kvůli tomu, že se v oblasti statistiky, datové analýzy či strojového učení stále více používá programovací jazyk Python, je mnohdy nutné zajistit předávání dat (reprezentovaných ve formě N-rozměrných polí) právě mezi Pythonem a nástroji vytvořenými v dalších jazycích.
Taková data lze předávat v několika standardních formátech k tomu určených, ovšem využít lze i MessagePack. V tomto případě jsou data uložena jako „rozšiřující typ“ a pro jejich serializaci a deserializaci lze použít podpůrný balíček msgpack_numpy.
17. Serializace 1D polí z balíčku NumPy
Vyzkoušejme si nyní serializaci krátkého pole obsahujícího pouze tři prvky typu bajt. Povšimněte si, jak je kód balíčku msgpack modifikován přes funkci msgpack_numpy.patch(), což je mimochodem dosti nepěkný trik. Ovšem samotná serializace již probíhá standardním způsobem:
import msgpack import msgpack_numpy as m import numpy as np m.patch() value = np.array([1, 2, 3], dtype=np.byte) with open("array_1d_byte.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Výsledkem procesu serializace je soubor o délce 44 bajtů, což je na tříprvkové pole poměrně vysoká hodnota:
000000 85 c4 02 6e 64 c3 c4 04 74 79 70 65 a3 7c 69 31 >...nd...type.|i1< 000010 c4 04 6b 69 6e 64 c4 00 c4 05 73 68 61 70 65 91 >..kind....shape.< 000020 03 c4 04 64 61 74 61 c4 03 01 02 03 >...data.....< 00002c
Soubor začíná kódem 85, což značí mapu s pěti dvojicemi klíč-hodnota. Konkrétně se jedná o dvojice:
Klíč | Hodnota | Typ hodnoty |
---|---|---|
„nd“ | true | boolean |
„type“ | |i1 | string (typ prvků dle NumPy) |
„kind“ | [] | prázdný řetězec |
„shape“ | [3] | pole s jediným prvkem |
„data“ | [1, 2, 3] | pole se třemi krátkým celými čísly |
Konkrétní význam jednotlivých bajtů není složité rozluštit:
Bajty | Význam |
---|---|
85 | mapa s pěti dvojicemi klíč-hodnota |
c4 02 6e 64 | klíč „nd“ |
c3 | hodnota True |
c4 04 74 79 70 65 | klíč „type“ |
a3 7c 69 31 | hodnota „|i1“ |
c4 04 6b 69 6e 64 | klíč „kind“ (jako pole bajtů) |
c4 00 | prázdný řetězec (jako pole bajtů) |
c4 05 73 68 61 70 65 | klíč „shape“ (jako pole bajtů) |
91 03 | pole s jediným prvkem [3] |
c4 04 64 61 74 61 | klíč „data“ (jako pole bajtů) |
c4 03 01 02 03 | [1, 2, 3] |
Ve skutečnosti však velikost souboru roste (až na hlavičku) prakticky lineárně s délkou pole. To si ostatně můžeme snadno ověřit, a to na poli s deseti tisíci prvky typu float32. Čistá data tohoto pole zabírají 10000×sizeof(float32)=40000 bajtů:
import msgpack import msgpack_numpy as m import numpy as np m.patch() value = np.zeros([10000], dtype=np.float32) print(value.shape) with open("array_1d_large.bin", "wb") as outfile: packed = msgpack.packb(value) outfile.write(packed)
Výsledný soubor, který si zde pochopitelně celý ukazovat nebudeme, má délku 40044 bajtů, což znamená, že přidáno bylo pouze 44 bajtů s metainformacemi.
18. Příloha: alternativní binární formáty pro serializaci dat
Jak jsme se již zmínili v úvodní kapitole, existuje ve skutečnosti mnohem větší množství binárních formátů používaných jak pro serializaci dat, tak i pro komunikaci mezi různými službami (resp. přesněji řečeno pro posílání dat/zpráv mezi těmito službami nebo mikroslužbami). Alespoň krátce se tedy o některých z těchto formátů zmiňme.
Prvním alternativním binárním formátem, s nímž se v oblasti mikroslužeb setkáme, je formát nazvaný gob neboli Go Objects. Jedná se o formát určený primárně pro použití v programovacím jazyku Go, což znamená, že jeho využití je relativně specifické (ukládání rozsáhlých dat, komunikace mezi dvojicí služeb naprogramovaných v Go atd.). Tento formát umožňuje serializaci prakticky jakékoli datové struktury, ovšem je ho možné použít i pro primitivní datové typy, resp. pro jejich hodnoty. Pro Python existuje balíček umožňující s gob pracovat, což je v heterogenních systémech (každá mikroslužba může být naprogramována v jiném jazyce) užitečné.
Dalším binárním formátem určeným pro přenos prakticky libovolně strukturovaných dat je formát nazvaný CBOR neboli plným jménem Concise Binary Object Representation. Tímto formátem, jenž se snaží nabízet podobné vlastnosti jako JSON (až na možnost jeho přímého čtení člověkem), se budeme zabývat v navazujícím textu (interně je nepatrně složitější než MessagePack, navíc z MessagePacku vychází).
Dalším sice relativně novým, ale postupně se rozšiřujícím binárním formátem je formát nazvaný BSON (zde je odkaz na JSON nesporný). Možnosti tohoto formátu jsou již větší, například je podporován typ decimal128 určený pro použití v bankovnictví. Taktéž podporuje uložení časových razítek nebo i kódu v JavaScriptu.
19. Repositář s demonstračními příklady
Všechny demonstrační příklady využívající knihovnu PyTorch lze nalézt v repositáři https://github.com/tisnik/most-popular-python-libs. Následují odkazy na jednotlivé příklady:
20. Odkazy na Internetu
- Základní informace o MessagePacku
https://msgpack.org/ - Balíček msgpack na PyPi
https://pypi.org/project/msgpack/ - MessagePack na Wikipedii
https://en.wikipedia.org/wiki/MessagePack - Comparison of data-serialization formats (Wikipedia)
https://en.wikipedia.org/wiki/Comparison_of_data-serialization_formats - Repositáře msgpacku
https://github.com/msgpack - Specifikace ukládání různých typů dat
https://github.com/msgpack/msgpack/blob/master/spec.md - Podpora MessagePacku v různých programovacích jazycích
https://msgpack.org/#languages - Základní implementace formátu msgpack pro programovací jazyk Go
https://github.com/msgpack/msgpack-go - go-codec
https://github.com/ugorji/go - Gobs of data (odlišný serializační formát)
https://blog.golang.org/gobs-of-data - Formát BSON (odlišný serializační formát)
http://bsonspec.org/ - Problematika nulových hodnot v Go, aneb proč nil != nil
https://www.root.cz/clanky/problematika-nulovych-hodnot-v-go-aneb-proc-nil-nil/ - IEEE-754 Floating Point Converter
https://www.h-schmidt.net/FloatConverter/IEEE754.html - Base Convert: IEEE 754 Floating Point
https://baseconvert.com/ieee-754-floating-point - Brain Floating Point – nový formát uložení čísel pro strojové učení a chytrá čidla
https://www.root.cz/clanky/brain-floating-point-ndash-novy-format-ulozeni-cisel-pro-strojove-uceni-a-chytra-cidla/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Protocol Buffers
https://protobuf.dev/ - Protocol Buffers
https://en.wikipedia.org/wiki/Protocol_Buffers - What is the difference between Serialization and Marshaling?
https://stackoverflow.com/questions/770474/what-is-the-difference-between-serialization-and-marshaling - Comparison of data-serialization formats
https://en.wikipedia.org/wiki/Comparison_of_data-serialization_formats