diff --git a/rlp/decode.go b/rlp/decode.go
index e4262b64df9c6bee72bf4cf994a0067a67caadd6..8121ab2e72a5ad938757762ab40bb9be9651ff22 100644
--- a/rlp/decode.go
+++ b/rlp/decode.go
@@ -348,25 +348,23 @@ func decodeByteArray(s *Stream, val reflect.Value) error {
 	if err != nil {
 		return err
 	}
-	vlen := val.Len()
+	slice := byteArrayBytes(val)
 	switch kind {
 	case Byte:
-		if vlen == 0 {
+		if len(slice) == 0 {
 			return &decodeError{msg: "input string too long", typ: val.Type()}
-		}
-		if vlen > 1 {
+		} else if len(slice) > 1 {
 			return &decodeError{msg: "input string too short", typ: val.Type()}
 		}
-		bv, _ := s.Uint()
-		val.Index(0).SetUint(bv)
+		slice[0] = s.byteval
+		s.kind = -1
 	case String:
-		if uint64(vlen) < size {
+		if uint64(len(slice)) < size {
 			return &decodeError{msg: "input string too long", typ: val.Type()}
 		}
-		if uint64(vlen) > size {
+		if uint64(len(slice)) > size {
 			return &decodeError{msg: "input string too short", typ: val.Type()}
 		}
-		slice := val.Slice(0, vlen).Interface().([]byte)
 		if err := s.readFull(slice); err != nil {
 			return err
 		}
diff --git a/rlp/decode_test.go b/rlp/decode_test.go
index 87a330633230993fa64d00419542d4f3c62ee3e0..36d254e18eb9a95f5b4c25990aaf62318c43a4dd 100644
--- a/rlp/decode_test.go
+++ b/rlp/decode_test.go
@@ -26,6 +26,8 @@ import (
 	"reflect"
 	"strings"
 	"testing"
+
+	"github.com/ethereum/go-ethereum/common/math"
 )
 
 func TestStreamKind(t *testing.T) {
@@ -1063,7 +1065,7 @@ func ExampleStream() {
 	// [102 111 111 98 97 114] <nil>
 }
 
-func BenchmarkDecode(b *testing.B) {
+func BenchmarkDecodeUints(b *testing.B) {
 	enc := encodeTestSlice(90000)
 	b.SetBytes(int64(len(enc)))
 	b.ReportAllocs()
@@ -1078,7 +1080,7 @@ func BenchmarkDecode(b *testing.B) {
 	}
 }
 
-func BenchmarkDecodeIntSliceReuse(b *testing.B) {
+func BenchmarkDecodeUintsReused(b *testing.B) {
 	enc := encodeTestSlice(100000)
 	b.SetBytes(int64(len(enc)))
 	b.ReportAllocs()
@@ -1093,6 +1095,44 @@ func BenchmarkDecodeIntSliceReuse(b *testing.B) {
 	}
 }
 
+func BenchmarkDecodeByteArrayStruct(b *testing.B) {
+	enc, err := EncodeToBytes(&byteArrayStruct{})
+	if err != nil {
+		b.Fatal(err)
+	}
+	b.SetBytes(int64(len(enc)))
+	b.ReportAllocs()
+	b.ResetTimer()
+
+	var out byteArrayStruct
+	for i := 0; i < b.N; i++ {
+		if err := DecodeBytes(enc, &out); err != nil {
+			b.Fatal(err)
+		}
+	}
+}
+
+func BenchmarkDecodeBigInts(b *testing.B) {
+	ints := make([]*big.Int, 200)
+	for i := range ints {
+		ints[i] = math.BigPow(2, int64(i))
+	}
+	enc, err := EncodeToBytes(ints)
+	if err != nil {
+		b.Fatal(err)
+	}
+	b.SetBytes(int64(len(enc)))
+	b.ReportAllocs()
+	b.ResetTimer()
+
+	var out []*big.Int
+	for i := 0; i < b.N; i++ {
+		if err := DecodeBytes(enc, &out); err != nil {
+			b.Fatal(err)
+		}
+	}
+}
+
 func encodeTestSlice(n uint) []byte {
 	s := make([]uint, n)
 	for i := uint(0); i < n; i++ {
diff --git a/rlp/encode.go b/rlp/encode.go
index 2e1b0102caae87cd18a28921337972f578d543fd..3348644342965ec2de14766b1cfed0ebe7d02edc 100644
--- a/rlp/encode.go
+++ b/rlp/encode.go
@@ -124,19 +124,15 @@ func puthead(buf []byte, smalltag, largetag byte, size uint64) int {
 }
 
 type encbuf struct {
-	str      []byte        // string data, contains everything except list headers
-	lheads   []listhead    // all list headers
-	lhsize   int           // sum of sizes of all encoded list headers
-	sizebuf  [9]byte       // auxiliary buffer for uint encoding
-	bufvalue reflect.Value // used in writeByteArrayCopy
+	str     []byte     // string data, contains everything except list headers
+	lheads  []listhead // all list headers
+	lhsize  int        // sum of sizes of all encoded list headers
+	sizebuf [9]byte    // auxiliary buffer for uint encoding
 }
 
 // encbufs are pooled.
 var encbufPool = sync.Pool{
-	New: func() interface{} {
-		var bytes []byte
-		return &encbuf{bufvalue: reflect.ValueOf(&bytes).Elem()}
-	},
+	New: func() interface{} { return new(encbuf) },
 }
 
 func (w *encbuf) reset() {
@@ -429,21 +425,14 @@ func writeBytes(val reflect.Value, w *encbuf) error {
 	return nil
 }
 
-var byteType = reflect.TypeOf(byte(0))
-
 func makeByteArrayWriter(typ reflect.Type) writer {
-	length := typ.Len()
-	if length == 0 {
+	switch typ.Len() {
+	case 0:
 		return writeLengthZeroByteArray
-	} else if length == 1 {
+	case 1:
 		return writeLengthOneByteArray
-	}
-	if typ.Elem() != byteType {
-		return writeNamedByteArray
-	}
-	return func(val reflect.Value, w *encbuf) error {
-		writeByteArrayCopy(length, val, w)
-		return nil
+	default:
+		return writeByteArray
 	}
 }
 
@@ -462,29 +451,18 @@ func writeLengthOneByteArray(val reflect.Value, w *encbuf) error {
 	return nil
 }
 
-// writeByteArrayCopy encodes byte arrays using reflect.Copy. This is
-// the fast path for [N]byte where N > 1.
-func writeByteArrayCopy(length int, val reflect.Value, w *encbuf) {
-	w.encodeStringHeader(length)
-	offset := len(w.str)
-	w.str = append(w.str, make([]byte, length)...)
-	w.bufvalue.SetBytes(w.str[offset:])
-	reflect.Copy(w.bufvalue, val)
-}
-
-// writeNamedByteArray encodes byte arrays with named element type.
-// This exists because reflect.Copy can't be used with such types.
-func writeNamedByteArray(val reflect.Value, w *encbuf) error {
+func writeByteArray(val reflect.Value, w *encbuf) error {
 	if !val.CanAddr() {
-		// Slice requires the value to be addressable.
-		// Make it addressable by copying.
+		// Getting the byte slice of val requires it to be addressable. Make it
+		// addressable by copying.
 		copy := reflect.New(val.Type()).Elem()
 		copy.Set(val)
 		val = copy
 	}
-	size := val.Len()
-	slice := val.Slice(0, size).Bytes()
-	w.encodeString(slice)
+
+	slice := byteArrayBytes(val)
+	w.encodeStringHeader(len(slice))
+	w.str = append(w.str, slice...)
 	return nil
 }
 
diff --git a/rlp/encode_test.go b/rlp/encode_test.go
index 0177bb0350d97649050f991cca50381ee70000de..08a2a84c623a0057f9d2f2cadccbff4dab28c88f 100644
--- a/rlp/encode_test.go
+++ b/rlp/encode_test.go
@@ -513,3 +513,22 @@ func BenchmarkEncodeConcurrentInterface(b *testing.B) {
 	}
 	wg.Wait()
 }
+
+type byteArrayStruct struct {
+	A [20]byte
+	B [32]byte
+	C [32]byte
+}
+
+func BenchmarkEncodeByteArrayStruct(b *testing.B) {
+	var out bytes.Buffer
+	var value byteArrayStruct
+
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		out.Reset()
+		if err := Encode(&out, &value); err != nil {
+			b.Fatal(err)
+		}
+	}
+}
diff --git a/rlp/safe.go b/rlp/safe.go
new file mode 100644
index 0000000000000000000000000000000000000000..c881650a0df356b74758c7bcfc7ca84785d5b454
--- /dev/null
+++ b/rlp/safe.go
@@ -0,0 +1,26 @@
+// Copyright 2021 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 <http://www.gnu.org/licenses/>.
+
+// +build nacl js !cgo
+
+package rlp
+
+import "reflect"
+
+// byteArrayBytes returns a slice of the byte array v.
+func byteArrayBytes(v reflect.Value) []byte {
+    return v.Slice(0, v.Len()).Bytes()
+}
diff --git a/rlp/unsafe.go b/rlp/unsafe.go
new file mode 100644
index 0000000000000000000000000000000000000000..94ed5405a8e4e21fc7ffa81bf41cd14f155540f3
--- /dev/null
+++ b/rlp/unsafe.go
@@ -0,0 +1,35 @@
+// Copyright 2021 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 <http://www.gnu.org/licenses/>.
+
+// +build !nacl,!js,cgo
+
+package rlp
+
+import (
+	"reflect"
+	"unsafe"
+)
+
+// byteArrayBytes returns a slice of the byte array v.
+func byteArrayBytes(v reflect.Value) []byte {
+	len := v.Len()
+	var s []byte
+	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))
+	hdr.Data = v.UnsafeAddr()
+	hdr.Cap = len
+	hdr.Len = len
+	return s
+}