125 lines
3.5 KiB
Go
125 lines
3.5 KiB
Go
package git
|
|
|
|
import (
|
|
"io"
|
|
|
|
"github.com/github/git-lfs/tools"
|
|
)
|
|
|
|
type PacketWriter struct {
|
|
// buf is the internal buffer of bytes used to store data given in calls
|
|
// to Write that don't warrant a protocol write.
|
|
buf []byte
|
|
// proto is the place where packets get written.
|
|
proto *protocol
|
|
}
|
|
|
|
var _ io.Writer = new(PacketWriter)
|
|
|
|
// NewPacketWriter returns a new *PacketWriter, which will write to the
|
|
// underlying data stream "w". The internal buffer is initialized with the given
|
|
// capacity, "c".
|
|
//
|
|
// If "w" is already a `*PacketWriter`, it will be returned as-is.
|
|
func NewPacketWriter(w io.Writer, c int) *PacketWriter {
|
|
if pw, ok := w.(*PacketWriter); ok {
|
|
return pw
|
|
}
|
|
|
|
return &PacketWriter{
|
|
buf: make([]byte, 0, c),
|
|
proto: newProtocolRW(nil, w),
|
|
}
|
|
}
|
|
|
|
// Write implements the io.Writer interface's `Write` method by providing a
|
|
// packet-based backend to the given buffer "p".
|
|
//
|
|
// As many bytes are removed from "p" as possible and stored in an internal
|
|
// buffer until the amount of data in the internal buffer is enough to write a
|
|
// single packet. Once the internal buffer is full, a packet is written to the
|
|
// underlying stream of data, and the process repeats.
|
|
//
|
|
// When the caller has no more data to write in the given chunk of packets, a
|
|
// subsequent call to `Write(p []byte)` MUST be made with a nil slice, to flush
|
|
// the remaining data in the buffer, and write the terminating bytes to the
|
|
// underlying packet stream.
|
|
//
|
|
// Write returns the number of bytes in "p" actually written to the underlying
|
|
// protocol stream, not including the number of bytes written in those packets
|
|
// headers. If any error was encountered while either buffering or writing, that
|
|
// error is returned, along with the number of bytes written to the underlying
|
|
// protocol stream, as described above.
|
|
func (w *PacketWriter) Write(p []byte) (int, error) {
|
|
var n int
|
|
|
|
if p == nil {
|
|
// If we got an empty sequence of bytes, let's flush the data
|
|
// stored in the buffer, and then write the a packet termination
|
|
// sequence.
|
|
|
|
flushed, err := w.flush()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
n = n + flushed
|
|
|
|
if err = w.proto.writeFlush(); err != nil {
|
|
return n, err
|
|
}
|
|
}
|
|
|
|
for len(p) > 0 {
|
|
// While there is still data left to process in "p", grab as
|
|
// much of it as we can while not allowing the internal buffer
|
|
// to exceed the MaxPacketLength const.
|
|
m := tools.MinInt(len(p), MaxPacketLength-len(w.buf))
|
|
|
|
// Append on all of the data that we could into the internal
|
|
// buffer.
|
|
w.buf = append(w.buf, p[:m]...)
|
|
// Truncate "p" such that it no longer includes the data that we
|
|
// have in the internal buffer.
|
|
p = p[m:]
|
|
|
|
if len(w.buf) == MaxPacketLength {
|
|
// If we were able to grab an entire packet's worth of
|
|
// data, flush the buffer.
|
|
|
|
flushed, err := w.flush()
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
n = n + flushed
|
|
}
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// flush writes any data in the internal buffer out to the underlying protocol
|
|
// stream. If the amount of data in the internal buffer exceeds the
|
|
// MaxPacketLength, the data will be written in multiple packets to accommodate.
|
|
//
|
|
// flush returns the number of bytes written to the underlying packet stream,
|
|
// and any error that it encountered along the way.
|
|
func (w *PacketWriter) flush() (int, error) {
|
|
var n int
|
|
|
|
for len(w.buf) > 0 {
|
|
if err := w.proto.writePacket(w.buf); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
m := tools.MinInt(len(w.buf), MaxPacketLength)
|
|
|
|
w.buf = w.buf[m:]
|
|
|
|
n = n + m
|
|
}
|
|
|
|
return n, nil
|
|
}
|