blob: 486f07b9380d38c6f09a0d9312c2b599159daa1c [file] [log] [blame]
// Copyright 2015 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmpbin
import (
"bytes"
)
// WriteableBytesBuffer is the interface which corresponds to the subset of
// *bytes.Buffer required for writing.
type WriteableBytesBuffer interface {
ReadableBytesBuffer
String() string
Bytes() []byte
Grow(int)
Write([]byte) (int, error)
WriteByte(c byte) error
WriteString(s string) (int, error)
}
// ReadableBytesBuffer is the interface which corresponds to the subset of
// *bytes.Reader required for reading.
type ReadableBytesBuffer interface {
Len() int
Read([]byte) (int, error)
ReadByte() (byte, error)
}
var (
_ WriteableBytesBuffer = (*bytes.Buffer)(nil)
_ ReadableBytesBuffer = (*bytes.Reader)(nil)
)
// InvertibleBytesBuffer is just like Buffer, except that it also has a stateful
// SetInvert() method, which will cause all reads and writes to/from it to be
// inverted (e.g. every byte XOR 0xFF).
//
// In contexts where you need comparable byte sequences, like datastore queries,
// it requires manipulating the sortable fields (e.g. synthesizing them,
// parsing them, etc.). In particular, when you have a reverse-sorted field
// (e.g. high to low instead of low to high), it's achieved by having all the
// bits inverted.
//
// Serialization formats (like gae/service/datastore.Serialize) can include
// delimiter information, which the parsers only know to parse non-inverted. If
// we don't have this Invertible buffer, we'd basically have to invert every
// byte in the []byte array when we're trying to decode a reverse-ordered field
// (including the bytes of all fields after the one we intend to parse) so that
// the parser can consume as many bytes as it needs (and it only knows the
// number of bytes it needs as it decodes them). This InvertibleBytesBuffer
// lets that happen on the fly without having to flip the whole underlying
// []byte.
//
// If you know you need it, you'll know it's the right thing.
// If you're not sure then you definitely don't need it!
//
// See InvertBytes for doing one-off []byte inversions.
type InvertibleBytesBuffer interface {
WriteableBytesBuffer
SetInvert(inverted bool)
}
type invertibleBytesBuffer struct {
WriteableBytesBuffer
invert bool
}
// Invertible returns an InvertibleBuffer based on the Buffer.
func Invertible(b WriteableBytesBuffer) InvertibleBytesBuffer {
return &invertibleBytesBuffer{b, false}
}
func (ib *invertibleBytesBuffer) Read(bs []byte) (int, error) {
n, err := ib.WriteableBytesBuffer.Read(bs)
if ib.invert {
for i, b := range bs {
bs[i] = b ^ 0xFF
}
}
return n, err
}
func (ib *invertibleBytesBuffer) WriteString(s string) (int, error) {
if ib.invert {
ib.Grow(len(s))
for i := 0; i < len(s); i++ {
if err := ib.WriteableBytesBuffer.WriteByte(s[i] ^ 0xFF); err != nil {
return i, err
}
}
return len(s), nil
}
return ib.WriteableBytesBuffer.WriteString(s)
}
func (ib *invertibleBytesBuffer) Write(bs []byte) (int, error) {
if ib.invert {
ib.Grow(len(bs))
for i, b := range bs {
if err := ib.WriteableBytesBuffer.WriteByte(b ^ 0xFF); err != nil {
return i, err
}
}
return len(bs), nil
}
return ib.WriteableBytesBuffer.Write(bs)
}
func (ib *invertibleBytesBuffer) WriteByte(b byte) error {
if ib.invert {
b = b ^ 0xFF
}
return ib.WriteableBytesBuffer.WriteByte(b)
}
func (ib *invertibleBytesBuffer) ReadByte() (byte, error) {
ret, err := ib.WriteableBytesBuffer.ReadByte()
if ib.invert {
ret = ret ^ 0xFF
}
return ret, err
}
func (ib *invertibleBytesBuffer) SetInvert(inverted bool) {
ib.invert = inverted
}