diff --git a/cobs.go b/cobs.go index 12e3c91..5927bda 100644 --- a/cobs.go +++ b/cobs.go @@ -35,7 +35,9 @@ func TrimNull(p []byte) []byte { } // Encode a null-terminated slice of bytes to a cobs frame -func Encode(p []byte) (b []byte) { +func Encode(p []byte) []byte { + // preallocate output buffer with estimated size + b := make([]byte, 0, EncodedSize(len(p))) var x [0xff]byte x[0] = 1 for _, v := range p { @@ -58,7 +60,9 @@ func Encode(p []byte) (b []byte) { } // Decode a cobs frame to a null-terminated slice of bytes -func Decode(p []byte) (b []byte) { +func Decode(p []byte) []byte { + // preallocate output buffer, data will be smaller than input + b := make([]byte, 0, len(p)) for len(p) > 0 { n, data := p[0], p[1:] // invalid frame, abort diff --git a/cobs_test.go b/cobs_test.go index 2d97db8..8119a86 100644 --- a/cobs_test.go +++ b/cobs_test.go @@ -388,3 +388,42 @@ func ExampleEncode() { // Hello: [6 72 101 108 108 111] // [6 72 101 108 108 111]: Hello } + +func benchmarkEncode(i int, b *testing.B) { + data := make([]byte, i) + for i := range data { + data[i] = byte(i % 255) + } + data[len(data)-1] = 0 // zero-terminate input + + b.ResetTimer() + for i := 0; i < b.N; i++ { + Encode(data) + } +} + +func benchmarkDecode(i int, b *testing.B) { + data := make([]byte, i) + for i := range data { + data[i] = byte(i % 255) + } + data[len(data)-1] = 0 // zero-terminate input + enc := Encode(data) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + Decode(enc) + } +} + +func BenchmarkCobs(b *testing.B) { + cases := []int{32, 256, 1024, 4096} + for _, c := range cases { + b.Run(fmt.Sprintf("%d/Encode", c), func(b *testing.B) { + benchmarkEncode(c, b) + }) + b.Run(fmt.Sprintf("%d/Decode", c), func(b *testing.B) { + benchmarkDecode(c, b) + }) + } +}