You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

227 lines
5.2 KiB

  1. // Copyright (c) 2017 Couchbase, Inc.
  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. package vellum
  15. import (
  16. "encoding/binary"
  17. "fmt"
  18. "io"
  19. )
  20. const versionV1 = 1
  21. const oneTransition = 1 << 7
  22. const transitionNext = 1 << 6
  23. const stateFinal = 1 << 6
  24. const footerSizeV1 = 16
  25. func init() {
  26. registerEncoder(versionV1, func(w io.Writer) encoder {
  27. return newEncoderV1(w)
  28. })
  29. }
  30. type encoderV1 struct {
  31. bw *writer
  32. }
  33. func newEncoderV1(w io.Writer) *encoderV1 {
  34. return &encoderV1{
  35. bw: newWriter(w),
  36. }
  37. }
  38. func (e *encoderV1) reset(w io.Writer) {
  39. e.bw.Reset(w)
  40. }
  41. func (e *encoderV1) start() error {
  42. header := make([]byte, headerSize)
  43. binary.LittleEndian.PutUint64(header, versionV1)
  44. binary.LittleEndian.PutUint64(header[8:], uint64(0)) // type
  45. n, err := e.bw.Write(header)
  46. if err != nil {
  47. return err
  48. }
  49. if n != headerSize {
  50. return fmt.Errorf("short write of header %d/%d", n, headerSize)
  51. }
  52. return nil
  53. }
  54. func (e *encoderV1) encodeState(s *builderNode, lastAddr int) (int, error) {
  55. if len(s.trans) == 0 && s.final && s.finalOutput == 0 {
  56. return 0, nil
  57. } else if len(s.trans) != 1 || s.final {
  58. return e.encodeStateMany(s)
  59. } else if !s.final && s.trans[0].out == 0 && s.trans[0].addr == lastAddr {
  60. return e.encodeStateOneFinish(s, transitionNext)
  61. }
  62. return e.encodeStateOne(s)
  63. }
  64. func (e *encoderV1) encodeStateOne(s *builderNode) (int, error) {
  65. start := uint64(e.bw.counter)
  66. outPackSize := 0
  67. if s.trans[0].out != 0 {
  68. outPackSize = packedSize(s.trans[0].out)
  69. err := e.bw.WritePackedUintIn(s.trans[0].out, outPackSize)
  70. if err != nil {
  71. return 0, err
  72. }
  73. }
  74. delta := deltaAddr(start, uint64(s.trans[0].addr))
  75. transPackSize := packedSize(delta)
  76. err := e.bw.WritePackedUintIn(delta, transPackSize)
  77. if err != nil {
  78. return 0, err
  79. }
  80. packSize := encodePackSize(transPackSize, outPackSize)
  81. err = e.bw.WriteByte(packSize)
  82. if err != nil {
  83. return 0, err
  84. }
  85. return e.encodeStateOneFinish(s, 0)
  86. }
  87. func (e *encoderV1) encodeStateOneFinish(s *builderNode, next byte) (int, error) {
  88. enc := encodeCommon(s.trans[0].in)
  89. // not a common input
  90. if enc == 0 {
  91. err := e.bw.WriteByte(s.trans[0].in)
  92. if err != nil {
  93. return 0, err
  94. }
  95. }
  96. err := e.bw.WriteByte(oneTransition | next | enc)
  97. if err != nil {
  98. return 0, err
  99. }
  100. return e.bw.counter - 1, nil
  101. }
  102. func (e *encoderV1) encodeStateMany(s *builderNode) (int, error) {
  103. start := uint64(e.bw.counter)
  104. transPackSize := 0
  105. outPackSize := packedSize(s.finalOutput)
  106. anyOutputs := s.finalOutput != 0
  107. for i := range s.trans {
  108. delta := deltaAddr(start, uint64(s.trans[i].addr))
  109. tsize := packedSize(delta)
  110. if tsize > transPackSize {
  111. transPackSize = tsize
  112. }
  113. osize := packedSize(s.trans[i].out)
  114. if osize > outPackSize {
  115. outPackSize = osize
  116. }
  117. anyOutputs = anyOutputs || s.trans[i].out != 0
  118. }
  119. if !anyOutputs {
  120. outPackSize = 0
  121. }
  122. if anyOutputs {
  123. // output final value
  124. if s.final {
  125. err := e.bw.WritePackedUintIn(s.finalOutput, outPackSize)
  126. if err != nil {
  127. return 0, err
  128. }
  129. }
  130. // output transition values (in reverse)
  131. for j := len(s.trans) - 1; j >= 0; j-- {
  132. err := e.bw.WritePackedUintIn(s.trans[j].out, outPackSize)
  133. if err != nil {
  134. return 0, err
  135. }
  136. }
  137. }
  138. // output transition dests (in reverse)
  139. for j := len(s.trans) - 1; j >= 0; j-- {
  140. delta := deltaAddr(start, uint64(s.trans[j].addr))
  141. err := e.bw.WritePackedUintIn(delta, transPackSize)
  142. if err != nil {
  143. return 0, err
  144. }
  145. }
  146. // output transition keys (in reverse)
  147. for j := len(s.trans) - 1; j >= 0; j-- {
  148. err := e.bw.WriteByte(s.trans[j].in)
  149. if err != nil {
  150. return 0, err
  151. }
  152. }
  153. packSize := encodePackSize(transPackSize, outPackSize)
  154. err := e.bw.WriteByte(packSize)
  155. if err != nil {
  156. return 0, err
  157. }
  158. numTrans := encodeNumTrans(len(s.trans))
  159. // if number of transitions wont fit in edge header byte
  160. // write out separately
  161. if numTrans == 0 {
  162. if len(s.trans) == 256 {
  163. // this wouldn't fit in single byte, but reuse value 1
  164. // which would have always fit in the edge header instead
  165. err = e.bw.WriteByte(1)
  166. if err != nil {
  167. return 0, err
  168. }
  169. } else {
  170. err = e.bw.WriteByte(byte(len(s.trans)))
  171. if err != nil {
  172. return 0, err
  173. }
  174. }
  175. }
  176. // finally write edge header
  177. if s.final {
  178. numTrans |= stateFinal
  179. }
  180. err = e.bw.WriteByte(numTrans)
  181. if err != nil {
  182. return 0, err
  183. }
  184. return e.bw.counter - 1, nil
  185. }
  186. func (e *encoderV1) finish(count, rootAddr int) error {
  187. footer := make([]byte, footerSizeV1)
  188. binary.LittleEndian.PutUint64(footer, uint64(count)) // root addr
  189. binary.LittleEndian.PutUint64(footer[8:], uint64(rootAddr)) // root addr
  190. n, err := e.bw.Write(footer)
  191. if err != nil {
  192. return err
  193. }
  194. if n != footerSizeV1 {
  195. return fmt.Errorf("short write of footer %d/%d", n, footerSizeV1)
  196. }
  197. err = e.bw.Flush()
  198. if err != nil {
  199. return err
  200. }
  201. return nil
  202. }