From dae698cd6d0ec01c9dee61a3cf822f494725b542 Mon Sep 17 00:00:00 2001 From: Joubin Muhammad Houshyar Date: Thu, 9 Jun 2016 12:47:47 -0400 Subject: [PATCH 1/4] common: enhanced performance for BytesToBig * allocate padded buffer only if padding is possible * get bytes only once: each call to big.Int.Bytes() allocates. * nop - be explicit in func comment regarding valid input args. --- common/big.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/common/big.go b/common/big.go index 4ce87ee0c6..5e95224ef0 100644 --- a/common/big.go +++ b/common/big.go @@ -116,14 +116,19 @@ func FirstBitSet(v *big.Int) int { // // Returns the bytes of a big integer with the size specified by **base** // Attempts to pad the byte array with zeros. +// +// Note that input arg **num** is expected to be non-nil; +// nil argument will result in runtime panic. func BigToBytes(num *big.Int, base int) []byte { - ret := make([]byte, base/8) + bytes := num.Bytes() + blen := len(bytes) + bpb := base / 8 - if len(num.Bytes()) > base/8 { - return num.Bytes() + if blen > bpb { + return bytes } - - return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...) + padBuf := make([]byte, bpb) + return append(padBuf[:bpb-blen], bytes...) } // Big copy From d297ea7dd83d431310c580b6d781442ab1dc81f7 Mon Sep 17 00:00:00 2001 From: Joubin Muhammad Houshyar Date: Thu, 9 Jun 2016 19:11:36 -0400 Subject: [PATCH 2/4] common: cleanup CopyBytes and associated test --- common/bytes.go | 7 +------ common/bytes_test.go | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/common/bytes.go b/common/bytes.go index 4fb016a976..0fd5fa6255 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -107,14 +107,9 @@ func ReadVarInt(buff []byte) (ret uint64) { return } -// Copy bytes -// // Returns an exact copy of the provided bytes func CopyBytes(b []byte) (copiedBytes []byte) { - copiedBytes = make([]byte, len(b)) - copy(copiedBytes, b) - - return + return append([]byte{}, b...) } func HasHexPrefix(str string) bool { diff --git a/common/bytes_test.go b/common/bytes_test.go index 2e52084777..6b739ee622 100644 --- a/common/bytes_test.go +++ b/common/bytes_test.go @@ -76,10 +76,21 @@ func (s *BytesSuite) TestReadVarInt(c *checker.C) { } func (s *BytesSuite) TestCopyBytes(c *checker.C) { - data1 := []byte{1, 2, 3, 4} - exp1 := []byte{1, 2, 3, 4} - res1 := CopyBytes(data1) - c.Assert(res1, checker.DeepEquals, exp1) + data := [][]byte{ + []byte{0x00, 0x7f, 0x80, 0xff}, // interesting bytes + []byte{}, // empty case + } + + // test + for i := 0; i < len(data); i++ { + have := CopyBytes(data[i]) + if data[i] != nil { + // make sure we're not just returning the input arg + c.Assert(&have, checker.Not(checker.Equals), &data[i]) + } + // verify deep copy + c.Assert(have, checker.DeepEquals, data[i]) + } } func (s *BytesSuite) TestIsHex(c *checker.C) { From 78afc228ee2d3ae6eb8df91b5cb87d650c76b229 Mon Sep 17 00:00:00 2001 From: Joubin Muhammad Houshyar Date: Fri, 10 Jun 2016 05:41:41 -0400 Subject: [PATCH 3/4] common: fix BytesToNumber - remove redundant ReadVarInt * original completely broken with meaningless test. * revu: this appears to be deadcode but fixed to prevent bitrot. * todo: consider removing this function entirely. Both functions appear to be dead code. --- common/bytes.go | 56 +++++++++++---------------------------- common/bytes_test.go | 63 ++++++++++++++++++++++---------------------- 2 files changed, 47 insertions(+), 72 deletions(-) diff --git a/common/bytes.go b/common/bytes.go index 0fd5fa6255..59ff458127 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -61,50 +61,24 @@ func NumberToBytes(num interface{}, bits int) []byte { return buf.Bytes()[buf.Len()-(bits/8):] } -// Bytes to number -// -// Attempts to cast a byte slice to a unsigned integer +// Converts byte slice to a unsigned integer. Bytes are +// interpreted as big-endian image of unsigned 64bit integer. +// Slices less than 8 bytes are left padded before conversion; +// For slices larger than 8 bytes the trailing bytes are ignored. func BytesToNumber(b []byte) uint64 { - var number uint64 + var data = LeftPadBytes(b, 8) - // Make sure the buffer is 64bits - data := make([]byte, 8) - data = append(data[:len(b)], b...) + var n uint64 + n |= uint64(data[0]) << 56 + n |= uint64(data[1]) << 48 + n |= uint64(data[2]) << 40 + n |= uint64(data[3]) << 32 + n |= uint64(data[4]) << 24 + n |= uint64(data[5]) << 16 + n |= uint64(data[6]) << 8 + n |= uint64(data[7]) - buf := bytes.NewReader(data) - err := binary.Read(buf, binary.BigEndian, &number) - if err != nil { - fmt.Println("BytesToNumber failed:", err) - } - - return number -} - -// Read variable int -// -// Read a variable length number in big endian byte order -func ReadVarInt(buff []byte) (ret uint64) { - switch l := len(buff); { - case l > 4: - d := LeftPadBytes(buff, 8) - binary.Read(bytes.NewReader(d), binary.BigEndian, &ret) - case l > 2: - var num uint32 - d := LeftPadBytes(buff, 4) - binary.Read(bytes.NewReader(d), binary.BigEndian, &num) - ret = uint64(num) - case l > 1: - var num uint16 - d := LeftPadBytes(buff, 2) - binary.Read(bytes.NewReader(d), binary.BigEndian, &num) - ret = uint64(num) - default: - var num uint8 - binary.Read(bytes.NewReader(buff), binary.BigEndian, &num) - ret = uint64(num) - } - - return + return n } // Returns an exact copy of the provided bytes diff --git a/common/bytes_test.go b/common/bytes_test.go index 6b739ee622..49b4796a56 100644 --- a/common/bytes_test.go +++ b/common/bytes_test.go @@ -39,40 +39,41 @@ func (s *BytesSuite) TestNumberToBytes(c *checker.C) { } func (s *BytesSuite) TestBytesToNumber(c *checker.C) { - datasmall := []byte{0xe9, 0x38, 0xe9, 0x38} - datalarge := []byte{0xe9, 0x38, 0xe9, 0x38, 0xe9, 0x38, 0xe9, 0x38} + data := [][]byte{ + // check for zero-len + []byte{}, + // check for len < 8 + []byte{0xfe}, + []byte{0xca, 0xfe}, + []byte{0xca, 0xfe, 0xba, 0xbe}, + // check case len < 8 + []byte{0x7f, 0x6f, 0x5f, 0x4f, 0x3f, 0x2f, 0x1f, 0x0f}, + // check case len = 8 + []byte{0x7f, 0x6f, 0x5f, 0x4f, 0x3f, 0x2f, 0x1f, 0x0f, 0xba, 0xdc, 0x0d}, + } + expected := []uint64{ + 0x0, + 0xfe, + 0xcafe, + 0xcafebabe, + 0x7f6f5f4f3f2f1f0f, + 0x7f6f5f4f3f2f1f0f, + } - var expsmall uint64 = 0xe938e938 - var explarge uint64 = 0x0 + // assert-test-invariants: + c.Assert(len(data[0]), checker.Equals, 0) + c.Assert(len(data[1]), checker.Equals, 1) + c.Assert(len(data[2]), checker.Equals, 2) + c.Assert(len(data[3]), checker.Equals, 4) + c.Assert(len(data[4]), checker.Equals, 8) + c.Assert(len(data[5]), checker.Equals, 11) - ressmall := BytesToNumber(datasmall) - reslarge := BytesToNumber(datalarge) + c.Assert(len(expected), checker.Equals, len(data)) - c.Assert(ressmall, checker.Equals, expsmall) - c.Assert(reslarge, checker.Equals, explarge) - -} - -func (s *BytesSuite) TestReadVarInt(c *checker.C) { - data8 := []byte{1, 2, 3, 4, 5, 6, 7, 8} - data4 := []byte{1, 2, 3, 4} - data2 := []byte{1, 2} - data1 := []byte{1} - - exp8 := uint64(72623859790382856) - exp4 := uint64(16909060) - exp2 := uint64(258) - exp1 := uint64(1) - - res8 := ReadVarInt(data8) - res4 := ReadVarInt(data4) - res2 := ReadVarInt(data2) - res1 := ReadVarInt(data1) - - c.Assert(res8, checker.Equals, exp8) - c.Assert(res4, checker.Equals, exp4) - c.Assert(res2, checker.Equals, exp2) - c.Assert(res1, checker.Equals, exp1) + for i := 0; i < len(data); i++ { + have := BytesToNumber(data[i]) + c.Assert(have, checker.Equals, expected[i]) + } } func (s *BytesSuite) TestCopyBytes(c *checker.C) { From de243b05f3d07f08aa9d009b8682e6d3b198b33e Mon Sep 17 00:00:00 2001 From: Joubin Muhammad Houshyar Date: Fri, 10 Jun 2016 06:34:48 -0400 Subject: [PATCH 4/4] common: add pkgdoc.go for package godoc. --- common/bytes.go | 3 +-- common/pkgdoc.go | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 common/pkgdoc.go diff --git a/common/bytes.go b/common/bytes.go index 59ff458127..291e784fe9 100644 --- a/common/bytes.go +++ b/common/bytes.go @@ -14,7 +14,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// Package common contains various helper functions. package common import ( @@ -106,7 +105,7 @@ func Hex2Bytes(str string) []byte { return h } -func Hex2BytesFixed(str string, flen int) []byte { +func HexToBytesFixed(str string, flen int) []byte { h, _ := hex.DecodeString(str) if len(h) == flen { return h diff --git a/common/pkgdoc.go b/common/pkgdoc.go new file mode 100644 index 0000000000..72d08f8159 --- /dev/null +++ b/common/pkgdoc.go @@ -0,0 +1,18 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package common contains various helper functions. +package common