go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/data/cmpbin/invertible.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cmpbin 16 17 import ( 18 "bytes" 19 ) 20 21 // WriteableBytesBuffer is the interface which corresponds to the subset of 22 // *bytes.Buffer required for writing. 23 type WriteableBytesBuffer interface { 24 ReadableBytesBuffer 25 26 String() string 27 Bytes() []byte 28 29 Grow(int) 30 31 Write([]byte) (int, error) 32 WriteByte(c byte) error 33 WriteString(s string) (int, error) 34 } 35 36 // ReadableBytesBuffer is the interface which corresponds to the subset of 37 // *bytes.Reader required for reading. 38 type ReadableBytesBuffer interface { 39 Len() int 40 41 Read([]byte) (int, error) 42 ReadByte() (byte, error) 43 } 44 45 var ( 46 _ WriteableBytesBuffer = (*bytes.Buffer)(nil) 47 _ ReadableBytesBuffer = (*bytes.Reader)(nil) 48 ) 49 50 // InvertibleBytesBuffer is just like Buffer, except that it also has a stateful 51 // SetInvert() method, which will cause all reads and writes to/from it to be 52 // inverted (e.g. every byte XOR 0xFF). 53 // 54 // In contexts where you need comparable byte sequences, like datastore queries, 55 // it requires manipulating the sortable fields (e.g. synthesizing them, 56 // parsing them, etc.). In particular, when you have a reverse-sorted field 57 // (e.g. high to low instead of low to high), it's achieved by having all the 58 // bits inverted. 59 // 60 // Serialization formats (like gae/service/datastore.Serialize) can include 61 // delimiter information, which the parsers only know to parse non-inverted. If 62 // we don't have this Invertible buffer, we'd basically have to invert every 63 // byte in the []byte array when we're trying to decode a reverse-ordered field 64 // (including the bytes of all fields after the one we intend to parse) so that 65 // the parser can consume as many bytes as it needs (and it only knows the 66 // number of bytes it needs as it decodes them). This InvertibleBytesBuffer 67 // lets that happen on the fly without having to flip the whole underlying 68 // []byte. 69 // 70 // If you know you need it, you'll know it's the right thing. 71 // If you're not sure then you definitely don't need it! 72 // 73 // See InvertBytes for doing one-off []byte inversions. 74 type InvertibleBytesBuffer interface { 75 WriteableBytesBuffer 76 SetInvert(inverted bool) 77 } 78 79 type invertibleBytesBuffer struct { 80 WriteableBytesBuffer 81 invert bool 82 } 83 84 // Invertible returns an InvertibleBuffer based on the Buffer. 85 func Invertible(b WriteableBytesBuffer) InvertibleBytesBuffer { 86 return &invertibleBytesBuffer{b, false} 87 } 88 89 func (ib *invertibleBytesBuffer) Read(bs []byte) (int, error) { 90 n, err := ib.WriteableBytesBuffer.Read(bs) 91 if ib.invert { 92 for i, b := range bs { 93 bs[i] = b ^ 0xFF 94 } 95 } 96 return n, err 97 } 98 99 func (ib *invertibleBytesBuffer) WriteString(s string) (int, error) { 100 if ib.invert { 101 ib.Grow(len(s)) 102 for i := 0; i < len(s); i++ { 103 if err := ib.WriteableBytesBuffer.WriteByte(s[i] ^ 0xFF); err != nil { 104 return i, err 105 } 106 } 107 return len(s), nil 108 } 109 return ib.WriteableBytesBuffer.WriteString(s) 110 } 111 112 func (ib *invertibleBytesBuffer) Write(bs []byte) (int, error) { 113 if ib.invert { 114 ib.Grow(len(bs)) 115 for i, b := range bs { 116 if err := ib.WriteableBytesBuffer.WriteByte(b ^ 0xFF); err != nil { 117 return i, err 118 } 119 } 120 return len(bs), nil 121 } 122 return ib.WriteableBytesBuffer.Write(bs) 123 } 124 125 func (ib *invertibleBytesBuffer) WriteByte(b byte) error { 126 if ib.invert { 127 b = b ^ 0xFF 128 } 129 return ib.WriteableBytesBuffer.WriteByte(b) 130 } 131 132 func (ib *invertibleBytesBuffer) ReadByte() (byte, error) { 133 ret, err := ib.WriteableBytesBuffer.ReadByte() 134 if ib.invert { 135 ret = ret ^ 0xFF 136 } 137 return ret, err 138 } 139 140 func (ib *invertibleBytesBuffer) SetInvert(inverted bool) { 141 ib.invert = inverted 142 }