Add sbuf.
This commit is contained in:
parent
4f2f124707
commit
c2dca76803
|
@ -0,0 +1,15 @@
|
||||||
|
My source code has the following license:
|
||||||
|
|
||||||
|
Copyright (c) 2015 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,144 @@
|
||||||
|
// Package sbuf implements a byte buffer that can be wiped. The underlying
|
||||||
|
// byte slice is wiped on read before being declaimed, and when the
|
||||||
|
// buffer is closed, its storage is zeroised.
|
||||||
|
package sbuf
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func zero(in []byte, n int) {
|
||||||
|
if in == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stop := n
|
||||||
|
if stop > len(in) || stop == 0 {
|
||||||
|
stop = len(in)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < stop; i++ {
|
||||||
|
in[i] ^= in[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Buffer is a variable-sized buffer of bytes with Read and Write
|
||||||
|
// methods. The zero value for Buffer is an empty buffer ready to use.
|
||||||
|
type Buffer struct {
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBuffer creates a new buffer with the specified capacity.
|
||||||
|
func NewBuffer(n int) *Buffer {
|
||||||
|
return &Buffer{
|
||||||
|
buf: make([]byte, 0, n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBufferFrom creates a new buffer from the byte slice passed in. The
|
||||||
|
// original data will be wiped.
|
||||||
|
func NewBufferFrom(p []byte) *Buffer {
|
||||||
|
buf := NewBuffer(len(p))
|
||||||
|
buf.Write(p)
|
||||||
|
zero(p, len(p))
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads the next len(p) bytes from the buffer or until the buffer
|
||||||
|
// is drained. The return value n is the number of bytes read. If the
|
||||||
|
// buffer has no data to return, err is io.EOF (unless len(p) is zero);
|
||||||
|
// otherwise it is nil.
|
||||||
|
func (buf *Buffer) Read(p []byte) (int, error) {
|
||||||
|
if len(buf.buf) == 0 {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
copyLength := len(p)
|
||||||
|
if copyLength > len(buf.buf) {
|
||||||
|
copyLength = len(buf.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, buf.buf)
|
||||||
|
zero(buf.buf, len(p))
|
||||||
|
buf.buf = buf.buf[copyLength:]
|
||||||
|
return copyLength, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadByte reads the next byte from the buffer. If the buffer has no
|
||||||
|
// data to return, err is io.EOF; otherwise it is nil.
|
||||||
|
func (buf *Buffer) ReadByte() (byte, error) {
|
||||||
|
if len(buf.buf) == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
c := buf.buf[0]
|
||||||
|
buf.buf[0] = 0
|
||||||
|
buf.buf = buf.buf[1:]
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (buf *Buffer) grow(n int) {
|
||||||
|
tmp := make([]byte, len(buf.buf), len(buf.buf)+n)
|
||||||
|
copy(tmp, buf.buf)
|
||||||
|
zero(buf.buf, len(buf.buf))
|
||||||
|
buf.buf = tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write appends the contents of p to the buffer, growing the buffer
|
||||||
|
// as needed. The return value n is the length of p; err is always nil.
|
||||||
|
func (buf *Buffer) Write(p []byte) (int, error) {
|
||||||
|
r := len(buf.buf) + len(p)
|
||||||
|
if cap(buf.buf) < r {
|
||||||
|
l := r
|
||||||
|
for {
|
||||||
|
if l > r {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l *= 2
|
||||||
|
}
|
||||||
|
buf.grow(l - cap(buf.buf))
|
||||||
|
}
|
||||||
|
buf.buf = append(buf.buf, p...)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteByte adds the byte c to the buffer, growing the buffer as needed.
|
||||||
|
func (buf *Buffer) WriteByte(c byte) error {
|
||||||
|
r := len(buf.buf) + 1
|
||||||
|
if cap(buf.buf) < r {
|
||||||
|
l := r * 2
|
||||||
|
buf.grow(l - cap(buf.buf))
|
||||||
|
}
|
||||||
|
buf.buf = append(buf.buf, c)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close destroys and zeroises the buffer. The buffer will be re-opened
|
||||||
|
// on the next write.
|
||||||
|
func (buf *Buffer) Close() {
|
||||||
|
zero(buf.buf, len(buf.buf))
|
||||||
|
buf.buf = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the length of the buffer.
|
||||||
|
func (buf *Buffer) Len() int {
|
||||||
|
return len(buf.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cap returns the capacity of the buffer.
|
||||||
|
func (buf *Buffer) Cap() int {
|
||||||
|
return cap(buf.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the bytes currently in the buffer, and closes itself.
|
||||||
|
func (buf *Buffer) Bytes() []byte {
|
||||||
|
if buf.buf == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p := make([]byte, buf.Len())
|
||||||
|
buf.Read(p)
|
||||||
|
buf.Close()
|
||||||
|
return p
|
||||||
|
}
|
|
@ -0,0 +1,240 @@
|
||||||
|
package sbuf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/nacl/box"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
buf = &Buffer{}
|
||||||
|
testMessage1 = []byte("round and round and round we go, where we stop, no one knows")
|
||||||
|
testMessage2 = []byte("the deconstruction of falling stars")
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWrite(t *testing.T) {
|
||||||
|
n, err := buf.Write(testMessage1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != len(testMessage1) {
|
||||||
|
t.Fatalf("expected to write %d bytes, but only wrote %d bytes", len(testMessage1), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf.Len() != len(testMessage1) {
|
||||||
|
t.Fatalf("expected a length of %d, but have a length of %d", len(testMessage1), buf.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf.Cap() != (len(testMessage1) * 2) {
|
||||||
|
t.Fatalf("expected a capacity of %d, but have a capacity of %d", len(testMessage1)*2, buf.Cap())
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = buf.Write(testMessage2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != len(testMessage2) {
|
||||||
|
t.Fatalf("expected to write %d bytes, but only wrote %d bytes", len(testMessage2), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = buf.Write(testMessage2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != len(testMessage2) {
|
||||||
|
t.Fatalf("expected to write %d bytes, but only wrote %d bytes", len(testMessage2), n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRead(t *testing.T) {
|
||||||
|
var p = make([]byte, len(testMessage1))
|
||||||
|
n, err := buf.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != len(testMessage1) {
|
||||||
|
t.Fatalf("expected to read %d bytes, but only read %d bytes", len(testMessage1), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(p, testMessage1) {
|
||||||
|
t.Fatalf("expected p='%s', but p='%s'", testMessage1, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
p = make([]byte, len(testMessage2))
|
||||||
|
n, err = buf.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != len(testMessage2) {
|
||||||
|
t.Fatalf("expected to read %d bytes, but only read %d bytes", len(testMessage2), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(p, testMessage2) {
|
||||||
|
t.Fatalf("expected p='%s', but p='%s'", testMessage2, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Close()
|
||||||
|
n, err = buf.Read(p)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected EOF")
|
||||||
|
} else if n != 0 {
|
||||||
|
t.Fatalf("expect n=0, but n=%d", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
p = nil
|
||||||
|
n, err = buf.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
} else if n != 0 {
|
||||||
|
t.Fatalf("expect n=0, but n=%d", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShortRead(t *testing.T) {
|
||||||
|
tmpMessage := []byte("hello, world")
|
||||||
|
buf.Write(tmpMessage)
|
||||||
|
|
||||||
|
var p = make([]byte, len(testMessage1))
|
||||||
|
n, err := buf.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != len(tmpMessage) {
|
||||||
|
t.Fatalf("expected to read %d bytes, but only read %d bytes", len(testMessage1), n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewBuffer(t *testing.T) {
|
||||||
|
buf := NewBuffer(32)
|
||||||
|
if len(buf.buf) != 0 {
|
||||||
|
t.Fatalf("expected new buffer length to be 0, have %d",
|
||||||
|
len(buf.buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
if cap(buf.buf) != 32 {
|
||||||
|
t.Fatalf("expected new buffer capacity to be 0, have %d",
|
||||||
|
cap(buf.buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewBufferFrom(t *testing.T) {
|
||||||
|
p := make([]byte, len(testMessage1))
|
||||||
|
copy(p, testMessage1)
|
||||||
|
buf := NewBufferFrom(p)
|
||||||
|
if !bytes.Equal(buf.buf, testMessage1) {
|
||||||
|
t.Fatal("new buffer wasn't constructed properly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytes(t *testing.T) {
|
||||||
|
p := make([]byte, len(testMessage1))
|
||||||
|
copy(p, testMessage1)
|
||||||
|
buf := NewBufferFrom(p)
|
||||||
|
|
||||||
|
out := buf.Bytes()
|
||||||
|
if buf.buf != nil {
|
||||||
|
t.Fatal("buffer was not closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(out, testMessage1) {
|
||||||
|
t.Fatal("buffer did not return the right data")
|
||||||
|
}
|
||||||
|
|
||||||
|
out = buf.Bytes()
|
||||||
|
if out != nil {
|
||||||
|
t.Fatal("a closed buffer should return nil for Bytes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRWByte(t *testing.T) {
|
||||||
|
buf := NewBuffer(0)
|
||||||
|
c := byte(42)
|
||||||
|
err := buf.WriteByte(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c != 42 {
|
||||||
|
t.Fatal("Expected 42, have %d", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.ReadByte()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected EOF")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRead(b *testing.B) {
|
||||||
|
b.N = 2000
|
||||||
|
pub, priv, err := box.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err := buf.Write(priv[:])
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
_, err = buf.Write(pub[:])
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
b.SetBytes(64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFixed(b *testing.B) {
|
||||||
|
pub, priv, err := box.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = NewBuffer(64 * b.N)
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err := buf.Write(priv[:])
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
_, err = buf.Write(pub[:])
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
b.SetBytes(64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkWrite(b *testing.B) {
|
||||||
|
var pub = new([32]byte)
|
||||||
|
var priv = new([32]byte)
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N && buf.Len() >= 64; i++ {
|
||||||
|
_, err := buf.Read(priv[:])
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
_, err = buf.Read(pub[:])
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
b.SetBytes(64)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue