diff --git a/buffer.go b/buffer.go new file mode 100644 index 0000000..7c42e86 --- /dev/null +++ b/buffer.go @@ -0,0 +1,142 @@ +package buffers + +import ( + "github.com/reiver/go-opt" + "github.com/reiver/go-utf8" + + "io" + "unsafe" +) + +// A Buffer is used to store a series of bytes. +// +// A Buffer can be used as is, without any type of initialization (if not doesn't want the Buffer to have a limit): +// +// var buffer buffers.Buffer +// +// But if one wants the Buffer to have a limit, then it should be initialized like the following: +// +// var buffer buffers.Buffer = buffers.LimitedBuffer(4194304) // 4 MB +type Buffer struct { + data []byte + max opt.Optional[int] +} + +var _ io.ByteWriter = &Buffer{} +var _ io.ReaderFrom = &Buffer{} +var _ io.Writer = &Buffer{} + +// LimitedBuffer returns a Buffer with a maximum size. +// +// var buffer buffers.Buffer = buffers.LimitedBuffer(4194304) // 4 MB +func LimitedBuffer(max int) Buffer { + return Buffer { + max:opt.Something[int](max), + } +} + +// Len returns how many bytes have been accumulated in Buffer. +// +// receiver.Len() == len(receiver.String()) +func (receiver *Buffer) Len() int { + if nil == receiver { + return 0 + } + + return len(receiver.data) +} + +// ReadFrom reads from the io.Reader. Copying bytes until it gets an io.EOF. +func (receiver *Buffer) ReadFrom(r io.Reader) (int64, error) { + if nil == receiver { + return 0, errNilReceiver + } + if nil == r { + return 0, errNilReader + } + + return io.Copy(receiver, r) +} + +// String returns the bytes that have been accumulated. +func (receiver *Buffer) String() string { + if nil == receiver { + return "" + } + + if nil == receiver.data { + return "" + } + + return *(*string)(unsafe.Pointer(&receiver.data)) +} + +func (receiver *Buffer) Write(p []byte) (n int, err error) { + if nil == receiver { + return 0, errNilReceiver + } + + var lenP int + { + lenP = len(p) + if lenP <= 0 { + return 0, nil + } + } + + { + max, something := receiver.max.Get() + if something { + newLen := receiver.Len() + lenP + + if max < newLen { + return 0, BufferOverflow + } + } + } + + receiver.data = append(receiver.data, p...) + + return len(p), nil +} + +// WriteByte appends another byte to the Buffer. +func (receiver *Buffer) WriteByte(c byte) error { + if nil == receiver { + return errNilReceiver + } + + var b [1]byte + var p []byte = b[:] + b[0] = c + + { + n, err := receiver.Write(p) + if nil != err { + return err + } + if expected, actual := len(b), n; expected != actual { + return errShortWrite + } + } + + return nil +} + +// WriteRune appends the UTF-8 encoded rune to the Buffer. +func (receiver *Buffer) WriteRune(r rune) (int, error) { + if nil == receiver { + return 0, errNilReceiver + } + + return utf8.WriteRune(receiver, r) +} + +// WriteRune appends the string to the Buffer. +func (receiver *Buffer) WriteString(s string) (int, error) { + if nil == receiver { + return 0, errNilReceiver + } + + return io.WriteString(receiver, s) +} diff --git a/errors.go b/errors.go index f9fdf49..36ee38c 100644 --- a/errors.go +++ b/errors.go @@ -1,10 +1,16 @@ package buffers import ( - "errors" + "github.com/reiver/go-fck" ) var ( - errNilDestination = errors.New("buffers: Nil Destination") - errNilReceiver = errors.New("buffers: Nil Receiver") + BufferOverflow = fck.Error("buffer overflow") +) + +var ( + errNilDestination = fck.Error("nil destination") + errNilReader = fck.Error("nil reader") + errNilReceiver = fck.Error("nil receiver") + errShortWrite = fck.Error("short write") ) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c4e0fdb --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/reiver/go-buffers + +go 1.18 + +require ( + github.com/reiver/go-fck v0.0.1 // indirect + github.com/reiver/go-opt v1.3.0 // indirect + github.com/reiver/go-utf8 v2.0.1+incompatible // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..cf3d053 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/reiver/go-fck v0.0.1 h1:GhOiIp/4Au3iPUVC1YRfJJS5CdvLDNF9qt/n1lwgWnM= +github.com/reiver/go-fck v0.0.1/go.mod h1:i77J0nD9GkSF0osPcURZbv9u19F0keF/mrhkgIu9wvM= +github.com/reiver/go-opt v1.3.0 h1:DyDUwxXiRuszOFQxVkjJx52Rrhcr8jpTGrwn/KHA77A= +github.com/reiver/go-opt v1.3.0/go.mod h1:/GNk2aLHdf9WB/ATueDbAYCaQETT6Bq+PPOKhV1qV0U= +github.com/reiver/go-utf8 v2.0.1+incompatible h1:f1rRbwTIcUaX+1wnLM1+FWelKegAxTURE9MkZCRVOPM= +github.com/reiver/go-utf8 v2.0.1+incompatible/go.mod h1:tfGntNDUeEZIAsz0mTu0lF7/IlErc+BpQgO6yxYmJig= diff --git a/limitedbuffer_test.go b/limitedbuffer_test.go new file mode 100644 index 0000000..34867c3 --- /dev/null +++ b/limitedbuffer_test.go @@ -0,0 +1,414 @@ +package buffers_test + +import ( + "github.com/reiver/go-buffers" + + "testing" +) + +func TestLimitedBuffer_oneByteAtATime(t *testing.T) { + + tests := []struct{ + Data string + Expected string + Limit int + }{ + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "", + Limit: 0, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0", + Limit: 1, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "01", + Limit: 2, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "012", + Limit: 3, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123", + Limit: 4, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "01234", + Limit: 5, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "012345", + Limit: 6, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456", + Limit: 7, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "01234567", + Limit: 8, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "012345678", + Limit: 9, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789", + Limit: 10, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"A", + Limit: 11, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"AB", + Limit: 12, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABC", + Limit: 13, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABCD", + Limit: 14, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABCDE", + Limit: 15, + }, + + + + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXY", + Limit: 35, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ", + Limit: 36, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"a", + Limit: 37, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"ab", + Limit: 38, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abc", + Limit: 39, + }, + + + + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvw", + Limit: 59, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwx", + Limit: 60, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxy", + Limit: 61, + }, + { + Data: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Expected: "0123456789"+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"abcdefghijklmnopqrstuvwxyz", + Limit: 62, + }, + } + + loop: for testNumber, test := range tests { + + { + var buffer buffers.Buffer = buffers.LimitedBuffer(test.Limit) + + var i int + for i=0; i