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.

enumerator.go 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright (c) 2018 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 zap
  15. import (
  16. "bytes"
  17. "github.com/blevesearch/vellum"
  18. )
  19. // enumerator provides an ordered traversal of multiple vellum
  20. // iterators. Like JOIN of iterators, the enumerator produces a
  21. // sequence of (key, iteratorIndex, value) tuples, sorted by key ASC,
  22. // then iteratorIndex ASC, where the same key might be seen or
  23. // repeated across multiple child iterators.
  24. type enumerator struct {
  25. itrs []vellum.Iterator
  26. currKs [][]byte
  27. currVs []uint64
  28. lowK []byte
  29. lowIdxs []int
  30. lowCurr int
  31. }
  32. // newEnumerator returns a new enumerator over the vellum Iterators
  33. func newEnumerator(itrs []vellum.Iterator) (*enumerator, error) {
  34. rv := &enumerator{
  35. itrs: itrs,
  36. currKs: make([][]byte, len(itrs)),
  37. currVs: make([]uint64, len(itrs)),
  38. lowIdxs: make([]int, 0, len(itrs)),
  39. }
  40. for i, itr := range rv.itrs {
  41. rv.currKs[i], rv.currVs[i] = itr.Current()
  42. }
  43. rv.updateMatches(false)
  44. if rv.lowK == nil && len(rv.lowIdxs) == 0 {
  45. return rv, vellum.ErrIteratorDone
  46. }
  47. return rv, nil
  48. }
  49. // updateMatches maintains the low key matches based on the currKs
  50. func (m *enumerator) updateMatches(skipEmptyKey bool) {
  51. m.lowK = nil
  52. m.lowIdxs = m.lowIdxs[:0]
  53. m.lowCurr = 0
  54. for i, key := range m.currKs {
  55. if (key == nil && m.currVs[i] == 0) || // in case of empty iterator
  56. (len(key) == 0 && skipEmptyKey) { // skip empty keys
  57. continue
  58. }
  59. cmp := bytes.Compare(key, m.lowK)
  60. if cmp < 0 || len(m.lowIdxs) == 0 {
  61. // reached a new low
  62. m.lowK = key
  63. m.lowIdxs = m.lowIdxs[:0]
  64. m.lowIdxs = append(m.lowIdxs, i)
  65. } else if cmp == 0 {
  66. m.lowIdxs = append(m.lowIdxs, i)
  67. }
  68. }
  69. }
  70. // Current returns the enumerator's current key, iterator-index, and
  71. // value. If the enumerator is not pointing at a valid value (because
  72. // Next returned an error previously), Current will return nil,0,0.
  73. func (m *enumerator) Current() ([]byte, int, uint64) {
  74. var i int
  75. var v uint64
  76. if m.lowCurr < len(m.lowIdxs) {
  77. i = m.lowIdxs[m.lowCurr]
  78. v = m.currVs[i]
  79. }
  80. return m.lowK, i, v
  81. }
  82. // GetLowIdxsAndValues will return all of the iterator indices
  83. // which point to the current key, and their corresponding
  84. // values. This can be used by advanced caller which may need
  85. // to peek into these other sets of data before processing.
  86. func (m *enumerator) GetLowIdxsAndValues() ([]int, []uint64) {
  87. values := make([]uint64, 0, len(m.lowIdxs))
  88. for _, idx := range m.lowIdxs {
  89. values = append(values, m.currVs[idx])
  90. }
  91. return m.lowIdxs, values
  92. }
  93. // Next advances the enumerator to the next key/iterator/value result,
  94. // else vellum.ErrIteratorDone is returned.
  95. func (m *enumerator) Next() error {
  96. m.lowCurr += 1
  97. if m.lowCurr >= len(m.lowIdxs) {
  98. // move all the current low iterators forwards
  99. for _, vi := range m.lowIdxs {
  100. err := m.itrs[vi].Next()
  101. if err != nil && err != vellum.ErrIteratorDone {
  102. return err
  103. }
  104. m.currKs[vi], m.currVs[vi] = m.itrs[vi].Current()
  105. }
  106. // can skip any empty keys encountered at this point
  107. m.updateMatches(true)
  108. }
  109. if m.lowK == nil && len(m.lowIdxs) == 0 {
  110. return vellum.ErrIteratorDone
  111. }
  112. return nil
  113. }
  114. // Close all the underlying Iterators. The first error, if any, will
  115. // be returned.
  116. func (m *enumerator) Close() error {
  117. var rv error
  118. for _, itr := range m.itrs {
  119. err := itr.Close()
  120. if rv == nil {
  121. rv = err
  122. }
  123. }
  124. return rv
  125. }