Obsah
1. Serializace a deserializace datových struktur v programovacím jazyce Go (2.část)
2. Uložení jednoduchých datových typů do formátu gob
3. Serializace celých čísel bez znaménka
4. Serializace celých čísel se znaménkem
5. Uložení složitějších datových struktur – řezů celých čísel
6. Datové struktury s ukazateli – binární stromy
7. Serializace binárního stromu do formátu gob
8. Serializace a deserializace binárního stromu s využitím formátu gob
9. Serializace a deserializace binárního stromu s využitím formátu JSON
10. Serializace a deserializace binárního stromu s využitím formátu BSON
11. Porovnání velikosti výsledných souborů se serializovanými daty
12. Vektor obsahující hodnoty s plovoucí řádovou čárkou
13. Serializovaná struktura obsahující jediný atribut – dlouhý řetězec
14. Serializovaný strom s 255 uzly
15. Výsledek porovnání, vliv komprimace dat
16. Problematika serializace grafových struktur
18. Cyklus v datových strukturách (grafu atd.)
19. Repositář s demonstračními příklady
1. Serializace a deserializace datových struktur v programovacím jazyce Go (2.část)
V dnešní části seriálu o programovacím jazyce Go budeme pokračovat v popisu problematiky, které jsme se začali zabývat minule. Připomeňme si, že se jedná o serializaci i deserializaci datových struktur. Jedná se o velmi často používanou operaci, neboť mnohdy je nutné datové struktury ukládat (ať již do běžných souborů, nebo do (post)relačních databází) a taktéž přenášet. Přes serializaci a deserializaci datových struktur lze poměrně jednoduše realizovat i vzdálené volání procedur neboli RPC (Remote Procedure Call) (použít lze i balíček net/rpc, popř. v případě potřeby složitější mechanismy). Dnes se naposledy budeme zabývat použitím binárního formátu BSON i nativního formátu jazyka Go – gob. Navíc provedeme porovnání velikosti souborů se serializovanými daty, protože právě snahy o zmenšení objemu dat vedou k používání binárních formátů oproti formátům textovým (další snahou je snížení doby serializace a deserializace).
2. Uložení jednoduchých datových typů do formátu gob
Už minule jsme se zmínili o formátu gob neboli Go Object(s). Jak již z názvu tohoto formátu vyplývá, je primárně určen pro použití v programovacím jazyku Go pro serializaci a deserializaci prakticky libovolné datové struktury popř. struktur, které mohou být propojeny přes reference (neboli ukazatele). Zajímavé je, jak jsou serializovány celočíselné hodnoty – zde nezáleží na použitém datovém typu (int8, int16, int32, int64 atd.), ale na velikosti ukládané konstanty. V dnešním prvním demonstračním příkladu si toto chování ověříme, neboť budeme provádět serializaci hodnot různých typů (tedy i bitové šířky):
var b bool = true var x uint8 = 42 var y uint16 = 42 var z uint32 = 42
Demonstrační příklad, v němž je serializace provedena (lokálně do bufferu) může vypadat následovně:
package main import ( "bytes" "encoding/gob" "encoding/hex" "fmt" ) func encodeAndDecode(msg string, value interface{}) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(value) if err != nil { fmt.Println(err) } else { content := buffer.Bytes() fmt.Printf("%s value encoded into %d bytes\n", msg, len(content)) encoded := hex.EncodeToString(content) fmt.Println(encoded) } } func main() { var b bool = true encodeAndDecode("Boolean", b) var x uint8 = 42 encodeAndDecode("Uint8", x) var y uint16 = 42 encodeAndDecode("Uint16", y) var z uint32 = 42 encodeAndDecode("Uint32", z) }
Z výsledků je patrné, že nezávisle na velikosti datové struktury jsou data serializována do stejně dlouhé sekvence, zde konkrétně do čtyř bajtů. První bajt obsahuje délku sekvence (3 bajty), další typ, poté index v rámci datové pseudostruktury a konečně následuje ukládaná hodnota:
Boolean value encoded into 4 bytes 03020001 Uint8 value encoded into 4 bytes 0306002a Uint16 value encoded into 4 bytes 0306002a Uint32 value encoded into 4 bytes 0306002a
3. Serializace celých čísel bez znaménka
S celými čísly se při použití formátu gob pracuje podobně jako s celočíselnými konstantami. Jedná se o abstraktní typ bez explicitní specifikace velikosti, protože velikost čísla (v bajtech) je odvozena od jeho konkrétní hodnoty a nikoli od datového typu. Způsob uložení je přitom následující:
- Pokud je hodnota menší než 128, je přímo uložena jako jeden bajt dogobu.
- U větší hodnoty je zjištěn počet potřebných bajtů. Následně je tento počet uložen do gobu v negované podobě (aby se nespletl s malým číslem) a poté je uložena sekvence bajtů představující ono číslo (v pořadí big endian).
Příkladem může být hodnota 256, která je uložena do sekvence bajtů FE 01 00
Ukažme si nyní způsob uložení hodnot, které „překlenují“ celý 64bitový prostor datového typu uint64:
var value uint64 = 1 for i := 0; i < 64; i++ { encodeAndDecodeUint(value) value <<= 1 }
Realizace demonstračního příkladu vypadá následovně:
package main import ( "bytes" "encoding/gob" "encoding/hex" "fmt" ) func encodeAndDecodeUint(value uint64) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(value) if err != nil { fmt.Println(err) } else { content := buffer.Bytes() fmt.Printf("%20d value encoded into %d bytes: ", value, len(content)) encoded := hex.EncodeToString(content) fmt.Println(encoded) } } func main() { var value uint64 = 1 for i := 0; i < 64; i++ { encodeAndDecodeUint(value) value <<= 1 } }
Z výsledků je patrné, jak se postupně se zvyšující se hodnotou mění i počet potřebných bajtů:
1 value encoded into 4 bytes: 03060001 2 value encoded into 4 bytes: 03060002 4 value encoded into 4 bytes: 03060004 8 value encoded into 4 bytes: 03060008 16 value encoded into 4 bytes: 03060010 32 value encoded into 4 bytes: 03060020 64 value encoded into 4 bytes: 03060040 128 value encoded into 5 bytes: 040600ff80 256 value encoded into 6 bytes: 050600fe0100 512 value encoded into 6 bytes: 050600fe0200 1024 value encoded into 6 bytes: 050600fe0400 2048 value encoded into 6 bytes: 050600fe0800 4096 value encoded into 6 bytes: 050600fe1000 8192 value encoded into 6 bytes: 050600fe2000 16384 value encoded into 6 bytes: 050600fe4000 32768 value encoded into 6 bytes: 050600fe8000 65536 value encoded into 7 bytes: 060600fd010000 131072 value encoded into 7 bytes: 060600fd020000 262144 value encoded into 7 bytes: 060600fd040000 524288 value encoded into 7 bytes: 060600fd080000 1048576 value encoded into 7 bytes: 060600fd100000 2097152 value encoded into 7 bytes: 060600fd200000 4194304 value encoded into 7 bytes: 060600fd400000 8388608 value encoded into 7 bytes: 060600fd800000 16777216 value encoded into 8 bytes: 070600fc01000000 33554432 value encoded into 8 bytes: 070600fc02000000 67108864 value encoded into 8 bytes: 070600fc04000000 134217728 value encoded into 8 bytes: 070600fc08000000 268435456 value encoded into 8 bytes: 070600fc10000000 536870912 value encoded into 8 bytes: 070600fc20000000 1073741824 value encoded into 8 bytes: 070600fc40000000 2147483648 value encoded into 8 bytes: 070600fc80000000 4294967296 value encoded into 9 bytes: 080600fb0100000000 8589934592 value encoded into 9 bytes: 080600fb0200000000 17179869184 value encoded into 9 bytes: 080600fb0400000000 34359738368 value encoded into 9 bytes: 080600fb0800000000 68719476736 value encoded into 9 bytes: 080600fb1000000000 137438953472 value encoded into 9 bytes: 080600fb2000000000 274877906944 value encoded into 9 bytes: 080600fb4000000000 549755813888 value encoded into 9 bytes: 080600fb8000000000 1099511627776 value encoded into 10 bytes: 090600fa010000000000 2199023255552 value encoded into 10 bytes: 090600fa020000000000 4398046511104 value encoded into 10 bytes: 090600fa040000000000 8796093022208 value encoded into 10 bytes: 090600fa080000000000 17592186044416 value encoded into 10 bytes: 090600fa100000000000 35184372088832 value encoded into 10 bytes: 090600fa200000000000 70368744177664 value encoded into 10 bytes: 090600fa400000000000 140737488355328 value encoded into 10 bytes: 090600fa800000000000 281474976710656 value encoded into 11 bytes: 0a0600f901000000000000 562949953421312 value encoded into 11 bytes: 0a0600f902000000000000 1125899906842624 value encoded into 11 bytes: 0a0600f904000000000000 2251799813685248 value encoded into 11 bytes: 0a0600f908000000000000 4503599627370496 value encoded into 11 bytes: 0a0600f910000000000000 9007199254740992 value encoded into 11 bytes: 0a0600f920000000000000 18014398509481984 value encoded into 11 bytes: 0a0600f940000000000000 36028797018963968 value encoded into 11 bytes: 0a0600f980000000000000 72057594037927936 value encoded into 12 bytes: 0b0600f80100000000000000 144115188075855872 value encoded into 12 bytes: 0b0600f80200000000000000 288230376151711744 value encoded into 12 bytes: 0b0600f80400000000000000 576460752303423488 value encoded into 12 bytes: 0b0600f80800000000000000 1152921504606846976 value encoded into 12 bytes: 0b0600f81000000000000000 2305843009213693952 value encoded into 12 bytes: 0b0600f82000000000000000 4611686018427387904 value encoded into 12 bytes: 0b0600f84000000000000000 9223372036854775808 value encoded into 12 bytes: 0b0600f88000000000000000
4. Serializace celých čísel se znaménkem
Podobným způsobem, tedy s proměnnou bajtovou šířkou, jsou serializovány i hodnoty se znaménkem. Bude se pochopitelně lišit datový typ položky (druhý bajt) i samotné kódování, kde poslední bit (s nejnižší vahou) nese hodnotu znaménka. Opět si toto chování ověřme na demonstračním příkladu:
package main import ( "bytes" "encoding/gob" "encoding/hex" "fmt" ) func encodeAndDecodeInt(value int64) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(value) if err != nil { fmt.Println(err) } else { content := buffer.Bytes() fmt.Printf("%20d value encoded into %d bytes: ", value, len(content)) encoded := hex.EncodeToString(content) fmt.Println(encoded) } } func main() { var value int64 = -1 for i := 0; i < 63; i++ { encodeAndDecodeInt(value) value <<= 1 } }
Výsledky budou v tomto případě vypadat podobně jako u demonstračního příkladu popsaného v předchozí kapitole:
-1 value encoded into 4 bytes: 03040001 -2 value encoded into 4 bytes: 03040003 -4 value encoded into 4 bytes: 03040007 -8 value encoded into 4 bytes: 0304000f -16 value encoded into 4 bytes: 0304001f -32 value encoded into 4 bytes: 0304003f -64 value encoded into 4 bytes: 0304007f -128 value encoded into 5 bytes: 040400ffff -256 value encoded into 6 bytes: 050400fe01ff -512 value encoded into 6 bytes: 050400fe03ff -1024 value encoded into 6 bytes: 050400fe07ff -2048 value encoded into 6 bytes: 050400fe0fff -4096 value encoded into 6 bytes: 050400fe1fff -8192 value encoded into 6 bytes: 050400fe3fff -16384 value encoded into 6 bytes: 050400fe7fff -32768 value encoded into 6 bytes: 050400feffff -65536 value encoded into 7 bytes: 060400fd01ffff -131072 value encoded into 7 bytes: 060400fd03ffff -262144 value encoded into 7 bytes: 060400fd07ffff -524288 value encoded into 7 bytes: 060400fd0fffff -1048576 value encoded into 7 bytes: 060400fd1fffff -2097152 value encoded into 7 bytes: 060400fd3fffff -4194304 value encoded into 7 bytes: 060400fd7fffff -8388608 value encoded into 7 bytes: 060400fdffffff -16777216 value encoded into 8 bytes: 070400fc01ffffff -33554432 value encoded into 8 bytes: 070400fc03ffffff -67108864 value encoded into 8 bytes: 070400fc07ffffff -134217728 value encoded into 8 bytes: 070400fc0fffffff -268435456 value encoded into 8 bytes: 070400fc1fffffff -536870912 value encoded into 8 bytes: 070400fc3fffffff -1073741824 value encoded into 8 bytes: 070400fc7fffffff -2147483648 value encoded into 8 bytes: 070400fcffffffff -4294967296 value encoded into 9 bytes: 080400fb01ffffffff -8589934592 value encoded into 9 bytes: 080400fb03ffffffff -17179869184 value encoded into 9 bytes: 080400fb07ffffffff -34359738368 value encoded into 9 bytes: 080400fb0fffffffff -68719476736 value encoded into 9 bytes: 080400fb1fffffffff -137438953472 value encoded into 9 bytes: 080400fb3fffffffff -274877906944 value encoded into 9 bytes: 080400fb7fffffffff -549755813888 value encoded into 9 bytes: 080400fbffffffffff -1099511627776 value encoded into 10 bytes: 090400fa01ffffffffff -2199023255552 value encoded into 10 bytes: 090400fa03ffffffffff -4398046511104 value encoded into 10 bytes: 090400fa07ffffffffff -8796093022208 value encoded into 10 bytes: 090400fa0fffffffffff -17592186044416 value encoded into 10 bytes: 090400fa1fffffffffff -35184372088832 value encoded into 10 bytes: 090400fa3fffffffffff -70368744177664 value encoded into 10 bytes: 090400fa7fffffffffff -140737488355328 value encoded into 10 bytes: 090400faffffffffffff -281474976710656 value encoded into 11 bytes: 0a0400f901ffffffffffff -562949953421312 value encoded into 11 bytes: 0a0400f903ffffffffffff -1125899906842624 value encoded into 11 bytes: 0a0400f907ffffffffffff -2251799813685248 value encoded into 11 bytes: 0a0400f90fffffffffffff -4503599627370496 value encoded into 11 bytes: 0a0400f91fffffffffffff -9007199254740992 value encoded into 11 bytes: 0a0400f93fffffffffffff -18014398509481984 value encoded into 11 bytes: 0a0400f97fffffffffffff -36028797018963968 value encoded into 11 bytes: 0a0400f9ffffffffffffff -72057594037927936 value encoded into 12 bytes: 0b0400f801ffffffffffffff -144115188075855872 value encoded into 12 bytes: 0b0400f803ffffffffffffff -288230376151711744 value encoded into 12 bytes: 0b0400f807ffffffffffffff -576460752303423488 value encoded into 12 bytes: 0b0400f80fffffffffffffff -1152921504606846976 value encoded into 12 bytes: 0b0400f81fffffffffffffff -2305843009213693952 value encoded into 12 bytes: 0b0400f83fffffffffffffff -4611686018427387904 value encoded into 12 bytes: 0b0400f87fffffffffffffff
5. Uložení složitějších datových struktur – řezů celých čísel
Velmi často se setkáme s potřebou uložení mnohdy velmi dlouhých řezů s celočíselnými hodnotami popř. hodnotami typu float32 a float64. Samotný řez je přitom přenesen relativně komplikovaným způsobem – jako explicitně popsaný datový typ s délkou a vlastními daty. Konkrétní chování si opět můžeme ukázat na příkladu, nyní na následujících řezech, které jsou sice stejného typu, ovšem liší se počtem položek i jejich rozsahem:
values1 := []int64{} values2 := []int64{1, 2, 3, 4} values3 := []int64{1, 2, 3, 4, 5} values4 := []int64{1000000, 2000000, 3000000, 4000000}
Výsledek bude po spuštění tohoto demonstračního příkladu vypadat následovně:
Slice with 0 values encoded into 18 bytes: 0cff81020102ff82000104000004ff820000 Slice with 4 values encoded into 22 bytes: 0cff81020102ff82000104000008ff82000402040608 Slice with 5 values encoded into 23 bytes: 0cff81020102ff82000104000009ff820005020406080a Slice with 4 values encoded into 34 bytes: 0cff81020102ff82000104000014ff820004fd1e8480fd3d0900fd5b8d80fd7a1200
Povšimněte si naprosto stejného prefixu u všech čtyř řezů (13 bajtů):
0cff81020102ff820001040000
Liší se obsah řezu, tedy jeho délka (v bajtech), počet položek a jejich konkrétní hodnoty zakódované způsobem popsaným v předchozích dvou kapitolách.
Úplný zdrojový kód takto připraveného demonstračního příkladu vypadá následovně:
package main import ( "bytes" "encoding/gob" "encoding/hex" "fmt" ) func encodeAndDecodeSliceOfInts(values []int64) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(values) if err != nil { fmt.Println(err) } else { content := buffer.Bytes() fmt.Printf("Slice with %d values encoded into %d bytes: ", len(values), len(content)) encoded := hex.EncodeToString(content) fmt.Println(encoded) } } func main() { values1 := []int64{} encodeAndDecodeSliceOfInts(values1) values2 := []int64{1, 2, 3, 4} encodeAndDecodeSliceOfInts(values2) values3 := []int64{1, 2, 3, 4, 5} encodeAndDecodeSliceOfInts(values3) values4 := []int64{1000000, 2000000, 3000000, 4000000} encodeAndDecodeSliceOfInts(values4) }
6. Datové struktury s ukazateli – binární stromy
Už v předchozím textu jsme si řekli, že do formátu gob lze ukládat i datové struktury obsahující ukazatele. Ukažme si tuto velmi užitečnou vlastnost na velmi známé datové struktuře založené na ukazatelích. Jedná se o binární stromy (binary tree), konkrétně o binární stromy, do nichž se prvky ukládají takovým způsobem, aby se u vyváženého stromu zajistilo vyhledání prvku v logaritmickém čase (binary search tree). Dále uvedená implementace binárních stromů je podrobněji popsána v článku Go Data Structures: Binary Search Tree. Uzel je popsán strukturou Node, vlastní uložená hodnota pak datovým typem Item:
type Item int type Node struct { value Item left *Node right *Node } type BinaryTree struct { root *Node }
Důležitá je metoda Insert, která společně s pomocnou funkcí insertNode zajistí vložení nového uzlu do stromu na správné místo (uzly nalevo obsahují menší hodnotu, uzly napravo hodnotu větší):
func (bt *BinaryTree) Insert(value Item) { node := &Node{value, nil, nil} if bt.root == nil { bt.root = node } else { insertNode(bt.root, node) } } func insertNode(node, newNode *Node) { if newNode.value < node.value { if node.left == nil { node.left = newNode } else { insertNode(node.left, newNode) } } else { if node.right == nil { node.right = newNode } else { insertNode(node.right, newNode) } } }
7. Serializace binárního stromu do formátu gob
Samozřejmě nám nic nebrání pokusit se o serializaci binárního stromu do formátu gob. Pokusíme se nejprve o zobrazení serializovaných dat v hexadecimálním tvaru i o uložení bufferu do externího souboru:
func encodeAndDecodeBinaryTree(bt BinaryTree) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(bt) if err != nil { fmt.Println(err) } else { content := buffer.Bytes() fmt.Printf("Binary tree encoded into %d bytes: ", len(content)) encoded := hex.EncodeToString(content) fmt.Println(encoded) err = ioutil.WriteFile("tree1.gob", content, 0644) if err != nil { fmt.Println(err) } else { fmt.Println("And stored into file") } } }
Celý zdrojový soubor vypadá následovně:
package main import ( "bytes" "encoding/gob" "encoding/hex" "fmt" "io/ioutil" ) type Item int type Node struct { value Item left *Node right *Node } type BinaryTree struct { root *Node } func (bt *BinaryTree) Insert(value Item) { node := &Node{value, nil, nil} if bt.root == nil { bt.root = node } else { insertNode(bt.root, node) } } func insertNode(node, newNode *Node) { if newNode.value < node.value { if node.left == nil { node.left = newNode } else { insertNode(node.left, newNode) } } else { if node.right == nil { node.right = newNode } else { insertNode(node.right, newNode) } } } func encodeAndDecodeBinaryTree(bt BinaryTree) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(bt) if err != nil { fmt.Println(err) } else { content := buffer.Bytes() fmt.Printf("Binary tree encoded into %d bytes: ", len(content)) encoded := hex.EncodeToString(content) fmt.Println(encoded) err = ioutil.WriteFile("tree1.gob", content, 0644) if err != nil { fmt.Println(err) } else { fmt.Println("And stored into file") } } } func main() { var bt BinaryTree bt.Insert(5) bt.Insert(3) bt.Insert(7) bt.Insert(1) bt.Insert(4) bt.Insert(6) bt.Insert(8) bt.Insert(9) bt.Insert(10) bt.Insert(0) encodeAndDecodeBinaryTree(bt) }
Při pokusu o serializaci však dojde k chybě způsobené tím, že žádný prvek struktury nezačíná velkým písmenem a není ho tedy možné exportovat:
gob: type main.BinaryTree has no exported fields
Úprava je snadná – pouze změníme jména všech prvků ve struktuře Node a BinaryTree:
package main import ( "bytes" "encoding/gob" "encoding/hex" "fmt" "io/ioutil" ) type Item int type Node struct { Value Item Left *Node Right *Node } type BinaryTree struct { Root *Node } func (bt *BinaryTree) Insert(value Item) { node := &Node{value, nil, nil} if bt.Root == nil { bt.Root = node } else { insertNode(bt.Root, node) } } func insertNode(node, newNode *Node) { if newNode.Value < node.Value { if node.Left == nil { node.Left = newNode } else { insertNode(node.Left, newNode) } } else { if node.Right == nil { node.Right = newNode } else { insertNode(node.Right, newNode) } } } func encodeAndDecodeBinaryTree(bt BinaryTree) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(bt) if err != nil { fmt.Println(err) } else { content := buffer.Bytes() fmt.Printf("Binary tree encoded into %d bytes: ", len(content)) encoded := hex.EncodeToString(content) fmt.Println(encoded) err = ioutil.WriteFile("tree1.gob", content, 0644) if err != nil { fmt.Println(err) } else { fmt.Println("And stored into file") } } } func main() { var bt BinaryTree bt.Insert(5) bt.Insert(3) bt.Insert(7) bt.Insert(1) bt.Insert(4) bt.Insert(6) bt.Insert(8) bt.Insert(9) bt.Insert(10) bt.Insert(0) encodeAndDecodeBinaryTree(bt) }
Nyní již bude možné strom vytvořit, výsledek bude mít délku pouze 127 bajtů, což je relativně málo (jak uvidíme dále):
Binary tree encoded into 127 bytes: 22ff810301010a42696e6172795472656501ff820001010104526f6f7401ff8400000031ff83030101044e6f646501ff84000103010556616c756501040001044c65667401ff84000105526967687401ff8400000029ff8201010a010106010102010000010108000001010e01010c00010110020112020114000000000000 And stored into file
8. Serializace a deserializace binárního stromu s využitím formátu gob
Nyní již máme k dispozici všechny potřebné informace nutné pro serializaci stromu do formátu gob a k jeho opětovné deserializaci. Strukturu binárního stromu zobrazíme touto pomocnou funkcí (ta zobrazuje strom „naležato“, což je implementačně jednodušší):
func printTree(node *Node, level int) { if node != nil { format := "" for i := 0; i < level; i++ { format += " " } format += "---[ " level++ printTree(node.Left, level) fmt.Printf(format+"%d\n", node.Value) printTree(node.Right, level) } }
Implementace serializace a opětovné deserializace vypadá následovně:
package main import ( "bytes" "encoding/gob" "fmt" "io/ioutil" ) type Item int type Node struct { Value Item Left *Node Right *Node } type BinaryTree struct { Root *Node } func (bt *BinaryTree) Insert(value Item) { node := &Node{value, nil, nil} if bt.Root == nil { bt.Root = node } else { insertNode(bt.Root, node) } } func insertNode(node, newNode *Node) { if newNode.Value < node.Value { if node.Left == nil { node.Left = newNode } else { insertNode(node.Left, newNode) } } else { if node.Right == nil { node.Right = newNode } else { insertNode(node.Right, newNode) } } } func printTree(node *Node, level int) { if node != nil { format := "" for i := 0; i < level; i++ { format += " " } format += "---[ " level++ printTree(node.Left, level) fmt.Printf(format+"%d\n", node.Value) printTree(node.Right, level) } } func encodeBinaryTree(bt BinaryTree) (bytes.Buffer, error) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(bt) if err != nil { return buffer, err } else { return buffer, nil } } func decodeBinaryTree(encodedTree bytes.Buffer) (BinaryTree, error) { var newTree BinaryTree decoder := gob.NewDecoder(&encodedTree) err := decoder.Decode(&newTree) return newTree, err } func saveBinaryTree(encodedTree bytes.Buffer, filename string) { err := ioutil.WriteFile(filename, encodedTree.Bytes(), 0644) if err != nil { fmt.Println(err) } else { fmt.Println("Stored into file\n") } } func main() { var bt BinaryTree bt.Insert(5) bt.Insert(3) bt.Insert(7) bt.Insert(1) bt.Insert(4) bt.Insert(6) bt.Insert(8) bt.Insert(9) bt.Insert(10) bt.Insert(0) printTree(bt.Root, 0) encodedTree, err := encodeBinaryTree(bt) if err != nil { fmt.Println(err) return } fmt.Println("\nBuffer with encoded tree: ", encodedTree.Len(), "\n") saveBinaryTree(encodedTree, "tree2.gob") decodedTree, err := decodeBinaryTree(encodedTree) if err != nil { fmt.Println(err) return } else { printTree(decodedTree.Root, 0) } }
Výsledek po spuštění naznačuje, že jsme vše provedli korektně:
---[ 0 ---[ 1 ---[ 3 ---[ 4 ---[ 5 ---[ 6 ---[ 7 ---[ 8 ---[ 9 ---[ 10 Buffer with encoded tree: 127 Stored into file ---[ 0 ---[ 1 ---[ 3 ---[ 4 ---[ 5 ---[ 6 ---[ 7 ---[ 8 ---[ 9 ---[ 10
9. Serializace a deserializace binárního stromu s využitím formátu JSON
Na binární strom (popř. i na lineárně vázaný seznam) se můžeme dívat i jinak, než na klasický orientovaný graf. Uzel stromu může obsahovat dva přímo navázané synovské uzly (poduzly), což mj. znamená, že jak strom, tak i seznam je s určitými problémy reprezentovatelný i ve formátu JSON a XML. Tyto formáty sice neumožňují používat ukazatele (v XML teoreticky ano, ale komplikovaně), ovšem na druhou stranu dokážou strom uložit jako jediný kořenový uzel, který obsahuje dva poduzly, jež mohou rekurzivně obsahovat další dva poduzly atd. Serializace stromu do JSONu je tedy možná, na rozdíl od jiných typů grafů (s kružnicí, s více cestami mezi uzly atd.). Můžeme se o tom snadno přesvědčit:
package main import ( "encoding/json" "fmt" "io/ioutil" ) type Item int type Node struct { Value Item Left *Node Right *Node } type BinaryTree struct { Root *Node } func (bt *BinaryTree) Insert(value Item) { node := &Node{value, nil, nil} if bt.Root == nil { bt.Root = node } else { insertNode(bt.Root, node) } } func insertNode(node, newNode *Node) { if newNode.Value < node.Value { if node.Left == nil { node.Left = newNode } else { insertNode(node.Left, newNode) } } else { if node.Right == nil { node.Right = newNode } else { insertNode(node.Right, newNode) } } } func printTree(node *Node, level int) { if node != nil { format := "" for i := 0; i < level; i++ { format += " " } format += "---[ " level++ printTree(node.Left, level) fmt.Printf(format+"%d\n", node.Value) printTree(node.Right, level) } } func encodeBinaryTree(bt BinaryTree) ([]byte, error) { jsonOutput, err := json.Marshal(bt) if err != nil { return jsonOutput, err } else { return jsonOutput, nil } } func decodeBinaryTree(encodedTree []byte) (BinaryTree, error) { var newTree BinaryTree err := json.Unmarshal(encodedTree, &newTree) return newTree, err } func saveBinaryTree(encodedTree []byte, filename string) { err := ioutil.WriteFile(filename, encodedTree, 0644) if err != nil { fmt.Println(err) } else { fmt.Println("Stored into file\n") } } func main() { var bt BinaryTree bt.Insert(5) bt.Insert(3) bt.Insert(7) bt.Insert(1) bt.Insert(4) bt.Insert(6) bt.Insert(8) bt.Insert(9) bt.Insert(10) bt.Insert(0) printTree(bt.Root, 0) encodedTree, err := encodeBinaryTree(bt) if err != nil { fmt.Println(err) return } fmt.Println("\nBuffer with encoded tree: ", len(encodedTree), "\n") saveBinaryTree(encodedTree, "tree.json") decodedTree, err := decodeBinaryTree(encodedTree) if err != nil { fmt.Println(err) return } else { printTree(decodedTree.Root, 0) } }
Test funkčnosti předchozího příkladu:
---[ 0 ---[ 1 ---[ 3 ---[ 4 ---[ 5 ---[ 6 ---[ 7 ---[ 8 ---[ 9 ---[ 10 Buffer with encoded tree: 334 Stored into file ---[ 0 ---[ 1 ---[ 3 ---[ 4 ---[ 5 ---[ 6 ---[ 7 ---[ 8 ---[ 9 ---[ 10
10. Serializace a deserializace binárního stromu s využitím formátu BSON
Prakticky naprosto stejným způsobem můžeme provést serializaci a deserializaci binárního stromu do binárního formátu BSON, s nímž jsme se seznámili v předchozí části tohoto seriálu. Podobnost dále uvedeného příkladu s příkladem z deváté kapitoly není náhodná – rozhraní knihoven encoding/json a gopkg.in/mgo.v2/bson je z pohledu vývojáře takřka totožné:
package main import ( "fmt" "gopkg.in/mgo.v2/bson" "io/ioutil" ) type Item int type Node struct { Value Item Left *Node Right *Node } type BinaryTree struct { Root *Node } func (bt *BinaryTree) Insert(value Item) { node := &Node{value, nil, nil} if bt.Root == nil { bt.Root = node } else { insertNode(bt.Root, node) } } func insertNode(node, newNode *Node) { if newNode.Value < node.Value { if node.Left == nil { node.Left = newNode } else { insertNode(node.Left, newNode) } } else { if node.Right == nil { node.Right = newNode } else { insertNode(node.Right, newNode) } } } func printTree(node *Node, level int) { if node != nil { format := "" for i := 0; i < level; i++ { format += " " } format += "---[ " level++ printTree(node.Left, level) fmt.Printf(format+"%d\n", node.Value) printTree(node.Right, level) } } func encodeBinaryTree(bt BinaryTree) ([]byte, error) { bsonOutput, err := bson.Marshal(bt) if err != nil { return bsonOutput, err } else { return bsonOutput, nil } } func decodeBinaryTree(encodedTree []byte) (BinaryTree, error) { var newTree BinaryTree err := bson.Unmarshal(encodedTree, &newTree) return newTree, err } func saveBinaryTree(encodedTree []byte, filename string) { err := ioutil.WriteFile(filename, encodedTree, 0644) if err != nil { fmt.Println(err) } else { fmt.Println("Stored into file\n") } } func main() { var bt BinaryTree bt.Insert(5) bt.Insert(3) bt.Insert(7) bt.Insert(1) bt.Insert(4) bt.Insert(6) bt.Insert(8) bt.Insert(9) bt.Insert(10) bt.Insert(0) printTree(bt.Root, 0) encodedTree, err := encodeBinaryTree(bt) if err != nil { fmt.Println(err) return } fmt.Println("\nBuffer with encoded tree: ", len(encodedTree), "\n") saveBinaryTree(encodedTree, "tree.bson") decodedTree, err := decodeBinaryTree(encodedTree) if err != nil { fmt.Println(err) return } else { printTree(decodedTree.Root, 0) } }
Výsledek běhu tohoto příkladu:
---[ 0 ---[ 1 ---[ 3 ---[ 4 ---[ 5 ---[ 6 ---[ 7 ---[ 8 ---[ 9 ---[ 10 Buffer with encoded tree: 301 Stored into file ---[ 0 ---[ 1 ---[ 3 ---[ 4 ---[ 5 ---[ 6 ---[ 7 ---[ 8 ---[ 9 ---[ 10
Pro zajímavost si vyzkoušejme soubor vytvořený předchozím příkladem načíst do skriptu naprogramovaného v Pythonu s použitím knihovny BSON, jejíž zdrojové kódy lze nalézt na adrese https://github.com/py-bson/bson. Nejprve knihovnu BSON nainstalujeme pomocí nástroje pip:
$ pip3 install --user bson Downloading/unpacking bson Downloading bson-0.5.9.tar.gz Running setup.py (path:/tmp/ramdisk/pip_build_tester/bson/setup.py) egg_info for package bson Downloading/unpacking python-dateutil>=2.4.0 (from bson) Downloading python_dateutil-2.8.1-py2.py3-none-any.whl (227kB): 227kB downloaded Requirement already satisfied (use --upgrade to upgrade): six>=1.9.0 in ./.local/lib/python3.4/site-packages (from bson) Installing collected packages: bson, python-dateutil Running setup.py install for bson Successfully installed bson python-dateutil Cleaning up...
První skript pro načtení serializovaného stromu používá pro zobrazení metodu PrettyPrinter.pprint:
import pprint import bson with open("tree.bson", "rb") as fin: content = fin.read() binary_tree = bson.loads(content) pp = pprint.PrettyPrinter(indent=4) pp.pprint(binary_tree)
Výsledek je ovšem poněkud nečitelný:
{ 'root': { 'left': { 'left': { 'left': { 'left': None, 'right': None, 'value': 0}, 'right': None, 'value': 1}, 'right': { 'left': None, 'right': None, 'value': 4}, 'value': 3}, 'right': { 'left': { 'left': None, 'right': None, 'value': 6}, 'right': { 'left': None, 'right': { 'left': None, 'right': { 'left': None, 'right': None, 'value': 10}, 'value': 9}, 'value': 8}, 'value': 7}, 'value': 5}}
Z tohoto důvodu funkci printTree, kterou jsme již implementovali v jazyce Go, přepíšeme do Pythonu a použijeme ji pro vypsání obsahu deserializovaného stromu:
import bson def printTree(node, level): if node is not None: format = level * " " format += "---[ " level += 1 printTree(node["left"], level) print(format+str(node["value"])) printTree(node["right"], level) pass with open("tree.bson", "rb") as fin: content = fin.read() binary_tree = bson.loads(content) printTree(binary_tree["root"], 0)
Z výsledku je patrné, že deserializovaný strom má v Pythonu stejnou strukturu, jako v jazyce Go (samozřejmě až na zcela odlišný typový systém obou jazyků):
---[ 0 ---[ 1 ---[ 3 ---[ 4 ---[ 5 ---[ 6 ---[ 7 ---[ 8 ---[ 9 ---[ 10
11. Porovnání velikosti výsledných souborů se serializovanými daty
K binárním přenosovým formátům se většinou uchylujeme v těchto případech:
- Je nutné zajistit větší rychlost serializace a/nebo deserializace
- Z různých důvodů potřebujeme, aby serializovaná data byla uložena úsporněji, než v čistě textových formátech.
V následujících kapitolách si ukážeme trojici příkladů, na nichž zjistíme, do jaké míry zajišťují binární formáty menší objem serializovaných (a tím pádem i přenášených) dat. Zaměříme se na:
- Serializaci vektoru obsahujícího hodnoty s plovoucí řádovou čárkou (konkrétně typ float64)
- Serializaci struktury s jediným delším řetězcem
- Serializaci binárního stromu, nyní s více uzly (255) a tudíž i větší hloubkou
Použity budou textové formáty XML a JSON i binární formáty BSON a gob. Navíc všechny soubory následně zkomprimujeme a porovnáme i komprimované velikosti.
Výsledky budou shrnuty v patnácté kapitole.
12. Vektor obsahující hodnoty s plovoucí řádovou čárkou
V prvním příkladu vytvoříme vektor s jedním tisícem položek typu float64, které budou následně serializovány do všech podporovaných formátů:
var array [1000]float64
Úplný zdrojový kód tohoto příkladu vypadá následovně (výsledky budou ukázány v patnácté kapitole):
package main import ( "bytes" "encoding/gob" "encoding/json" "encoding/xml" "fmt" "gopkg.in/mgo.v2/bson" "io/ioutil" ) type Vector []float64 func encodeVectorIntoBSON(vector Vector) ([]byte, error) { bsonOutput, err := bson.Marshal(vector) if err != nil { return bsonOutput, err } else { return bsonOutput, nil } } func encodeVectorIntoJSON(vector Vector) ([]byte, error) { jsonOutput, err := json.Marshal(vector) if err != nil { return jsonOutput, err } else { return jsonOutput, nil } } func encodeVectorIntoIndentedJSON(vector Vector) ([]byte, error) { jsonOutput, err := json.MarshalIndent(vector, "", " ") if err != nil { return jsonOutput, err } else { return jsonOutput, nil } } func encodeVectorIntoXML(vector Vector) ([]byte, error) { xmlOutput, err := xml.Marshal(vector) if err != nil { return xmlOutput, err } else { return xmlOutput, nil } } func encodeVectorIntoIndentedXML(vector Vector) ([]byte, error) { xmlOutput, err := xml.MarshalIndent(vector, "", " ") if err != nil { return xmlOutput, err } else { return xmlOutput, nil } } func encodeVectorIntoGob(vector Vector) ([]byte, error) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(vector) if err != nil { return buffer.Bytes(), err } else { return buffer.Bytes(), nil } } func saveVector(encodedVector []byte, filename string) { err := ioutil.WriteFile(filename, encodedVector, 0644) if err != nil { fmt.Println(err) } else { fmt.Println("Stored into file", filename) } } func printBufferInfo(buffer []byte) { fmt.Println("\nBuffer with encoded vector: ", len(buffer)) } func main() { var array [1000]float64 for i := 0; i < len(array); i++ { if i == 0 { array[i] = 1.0 } else { array[i] = 1.0 / float64(i) } } var vector Vector = array[:] encodedVector, err := encodeVectorIntoXML(vector) if err != nil { fmt.Println(err) return } printBufferInfo(encodedVector) saveVector(encodedVector, "vector1.xml") encodedVector, err = encodeVectorIntoIndentedXML(vector) if err != nil { fmt.Println(err) return } printBufferInfo(encodedVector) saveVector(encodedVector, "vector2.xml") encodedVector, err = encodeVectorIntoJSON(vector) if err != nil { fmt.Println(err) return } printBufferInfo(encodedVector) saveVector(encodedVector, "vector1.json") encodedVector, err = encodeVectorIntoIndentedJSON(vector) if err != nil { fmt.Println(err) return } printBufferInfo(encodedVector) saveVector(encodedVector, "vector2.json") encodedVector, err = encodeVectorIntoBSON(vector) if err != nil { fmt.Println(err) return } printBufferInfo(encodedVector) saveVector(encodedVector, "vector1.bson") encodedVector, err = encodeVectorIntoGob(vector) if err != nil { fmt.Println(err) return } printBufferInfo(encodedVector) saveVector(encodedVector, "vector1.gob") }
13. Serializovaná struktura obsahující jediný atribut – dlouhý řetězec
Ve druhém příkladu bude serializována jednoduchá struktura obsahující jediný atribut, kterým je řetězec:
type Item struct { Value string }
Testovací řetězec byl vytvořen známým generátorem náhodných proslovů:
„Další rozvoj různých forem činnosti vyzaduje rozšiřování logistických
prostředků a nových návrhů. Pestré a bohaté zkušenosti jasně říkají, že
konzultace se širokým aktivem dostatečně oddaluje propad odpovídajících
podmínek aktivizace. Ideové úvahy nejvyššího řádu a rovněž stabilní a
kvantitativní vzrůst a sféra naší aktivity ve značné míře podmiňuje vytvoření
systému výchovy pracovníků odpovídajících aktuálním potřebám. Nesmíme však
zapomínat, že navržená struktura organizace zvyšuje potřebu aplikace
existujících finančních a administrativních podmínek. Poslání organizace,
zejména pak stálé, informačně-propagandistické zabezpečení naší práce pomáhá
udržovat kumulativní progresi pozic jednotlivých účastníků k zadaným úkolům.
Tímto způsobem realizace plánovaných vytyčených úkolů vyvolává proces zavádění
a modernizace systému masové účasti. Naše dlouhodobé ambice, stejně jako nový
model organizační činnosti přetváří strukturu vedení směru progresivního
rozvoje“
Opět si pro jistotu ukažme úplný zdrojový kód tohoto demonstračního příkladu:
package main import ( "bytes" "encoding/gob" "encoding/json" "encoding/xml" "fmt" "gopkg.in/mgo.v2/bson" "io/ioutil" ) type Item struct { Value string } func encodeStringIntoBSON(item Item) ([]byte, error) { bsonOutput, err := bson.Marshal(item) if err != nil { return bsonOutput, err } else { return bsonOutput, nil } } func encodeStringIntoJSON(item Item) ([]byte, error) { jsonOutput, err := json.Marshal(item) if err != nil { return jsonOutput, err } else { return jsonOutput, nil } } func encodeStringIntoIndentedJSON(item Item) ([]byte, error) { jsonOutput, err := json.MarshalIndent(item, "", " ") if err != nil { return jsonOutput, err } else { return jsonOutput, nil } } func encodeStringIntoXML(item Item) ([]byte, error) { xmlOutput, err := xml.Marshal(item) if err != nil { return xmlOutput, err } else { return xmlOutput, nil } } func encodeStringIntoIndentedXML(item Item) ([]byte, error) { xmlOutput, err := xml.MarshalIndent(item, "", " ") if err != nil { return xmlOutput, err } else { return xmlOutput, nil } } func encodeStringIntoGob(item Item) ([]byte, error) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(item) if err != nil { return buffer.Bytes(), err } else { return buffer.Bytes(), nil } } func saveString(encodedString []byte, filename string) { err := ioutil.WriteFile(filename, encodedString, 0644) if err != nil { fmt.Println(err) } else { fmt.Println("Stored into file", filename) } } func printBufferInfo(buffer []byte) { fmt.Println("\nBuffer with encoded string: ", len(buffer)) } func main() { var item Item item.Value = `Další rozvoj různých forem činnosti vyžaduje rozšiřování logistických prostředků a nových návrhů. Pestré a bohaté zkušenosti jasně říkají, že konzultace se širokým aktivem dostatečně oddaluje propad odpovídajících podmínek aktivizace. Ideové úvahy nejvyššího řádu a rovněž stabilní a kvantitativní vzrůst a sféra naší aktivity ve značné míře podmiňuje vytvoření systému výchovy pracovníků odpovídajících aktuálním potřebám. Nesmíme však zapomínat, že navržená struktura organizace zvyšuje potřebu aplikace existujících finančních a administrativních podmínek. Poslání organizace, zejména pak stálé, informačně-propagandistické zabezpečení naší práce pomáhá udržovat kumulativní progresi pozic jednotlivých účastníků k zadaným úkolům. Tímto způsobem realizace plánovaných vytyčených úkolů vyvolává proces zavádění a modernizace systému masové účasti. Naše dlouhodobé ambice, stejně jako nový model organizační činnosti přetváří strukturu vedení směru progresivního rozvoje.` encodedString, err := encodeStringIntoXML(item) if err != nil { fmt.Println(err) return } printBufferInfo(encodedString) saveString(encodedString, "string1.xml") encodedString, err = encodeStringIntoIndentedXML(item) if err != nil { fmt.Println(err) return } printBufferInfo(encodedString) saveString(encodedString, "string2.xml") encodedString, err = encodeStringIntoJSON(item) if err != nil { fmt.Println(err) return } printBufferInfo(encodedString) saveString(encodedString, "string1.json") encodedString, err = encodeStringIntoIndentedJSON(item) if err != nil { fmt.Println(err) return } printBufferInfo(encodedString) saveString(encodedString, "string2.json") encodedString, err = encodeStringIntoBSON(item) if err != nil { fmt.Println(err) return } printBufferInfo(encodedString) saveString(encodedString, "string1.bson") encodedString, err = encodeStringIntoGob(item) if err != nil { fmt.Println(err) return } printBufferInfo(encodedString) saveString(encodedString, "string1.gob") }
14. Serializovaný strom s 255 uzly
Poslední benchmark je založen na serializaci binárního stromu s 255 uzly. Samotnou konstrukci tohoto stromu (tak, aby byl vyvážený) zajišťuje tato rekurzivní funkce:
func constructTree(bt *BinaryTree, min int, max int) { middle := (min + max) / 2 if min < middle && middle < max { fmt.Println(middle) bt.Insert(Item(middle)) constructTree(bt, min, middle) constructTree(bt, middle, max) } }
Opět si pochopitelně ukážeme celý zdrojový kód příkladu:
package main import ( "bytes" "encoding/gob" "encoding/json" "encoding/xml" "fmt" "gopkg.in/mgo.v2/bson" "io/ioutil" ) type Item int type Node struct { Value Item Left *Node Right *Node } type BinaryTree struct { Root *Node } func (bt *BinaryTree) Insert(value Item) { node := &Node{value, nil, nil} if bt.Root == nil { bt.Root = node } else { insertNode(bt.Root, node) } } func insertNode(node, newNode *Node) { if newNode.Value < node.Value { if node.Left == nil { node.Left = newNode } else { insertNode(node.Left, newNode) } } else { if node.Right == nil { node.Right = newNode } else { insertNode(node.Right, newNode) } } } func printTree(node *Node, level int) { if node != nil { format := "" for i := 0; i < level; i++ { format += " " } format += "---[ " level++ printTree(node.Left, level) fmt.Printf(format+"%d\n", node.Value) printTree(node.Right, level) } } func encodeBinaryTreeIntoBSON(bt BinaryTree) ([]byte, error) { bsonOutput, err := bson.Marshal(bt) if err != nil { return bsonOutput, err } else { return bsonOutput, nil } } func encodeBinaryTreeIntoJSON(bt BinaryTree) ([]byte, error) { jsonOutput, err := json.Marshal(bt) if err != nil { return jsonOutput, err } else { return jsonOutput, nil } } func encodeBinaryTreeIntoIndentedJSON(bt BinaryTree) ([]byte, error) { jsonOutput, err := json.MarshalIndent(bt, "", " ") if err != nil { return jsonOutput, err } else { return jsonOutput, nil } } func encodeBinaryTreeIntoXML(bt BinaryTree) ([]byte, error) { xmlOutput, err := xml.Marshal(bt) if err != nil { return xmlOutput, err } else { return xmlOutput, nil } } func encodeBinaryTreeIntoIndentedXML(bt BinaryTree) ([]byte, error) { xmlOutput, err := xml.MarshalIndent(bt, "", " ") if err != nil { return xmlOutput, err } else { return xmlOutput, nil } } func encodeBinaryTreeIntoGob(bt BinaryTree) ([]byte, error) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(bt) if err != nil { return buffer.Bytes(), err } else { return buffer.Bytes(), nil } } func saveBinaryTree(encodedTree []byte, filename string) { err := ioutil.WriteFile(filename, encodedTree, 0644) if err != nil { fmt.Println(err) } else { fmt.Println("Stored into file", filename) } } func constructTree(bt *BinaryTree, min int, max int) { middle := (min + max) / 2 if min < middle && middle < max { fmt.Println(middle) bt.Insert(Item(middle)) constructTree(bt, min, middle) constructTree(bt, middle, max) } } func printBufferInfo(buffer []byte) { fmt.Println("\nBuffer with encoded tree: ", len(buffer)) } func main() { var bt BinaryTree constructTree(&bt, 0, 256) printTree(bt.Root, 0) encodedTree, err := encodeBinaryTreeIntoXML(bt) if err != nil { fmt.Println(err) return } printBufferInfo(encodedTree) saveBinaryTree(encodedTree, "tree1.xml") encodedTree, err = encodeBinaryTreeIntoIndentedXML(bt) if err != nil { fmt.Println(err) return } printBufferInfo(encodedTree) saveBinaryTree(encodedTree, "tree2.xml") encodedTree, err = encodeBinaryTreeIntoJSON(bt) if err != nil { fmt.Println(err) return } printBufferInfo(encodedTree) saveBinaryTree(encodedTree, "tree1.json") encodedTree, err = encodeBinaryTreeIntoIndentedJSON(bt) if err != nil { fmt.Println(err) return } printBufferInfo(encodedTree) saveBinaryTree(encodedTree, "tree2.json") encodedTree, err = encodeBinaryTreeIntoBSON(bt) if err != nil { fmt.Println(err) return } printBufferInfo(encodedTree) saveBinaryTree(encodedTree, "tree1.bson") encodedTree, err = encodeBinaryTreeIntoGob(bt) if err != nil { fmt.Println(err) return } printBufferInfo(encodedTree) saveBinaryTree(encodedTree, "tree1.gob") }
15. Výsledek porovnání, vliv komprimace dat
Nyní se tedy podívejme na slibované výsledky:
Data/formát | JSON | JSON indent. | BSON | XML | XML indent | gob |
---|---|---|---|---|---|---|
[]float64 | 21017 | 26018 | 12895 | 39016 | 40015 | 8960 |
string | 1118 | 1125 | 1123 | 1134 | 1140 | 1145 |
binary tree | 8575 | 42115 | 7406 | 8076 | 31378 | 1431 |
Vidíme, že formát gob je většinou nejúspornější, a to přesto, že nese plné informace o vlastnostech původní datové struktury (což se projevilo v případě řetězce). Přednosti formátu BSON se nejvíce projevily u jednoduché, ale rozsáhlé struktury, tj. u prvního vektoru hodnot typu float64.
A jaký vliv bude mít následná komprimace dat? Použijeme bzip2:
Data/formát | JSON | JSON indent. | BSON | XML | XML indent | gob |
---|---|---|---|---|---|---|
[]float64 | 7161 | 7175 | 7246 | 7226 | 7227 | 6223 |
string | 681 | 683 | 670 | 675 | 693 | 702 |
binary tree | 587 | 684 | 906 | 551 | 721 | 653 |
Zde podle očekávání komprimace nejvíce pomohla textovým formátům, které se prakticky zcela přiblížily formátům binárním (taktéž po komprimaci) a někdy se dokonce dosáhlo ještě menšího objemu dat.
16. Problematika serializace grafových struktur
Ještě se krátce zmiňme o problematice serializace grafových struktur. Nejprve se uvedeme příklad grafu, v němž každý uzel může ukazovat na další dva uzly (jedním z příkladů takto navrženého grafu je binární strom):
type Item int type Node struct { Value Item Left *Node Right *Node }
Dále vytvoříme trojici uzlů, které jsou spojeny hranami tak, že mezi uzlem A a C existují dvě cesty – jedna přímá, druhá přes uzel B:
var a, b, c Node a.Value = 1 b.Value = 2 c.Value = 3 a.Left = &b a.Right = &c b.Left = nil b.Right = &c c.Left = nil c.Right = nil
Tyto původně tři jsou serializovány následujícím způsobem:
{ "Value": 1, "Left": { "Value": 2, "Left": null, "Right": { "Value": 3, "Left": null, "Right": null } }, "Right": { "Value": 3, "Left": null, "Right": null } }
a:
<Node> <Value>1</Value> <Left> <Value>2</Value> <Right> <Value>3</Value> </Right> </Left> <Right> <Value>3</Value> </Right> </Node>
Vidíme tedy, že se uzel číslo 3 © uložil dvakrát! Rekonstrukce původního grafu tedy nebude jednoduchá a formáty JSON, BSON či XML nám to (přímo) neumožní.
17. Graf po deserialiaci
Mohlo by se zdát, že výše zmíněnou grafovou strukturu bude možné korektně uložit na následně obnovit s využitím formátu gob, který dokáže pracovat s ukazateli. Pojďme si tedy tuto domněnku ověřit následujícím demonstračním příkladem:
package main import ( "bytes" "encoding/gob" "fmt" ) type Item int type Node struct { Value Item Left *Node Right *Node } func main() { var a, b, c Node a.Value = 1 b.Value = 2 c.Value = 3 a.Left = &b a.Right = &c b.Left = nil b.Right = &c c.Left = nil c.Right = nil var x Node var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(&a) decoder := gob.NewDecoder(&buffer) err = decoder.Decode(&x) if err != nil { fmt.Println(err) return } fmt.Printf("Node: %p %v\n", &x, x) fmt.Printf("Left: %p %v\n", x.Left, *x.Left) fmt.Printf("Right: %p %v\n", x.Right, *x.Right) fmt.Printf("Left/Right: %p %v\n", x.Left.Right, *x.Left.Right) }
Po spuštění příkladu však získáme informaci o čtyřech uzlech na adresách končících a8a0, ae80, aec0 a aea0, i když by měly existovat jen tři uzly:
Node: 0xc00000a8a0 {1 0xc00000ae80 0xc00000aec0} Left: 0xc00000ae80 {2 <nil> 0xc00000aea0} Right: 0xc00000aec0 {3 <nil> <nil>} Left/Right: 0xc00000aea0 {3 <nil> <nil>}
18. Cyklus v datových strukturách (grafu atd.)
Zdaleka největší problém však nastane ve chvíli, kdy uzel obsahuje cyklu (kružnici):
type Item int type Node struct { Value Item Next *Node } var a, b Node a.Value = 1 b.Value = 2 a.Next = &b b.Next = &a
Tento fakt není detekován (v žádném serializačním formátu!) a proto následující demonstrační příklad skončí po delší době chybou – tato doba bude tím delší, čím více paměti je k dispozici:
package main import ( "bytes" "encoding/gob" "encoding/json" "encoding/xml" "fmt" "gopkg.in/mgo.v2/bson" "io/ioutil" ) type Item int type Node struct { Value Item Next *Node } func encodeStructureIntoBSON(s Node) ([]byte, error) { bsonOutput, err := bson.Marshal(s) if err != nil { return bsonOutput, err } else { return bsonOutput, nil } } func encodeStructureIntoJSON(s Node) ([]byte, error) { jsonOutput, err := json.Marshal(s) if err != nil { return jsonOutput, err } else { return jsonOutput, nil } } func encodeStructureIntoIndentedJSON(s Node) ([]byte, error) { jsonOutput, err := json.MarshalIndent(s, "", " ") if err != nil { return jsonOutput, err } else { return jsonOutput, nil } } func encodeStructureIntoXML(s Node) ([]byte, error) { xmlOutput, err := xml.Marshal(s) if err != nil { return xmlOutput, err } else { return xmlOutput, nil } } func encodeStructureIntoIndentedXML(s Node) ([]byte, error) { xmlOutput, err := xml.MarshalIndent(s, "", " ") if err != nil { return xmlOutput, err } else { return xmlOutput, nil } } func encodeStructureIntoGob(s Node) ([]byte, error) { var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(s) if err != nil { return buffer.Bytes(), err } else { return buffer.Bytes(), nil } } func save(encodedStructure []byte, filename string) { err := ioutil.WriteFile(filename, encodedStructure, 0644) if err != nil { fmt.Println(err) } else { fmt.Println("Stored into file", filename) } } func printBufferInfo(buffer []byte) { fmt.Println("\nBuffer with encoded structure: ", len(buffer)) } func main() { var a, b Node a.Value = 1 b.Value = 2 a.Next = &b b.Next = &a encodedStructure, err := encodeStructureIntoXML(a) if err != nil { fmt.Println(err) return } printBufferInfo(encodedStructure) save(encodedStructure, "structure1.xml") encodedStructure, err = encodeStructureIntoIndentedXML(a) if err != nil { fmt.Println(err) return } printBufferInfo(encodedStructure) save(encodedStructure, "structure2.xml") encodedStructure, err = encodeStructureIntoJSON(a) if err != nil { fmt.Println(err) return } printBufferInfo(encodedStructure) save(encodedStructure, "structure1.json") encodedStructure, err = encodeStructureIntoIndentedJSON(a) if err != nil { fmt.Println(err) return } printBufferInfo(encodedStructure) save(encodedStructure, "structure2.json") encodedStructure, err = encodeStructureIntoBSON(a) if err != nil { fmt.Println(err) return } printBufferInfo(encodedStructure) save(encodedStructure, "structure1.bson") encodedStructure, err = encodeStructureIntoGob(a) if err != nil { fmt.Println(err) return } printBufferInfo(encodedStructure) save(encodedStructure, "structure1.gob") }
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně šest až sedm megabajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:
# | Příklad | Stručný popis | Cesta |
---|---|---|---|
1 | 01_gob_marshal_basic_types.go | rozšířený příklad z minula – serializace základních typů do gob | https://github.com/tisnik/go-root/blob/master/article58/01_gob_marshal_basic_types.go |
2 | 02_gob_unsigned_integers.go | uložení celých čísel bez znaménka do binárního formátu gob | https://github.com/tisnik/go-root/blob/master/article58/02_gob_unsigned_integers.go |
3 | 03_gob_signed_integers.go | uložení celých čísel se znaménkem do binárního formátu gob | https://github.com/tisnik/go-root/blob/master/article58/03_gob_signed_integers.go |
4 | 04_gob_slice_of_integers.go | uložení řezu celých čísel do binárního formátu gob | https://github.com/tisnik/go-root/blob/master/article58/04_gob_slice_of_integers.go |
5 | 05_gob_binary_tree.go | práce s binárním stromem, jeho uložení do formátu gob | https://github.com/tisnik/go-root/blob/master/article58/05_gob_binary_tree.go |
6 | 06_gob_binary_tree2.go | vylepšení předchozího demonstračního příkladu | https://github.com/tisnik/go-root/blob/master/article58/06_gob_binary_tree2.go |
7 | 07_gob_binary_tree_decode.go | serializace a deserializace binárního stromu (položky jsou exportovány) | https://github.com/tisnik/go-root/blob/master/article58/07_gob_binary_tree_decode.go |
8 | 08_json_binary_tree_decode.go | serializace binárního stromu do formátu JSON | https://github.com/tisnik/go-root/blob/master/article58/08_json_binary_tree_decode.go |
9 | 09_bson_binary_tree_decode.go | serializace binárního stromu do formátu BSON | https://github.com/tisnik/go-root/blob/master/article58/09_bson_binary_tree_decode.go |
10 | bson_decode_pprint.py | načtení binárního souboru ve formátu BSON do Pythonu | https://github.com/tisnik/go-root/blob/master/article58/bson_decode_pprint.py |
11 | bson_decode_better_output.py | vylepšená varianta předchozího příkladu | https://github.com/tisnik/go-root/blob/master/article58/bson_decode_better_output.py |
12 | 10_size_comparison_A.go | porovnání velikosti serializovaných dat – vektor obsahující hodnoty s plovoucí řádovou čárkou | https://github.com/tisnik/go-root/blob/master/article58/10_size_comparison_A.go |
13 | 11_size_comparison_B.go | porovnání velikosti serializovaných dat – serializovaná struktura obsahující jediný atribut – dlouhý řetězec | https://github.com/tisnik/go-root/blob/master/article58/11_size_comparison_B.go |
14 | 12_size_comparison_C.go | porovnání velikosti serializovaných dat – serializovaný strom s 255 uzly | https://github.com/tisnik/go-root/blob/master/article58/12_size_comparison_C.go |
15 | 13_graph.go | serializace jednoduchého grafu | https://github.com/tisnik/go-root/blob/master/article58/13_graph.go |
16 | 14_graph_encode_decode.go | serializace grafu s více cestami do jednoho uzlu | https://github.com/tisnik/go-root/blob/master/article58/14_graph_encode_decode.go |
17 | 15_cycle_in_graph.go | serializace grafu s cyklem (kružnicí) | https://github.com/tisnik/go-root/blob/master/article58/15_cycle_in_graph.go |
20. Odkazy na Internetu
- Go Data Structures: Binary Search Tree
https://flaviocopes.com/golang-data-structure-binary-search-tree/ - Gobs of data
https://blog.golang.org/gobs-of-data - Formát BSON
http://bsonspec.org/ - Golang Guide: A List of Top Golang Frameworks, IDEs & Tools
https://blog.intelligentbee.com/2017/08/14/golang-guide-list-top-golang-frameworks-ides-tools/ - Tvorba univerzálních projevů
http://www.kyblsoft.cz/projevy - Repositář projektu Gift
https://github.com/disintegration/gift - Dokumentace k projektu Gift
https://godoc.org/github.com/disintegration/gift - Online x86 / x64 Assembler and Disassembler
https://defuse.ca/online-x86-assembler.htm#disassembly2 - The Design of the Go Assembler
https://talks.golang.org/2016/asm.slide#1 - A Quick Guide to Go's Assembler
https://golang.org/doc/asm - AssemblyPolicy
https://github.com/golang/go/wiki/AssemblyPolicy - Geohash in Golang Assembly
https://mmcloughlin.com/posts/geohash-assembly - Command objdump
https://golang.org/cmd/objdump/ - Assembly
https://goroutines.com/asm - Go & Assembly
http://www.doxsey.net/blog/go-and-assembly - A Foray Into Go Assembly Programming
https://blog.sgmansfield.com/2017/04/a-foray-into-go-assembly-programming/ - Golang Capturing log.Println And fmt.Println Output
https://medium.com/@hau12a1/golang-capturing-log-println-and-fmt-println-output-770209c791b4 - Stránka projektu plotly
https://plot.ly/ - Plotly JavaScript Open Source Graphing Library
https://plot.ly/javascript/ - Domain coloring
https://en.wikipedia.org/wiki/Domain_coloring - Michael Fogleman's projects
https://www.michaelfogleman.com/projects/tagged/graphics/ - Color Graphs of Complex Functions
https://web.archive.org/web/20120511021419/http://w.american.edu/cas/mathstat/lcrone/ComplexPlot.html - A Gallery of Complex Functions
http://wismuth.com/complex/gallery.html - package glot
https://godoc.org/github.com/Arafatk/glot - Gnuplotting: Output terminals
http://www.gnuplotting.org/output-terminals/ - Introducing Glot the plotting library for Golang
https://medium.com/@Arafat./introducing-glot-the-plotting-library-for-golang-3133399948a1 - Introducing Glot the plotting library for Golang
https://blog.gopheracademy.com/advent-2018/introducing-glot/ - Glot is a plotting library for Golang built on top of gnuplot
https://github.com/Arafatk/glot - Example plots (gonum/plot)
https://github.com/gonum/plot/wiki/Example-plots - A repository for plotting and visualizing data (gonum/plot)
https://github.com/gonum/plot - golang library to make https://chartjs.org/ plots
https://github.com/brentp/go-chartjs - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - The Gonum Numerical Computing Package
https://www.gonum.org/post/introtogonum/ - Gomacro na GitHubu
https://github.com/cosmos72/gomacro - gophernotes – Use Go in Jupyter notebooks and nteract
https://github.com/gopherdata/gophernotes - gonum
https://github.com/gonum - go-gota/gota – DataFrames and data wrangling in Go (Golang)
https://porter.io/github.com/go-gota/gota - A repository for plotting and visualizing data
https://github.com/gonum/plot - Gonum Numerical Packages
https://www.gonum.org/ - Stránky projektu MinIO
https://min.io/ - MinIO Quickstart Guide
https://docs.min.io/docs/minio-quickstart-guide.html - MinIO Go Client API Reference
https://docs.min.io/docs/golang-client-api-reference - MinIO Python Client API Reference
https://docs.min.io/docs/python-client-api-reference.html - Performance at Scale: MinIO Pushes Past 1.4 terabits per second with 256 NVMe Drives
https://blog.min.io/performance-at-scale-minio-pushes-past-1–3-terabits-per-second-with-256-nvme-drives/ - Benchmarking MinIO vs. AWS S3 for Apache Spark
https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/ - MinIO Client Quickstart Guide
https://docs.min.io/docs/minio-client-quickstart-guide.html - Analýza kvality zdrojových kódů Minia
https://goreportcard.com/report/github.com/minio/minio - This is MinIO
https://www.youtube.com/watch?v=vF0lQh0XOCs - Running MinIO Standalone
https://www.youtube.com/watch?v=dIQsPCHvHoM - „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
https://www.youtube.com/watch?v=wlpn8K0jJ4U - Ginkgo
http://onsi.github.io/ginkgo/ - Gomega
https://onsi.github.io/gomega/ - Ginkgo's Preferred Matcher Library na GitHubu
https://github.com/onsi/gomega/ - Provided Matchers
http://onsi.github.io/gomega/#provided-matchers - Dokumentace k balíčku goexpect
https://godoc.org/github.com/google/goexpect - Balíček goexpect
https://github.com/google/goexpect - Balíček go-expect
https://github.com/Netflix/go-expect - Balíček gexpect
https://github.com/ThomasRooney/gexpect - Expect (originál naprogramovaný v TCL)
https://core.tcl-lang.org/expect/index - Expect (Wikipedia)
https://en.wikipedia.org/wiki/Expect - Pexpect
https://pexpect.readthedocs.io/en/stable/ - Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
http://networkbit.ch/golang-ssh-client/ - goblin na GitHubu
https://github.com/franela/goblin - Mocha framework
https://mochajs.org/ - frisby na GitHubu
https://github.com/verdverm/frisby - package frisby
https://godoc.org/github.com/verdverm/frisby - Frisby alternatives and similar packages (generováno)
https://go.libhunt.com/frisby-alternatives - Cucumber for golang
https://github.com/DATA-DOG/godog - How to Use Godog for Behavior-driven Development in Go
https://semaphoreci.com/community/tutorials/how-to-use-godog-for-behavior-driven-development-in-go - Comparative Analysis Of GoLang Testing Frameworks
https://www.slideshare.net/DushyantBhalgami/comparative-analysis-of-golang-testing-frameworks - A Quick Guide to Testing in Golang
https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/ - Tom's Obvious, Minimal Language.
https://github.com/toml-lang/toml - xml.org
http://www.xml.org/ - Soubory .properties
https://en.wikipedia.org/wiki/.properties - Soubory INI
https://en.wikipedia.org/wiki/INI_file - JSON to YAML
https://www.json2yaml.com/ - Data Format Converter
https://toolkit.site/format.html - Viper na GitHubu
https://github.com/spf13/viper - GoDotEnv na GitHubu
https://github.com/joho/godotenv - The fantastic ORM library for Golang
http://gorm.io/ - Dokumentace k balíčku gorilla/mux
https://godoc.org/github.com/gorilla/mux - Gorilla web toolkitk
http://www.gorillatoolkit.org/ - Metric types
https://prometheus.io/docs/concepts/metric_types/ - Histograms with Prometheus: A Tale of Woe
http://linuxczar.net/blog/2017/06/15/prometheus-histogram-2/ - Why are Prometheus histograms cumulative?
https://www.robustperception.io/why-are-prometheus-histograms-cumulative - Histograms and summaries
https://prometheus.io/docs/practices/histograms/ - Instrumenting Golang server in 5 min
https://medium.com/@gsisimogang/instrumenting-golang-server-in-5-min-c1c32489add3 - Semantic Import Versioning in Go
https://www.aaronzhuo.com/semantic-import-versioning-in-go/ - Sémantické verzování
https://semver.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go Lang: Memory Management and Garbage Collection
https://vikash1976.wordpress.com/2017/03/26/go-lang-memory-management-and-garbage-collection/ - Golang Internals, Part 4: Object Files and Function Metadata
https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html - What is REPL?
https://pythonprogramminglanguage.com/repl/ - What is a REPL?
https://codewith.mu/en/tutorials/1.0/repl - Programming at the REPL: Introduction
https://clojure.org/guides/repl/introduction - What is REPL? (Quora)
https://www.quora.com/What-is-REPL - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Read-eval-print loop (Wikipedia)
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - Vim as a Go (Golang) IDE using LSP and vim-go
https://octetz.com/posts/vim-as-go-ide - gopls
https://github.com/golang/go/wiki/gopls - IDE Integration Guide
https://github.com/stamblerre/gocode/blob/master/docs/IDE_integration.md - How to instrument Go code with custom expvar metrics
https://sysdig.com/blog/golang-expvar-custom-metrics/ - Golang expvar metricset (Metricbeat Reference)
https://www.elastic.co/guide/en/beats/metricbeat/7.x/metricbeat-metricset-golang-expvar.html - Package expvar
https://golang.org/pkg/expvar/#NewInt - Java Platform Debugger Architecture: Overview
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html - The JVM Tool Interface (JVM TI): How VM Agents Work
https://www.oracle.com/technetwork/articles/javase/index-140680.html - JVM Tool Interface Version 11.0
https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - Go & cgo: integrating existing C code with Go
http://akrennmair.github.io/golang-cgo-slides/#1 - Using cgo to call C code from within Go code
https://wenzr.wordpress.com/2018/06/07/using-cgo-to-call-c-code-from-within-go-code/ - Package trace
https://golang.org/pkg/runtime/trace/ - Introducing HTTP Tracing
https://blog.golang.org/http-tracing - Command trace
https://golang.org/cmd/trace/ - A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
https://github.com/wesovilabs/koazee - Funkce vyššího řádu v knihovně Underscore
https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/ - Delve: a debugger for the Go programming language.
https://github.com/go-delve/delve - Příkazy debuggeru Delve
https://github.com/go-delve/delve/tree/master/Documentation/cli - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Debugging Go Code with GDB
https://golang.org/doc/gdb - Debugging Go (golang) programs with gdb
https://thornydev.blogspot.com/2014/01/debugging-go-golang-programs-with-gdb.html - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - Go is on a Trajectory to Become the Next Enterprise Programming Language
https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e - Go Proverbs: Simple, Poetic, Pithy
https://go-proverbs.github.io/ - Handling Sparse Files on Linux
https://www.systutorials.com/136652/handling-sparse-files-on-linux/ - Gzip (Wikipedia)
https://en.wikipedia.org/wiki/Gzip - Deflate
https://en.wikipedia.org/wiki/DEFLATE - 10 tools written in Go that every developer needs to know
https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/ - Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
https://www.root.cz/clanky/hexadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/ - Hex dump
https://en.wikipedia.org/wiki/Hex_dump - Rozhraní io.ByteReader
https://golang.org/pkg/io/#ByteReader - Rozhraní io.RuneReader
https://golang.org/pkg/io/#RuneReader - Rozhraní io.ByteScanner
https://golang.org/pkg/io/#ByteScanner - Rozhraní io.RuneScanner
https://golang.org/pkg/io/#RuneScanner - Rozhraní io.Closer
https://golang.org/pkg/io/#Closer - Rozhraní io.Reader
https://golang.org/pkg/io/#Reader - Rozhraní io.Writer
https://golang.org/pkg/io/#Writer - Typ Strings.Reader
https://golang.org/pkg/strings/#Reader - VACUUM (SQL)
https://www.sqlite.org/lang_vacuum.html - VACUUM (Postgres)
https://www.postgresql.org/docs/8.4/sql-vacuum.html - go-cron
https://github.com/rk/go-cron - gocron
https://github.com/jasonlvhit/gocron - clockwork
https://github.com/whiteShtef/clockwork - clockwerk
https://github.com/onatm/clockwerk - JobRunner
https://github.com/bamzi/jobrunner - Rethinking Cron
https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/ - In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 5 simple tips and tricks for writing unit tests in #golang
https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - How the Go runtime implements maps efficiently (without generics)
https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/ - 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Goroutine IDs
https://blog.sgmansfield.com/2015/12/goroutine-ids/ - Different ways to pass channels as arguments in function in go (golang)
https://stackoverflow.com/questions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang - justforfunc #22: using the Go execution tracer
https://www.youtube.com/watch?v=ySy3sR1LFCQ - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation - The zero value of a slice is not nil
https://stackoverflow.com/questions/30806931/the-zero-value-of-a-slice-is-not-nil - Go-tcha: When nil != nil
https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic - Nils in Go
https://www.doxsey.net/blog/nils-in-go