initial commits

master
Charles Iliya Krempeaux 2022-11-15 21:55:12 -08:00
parent 068d259699
commit cc99ea7cf8
5 changed files with 580 additions and 3 deletions

142
buffer.go 100644
View File

@ -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)
}

View File

@ -1,10 +1,16 @@
package buffers package buffers
import ( import (
"errors" "github.com/reiver/go-fck"
) )
var ( var (
errNilDestination = errors.New("buffers: Nil Destination") BufferOverflow = fck.Error("buffer overflow")
errNilReceiver = errors.New("buffers: Nil Receiver") )
var (
errNilDestination = fck.Error("nil destination")
errNilReader = fck.Error("nil reader")
errNilReceiver = fck.Error("nil receiver")
errShortWrite = fck.Error("short write")
) )

9
go.mod 100644
View File

@ -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
)

6
go.sum 100644
View File

@ -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=

View File

@ -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<test.Limit; i++ {
var b [1]byte
var p []byte = b[:]
b[0] = test.Data[i]
n, err := buffer.Write(p)
if nil != err {
t.Errorf("For test #%d, did not expect an error but actually got one,", testNumber)
t.Logf("ERROR: (%T) %q", err, err)
t.Logf("DATA: %q", test.Data)
t.Logf("EXPECTED-DATA: %q", test.Expected)
t.Logf("ACTUAL-DATA: %q", buffer.String())
t.Logf("LIMIT: %d", test.Limit)
continue loop
}
if expected, actual := 1, n; expected != actual {
t.Errorf("For test #%d, the actual number of bytes written is not what was expected.", testNumber)
t.Logf("EXPECTED: %d", expected)
t.Logf("ACTUAL: %d", actual)
t.Logf("DATA: %q", test.Data)
t.Logf("EXPECTED-DATA: %q", test.Expected)
t.Logf("ACTUAL-DATA: %q", buffer.String())
continue loop
}
}
{
var b [1]byte
var p []byte = b[:]
if len(test.Data) < i {
b[0] = test.Data[i]
} else {
b[0] = '?'
}
n, err := buffer.Write(p)
if nil == err {
t.Errorf("For test #%d, expected an error but did not actually get one,", testNumber)
t.Logf("ERROR: (%T) %q", err, err)
t.Logf("DATA: %q", test.Data)
t.Logf("EXPECTED-DATA: %q", test.Expected)
t.Logf("ACTUAL-DATA: %q", buffer.String())
t.Logf("LIMIT: %d", test.Limit)
continue loop
}
if buffers.BufferOverflow != err {
t.Errorf("For test #%d, expected a buffer-overflow error but actually got a different error.", testNumber)
t.Logf("ERROR: (%T) %q", err, err)
t.Logf("DATA: %q", test.Data)
t.Logf("EXPECTED-DATA: %q", test.Expected)
t.Logf("ACTUAL-DATA: %q", buffer.String())
t.Logf("LIMIT: %d", test.Limit)
continue loop
}
if expected, actual := 0, n; expected != actual {
t.Errorf("For test #%d, the actual number of bytes written is not what was expected.", testNumber)
t.Logf("EXPECTED: %d", expected)
t.Logf("ACTUAL: %d", actual)
t.Logf("DATA: %q", test.Data)
t.Logf("EXPECTED-DATA: %q", test.Expected)
t.Logf("ACTUAL-DATA: %q", buffer.String())
continue loop
}
}
{
var expected string = test.Expected
var actual string = buffer.String()
if expected != actual {
t.Errorf("For test #%d, the actual value in the buffer is not what was expected.", testNumber)
t.Logf("EXPECTED: %q", expected)
t.Logf("ACTUAL: %q", actual)
t.Logf("DATA: %q", test.Data)
t.Logf("LIMIT: %d", test.Limit)
continue loop
}
}
}
}
}
func TestLimitedBuffer_all(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 limit int = len(test.Expected)
var buffer buffers.Buffer = buffers.LimitedBuffer(limit)
n, err := buffer.Write([]byte(test.Expected))
if nil != err {
t.Errorf("For test #%d, did not expect an error but actually got one,", testNumber)
t.Logf("ERROR: (%T) %q", err, err)
t.Logf("DATA: %q", test.Data)
t.Logf("EXPECTED-DATA: %q", test.Expected)
t.Logf("ACTUAL-DATA: %q", buffer.String())
t.Logf("LIMIT: %d", test.Limit)
continue loop
}
if expected, actual := limit, n; expected != actual {
t.Errorf("For test #%d, the actual number of bytes written is not what was expected.", testNumber)
t.Logf("EXPECTED: %d", expected)
t.Logf("ACTUAL: %d", actual)
t.Logf("DATA: %q", test.Data)
t.Logf("EXPECTED-DATA: %q", test.Expected)
continue loop
}
{
var expected string = test.Expected
var actual string = buffer.String()
if expected != actual {
t.Errorf("For test #%d, the actual value in the buffer is not what was expected.", testNumber)
t.Logf("EXPECTED: %q", expected)
t.Logf("ACTUAL: %q", actual)
t.Logf("DATA: %q", test.Data)
t.Logf("LIMIT: %d", test.Limit)
continue loop
}
}
}
}