git: implement type PacketReader
This commit is contained in:
parent
9827b90ff4
commit
8a8a9b5d10
62
git/packet_reader.go
Normal file
62
git/packet_reader.go
Normal file
@ -0,0 +1,62 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
type packetReader struct {
|
||||
proto *protocol
|
||||
|
||||
buf []byte
|
||||
}
|
||||
|
||||
var _ io.Reader = new(packetReader)
|
||||
|
||||
func (r *packetReader) Read(p []byte) (int, error) {
|
||||
var n int
|
||||
|
||||
if len(r.buf) > 0 {
|
||||
// If there is data in the buffer, shift as much out of it and
|
||||
// into the given "p" as we can.
|
||||
n = int(math.Min(float64(len(p)), float64(len(r.buf))))
|
||||
|
||||
copy(p, r.buf[:n])
|
||||
r.buf = r.buf[n:]
|
||||
}
|
||||
|
||||
// Loop and grab as many packets as we can in a given "run", until we
|
||||
// have either, a) overfilled the given buffer "p", or we have started
|
||||
// to internally buffer in "r.buf".
|
||||
for len(r.buf) == 0 {
|
||||
chunk, err := r.proto.readPacket()
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
if len(chunk) == 0 {
|
||||
// If we got an empty chunk, then we know that we have
|
||||
// reached the end of processing for this particular
|
||||
// packet, so let's terminate.
|
||||
|
||||
return n, io.EOF
|
||||
}
|
||||
|
||||
// Figure out how much of the packet we can read into "p".
|
||||
nn := int(math.Min(float64(len(chunk)), float64(len(p))))
|
||||
|
||||
// Move that amount into "p", from where we left off.
|
||||
copy(p[n:], chunk[:nn])
|
||||
// And move the rest into the buffer.
|
||||
r.buf = append(r.buf, chunk[nn:]...)
|
||||
|
||||
// Mark that we have read "nn" bytes into "p"
|
||||
n += nn
|
||||
|
||||
if n >= len(p) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
142
git/packet_reader_test.go
Normal file
142
git/packet_reader_test.go
Normal file
@ -0,0 +1,142 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// writePackets
|
||||
func writePacket(w io.Writer, datas ...[]byte) {
|
||||
for _, data := range datas {
|
||||
io.WriteString(w, fmt.Sprintf("%04x", len(data)+4))
|
||||
w.Write(data)
|
||||
|
||||
}
|
||||
io.WriteString(w, fmt.Sprintf("%04x", 0))
|
||||
}
|
||||
|
||||
func TestPacketReaderReadsSinglePacketsInOneCall(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
writePacket(&buf, []byte("asdf"))
|
||||
|
||||
pr := &packetReader{proto: newProtocolRW(&buf, nil)}
|
||||
|
||||
data, err := ioutil.ReadAll(pr)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, []byte("asdf"), data)
|
||||
}
|
||||
|
||||
func TestPacketReaderReadsManyPacketsInOneCall(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
writePacket(&buf, []byte("first\n"), []byte("second"))
|
||||
|
||||
pr := &packetReader{proto: newProtocolRW(&buf, nil)}
|
||||
|
||||
data, err := ioutil.ReadAll(pr)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, []byte("first\nsecond"), data)
|
||||
}
|
||||
|
||||
func TestPacketReaderReadsSinglePacketsInMultipleCallsWithUnevenBuffering(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
writePacket(&buf, []byte("asdf"))
|
||||
|
||||
pr := &packetReader{proto: newProtocolRW(&buf, nil)}
|
||||
|
||||
var p1 [3]byte
|
||||
var p2 [1]byte
|
||||
|
||||
n1, e1 := pr.Read(p1[:])
|
||||
assert.Equal(t, 3, n1)
|
||||
assert.Equal(t, []byte("asd"), p1[:])
|
||||
assert.Nil(t, e1)
|
||||
|
||||
n2, e2 := pr.Read(p2[:])
|
||||
assert.Equal(t, 1, n2)
|
||||
assert.Equal(t, []byte("f"), p2[:])
|
||||
assert.Equal(t, io.EOF, e2)
|
||||
}
|
||||
|
||||
func TestPacketReaderReadsManyPacketsInMultipleCallsWithUnevenBuffering(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
writePacket(&buf, []byte("first"), []byte("second"))
|
||||
|
||||
pr := &packetReader{proto: newProtocolRW(&buf, nil)}
|
||||
|
||||
var p1 [4]byte
|
||||
var p2 [7]byte
|
||||
var p3 []byte
|
||||
|
||||
n1, e1 := pr.Read(p1[:])
|
||||
assert.Equal(t, 4, n1)
|
||||
assert.Equal(t, []byte("firs"), p1[:])
|
||||
assert.Nil(t, e1)
|
||||
|
||||
n2, e2 := pr.Read(p2[:])
|
||||
assert.Equal(t, 7, n2)
|
||||
assert.Equal(t, []byte("tsecond"), p2[:])
|
||||
assert.Equal(t, nil, e2)
|
||||
|
||||
n3, e3 := pr.Read(p3[:])
|
||||
assert.Equal(t, 0, n3)
|
||||
assert.Empty(t, p3)
|
||||
assert.Equal(t, io.EOF, e3)
|
||||
}
|
||||
|
||||
func TestPacketReaderReadsSinglePacketsInMultipleCallsWithEvenBuffering(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
writePacket(&buf, []byte("firstother"))
|
||||
|
||||
pr := &packetReader{proto: newProtocolRW(&buf, nil)}
|
||||
|
||||
var p1 [5]byte
|
||||
var p2 [5]byte
|
||||
|
||||
n1, e1 := pr.Read(p1[:])
|
||||
assert.Equal(t, 5, n1)
|
||||
assert.Equal(t, []byte("first"), p1[:])
|
||||
assert.Nil(t, e1)
|
||||
|
||||
n2, e2 := pr.Read(p2[:])
|
||||
assert.Equal(t, 5, n2)
|
||||
assert.Equal(t, []byte("other"), p2[:])
|
||||
assert.Equal(t, io.EOF, e2)
|
||||
}
|
||||
|
||||
func TestPacketReaderReadsManyPacketsInMultipleCallsWithEvenBuffering(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
writePacket(&buf, []byte("first"), []byte("other"))
|
||||
|
||||
pr := &packetReader{proto: newProtocolRW(&buf, nil)}
|
||||
|
||||
var p1 [5]byte
|
||||
var p2 [5]byte
|
||||
var p3 []byte
|
||||
|
||||
n1, e1 := pr.Read(p1[:])
|
||||
assert.Equal(t, 5, n1)
|
||||
assert.Equal(t, []byte("first"), p1[:])
|
||||
assert.Nil(t, e1)
|
||||
|
||||
n2, e2 := pr.Read(p2[:])
|
||||
assert.Equal(t, 5, n2)
|
||||
assert.Equal(t, []byte("other"), p2[:])
|
||||
assert.Equal(t, nil, e2)
|
||||
|
||||
n3, e3 := pr.Read(p3)
|
||||
assert.Equal(t, 0, n3)
|
||||
assert.Equal(t, io.EOF, e3)
|
||||
}
|
Loading…
Reference in New Issue
Block a user