summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/couchbase/vellum/encoder_v1.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/couchbase/vellum/encoder_v1.go')
-rw-r--r--vendor/github.com/couchbase/vellum/encoder_v1.go227
1 files changed, 227 insertions, 0 deletions
diff --git a/vendor/github.com/couchbase/vellum/encoder_v1.go b/vendor/github.com/couchbase/vellum/encoder_v1.go
new file mode 100644
index 0000000000..0651fc8614
--- /dev/null
+++ b/vendor/github.com/couchbase/vellum/encoder_v1.go
@@ -0,0 +1,227 @@
+// Copyright (c) 2017 Couchbase, Inc.
+//
+// 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 vellum
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+)
+
+const versionV1 = 1
+const oneTransition = 1 << 7
+const transitionNext = 1 << 6
+const stateFinal = 1 << 6
+const footerSizeV1 = 16
+
+func init() {
+ registerEncoder(versionV1, func(w io.Writer) encoder {
+ return newEncoderV1(w)
+ })
+}
+
+type encoderV1 struct {
+ bw *writer
+}
+
+func newEncoderV1(w io.Writer) *encoderV1 {
+ return &encoderV1{
+ bw: newWriter(w),
+ }
+}
+
+func (e *encoderV1) reset(w io.Writer) {
+ e.bw.Reset(w)
+}
+
+func (e *encoderV1) start() error {
+ header := make([]byte, headerSize)
+ binary.LittleEndian.PutUint64(header, versionV1)
+ binary.LittleEndian.PutUint64(header[8:], uint64(0)) // type
+ n, err := e.bw.Write(header)
+ if err != nil {
+ return err
+ }
+ if n != headerSize {
+ return fmt.Errorf("short write of header %d/%d", n, headerSize)
+ }
+ return nil
+}
+
+func (e *encoderV1) encodeState(s *builderNode, lastAddr int) (int, error) {
+ if len(s.trans) == 0 && s.final && s.finalOutput == 0 {
+ return 0, nil
+ } else if len(s.trans) != 1 || s.final {
+ return e.encodeStateMany(s)
+ } else if !s.final && s.trans[0].out == 0 && s.trans[0].addr == lastAddr {
+ return e.encodeStateOneFinish(s, transitionNext)
+ }
+ return e.encodeStateOne(s)
+}
+
+func (e *encoderV1) encodeStateOne(s *builderNode) (int, error) {
+ start := uint64(e.bw.counter)
+ outPackSize := 0
+ if s.trans[0].out != 0 {
+ outPackSize = packedSize(s.trans[0].out)
+ err := e.bw.WritePackedUintIn(s.trans[0].out, outPackSize)
+ if err != nil {
+ return 0, err
+ }
+ }
+ delta := deltaAddr(start, uint64(s.trans[0].addr))
+ transPackSize := packedSize(delta)
+ err := e.bw.WritePackedUintIn(delta, transPackSize)
+ if err != nil {
+ return 0, err
+ }
+
+ packSize := encodePackSize(transPackSize, outPackSize)
+ err = e.bw.WriteByte(packSize)
+ if err != nil {
+ return 0, err
+ }
+
+ return e.encodeStateOneFinish(s, 0)
+}
+
+func (e *encoderV1) encodeStateOneFinish(s *builderNode, next byte) (int, error) {
+ enc := encodeCommon(s.trans[0].in)
+
+ // not a common input
+ if enc == 0 {
+ err := e.bw.WriteByte(s.trans[0].in)
+ if err != nil {
+ return 0, err
+ }
+ }
+ err := e.bw.WriteByte(oneTransition | next | enc)
+ if err != nil {
+ return 0, err
+ }
+
+ return e.bw.counter - 1, nil
+}
+
+func (e *encoderV1) encodeStateMany(s *builderNode) (int, error) {
+ start := uint64(e.bw.counter)
+ transPackSize := 0
+ outPackSize := packedSize(s.finalOutput)
+ anyOutputs := s.finalOutput != 0
+ for i := range s.trans {
+ delta := deltaAddr(start, uint64(s.trans[i].addr))
+ tsize := packedSize(delta)
+ if tsize > transPackSize {
+ transPackSize = tsize
+ }
+ osize := packedSize(s.trans[i].out)
+ if osize > outPackSize {
+ outPackSize = osize
+ }
+ anyOutputs = anyOutputs || s.trans[i].out != 0
+ }
+ if !anyOutputs {
+ outPackSize = 0
+ }
+
+ if anyOutputs {
+ // output final value
+ if s.final {
+ err := e.bw.WritePackedUintIn(s.finalOutput, outPackSize)
+ if err != nil {
+ return 0, err
+ }
+ }
+ // output transition values (in reverse)
+ for j := len(s.trans) - 1; j >= 0; j-- {
+ err := e.bw.WritePackedUintIn(s.trans[j].out, outPackSize)
+ if err != nil {
+ return 0, err
+ }
+ }
+ }
+
+ // output transition dests (in reverse)
+ for j := len(s.trans) - 1; j >= 0; j-- {
+ delta := deltaAddr(start, uint64(s.trans[j].addr))
+ err := e.bw.WritePackedUintIn(delta, transPackSize)
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ // output transition keys (in reverse)
+ for j := len(s.trans) - 1; j >= 0; j-- {
+ err := e.bw.WriteByte(s.trans[j].in)
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ packSize := encodePackSize(transPackSize, outPackSize)
+ err := e.bw.WriteByte(packSize)
+ if err != nil {
+ return 0, err
+ }
+
+ numTrans := encodeNumTrans(len(s.trans))
+
+ // if number of transitions wont fit in edge header byte
+ // write out separately
+ if numTrans == 0 {
+ if len(s.trans) == 256 {
+ // this wouldn't fit in single byte, but reuse value 1
+ // which would have always fit in the edge header instead
+ err = e.bw.WriteByte(1)
+ if err != nil {
+ return 0, err
+ }
+ } else {
+ err = e.bw.WriteByte(byte(len(s.trans)))
+ if err != nil {
+ return 0, err
+ }
+ }
+ }
+
+ // finally write edge header
+ if s.final {
+ numTrans |= stateFinal
+ }
+ err = e.bw.WriteByte(numTrans)
+ if err != nil {
+ return 0, err
+ }
+
+ return e.bw.counter - 1, nil
+}
+
+func (e *encoderV1) finish(count, rootAddr int) error {
+ footer := make([]byte, footerSizeV1)
+ binary.LittleEndian.PutUint64(footer, uint64(count)) // root addr
+ binary.LittleEndian.PutUint64(footer[8:], uint64(rootAddr)) // root addr
+ n, err := e.bw.Write(footer)
+ if err != nil {
+ return err
+ }
+ if n != footerSizeV1 {
+ return fmt.Errorf("short write of footer %d/%d", n, footerSizeV1)
+ }
+ err = e.bw.Flush()
+ if err != nil {
+ return err
+ }
+ return nil
+}