blob: 2f25793609e74e782c63337067d2718435712042 [file] [log] [blame]
/*
* Copyright 2018 The Kythe Authors. All rights reserved.
*
* 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 riegeli
import (
"encoding/binary"
"io"
"github.com/golang/protobuf/proto"
)
// A tagID is an overload of the protocol buffer wire key encodings.
type tagID uint32
const (
noOpTag tagID = iota
nonProtoTag
startOfSubmessageTag
startOfMessageTag
rootTag // never encoded
)
// protoWireTypes are the standard protocol buffer wire key types with the
// addition of the Riegeli protoSubmessageType.
type protoWireType int
const (
protoVarintType protoWireType = proto.WireVarint
protoFixed64Type = proto.WireFixed64
protoBytesType = proto.WireBytes
protoStartGroupType = proto.WireStartGroup
protoEndGroupType = proto.WireEndGroup
protoFixed32Type = proto.WireFixed32
protoSubmessageType = 6
)
// A tagSubtype differentiates varint/delimited tag types.
type tagSubtype int
const (
trivialSubtype tagSubtype = 0
// proto.WireVarint subtypes
varint1Subtype = 0
varintMaxSubtype = varint1Subtype + binary.MaxVarintLen64 - 1
varintInline0Subtype = varintMaxSubtype + 1
varintInlineMaxSubtype = varintInline0Subtype + 0x7f
// proto.WireBytes subtypes
delimitedStringSubtype = 0
delimitedStartOfSubmessageSubtype = 1
delimitedEndOfSubmessageSubtype = 2
)
// hasDataBuffer returns whether a tag/subtype pairing has an associated data
// buffer.
func hasDataBuffer(tag uint64, subtype tagSubtype) bool {
switch protoWireType(tag & 7) {
case protoVarintType:
return subtype < varintInline0Subtype
case protoFixed32Type, protoFixed64Type:
return true
case protoBytesType:
return subtype == delimitedStringSubtype
default:
return false
}
}
// validProtoTag returns whether a tag is an actual protocol buffer tag (rather
// than a Riegeli overload).
func validProtoTag(tag uint64) bool {
switch protoWireType(tag & 7) {
case protoVarintType, protoFixed32Type, protoFixed64Type, protoBytesType, protoStartGroupType, protoEndGroupType:
return tag >= 8
default:
return false
}
}
// hasSubtype returns whether a tag has an associated subtype.
func hasSubtype(tag uint64) bool {
if protoWireType(tag&7) == protoVarintType {
return true
}
return false
}
// A backwardWriter buffers each []byte pushed into it and allows them to be
// read in reverse order.
//
// Note: this is necessary for transposed Riegeli records because each of theirs
// tags are encoded in reverse order
type backwardWriter struct {
pieces [][]byte
size int
}
// Reset resets the writer to an empty state.
func (w *backwardWriter) Reset() { *w = backwardWriter{} }
// Push adds the given []byte to the front of the data to be read.
func (w *backwardWriter) Push(b []byte) {
if len(b) != 0 {
w.pieces = append(w.pieces, b)
w.size += len(b)
}
}
// PushUvarint encodes and pushes a uvarint to the front of the data to be read.
func (w *backwardWriter) PushUvarint(n uint64) {
var buf [binary.MaxVarintLen64]byte
size := binary.PutUvarint(buf[:], n)
w.Push(buf[:size])
}
// Len returns the total size of data left to be read.
func (w *backwardWriter) Len() int { return w.size }
// Read implements the io.Reader interface. It reads the []byte buffers in
// reverse order of how they were pushed.
func (w *backwardWriter) Read(b []byte) (n int, err error) {
if len(w.pieces) == 0 {
return 0, io.EOF
}
for p := len(w.pieces) - 1; p >= 0; p-- {
left := len(b) - n
piece := w.pieces[p]
if left >= len(piece) {
copy(b[n:], piece)
n += len(piece)
w.size -= len(piece)
w.pieces = w.pieces[:p]
continue
}
copy(b[n:], piece[:left])
n += left
w.size -= left
w.pieces[p] = piece[left:]
break
}
return
}