forked from divan/txqr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
decode.go
155 lines (131 loc) · 3.44 KB
/
decode.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package txqr
import (
"fmt"
"math/rand"
"strings"
fountain "github.com/google/gofountain"
)
// Decoder represents protocol decode.
type Decoder struct {
chunkLen int
codec fountain.Codec
fd fountain.Decoder
completed bool
total int
cache map[string]struct{}
}
// NewDecoder creates and inits a new decoder.
func NewDecoder() *Decoder {
return &Decoder{
cache: make(map[string]struct{}),
}
}
// NewDecoderSize creates and inits a new decoder for the known size.
func NewDecoderSize(size, chunkLen int) *Decoder {
numChunks := numberOfChunks(size, chunkLen)
codec := fountain.NewLubyCodec(numChunks, rand.New(fountain.NewMersenneTwister(200)), solitonDistribution(numChunks))
return &Decoder{
codec: codec,
fd: codec.NewDecoder(size),
total: size,
chunkLen: chunkLen,
cache: make(map[string]struct{}),
}
}
// Decode takes a single chunk of data and decodes it.
// Chunk expected to be validated (see Validate) before.
func (d *Decoder) Decode(chunk string) error {
idx := strings.IndexByte(chunk, '|') // expected to be validated before
if idx == -1 {
return fmt.Errorf("invalid frame: \"%s\"", chunk)
}
header := chunk[:idx]
// continuous QR reading often sends the same chunk in a row, skip it
if d.isCached(header) {
return nil
}
var (
blockCode int64
chunkLen, total int
)
_, err := fmt.Sscanf(header, "%d/%d/%d", &blockCode, &chunkLen, &total)
if err != nil {
return fmt.Errorf("invalid header: %v (%s)", err, header)
}
payload := chunk[idx+1:]
lubyBlock := fountain.LTBlock{
BlockCode: blockCode,
Data: []byte(payload),
}
if d.fd == nil {
d.total = total
d.chunkLen = chunkLen
numChunks := numberOfChunks(d.total, d.chunkLen)
d.codec = fountain.NewLubyCodec(numChunks, rand.New(fountain.NewMersenneTwister(200)), solitonDistribution(numChunks))
d.fd = d.codec.NewDecoder(total)
}
d.completed = d.fd.AddBlocks([]fountain.LTBlock{lubyBlock})
return nil
}
// Validate checks if a given chunk of data is a valid txqr protocol packet.
func (d *Decoder) Validate(chunk string) error {
if chunk == "" || len(chunk) < 4 {
return fmt.Errorf("invalid frame: \"%s\"", chunk)
}
idx := strings.IndexByte(chunk, '|')
if idx == -1 {
return fmt.Errorf("invalid frame: \"%s\"", chunk)
}
return nil
}
// Data returns decoded data.
func (d *Decoder) Data() string {
return string(d.DataBytes())
}
// DataBytes returns decoded data as a byte slice.
func (d *Decoder) DataBytes() []byte {
if d.fd == nil {
return []byte{}
}
if !d.completed {
return []byte{}
}
return d.fd.Decode()
}
// Length returns length of the decoded data.
// TODO: remove
func (d *Decoder) Length() int {
return 0
}
// Read returns amount of currently read bytes.
// TODO: remove
func (d *Decoder) Read() int {
return 0
}
// Total returns total amount of data.
func (d *Decoder) Total() int {
return d.total
}
// IsCompleted reports whether the read was completed successfully or not.
func (d *Decoder) IsCompleted() bool {
return d.completed
}
// Reset resets decoder, preparing it for the next run.
func (d *Decoder) Reset() {
d.fd = nil
d.completed = false
d.chunkLen = 0
d.total = 0
d.cache = map[string]struct{}{}
d.codec = nil
}
// isCached takes the header of chunk data and see if it's been cached.
// If not, it caches it.
func (d *Decoder) isCached(header string) bool {
if _, ok := d.cache[header]; ok {
return true
}
// cache it
d.cache[header] = struct{}{}
return false
}