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  }