mirror of
				https://source.quilibrium.com/quilibrium/ceremonyclient.git
				synced 2025-11-04 03:07:28 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			292 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			292 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2021 The LevelDB-Go and Pebble Authors. All rights reserved. Use
 | 
						|
// of this source code is governed by a BSD-style license that can be found in
 | 
						|
// the LICENSE file.
 | 
						|
 | 
						|
package keyspan
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/cockroachdb/datadriven"
 | 
						|
	"github.com/cockroachdb/pebble/internal/base"
 | 
						|
	"github.com/cockroachdb/pebble/internal/testkeys"
 | 
						|
	"github.com/stretchr/testify/require"
 | 
						|
)
 | 
						|
 | 
						|
func TestInterleavingIter(t *testing.T) {
 | 
						|
	runInterleavingIterTest(t, "testdata/interleaving_iter")
 | 
						|
}
 | 
						|
 | 
						|
func TestInterleavingIter_Masking(t *testing.T) {
 | 
						|
	runInterleavingIterTest(t, "testdata/interleaving_iter_masking")
 | 
						|
}
 | 
						|
 | 
						|
type maskingHooks struct {
 | 
						|
	log        io.Writer
 | 
						|
	cmp        base.Compare
 | 
						|
	split      base.Split
 | 
						|
	threshold  []byte
 | 
						|
	maskSuffix []byte
 | 
						|
}
 | 
						|
 | 
						|
func (m *maskingHooks) SpanChanged(s *Span) {
 | 
						|
	if m.log != nil {
 | 
						|
		if s == nil {
 | 
						|
			fmt.Fprintln(m.log, "-- SpanChanged(nil)")
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(m.log, "-- SpanChanged(%s)\n", s)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Find the smallest suffix of a key contained within the Span, excluding
 | 
						|
	// suffixes less than m.threshold.
 | 
						|
	m.maskSuffix = nil
 | 
						|
	if s == nil || m.threshold == nil || len(s.Keys) == 0 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	for i := range s.Keys {
 | 
						|
		if s.Keys[i].Suffix == nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if m.cmp(s.Keys[i].Suffix, m.threshold) < 0 {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if m.maskSuffix == nil || m.cmp(m.maskSuffix, s.Keys[i].Suffix) > 0 {
 | 
						|
			m.maskSuffix = s.Keys[i].Suffix
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (m *maskingHooks) SkipPoint(userKey []byte) bool {
 | 
						|
	pointSuffix := userKey[m.split(userKey):]
 | 
						|
	return m.maskSuffix != nil && len(pointSuffix) > 0 && m.cmp(m.maskSuffix, pointSuffix) < 0
 | 
						|
}
 | 
						|
 | 
						|
func runInterleavingIterTest(t *testing.T, filename string) {
 | 
						|
	cmp := testkeys.Comparer.Compare
 | 
						|
	var keyspanIter MergingIter
 | 
						|
	var pointIter pointIterator
 | 
						|
	var iter InterleavingIter
 | 
						|
	var buf bytes.Buffer
 | 
						|
	hooks := maskingHooks{
 | 
						|
		log:   &buf,
 | 
						|
		cmp:   testkeys.Comparer.Compare,
 | 
						|
		split: testkeys.Comparer.Split,
 | 
						|
	}
 | 
						|
 | 
						|
	var prevKey *base.InternalKey
 | 
						|
	formatKey := func(k *base.InternalKey, _ base.LazyValue) {
 | 
						|
		if k == nil {
 | 
						|
			fmt.Fprint(&buf, ".")
 | 
						|
			return
 | 
						|
		}
 | 
						|
		prevKey = k
 | 
						|
		s := iter.Span()
 | 
						|
		fmt.Fprintf(&buf, "PointKey: %s\n", k.String())
 | 
						|
		if s != nil {
 | 
						|
			fmt.Fprintf(&buf, "Span: %s\n-", s)
 | 
						|
		} else {
 | 
						|
			fmt.Fprintf(&buf, "Span: %s\n-", Span{})
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	datadriven.RunTest(t, filename, func(t *testing.T, td *datadriven.TestData) string {
 | 
						|
		buf.Reset()
 | 
						|
		switch td.Cmd {
 | 
						|
		case "set-masking-threshold":
 | 
						|
			hooks.threshold = []byte(strings.TrimSpace(td.Input))
 | 
						|
			return "OK"
 | 
						|
		case "define-rangekeys":
 | 
						|
			var spans []Span
 | 
						|
			lines := strings.Split(strings.TrimSpace(td.Input), "\n")
 | 
						|
			for _, line := range lines {
 | 
						|
				spans = append(spans, ParseSpan(line))
 | 
						|
			}
 | 
						|
			keyspanIter.Init(cmp, noopTransform, new(MergingBuffers), NewIter(cmp, spans))
 | 
						|
			hooks.maskSuffix = nil
 | 
						|
			iter.Init(testkeys.Comparer, &pointIter, &keyspanIter,
 | 
						|
				InterleavingIterOpts{Mask: &hooks})
 | 
						|
			return "OK"
 | 
						|
		case "define-pointkeys":
 | 
						|
			var points []base.InternalKey
 | 
						|
			lines := strings.Split(strings.TrimSpace(td.Input), "\n")
 | 
						|
			for _, line := range lines {
 | 
						|
				points = append(points, base.ParseInternalKey(line))
 | 
						|
			}
 | 
						|
			pointIter = pointIterator{cmp: cmp, keys: points}
 | 
						|
			hooks.maskSuffix = nil
 | 
						|
			iter.Init(testkeys.Comparer, &pointIter, &keyspanIter,
 | 
						|
				InterleavingIterOpts{Mask: &hooks})
 | 
						|
			return "OK"
 | 
						|
		case "iter":
 | 
						|
			buf.Reset()
 | 
						|
			// Clear any previous bounds.
 | 
						|
			iter.SetBounds(nil, nil)
 | 
						|
			prevKey = nil
 | 
						|
			lines := strings.Split(strings.TrimSpace(td.Input), "\n")
 | 
						|
			for _, line := range lines {
 | 
						|
				bufLen := buf.Len()
 | 
						|
				line = strings.TrimSpace(line)
 | 
						|
				i := strings.IndexByte(line, ' ')
 | 
						|
				iterCmd := line
 | 
						|
				if i > 0 {
 | 
						|
					iterCmd = string(line[:i])
 | 
						|
				}
 | 
						|
				switch iterCmd {
 | 
						|
				case "first":
 | 
						|
					formatKey(iter.First())
 | 
						|
				case "last":
 | 
						|
					formatKey(iter.Last())
 | 
						|
				case "next":
 | 
						|
					formatKey(iter.Next())
 | 
						|
				case "next-prefix":
 | 
						|
					succKey := testkeys.Comparer.ImmediateSuccessor(nil, prevKey.UserKey[:testkeys.Comparer.Split(prevKey.UserKey)])
 | 
						|
					formatKey(iter.NextPrefix(succKey))
 | 
						|
				case "prev":
 | 
						|
					formatKey(iter.Prev())
 | 
						|
				case "seek-ge":
 | 
						|
					formatKey(iter.SeekGE([]byte(strings.TrimSpace(line[i:])), base.SeekGEFlagsNone))
 | 
						|
				case "seek-prefix-ge":
 | 
						|
					key := []byte(strings.TrimSpace(line[i:]))
 | 
						|
					prefix := key[:testkeys.Comparer.Split(key)]
 | 
						|
					formatKey(iter.SeekPrefixGE(prefix, key, base.SeekGEFlagsNone))
 | 
						|
				case "seek-lt":
 | 
						|
					formatKey(iter.SeekLT([]byte(strings.TrimSpace(line[i:])), base.SeekLTFlagsNone))
 | 
						|
				case "set-bounds":
 | 
						|
					bounds := strings.Fields(line[i:])
 | 
						|
					if len(bounds) != 2 {
 | 
						|
						return fmt.Sprintf("set-bounds expects 2 bounds, got %d", len(bounds))
 | 
						|
					}
 | 
						|
					l, u := []byte(bounds[0]), []byte(bounds[1])
 | 
						|
					if bounds[0] == "." {
 | 
						|
						l = nil
 | 
						|
					}
 | 
						|
					if bounds[1] == "." {
 | 
						|
						u = nil
 | 
						|
					}
 | 
						|
					iter.SetBounds(l, u)
 | 
						|
				default:
 | 
						|
					return fmt.Sprintf("unrecognized iter command %q", iterCmd)
 | 
						|
				}
 | 
						|
				require.NoError(t, iter.Error())
 | 
						|
				if buf.Len() > bufLen {
 | 
						|
					fmt.Fprintln(&buf)
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return strings.TrimSpace(buf.String())
 | 
						|
		default:
 | 
						|
			return fmt.Sprintf("unrecognized command %q", td.Cmd)
 | 
						|
		}
 | 
						|
	})
 | 
						|
	require.NoError(t, iter.Close())
 | 
						|
}
 | 
						|
 | 
						|
type pointIterator struct {
 | 
						|
	cmp   base.Compare
 | 
						|
	keys  []base.InternalKey
 | 
						|
	lower []byte
 | 
						|
	upper []byte
 | 
						|
	index int
 | 
						|
}
 | 
						|
 | 
						|
var _ base.InternalIterator = &pointIterator{}
 | 
						|
 | 
						|
func (i *pointIterator) SeekGE(
 | 
						|
	key []byte, flags base.SeekGEFlags,
 | 
						|
) (*base.InternalKey, base.LazyValue) {
 | 
						|
	i.index = sort.Search(len(i.keys), func(j int) bool {
 | 
						|
		return i.cmp(i.keys[j].UserKey, key) >= 0
 | 
						|
	})
 | 
						|
	if i.index < 0 || i.index >= len(i.keys) {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	if i.upper != nil && i.cmp(i.keys[i.index].UserKey, i.upper) >= 0 {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	return &i.keys[i.index], base.LazyValue{}
 | 
						|
}
 | 
						|
 | 
						|
func (i *pointIterator) SeekPrefixGE(
 | 
						|
	prefix, key []byte, flags base.SeekGEFlags,
 | 
						|
) (*base.InternalKey, base.LazyValue) {
 | 
						|
	return i.SeekGE(key, flags)
 | 
						|
}
 | 
						|
 | 
						|
func (i *pointIterator) SeekLT(
 | 
						|
	key []byte, flags base.SeekLTFlags,
 | 
						|
) (*base.InternalKey, base.LazyValue) {
 | 
						|
	i.index = sort.Search(len(i.keys), func(j int) bool {
 | 
						|
		return i.cmp(i.keys[j].UserKey, key) >= 0
 | 
						|
	})
 | 
						|
	i.index--
 | 
						|
	if i.index < 0 || i.index >= len(i.keys) {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	if i.lower != nil && i.cmp(i.keys[i.index].UserKey, i.lower) < 0 {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	return &i.keys[i.index], base.LazyValue{}
 | 
						|
}
 | 
						|
 | 
						|
func (i *pointIterator) First() (*base.InternalKey, base.LazyValue) {
 | 
						|
	i.index = 0
 | 
						|
	if i.index < 0 || i.index >= len(i.keys) {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	if i.upper != nil && i.cmp(i.keys[i.index].UserKey, i.upper) >= 0 {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	return &i.keys[i.index], base.LazyValue{}
 | 
						|
}
 | 
						|
 | 
						|
func (i *pointIterator) Last() (*base.InternalKey, base.LazyValue) {
 | 
						|
	i.index = len(i.keys) - 1
 | 
						|
	if i.index < 0 || i.index >= len(i.keys) {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	if i.lower != nil && i.cmp(i.keys[i.index].UserKey, i.lower) < 0 {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	return &i.keys[i.index], base.LazyValue{}
 | 
						|
}
 | 
						|
 | 
						|
func (i *pointIterator) Next() (*base.InternalKey, base.LazyValue) {
 | 
						|
	i.index++
 | 
						|
	if i.index < 0 || i.index >= len(i.keys) {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	if i.upper != nil && i.cmp(i.keys[i.index].UserKey, i.upper) >= 0 {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	return &i.keys[i.index], base.LazyValue{}
 | 
						|
}
 | 
						|
 | 
						|
func (i *pointIterator) NextPrefix(succKey []byte) (*base.InternalKey, base.LazyValue) {
 | 
						|
	return i.SeekGE(succKey, base.SeekGEFlagsNone)
 | 
						|
}
 | 
						|
 | 
						|
func (i *pointIterator) Prev() (*base.InternalKey, base.LazyValue) {
 | 
						|
	i.index--
 | 
						|
	if i.index < 0 || i.index >= len(i.keys) {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	if i.lower != nil && i.cmp(i.keys[i.index].UserKey, i.lower) < 0 {
 | 
						|
		return nil, base.LazyValue{}
 | 
						|
	}
 | 
						|
	return &i.keys[i.index], base.LazyValue{}
 | 
						|
}
 | 
						|
 | 
						|
func (i *pointIterator) Close() error   { return nil }
 | 
						|
func (i *pointIterator) Error() error   { return nil }
 | 
						|
func (i *pointIterator) String() string { return "test-point-iterator" }
 | 
						|
func (i *pointIterator) SetBounds(lower, upper []byte) {
 | 
						|
	i.lower, i.upper = lower, upper
 | 
						|
}
 | 
						|
func (i *pointIterator) SetContext(_ context.Context) {}
 |