forked from aphistic/golf
/
chunker.go
89 lines (73 loc) · 1.65 KB
/
chunker.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
package golf
import (
"errors"
"io"
"math"
"github.com/google/uuid"
)
type chunker struct {
chunkSize int
buff []byte
w io.Writer
}
func newChunker(w io.Writer, chunkSize int) (*chunker, error) {
if chunkSize < 13 {
return nil, ErrChunkTooSmall
}
c := &chunker{
chunkSize: chunkSize,
buff: make([]byte, 0),
w: w,
}
return c, nil
}
func (c *chunker) reset() {
c.buff = make([]byte, 0)
}
func (c *chunker) Write(p []byte) (int, error) {
c.buff = append(c.buff, p...)
return len(p), nil
}
func (c *chunker) Flush() error {
idFull, err := uuid.NewRandom()
if err != nil {
return err
}
idBytes, err := idFull.MarshalBinary()
if err != nil {
return err
}
err = c.flushWithId(idBytes[0:8])
return err
}
func (c *chunker) flushWithId(id []byte) error {
if len(id) < 8 || len(id) > 8 {
return errors.New("id length must be equal to 8")
}
offset := 0
buffLen := len(c.buff)
chunkSize := c.chunkSize - 12
// Reusing this buffer may cause problems with duplicate data being sent
// if the data isn't written to something else by the io.Writer before
// the chunk's data is updated.
chunkBuff := make([]byte, c.chunkSize)
copy(chunkBuff[0:2], []byte{0x1e, 0x0f})
copy(chunkBuff[2:10], id)
totalChunks := int(math.Ceil(float64(buffLen) / float64(chunkSize)))
chunkBuff[11] = byte(totalChunks)
for {
left := buffLen - offset
if left > chunkSize {
copy(chunkBuff[12:], c.buff[offset:offset+chunkSize])
c.w.Write(chunkBuff)
} else {
copy(chunkBuff[12:], c.buff[offset:offset+left])
c.w.Write(chunkBuff[0 : left+12])
break
}
offset += chunkSize
chunkBuff[10] += 1
}
c.reset()
return nil
}