Add sbuf.

This commit is contained in:
Kyle Isom 2015-09-22 01:26:02 -07:00
parent 4f2f124707
commit c2dca76803
3 changed files with 399 additions and 0 deletions

15
sbuf/LICENSE Normal file
View File

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

144
sbuf/sbuf.go Normal file
View File

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

240
sbuf/sbuf_test.go Normal file
View File

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