Skip to content

Commit

Permalink
Merge pull request #34 from Stebalien/steb/optimize
Browse files Browse the repository at this point in the history
Optimize decoding
  • Loading branch information
whyrusleeping authored Aug 6, 2020
2 parents 6a3894a + e6c8c84 commit 63aa96c
Show file tree
Hide file tree
Showing 8 changed files with 359 additions and 245 deletions.
24 changes: 9 additions & 15 deletions gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -589,16 +589,14 @@ func emitCborUnmarshalStructField(w io.Writer, f Field) error {
return doTemplate(w, f, `
{
{{ if .Pointer }}
pb, err := br.PeekByte()
b, err := br.ReadByte()
if err != nil {
return err
}
if pb == cbg.CborNull[0] {
var nbuf [1]byte
if _, err := br.Read(nbuf[:]); err != nil {
if b != cbg.CborNull[0] {
if err := br.UnreadByte(); err != nil {
return err
}
} else {
{{ end }}
c, err := cbg.ReadCid(br)
if err != nil {
Expand Down Expand Up @@ -628,16 +626,14 @@ func emitCborUnmarshalStructField(w io.Writer, f Field) error {
return doTemplate(w, f, `
{
{{ if .Pointer }}
pb, err := br.PeekByte()
b, err := br.ReadByte()
if err != nil {
return err
}
if pb == cbg.CborNull[0] {
var nbuf [1]byte
if _, err := br.Read(nbuf[:]); err != nil {
if b != cbg.CborNull[0] {
if err := br.UnreadByte(); err != nil {
return err
}
} else {
{{ .Name }} = new({{ .TypeName }})
if err := {{ .Name }}.UnmarshalCBOR(br); err != nil {
return xerrors.Errorf("unmarshaling {{ .Name }} pointer: %w", err)
Expand Down Expand Up @@ -685,16 +681,14 @@ func emitCborUnmarshalUint64Field(w io.Writer, f Field) error {
return doTemplate(w, f, `
{
{{ if .Pointer }}
pb, err := br.PeekByte()
b, err := br.ReadByte()
if err != nil {
return err
}
if pb == cbg.CborNull[0] {
var nbuf [1]byte
if _, err := br.Read(nbuf[:]); err != nil {
if b != cbg.CborNull[0] {
if err := br.UnreadByte(); err != nil {
return err
}
} else {
maj, extra, err = {{ ReadHeader "br" }}
if err != nil {
return err
Expand Down
80 changes: 80 additions & 0 deletions peeker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package typegen

import (
"bufio"
"io"
)

// BytePeeker combines the Reader and ByteScanner interfaces.
type BytePeeker interface {
io.Reader
io.ByteScanner
}

func GetPeeker(r io.Reader) BytePeeker {
if r, ok := r.(BytePeeker); ok {
return r
}
return &peeker{reader: r}
}

// peeker is a non-buffering BytePeeker.
type peeker struct {
reader io.Reader
peekState int
lastByte byte
}

const (
peekEmpty = iota
peekSet
peekUnread
)

func (p *peeker) Read(buf []byte) (n int, err error) {
// Read "nothing". I.e., read an error, maybe.
if len(buf) == 0 {
// There's something pending in the
if p.peekState == peekUnread {
return 0, nil
}
return p.reader.Read(nil)
}

if p.peekState == peekUnread {
buf[0] = p.lastByte
n, err = p.reader.Read(buf[1:])
n += 1
} else {
n, err = p.reader.Read(buf)
}
if n > 0 {
p.peekState = peekSet
p.lastByte = buf[n-1]
}
return n, err
}

func (p *peeker) ReadByte() (byte, error) {
if p.peekState == peekUnread {
p.peekState = peekSet
return p.lastByte, nil
}
var buf [1]byte
n, err := p.reader.Read(buf[:])
if n == 0 {
return 0, err
}
b := buf[0]
p.lastByte = b
p.peekState = peekSet
return b, err
}

func (p *peeker) UnreadByte() error {
if p.peekState != peekSet {
return bufio.ErrInvalidUnreadByte
}
p.peekState = peekUnread
return nil
}
103 changes: 103 additions & 0 deletions peeker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package typegen

import (
"bufio"
"bytes"
"io"
"testing"
)

func TestPeeker(t *testing.T) {
buf := bytes.NewBuffer([]byte{0, 1, 2, 3})
p := peeker{reader: buf}
n, err := p.Read(nil)
if err != nil {
t.Fatal(err)
}
if n != 0 {
t.Fatal(err)
}

err = p.UnreadByte()
if err != bufio.ErrInvalidUnreadByte {
t.Fatal(err)
}

// read 2 bytes
var out [2]byte
n, err = p.Read(out[:])
if err != nil {
t.Fatal(err)
}
if n != 2 {
t.Fatalf("expected 2 bytes, got %d", n)
}
if !bytes.Equal(out[:], []byte{0, 1}) {
t.Fatalf("unexpected output")
}

// unread that last byte and read it again.
err = p.UnreadByte()
if err != nil {
t.Fatal(err)
}
b, err := p.ReadByte()
if err != nil {
t.Fatal(err)
}
if b != 1 {
t.Fatal("expected 1")
}

// unread that last byte then read 2
err = p.UnreadByte()
if err != nil {
t.Fatal(err)
}
n, err = p.Read(out[:])
if err != nil {
t.Fatal(err)
}
if n != 2 {
t.Fatalf("expected 2 bytes, got %d", n)
}
if !bytes.Equal(out[:], []byte{1, 2}) {
t.Fatalf("unexpected output")
}

// read another byte
b, err = p.ReadByte()
if err != nil {
t.Fatal(err)
}
if b != 3 {
t.Fatal("expected 1")
}

// Should read eof at end.
n, err = p.Read(out[:])
if err != io.EOF {
t.Fatal(err)
}
if n != 0 {
t.Fatal("should have been at end")
}
// should unread eof
err = p.UnreadByte()
if err != nil {
t.Fatal(err)
}

_, err = p.Read(nil)
if err != nil {
t.Fatal(err)
}

b, err = p.ReadByte()
if err != nil {
t.Fatal(err)
}
if b != 3 {
t.Fatal("expected 1")
}
}
55 changes: 47 additions & 8 deletions testing/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package testing

import (
"bytes"
"encoding/hex"
"io"
"io/ioutil"
"math/rand"
Expand Down Expand Up @@ -34,21 +33,28 @@ func BenchmarkMarshaling(b *testing.B) {
}

func BenchmarkUnmarshaling(b *testing.B) {
hx := "8989f68080807859f099a586f0908093f1af9fb6f3a0ad82e8aaa0efbfbdf1b88688f29d8aaeecacabf0a4be94f19295a0f19b9081f0b6bf8ff3ad83a6f09e9ca7f2be8a8bf187a8a0f280a8b3f4899a9bf181afb1f0bca2b0f1b5ab9bf2b0a3ac80f6f68384787af2b1a082f3b99d98f1adb9b6f3b9868df29fbbb0f3858791f3b5b39df2b68e92f2a9bb9af282b4b2f18fba9cf294b8b2f3a0a1a9f0aab1aaf28cb994f09796aef195bc90f488be81e0a59af2928183f1a0a4abf393bbbae39d8df28fb287f0bf8fa6f0b79a89f188babcf395b0b1f29ebab7f2b0a091f29db48f1b527ef13ee4f5321a403b2130370299eeb937847848f287b8b9f1ad9e90f1b1b9bbf18ebc91f3908583f0be9ab3f2aca8abf0a8acadf380a7abf293a8aaf1b2b6a6f3b89587f3809fadf3a39f97f3a8b48cf3b299bff19cab9df28399a01b374797708d2015d3401b6bfb7066c509754c8478a1f0ab8f96f287988df297aea7f3afa699f3859788f2a2b2b8f2b681a6f29a95a4f382978cf396b183e2acbdf39cbdb5f0b99b94f1a2baaaf1ba89b0f3a8a7bbf397bdabf3af8c83f1b38ebef0beb1a0f3939f83f0b9ad90f1acb597f0b49eb0f29ab3a3f480808ef39b878ae5989ff0a7b789f48981b6f281aba6f2a9ad88f09fb395f0aa95adf0a1a59ff38a8d97f397b7b0eebfa9f2a5ab87f2afa7b8f0b992b81bab566703ac0b139c401b463b0320db277de1841b73dd7cd1861ff4561bc0256739761d28dd1b39c9019ac37c08721b2f08fbf368bf7f94813b075c40eb7f66e0488078b4f288898bf486bc90e9b8bdf180ab8ff39db1a7f0b1afb8f3ab9fa6f1b19182f189bdaff3bf80a5f1a18fb4f39c99a9f0ba839af2adb88fe4bd9df39bba8bf28ebf9ef2b3a783f0b6b395f197be84f3a1998af1b0898bf3b0b08ef1b49b94f094b59df19dbfa6f2aa8494f48ba0b2e28181f1a08999f2b3ac81eaa689f1bb80bbf2ae918bf0a19397f1a19d9cf3b095b5f1b4baa2f0b7ad92f3ab8c8ef38fab92f489b499f18d9899f0b5bcb5f2a3b6a5f2a1acb1831be5bdbd1384238b4b1b8a95991fbf9ca8d11baf61be2ac6477c7d1ba1d9dac0cecd182d1b4175138c8c7fbb4e8384785ef0a7adaef39b8a8ff1b79bacc693f1948ebef0938383f48aa6abf09ab684f1ba8c89f188b091f18ab2b5f1ac8484f2b7b089f18b97bdf1838aacf397ad98f0b9a8aff394a2a3f39eb6bff09ab8bef39189bef18f89aaf3aca982f29381901b7f0bf8763d569f3b403b75c40e5c6163108084786af1879fa8f2b4af9ef3a8b3b6f3b0be93f0aba9a8f0a1a698f3b6a7a7e6adbef1a8849bf28087a3f3b89f82f38caab6f0b7b09ff1bf938ff0a0b1aff2b79691f0a5b29bf4858896f484a5abf393bbbbf3a2b8bdf29393a6eba180f1a1b3b0f29da098f1b09ca7f3bda2901b6f088c64a0854512401b564b5898ca46ac958467f29c9188eea5941bb7b58825b1edf1ee403b6dcbd95c52f6ca33"
r := rand.New(rand.NewSource(123456))
val, ok := quick.Value(reflect.TypeOf(SimpleTypeTwo{}), r)
if !ok {
b.Fatal("failed to construct type")
}

d, err := hex.DecodeString(hx)
if err != nil {
tt := val.Interface().(SimpleTypeTwo)

buf := new(bytes.Buffer)
if err := tt.MarshalCBOR(buf); err != nil {
b.Fatal(err)
}

buf := bytes.NewReader(d)
reader := bytes.NewReader(buf.Bytes())

b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
buf.Seek(0, io.SeekStart)
reader.Seek(0, io.SeekStart)
var tt SimpleTypeTwo
if err := tt.UnmarshalCBOR(buf); err != nil {
if err := tt.UnmarshalCBOR(reader); err != nil {
b.Fatal(err)
}
}
Expand All @@ -69,11 +75,44 @@ func BenchmarkLinkScan(b *testing.B) {
b.Fatal(err)
}

reader := bytes.NewReader(buf.Bytes())

b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
reader.Seek(0, io.SeekStart)
if err := cbg.ScanForLinks(reader, func(cid.Cid) {}); err != nil {
b.Fatal(err)
}
}
}

func BenchmarkDeferred(b *testing.B) {
r := rand.New(rand.NewSource(123456))
val, ok := quick.Value(reflect.TypeOf(SimpleTypeTwo{}), r)
if !ok {
b.Fatal("failed to construct type")
}

tt := val.Interface().(SimpleTypeTwo)

buf := new(bytes.Buffer)
if err := tt.MarshalCBOR(buf); err != nil {
b.Fatal(err)
}

var (
deferred cbg.Deferred
reader = bytes.NewReader(buf.Bytes())
)

b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
if err := cbg.ScanForLinks(bytes.NewReader(buf.Bytes()), func(cid.Cid) {}); err != nil {
reader.Seek(0, io.SeekStart)
if err := deferred.UnmarshalCBOR(reader); err != nil {
b.Fatal(err)
}
}
Expand Down
Loading

0 comments on commit 63aa96c

Please sign in to comment.