2017-04-15 17:07:32 +02:00
|
|
|
package chunking
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-04-26 20:25:53 +02:00
|
|
|
"encoding/binary"
|
|
|
|
"io"
|
2017-04-15 17:07:32 +02:00
|
|
|
)
|
|
|
|
|
2017-04-26 20:25:53 +02:00
|
|
|
var ChunkBufSize uint32 = 32 * 1024
|
2017-04-15 17:07:32 +02:00
|
|
|
var ChunkHeaderByteOrder = binary.LittleEndian
|
|
|
|
|
|
|
|
type Unchunker struct {
|
2017-04-26 20:25:53 +02:00
|
|
|
ChunkCount int
|
|
|
|
in io.Reader
|
2017-04-15 17:07:32 +02:00
|
|
|
remainingChunkBytes uint32
|
|
|
|
}
|
|
|
|
|
2017-04-16 21:38:31 +02:00
|
|
|
func NewUnchunker(conn io.Reader) *Unchunker {
|
|
|
|
return &Unchunker{
|
2017-04-26 20:25:53 +02:00
|
|
|
in: conn,
|
|
|
|
remainingChunkBytes: 0,
|
2017-04-15 17:07:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Unchunker) Read(b []byte) (n int, err error) {
|
|
|
|
|
|
|
|
if c.remainingChunkBytes == 0 {
|
|
|
|
|
|
|
|
var nextChunkLen uint32
|
|
|
|
err = binary.Read(c.in, ChunkHeaderByteOrder, &nextChunkLen)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// A chunk of len 0 indicates end of stream
|
|
|
|
if nextChunkLen == 0 {
|
|
|
|
return 0, io.EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
c.remainingChunkBytes = nextChunkLen
|
|
|
|
c.ChunkCount++
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
maxRead := min(int(c.remainingChunkBytes), len(b))
|
|
|
|
if maxRead < 0 {
|
|
|
|
panic("Cannot read negative amount of bytes")
|
|
|
|
}
|
|
|
|
if maxRead == 0 {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
n, err = c.in.Read(b[0:maxRead])
|
2017-04-26 20:25:53 +02:00
|
|
|
if err != nil {
|
2017-04-15 17:07:32 +02:00
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
c.remainingChunkBytes -= uint32(n)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func min(a, b int) int {
|
|
|
|
if a < b {
|
|
|
|
return a
|
|
|
|
} else {
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Chunker struct {
|
2017-04-26 20:25:53 +02:00
|
|
|
ChunkCount int
|
|
|
|
in io.Reader
|
2017-04-15 17:07:32 +02:00
|
|
|
remainingChunkBytes int
|
2017-04-26 20:25:53 +02:00
|
|
|
payloadBuf []byte
|
|
|
|
headerBuf *bytes.Buffer
|
2017-04-15 17:07:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewChunker(conn io.Reader) Chunker {
|
|
|
|
return NewChunkerSized(conn, ChunkBufSize)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewChunkerSized(conn io.Reader, chunkSize uint32) Chunker {
|
|
|
|
|
|
|
|
buf := make([]byte, int(chunkSize)-binary.Size(chunkSize))
|
|
|
|
|
|
|
|
return Chunker{
|
2017-04-26 20:25:53 +02:00
|
|
|
in: conn,
|
2017-04-15 17:07:32 +02:00
|
|
|
remainingChunkBytes: 0,
|
2017-04-26 20:25:53 +02:00
|
|
|
payloadBuf: buf,
|
|
|
|
headerBuf: &bytes.Buffer{},
|
2017-04-15 17:07:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Chunker) Read(b []byte) (n int, err error) {
|
|
|
|
|
|
|
|
//fmt.Printf("chunker: c.remainingChunkBytes: %d len(b): %d\n", c.remainingChunkBytes, len(b))
|
|
|
|
|
|
|
|
n = 0
|
|
|
|
if c.remainingChunkBytes == 0 {
|
|
|
|
|
|
|
|
newPayloadLen, err := c.in.Read(c.payloadBuf)
|
|
|
|
|
|
|
|
if newPayloadLen == 0 {
|
|
|
|
return 0, io.EOF
|
|
|
|
} else if err != nil {
|
|
|
|
return newPayloadLen, err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.remainingChunkBytes = newPayloadLen
|
|
|
|
|
|
|
|
// Write chunk header
|
|
|
|
c.headerBuf.Reset()
|
2017-04-26 20:25:53 +02:00
|
|
|
nextChunkLen := uint32(newPayloadLen)
|
2017-04-15 17:07:32 +02:00
|
|
|
headerLen := binary.Size(nextChunkLen)
|
|
|
|
err = binary.Write(c.headerBuf, ChunkHeaderByteOrder, nextChunkLen)
|
|
|
|
if err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
copy(b[0:headerLen], c.headerBuf.Bytes())
|
|
|
|
n += headerLen
|
|
|
|
c.ChunkCount++
|
|
|
|
}
|
|
|
|
|
|
|
|
remainingBuf := b[n:]
|
|
|
|
n2 := copy(remainingBuf, c.payloadBuf[:c.remainingChunkBytes])
|
|
|
|
//fmt.Printf("chunker: written: %d\n", n+int(n2))
|
|
|
|
c.remainingChunkBytes -= n2
|
2017-04-26 20:25:53 +02:00
|
|
|
return n + int(n2), err
|
|
|
|
}
|