github.com/segmentio/encoding@v0.4.0/proto/bytes.go (about) 1 package proto 2 3 import ( 4 "fmt" 5 "io" 6 "reflect" 7 "unsafe" 8 ) 9 10 var bytesCodec = codec{ 11 wire: varlen, 12 size: sizeOfBytes, 13 encode: encodeBytes, 14 decode: decodeBytes, 15 } 16 17 func sizeOfBytes(p unsafe.Pointer, flags flags) int { 18 if p != nil { 19 if v := *(*[]byte)(p); v != nil || flags.has(wantzero) { 20 return sizeOfVarlen(len(v)) 21 } 22 } 23 return 0 24 } 25 26 func encodeBytes(b []byte, p unsafe.Pointer, flags flags) (int, error) { 27 if p != nil { 28 if v := *(*[]byte)(p); v != nil || flags.has(wantzero) { 29 n, err := encodeVarint(b, uint64(len(v))) 30 if err != nil { 31 return n, err 32 } 33 c := copy(b[n:], v) 34 n += c 35 if c < len(v) { 36 err = io.ErrShortBuffer 37 } 38 return n, err 39 } 40 } 41 return 0, nil 42 } 43 44 func decodeBytes(b []byte, p unsafe.Pointer, _ flags) (int, error) { 45 v, n, err := decodeVarlen(b) 46 pb := (*[]byte)(p) 47 if *pb == nil { 48 *pb = make([]byte, 0, len(v)) 49 } 50 *pb = append((*pb)[:0], v...) 51 return n, err 52 } 53 54 func makeBytes(p unsafe.Pointer, n int) []byte { 55 return *(*[]byte)(unsafe.Pointer(&sliceHeader{ 56 Data: p, 57 Len: n, 58 Cap: n, 59 })) 60 } 61 62 type sliceHeader struct { 63 Data unsafe.Pointer 64 Len int 65 Cap int 66 } 67 68 // isZeroBytes is an optimized version of this loop: 69 // 70 // for i := range b { 71 // if b[i] != 0 { 72 // return false 73 // } 74 // } 75 // return true 76 // 77 // This implementation significantly reduces the CPU footprint of checking for 78 // slices to be zero, especially when the length increases (these cases should 79 // be rare tho). 80 // 81 // name old time/op new time/op delta 82 // IsZeroBytes0 1.78ns ± 1% 2.29ns ± 4% +28.65% (p=0.000 n=8+10) 83 // IsZeroBytes4 3.17ns ± 3% 2.37ns ± 3% -25.21% (p=0.000 n=10+10) 84 // IsZeroBytes7 3.97ns ± 4% 3.26ns ± 3% -18.02% (p=0.000 n=10+10) 85 // IsZeroBytes64K 14.8µs ± 3% 1.9µs ± 3% -87.34% (p=0.000 n=10+10) 86 func isZeroBytes(b []byte) bool { 87 if n := len(b) / 8; n != 0 { 88 if !isZeroUint64(*(*[]uint64)(unsafe.Pointer(&sliceHeader{ 89 Data: unsafe.Pointer(&b[0]), 90 Len: n, 91 Cap: n, 92 }))) { 93 return false 94 } 95 b = b[n*8:] 96 } 97 switch len(b) { 98 case 7: 99 return bto32(b) == 0 && bto16(b[4:]) == 0 && b[6] == 0 100 case 6: 101 return bto32(b) == 0 && bto16(b[4:]) == 0 102 case 5: 103 return bto32(b) == 0 && b[4] == 0 104 case 4: 105 return bto32(b) == 0 106 case 3: 107 return bto16(b) == 0 && b[2] == 0 108 case 2: 109 return bto16(b) == 0 110 case 1: 111 return b[0] == 0 112 default: 113 return true 114 } 115 } 116 117 func bto32(b []byte) uint32 { 118 return *(*uint32)(unsafe.Pointer(&b[0])) 119 } 120 121 func bto16(b []byte) uint16 { 122 return *(*uint16)(unsafe.Pointer(&b[0])) 123 } 124 125 func isZeroUint64(b []uint64) bool { 126 for i := range b { 127 if b[i] != 0 { 128 return false 129 } 130 } 131 return true 132 } 133 134 func byteArrayCodecOf(t reflect.Type, seen map[reflect.Type]*codec) *codec { 135 n := t.Len() 136 c := &codec{ 137 wire: varlen, 138 size: byteArraySizeFuncOf(n), 139 encode: byteArrayEncodeFuncOf(n), 140 decode: byteArrayDecodeFuncOf(n), 141 } 142 seen[t] = c 143 return c 144 } 145 146 func byteArraySizeFuncOf(n int) sizeFunc { 147 size := sizeOfVarlen(n) 148 return func(p unsafe.Pointer, flags flags) int { 149 if p != nil && (flags.has(wantzero) || !isZeroBytes(makeBytes(p, n))) { 150 return size 151 } 152 return 0 153 } 154 } 155 156 func byteArrayEncodeFuncOf(n int) encodeFunc { 157 return func(b []byte, p unsafe.Pointer, flags flags) (int, error) { 158 if p != nil { 159 if v := makeBytes(p, n); flags.has(wantzero) || !isZeroBytes(v) { 160 return encodeBytes(b, unsafe.Pointer(&v), noflags) 161 } 162 } 163 return 0, nil 164 } 165 } 166 167 func byteArrayDecodeFuncOf(n int) decodeFunc { 168 return func(b []byte, p unsafe.Pointer, _ flags) (int, error) { 169 v, r, err := decodeVarlen(b) 170 if err == nil { 171 if copy(makeBytes(p, n), v) != n { 172 err = fmt.Errorf("cannot decode byte sequence of size %d into byte array of size %d", len(v), n) 173 } 174 } 175 return r, err 176 } 177 }