Hlavní navigace

Serializace a deserializace datových struktur v programovacím jazyce Go (2.část)

Pavel Tišnovský

Popisem problematiky serializace datových struktur se budeme zabývat i dnes. Mj. si ukážeme, jakým způsobem lze BSON zapsaný v Go přečíst v Pythonu a jakou katastrofou mohou být při serializaci grafy obsahující cyklus.

Doba čtení: 56 minut

Sdílet

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

17. Graf po deserialiaci

18. Cyklus v datových strukturách (grafu atd.)

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

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).

Poznámka: při serializaci složitějších datových struktur, například grafů, může docházet k některých neočekávaným situacím. I na ty v dnešním článku upozorníme.

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
Poznámka: v nejhorším případě je použito dvanáct bajtů, protože musíme započítat celkovou délku (jeden bajt), typ (druhý bajt), index v rámci datové pseudostruktury (třetí bajt), negovanou šířku čísla (čtvrtý bajt) a následně osm bajtů s vlastní hodnotou.

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
Poznámka: povšimněte si, jak jsou záporná čísla ukládána – poslední bit vždy obsahuje znaménko.

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.

Poznámka: přeneseny jsou i nulové hodnoty, na rozdíl od situace, kdy se jedná o nulovou hodnotu primitivního datového typu.

Ú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)
                }
        }
}
Poznámka: povšimněte si, jak je implementace v Go jednoduchá například v porovnání s céčkovou implementací – jednoduchost je zajištěna automatickým správcem paměti, kdy se nemusíme starat o alokaci a dealokaci celé struktury binárního stromu.

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:

  1. Je nutné zajistit větší rychlost serializace a/nebo deserializace
  2. 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:

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
}
Poznámka: musíme použít datovou strukturu, protože formát BSON nedokáže serializovat jediný (samotný) řetězec.

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>}
Poznámka: i zde tedy došlo k duplikaci uzlu číslo 3 ©.

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):

Root obecný tip

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_mar­shal_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_un­signed_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_sig­ned_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_sli­ce_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_bi­nary_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_bi­nary_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_bi­nary_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_bi­nary_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_bi­nary_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_de­code_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_de­code_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_si­ze_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_si­ze_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_si­ze_comparison_C.go
       
15 13_graph.go serializace jednoduchého grafu https://github.com/tisnik/go-root/blob/master/article58/13_grap­h.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_grap­h_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_cy­cle_in_graph.go

20. Odkazy na Internetu

  1. Go Data Structures: Binary Search Tree
    https://flaviocopes.com/golang-data-structure-binary-search-tree/
  2. Gobs of data
    https://blog.golang.org/gobs-of-data
  3. Formát BSON
    http://bsonspec.org/
  4. Golang Guide: A List of Top Golang Frameworks, IDEs & Tools
    https://blog.intelligentbe­e.com/2017/08/14/golang-guide-list-top-golang-frameworks-ides-tools/
  5. Tvorba univerzálních projevů
    http://www.kyblsoft.cz/projevy
  6. Repositář projektu Gift
    https://github.com/disinte­gration/gift
  7. Dokumentace k projektu Gift
    https://godoc.org/github.com/di­sintegration/gift
  8. Online x86 / x64 Assembler and Disassembler
    https://defuse.ca/online-x86-assembler.htm#disassembly2
  9. The Design of the Go Assembler
    https://talks.golang.org/2016/as­m.slide#1
  10. A Quick Guide to Go's Assembler
    https://golang.org/doc/asm
  11. AssemblyPolicy
    https://github.com/golang/go/wi­ki/AssemblyPolicy
  12. Geohash in Golang Assembly
    https://mmcloughlin.com/posts/geohash-assembly
  13. Command objdump
    https://golang.org/cmd/objdump/
  14. Assembly
    https://goroutines.com/asm
  15. Go & Assembly
    http://www.doxsey.net/blog/go-and-assembly
  16. A Foray Into Go Assembly Programming
    https://blog.sgmansfield.com/2017/04/a-foray-into-go-assembly-programming/
  17. Golang Capturing log.Println And fmt.Println Output
    https://medium.com/@hau12a1/golang-capturing-log-println-and-fmt-println-output-770209c791b4
  18. Stránka projektu plotly
    https://plot.ly/
  19. Plotly JavaScript Open Source Graphing Library
    https://plot.ly/javascript/
  20. Domain coloring
    https://en.wikipedia.org/wi­ki/Domain_coloring
  21. Michael Fogleman's projects
    https://www.michaelfogleman­.com/projects/tagged/grap­hics/
  22. Color Graphs of Complex Functions
    https://web.archive.org/web/20120511021419/htt­p://w.american.edu/cas/mat­hstat/lcrone/ComplexPlot.html
  23. A Gallery of Complex Functions
    http://wismuth.com/complex/ga­llery.html
  24. package glot
    https://godoc.org/github.com/A­rafatk/glot
  25. Gnuplotting: Output terminals
    http://www.gnuplotting.org/output-terminals/
  26. Introducing Glot the plotting library for Golang
    https://medium.com/@Arafat­./introducing-glot-the-plotting-library-for-golang-3133399948a1
  27. Introducing Glot the plotting library for Golang
    https://blog.gopheracademy.com/advent-2018/introducing-glot/
  28. Glot is a plotting library for Golang built on top of gnuplot
    https://github.com/Arafatk/glot
  29. Example plots (gonum/plot)
    https://github.com/gonum/plot/wi­ki/Example-plots
  30. A repository for plotting and visualizing data (gonum/plot)
    https://github.com/gonum/plot
  31. golang library to make https://chartjs.org/ plots
    https://github.com/brentp/go-chartjs
  32. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  33. The Gonum Numerical Computing Package
    https://www.gonum.org/pos­t/introtogonum/
  34. Gomacro na GitHubu
    https://github.com/cosmos72/gomacro
  35. gophernotes – Use Go in Jupyter notebooks and nteract
    https://github.com/gopher­data/gophernotes
  36. gonum
    https://github.com/gonum
  37. go-gota/gota – DataFrames and data wrangling in Go (Golang)
    https://porter.io/github.com/go-gota/gota
  38. A repository for plotting and visualizing data
    https://github.com/gonum/plot
  39. Gonum Numerical Packages
    https://www.gonum.org/
  40. Stránky projektu MinIO
    https://min.io/
  41. MinIO Quickstart Guide
    https://docs.min.io/docs/minio-quickstart-guide.html
  42. MinIO Go Client API Reference
    https://docs.min.io/docs/golang-client-api-reference
  43. MinIO Python Client API Reference
    https://docs.min.io/docs/python-client-api-reference.html
  44. 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/
  45. Benchmarking MinIO vs. AWS S3 for Apache Spark
    https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/
  46. MinIO Client Quickstart Guide
    https://docs.min.io/docs/minio-client-quickstart-guide.html
  47. Analýza kvality zdrojových kódů Minia
    https://goreportcard.com/re­port/github.com/minio/minio
  48. This is MinIO
    https://www.youtube.com/wat­ch?v=vF0lQh0XOCs
  49. Running MinIO Standalone
    https://www.youtube.com/wat­ch?v=dIQsPCHvHoM
  50. „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
    https://www.youtube.com/wat­ch?v=wlpn8K0jJ4U
  51. Ginkgo
    http://onsi.github.io/ginkgo/
  52. Gomega
    https://onsi.github.io/gomega/
  53. Ginkgo's Preferred Matcher Library na GitHubu
    https://github.com/onsi/gomega/
  54. Provided Matchers
    http://onsi.github.io/gomega/#provided-matchers
  55. Dokumentace k balíčku goexpect
    https://godoc.org/github.com/go­ogle/goexpect
  56. Balíček goexpect
    https://github.com/google/goexpect
  57. Balíček go-expect
    https://github.com/Netflix/go-expect
  58. Balíček gexpect
    https://github.com/Thomas­Rooney/gexpect
  59. Expect (originál naprogramovaný v TCL)
    https://core.tcl-lang.org/expect/index
  60. Expect (Wikipedia)
    https://en.wikipedia.org/wiki/Expect
  61. Pexpect
    https://pexpect.readthedoc­s.io/en/stable/
  62. Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
    http://networkbit.ch/golang-ssh-client/
  63. goblin na GitHubu
    https://github.com/franela/goblin
  64. Mocha framework
    https://mochajs.org/
  65. frisby na GitHubu
    https://github.com/verdverm/frisby
  66. package frisby
    https://godoc.org/github.com/ver­dverm/frisby
  67. Frisby alternatives and similar packages (generováno)
    https://go.libhunt.com/frisby-alternatives
  68. Cucumber for golang
    https://github.com/DATA-DOG/godog
  69. How to Use Godog for Behavior-driven Development in Go
    https://semaphoreci.com/com­munity/tutorials/how-to-use-godog-for-behavior-driven-development-in-go
  70. Comparative Analysis Of GoLang Testing Frameworks
    https://www.slideshare.net/Dushy­antBhalgami/comparative-analysis-of-golang-testing-frameworks
  71. A Quick Guide to Testing in Golang
    https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/
  72. Tom's Obvious, Minimal Language.
    https://github.com/toml-lang/toml
  73. xml.org
    http://www.xml.org/
  74. Soubory .properties
    https://en.wikipedia.org/wi­ki/.properties
  75. Soubory INI
    https://en.wikipedia.org/wi­ki/INI_file
  76. JSON to YAML
    https://www.json2yaml.com/
  77. Data Format Converter
    https://toolkit.site/format.html
  78. Viper na GitHubu
    https://github.com/spf13/viper
  79. GoDotEnv na GitHubu
    https://github.com/joho/godotenv
  80. The fantastic ORM library for Golang
    http://gorm.io/
  81. Dokumentace k balíčku gorilla/mux
    https://godoc.org/github.com/go­rilla/mux
  82. Gorilla web toolkitk
    http://www.gorillatoolkit.org/
  83. Metric types
    https://prometheus.io/doc­s/concepts/metric_types/
  84. Histograms with Prometheus: A Tale of Woe
    http://linuxczar.net/blog/2017/06/15/pro­metheus-histogram-2/
  85. Why are Prometheus histograms cumulative?
    https://www.robustperception.io/why-are-prometheus-histograms-cumulative
  86. Histograms and summaries
    https://prometheus.io/doc­s/practices/histograms/
  87. Instrumenting Golang server in 5 min
    https://medium.com/@gsisi­mogang/instrumenting-golang-server-in-5-min-c1c32489add3
  88. Semantic Import Versioning in Go
    https://www.aaronzhuo.com/semantic-import-versioning-in-go/
  89. Sémantické verzování
    https://semver.org/
  90. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  91. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  92. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  93. Modules
    https://github.com/golang/go/wi­ki/Modules
  94. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  95. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  96. Go Lang: Memory Management and Garbage Collection
    https://vikash1976.wordpres­s.com/2017/03/26/go-lang-memory-management-and-garbage-collection/
  97. Golang Internals, Part 4: Object Files and Function Metadata
    https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html
  98. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  99. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  100. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  101. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  102. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  103. Read-eval-print loop (Wikipedia)
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  104. Vim as a Go (Golang) IDE using LSP and vim-go
    https://octetz.com/posts/vim-as-go-ide
  105. gopls
    https://github.com/golang/go/wi­ki/gopls
  106. IDE Integration Guide
    https://github.com/stamble­rre/gocode/blob/master/doc­s/IDE_integration.md
  107. How to instrument Go code with custom expvar metrics
    https://sysdig.com/blog/golang-expvar-custom-metrics/
  108. Golang expvar metricset (Metricbeat Reference)
    https://www.elastic.co/gu­ide/en/beats/metricbeat/7­.x/metricbeat-metricset-golang-expvar.html
  109. Package expvar
    https://golang.org/pkg/expvar/#NewInt
  110. Java Platform Debugger Architecture: Overview
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jpda/jpda­.html
  111. The JVM Tool Interface (JVM TI): How VM Agents Work
    https://www.oracle.com/technet­work/articles/javase/index-140680.html
  112. JVM Tool Interface Version 11.0
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jvmti­.html
  113. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  114. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  115. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  116. Go & cgo: integrating existing C code with Go
    http://akrennmair.github.io/golang-cgo-slides/#1
  117. Using cgo to call C code from within Go code
    https://wenzr.wordpress.com/2018/06/07/u­sing-cgo-to-call-c-code-from-within-go-code/
  118. Package trace
    https://golang.org/pkg/runtime/trace/
  119. Introducing HTTP Tracing
    https://blog.golang.org/http-tracing
  120. Command trace
    https://golang.org/cmd/trace/
  121. A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
    https://github.com/wesovilabs/koazee
  122. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  123. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  124. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  125. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  126. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  127. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  128. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  129. 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/
  130. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  131. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  132. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  133. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  134. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  135. The LLDB Debugger
    http://lldb.llvm.org/
  136. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  137. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  138. 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
  139. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  140. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  141. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  142. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  143. 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/
  144. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  145. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  146. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  147. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  148. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  149. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  150. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  151. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  152. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  153. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  154. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  155. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  156. go-cron
    https://github.com/rk/go-cron
  157. gocron
    https://github.com/jasonlvhit/gocron
  158. clockwork
    https://github.com/whiteShtef/cloc­kwork
  159. clockwerk
    https://github.com/onatm/clockwerk
  160. JobRunner
    https://github.com/bamzi/jobrunner
  161. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  162. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  163. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  164. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  165. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  166. go-prompt
    https://github.com/c-bata/go-prompt
  167. readline
    https://github.com/chzyer/readline
  168. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  169. go-readline
    https://github.com/fiorix/go-readline
  170. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  171. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  172. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  173. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  174. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  175. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  176. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  177. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  178. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  179. Editline Library (libedit)
    http://thrysoee.dk/editline/
  180. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  181. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  182. WinEditLine
    http://mingweditline.sourceforge.net/
  183. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  184. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  185. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  186. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  187. history(3) – Linux man page
    https://linux.die.net/man/3/history
  188. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  189. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  190. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  191. Balíček ogletest
    https://github.com/jacobsa/ogletest
  192. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  193. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  194. package testing
    https://golang.org/pkg/testing/
  195. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  196. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  197. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  198. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  199. GoConvey
    http://goconvey.co/
  200. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  201. 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
  202. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  203. package gg
    https://godoc.org/github.com/fo­gleman/gg
  204. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  205. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  206. The Go image package
    https://blog.golang.org/go-image-package
  207. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  208. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  209. YAML
    https://yaml.org/
  210. edn
    https://github.com/edn-format/edn
  211. Smile
    https://github.com/FasterXML/smile-format-specification
  212. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  213. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  214. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  215. Introducing JSON
    http://json.org/
  216. Package json
    https://golang.org/pkg/encoding/json/
  217. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  218. Go by Example: JSON
    https://gobyexample.com/json
  219. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  220. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  221. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  222. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  223. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  224. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  225. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  226. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  227. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  228. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  229. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  230. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  231. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  232. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  233. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  234. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  235. Algorithms to Go
    https://yourbasic.org/
  236. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  237. 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/
  238. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  239. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  240. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  241. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  242. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  243. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  244. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  245. The Go Programming Language (home page)
    https://golang.org/
  246. GoDoc
    https://godoc.org/
  247. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  248. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  249. The Go Programming Language Specification
    https://golang.org/ref/spec
  250. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  251. Package builtin
    https://golang.org/pkg/builtin/
  252. Package fmt
    https://golang.org/pkg/fmt/
  253. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  254. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  255. Learning Go
    https://www.miek.nl/go/
  256. Go Bootcamp
    http://www.golangbootcamp.com/
  257. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  258. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  259. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  260. The Go Blog
    https://blog.golang.org/
  261. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  262. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  263. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  264. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  265. 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
  266. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  267. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  268. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  269. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  270. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  271. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  272. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  273. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  274. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  275. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  276. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  277. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  278. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  279. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  280. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  281. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  282. 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/
  283. 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
  284. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  285. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  286. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  287. Go vs. Python
    https://www.peterbe.com/plog/govspy
  288. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  289. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  290. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  291. Go by Example: Slices
    https://gobyexample.com/slices
  292. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  293. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  294. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  295. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  296. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  297. nils In Go
    https://go101.org/article/nil.html
  298. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  299. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  300. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  301. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  302. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  303. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  304. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  305. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  306. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  307. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  308. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  309. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  310. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  311. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  312. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  313. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  314. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  315. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  316. 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
  317. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  318. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  319. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  320. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  321. Selectors
    https://golang.org/ref/spec#Selectors
  322. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  323. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  324. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  325. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  326. Part 21: Goroutines
    https://golangbot.com/goroutines/
  327. Part 22: Channels
    https://golangbot.com/channels/
  328. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  329. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  330. 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/
  331. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  332. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  333. Control Structures
    https://www.golang-book.com/books/intro/5
  334. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  335. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  336. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  337. Goroutine IDs
    https://blog.sgmansfield.com/2015/12/go­routine-ids/
  338. Different ways to pass channels as arguments in function in go (golang)
    https://stackoverflow.com/qu­estions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang
  339. justforfunc #22: using the Go execution tracer
    https://www.youtube.com/wat­ch?v=ySy3sR1LFCQ
  340. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  341. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  342. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  343. Effective Go
    https://golang.org/doc/ef­fective_go.html
  344. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  345. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation
  346. The zero value of a slice is not nil
    https://stackoverflow.com/qu­estions/30806931/the-zero-value-of-a-slice-is-not-nil
  347. Go-tcha: When nil != nil
    https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic
  348. Nils in Go
    https://www.doxsey.net/blog/nils-in-go