2015-08-07 23:02:29 +00:00
|
|
|
package tsdb_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"math/rand"
|
|
|
|
"reflect"
|
|
|
|
"sort"
|
|
|
|
"testing"
|
|
|
|
"testing/quick"
|
|
|
|
|
|
|
|
"github.com/influxdb/influxdb/tsdb"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Ensure the multi-cursor can correctly iterate across a single subcursor.
|
|
|
|
func TestMultiCursor_Single(t *testing.T) {
|
2015-09-16 21:04:37 +00:00
|
|
|
mc := tsdb.MultiCursor(NewCursor([]CursorItem{
|
2015-09-20 22:27:22 +00:00
|
|
|
{Key: 0, Value: 0},
|
|
|
|
{Key: 1, Value: 10},
|
|
|
|
{Key: 2, Value: 20},
|
2015-09-16 21:04:37 +00:00
|
|
|
}, true))
|
2015-08-07 23:02:29 +00:00
|
|
|
|
2015-09-22 19:20:53 +00:00
|
|
|
if k, v := mc.SeekTo(0); k != 0 || v.(int) != 0 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 1 || v.(int) != 10 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 2 || v.(int) != 20 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != tsdb.EOF {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("expected eof, got: %x / %x", k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-02 17:25:42 +00:00
|
|
|
// Ensure the multi-cursor can correctly iterate across a single subcursor in reverse order.
|
|
|
|
func TestMultiCursor_Single_Reverse(t *testing.T) {
|
2015-09-16 21:04:37 +00:00
|
|
|
mc := tsdb.MultiCursor(NewCursor([]CursorItem{
|
2015-09-20 22:27:22 +00:00
|
|
|
{Key: 0, Value: 0},
|
|
|
|
{Key: 1, Value: 10},
|
|
|
|
{Key: 2, Value: 20},
|
2015-09-16 21:04:37 +00:00
|
|
|
}, false))
|
2015-09-02 17:25:42 +00:00
|
|
|
|
2015-09-22 19:20:53 +00:00
|
|
|
if k, v := mc.SeekTo(2); k != 2 || v.(int) != 20 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 1 || v.(int) != 10 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 0 || v.(int) != 0 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != tsdb.EOF {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("expected eof, got: %x / %x", k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-07 23:02:29 +00:00
|
|
|
// Ensure the multi-cursor can correctly iterate across multiple non-overlapping subcursors.
|
|
|
|
func TestMultiCursor_Multiple_NonOverlapping(t *testing.T) {
|
2015-09-16 21:04:37 +00:00
|
|
|
mc := tsdb.MultiCursor(
|
|
|
|
NewCursor([]CursorItem{
|
2015-09-20 22:27:22 +00:00
|
|
|
{Key: 0, Value: 0},
|
|
|
|
{Key: 3, Value: 30},
|
|
|
|
{Key: 4, Value: 40},
|
2015-09-16 21:04:37 +00:00
|
|
|
}, true),
|
|
|
|
NewCursor([]CursorItem{
|
2015-09-20 22:27:22 +00:00
|
|
|
{Key: 1, Value: 10},
|
|
|
|
{Key: 2, Value: 20},
|
2015-09-16 21:04:37 +00:00
|
|
|
}, true),
|
2015-08-07 23:02:29 +00:00
|
|
|
)
|
|
|
|
|
2015-09-22 19:20:53 +00:00
|
|
|
if k, v := mc.SeekTo(0); k != 0 || v.(int) != 0 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 1 || v.(int) != 10 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 2 || v.(int) != 20 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 3 || v.(int) != 30 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 4 || v.(int) != 40 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != tsdb.EOF {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("expected eof, got: %x / %x", k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-02 17:25:42 +00:00
|
|
|
// Ensure the multi-cursor can correctly iterate across multiple non-overlapping subcursors.
|
|
|
|
func TestMultiCursor_Multiple_NonOverlapping_Reverse(t *testing.T) {
|
2015-09-16 21:04:37 +00:00
|
|
|
mc := tsdb.MultiCursor(
|
|
|
|
NewCursor([]CursorItem{
|
2015-09-20 22:27:22 +00:00
|
|
|
{Key: 0, Value: 0},
|
|
|
|
{Key: 3, Value: 30},
|
|
|
|
{Key: 4, Value: 40},
|
2015-09-16 21:04:37 +00:00
|
|
|
}, false),
|
|
|
|
NewCursor([]CursorItem{
|
2015-09-20 22:27:22 +00:00
|
|
|
{Key: 1, Value: 10},
|
|
|
|
{Key: 2, Value: 20},
|
2015-09-16 21:04:37 +00:00
|
|
|
}, false),
|
2015-09-02 17:25:42 +00:00
|
|
|
)
|
|
|
|
|
2015-09-22 19:20:53 +00:00
|
|
|
if k, v := mc.SeekTo(4); k != 4 || v.(int) != 40 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 3 || v.(int) != 30 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 2 || v.(int) != 20 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 1 || v.(int) != 10 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 0 || v.(int) != 00 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != tsdb.EOF {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("expected eof, got: %x / %x", k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-07 23:02:29 +00:00
|
|
|
// Ensure the multi-cursor can correctly iterate across multiple overlapping subcursors.
|
|
|
|
func TestMultiCursor_Multiple_Overlapping(t *testing.T) {
|
2015-09-16 21:04:37 +00:00
|
|
|
mc := tsdb.MultiCursor(
|
|
|
|
NewCursor([]CursorItem{
|
2015-09-20 22:27:22 +00:00
|
|
|
{Key: 0, Value: 0},
|
|
|
|
{Key: 3, Value: 3},
|
|
|
|
{Key: 4, Value: 4},
|
2015-09-16 21:04:37 +00:00
|
|
|
}, true),
|
|
|
|
NewCursor([]CursorItem{
|
2015-09-20 22:27:22 +00:00
|
|
|
{Key: 0, Value: 0xF0},
|
|
|
|
{Key: 2, Value: 0xF2},
|
|
|
|
{Key: 4, Value: 0xF4},
|
2015-09-16 21:04:37 +00:00
|
|
|
}, true),
|
2015-08-07 23:02:29 +00:00
|
|
|
)
|
|
|
|
|
2015-09-22 19:20:53 +00:00
|
|
|
if k, v := mc.SeekTo(0); k != 0 || v.(int) != 0 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 2 || v.(int) != 0xF2 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 3 || v.(int) != 3 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 4 || v.(int) != 4 {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != tsdb.EOF {
|
2015-08-07 23:02:29 +00:00
|
|
|
t.Fatalf("expected eof, got: %x / %x", k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-02 17:25:42 +00:00
|
|
|
// Ensure the multi-cursor can correctly iterate across multiple overlapping subcursors.
|
|
|
|
func TestMultiCursor_Multiple_Overlapping_Reverse(t *testing.T) {
|
2015-09-16 21:04:37 +00:00
|
|
|
mc := tsdb.MultiCursor(
|
|
|
|
NewCursor([]CursorItem{
|
2015-09-20 22:27:22 +00:00
|
|
|
{Key: 0, Value: 0},
|
|
|
|
{Key: 3, Value: 3},
|
|
|
|
{Key: 4, Value: 4},
|
2015-09-16 21:04:37 +00:00
|
|
|
}, false),
|
|
|
|
NewCursor([]CursorItem{
|
2015-09-20 22:27:22 +00:00
|
|
|
{Key: 0, Value: 0xF0},
|
|
|
|
{Key: 2, Value: 0xF2},
|
|
|
|
{Key: 4, Value: 0xF4},
|
2015-09-16 21:04:37 +00:00
|
|
|
}, false),
|
2015-09-02 17:25:42 +00:00
|
|
|
)
|
|
|
|
|
2015-09-22 19:20:53 +00:00
|
|
|
if k, v := mc.SeekTo(4); k != 4 || v.(int) != 4 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 3 || v.(int) != 3 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 2 || v.(int) != 0xF2 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != 0 || v.(int) != 0 {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("unexpected key/value: %x / %x", k, v)
|
2015-09-20 22:27:22 +00:00
|
|
|
} else if k, v = mc.Next(); k != tsdb.EOF {
|
2015-09-02 17:25:42 +00:00
|
|
|
t.Fatalf("expected eof, got: %x / %x", k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-07 23:02:29 +00:00
|
|
|
// Ensure the multi-cursor can handle randomly generated data.
|
|
|
|
func TestMultiCursor_Quick(t *testing.T) {
|
2015-09-20 22:27:22 +00:00
|
|
|
quick.Check(func(useek uint64, cursors []Cursor) bool {
|
|
|
|
var got, exp []CursorItem
|
|
|
|
seek := int64(useek) % 100
|
2015-08-07 23:02:29 +00:00
|
|
|
|
|
|
|
// Merge all cursor data to determine expected output.
|
|
|
|
// First seen key overrides all other items with the same key.
|
2015-09-20 22:27:22 +00:00
|
|
|
m := make(map[int64]CursorItem)
|
2015-08-07 23:02:29 +00:00
|
|
|
for _, c := range cursors {
|
|
|
|
for _, item := range c.items {
|
2015-09-20 22:27:22 +00:00
|
|
|
if item.Key < seek {
|
2015-08-07 23:02:29 +00:00
|
|
|
continue
|
|
|
|
}
|
2015-09-20 22:27:22 +00:00
|
|
|
if _, ok := m[item.Key]; ok {
|
2015-08-07 23:02:29 +00:00
|
|
|
continue
|
|
|
|
}
|
2015-09-20 22:27:22 +00:00
|
|
|
m[item.Key] = item
|
2015-08-07 23:02:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert map back to single item list.
|
2015-09-20 22:27:22 +00:00
|
|
|
for _, item := range m {
|
|
|
|
exp = append(exp, item)
|
2015-08-07 23:02:29 +00:00
|
|
|
}
|
2015-09-20 22:27:22 +00:00
|
|
|
sort.Sort(CursorItems(exp))
|
2015-08-07 23:02:29 +00:00
|
|
|
|
|
|
|
// Create multi-cursor and iterate over all items.
|
2015-09-16 21:04:37 +00:00
|
|
|
mc := tsdb.MultiCursor(tsdbCursorSlice(cursors)...)
|
2015-09-22 19:20:53 +00:00
|
|
|
for k, v := mc.SeekTo(seek); k != tsdb.EOF; k, v = mc.Next() {
|
2015-09-20 22:27:22 +00:00
|
|
|
got = append(got, CursorItem{k, v.(int)})
|
2015-08-07 23:02:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify results.
|
|
|
|
if !reflect.DeepEqual(got, exp) {
|
|
|
|
t.Fatalf("mismatch: seek=%d\n\ngot=%+v\n\nexp=%+v", seek, got, exp)
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cursor represents an in-memory test cursor.
|
|
|
|
type Cursor struct {
|
2015-09-02 21:42:34 +00:00
|
|
|
items []CursorItem
|
|
|
|
index int
|
2015-09-16 21:04:37 +00:00
|
|
|
ascending bool
|
2015-08-07 23:02:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewCursor returns a new instance of Cursor.
|
2015-09-16 21:04:37 +00:00
|
|
|
func NewCursor(items []CursorItem, ascending bool) *Cursor {
|
2015-09-02 17:25:42 +00:00
|
|
|
index := 0
|
2015-08-07 23:02:29 +00:00
|
|
|
sort.Sort(CursorItems(items))
|
2015-09-02 17:25:42 +00:00
|
|
|
|
2015-09-16 21:04:37 +00:00
|
|
|
if !ascending {
|
2015-09-02 17:25:42 +00:00
|
|
|
index = len(items)
|
|
|
|
}
|
2015-09-16 21:04:37 +00:00
|
|
|
return &Cursor{
|
|
|
|
items: items,
|
|
|
|
index: index,
|
|
|
|
ascending: ascending,
|
|
|
|
}
|
2015-08-07 23:02:29 +00:00
|
|
|
}
|
|
|
|
|
2015-09-16 21:04:37 +00:00
|
|
|
func (c *Cursor) Ascending() bool { return c.ascending }
|
2015-08-27 22:55:55 +00:00
|
|
|
|
2015-08-07 23:02:29 +00:00
|
|
|
// Seek seeks to an item by key.
|
2015-09-22 19:20:53 +00:00
|
|
|
func (c *Cursor) SeekTo(seek int64) (key int64, value interface{}) {
|
2015-09-16 21:04:37 +00:00
|
|
|
if c.ascending {
|
2015-09-02 17:25:42 +00:00
|
|
|
return c.seekForward(seek)
|
|
|
|
}
|
|
|
|
return c.seekReverse(seek)
|
|
|
|
}
|
|
|
|
|
2015-09-20 22:27:22 +00:00
|
|
|
func (c *Cursor) seekForward(seek int64) (key int64, value interface{}) {
|
2015-08-07 23:02:29 +00:00
|
|
|
for c.index = 0; c.index < len(c.items); c.index++ {
|
2015-09-20 22:27:22 +00:00
|
|
|
if c.items[c.index].Key < seek { // skip keys less than seek
|
2015-08-07 23:02:29 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
return c.items[c.index].Key, c.items[c.index].Value
|
|
|
|
}
|
2015-09-20 22:27:22 +00:00
|
|
|
return tsdb.EOF, nil
|
2015-08-07 23:02:29 +00:00
|
|
|
}
|
|
|
|
|
2015-09-20 22:27:22 +00:00
|
|
|
func (c *Cursor) seekReverse(seek int64) (key int64, value interface{}) {
|
2015-09-02 17:25:42 +00:00
|
|
|
for c.index = len(c.items) - 1; c.index >= 0; c.index-- {
|
2015-09-20 22:27:22 +00:00
|
|
|
if c.items[c.index].Key > seek { // skip keys greater than seek
|
2015-09-02 17:25:42 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
return c.items[c.index].Key, c.items[c.index].Value
|
|
|
|
}
|
2015-09-20 22:27:22 +00:00
|
|
|
return tsdb.EOF, nil
|
2015-09-02 17:25:42 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 23:02:29 +00:00
|
|
|
// Next returns the next key/value pair.
|
2015-09-20 22:27:22 +00:00
|
|
|
func (c *Cursor) Next() (key int64, value interface{}) {
|
2015-09-16 21:04:37 +00:00
|
|
|
if !c.ascending && c.index < 0 {
|
2015-09-20 22:27:22 +00:00
|
|
|
return tsdb.EOF, nil
|
2015-08-07 23:02:29 +00:00
|
|
|
}
|
|
|
|
|
2015-09-16 21:04:37 +00:00
|
|
|
if c.ascending && c.index >= len(c.items) {
|
2015-09-20 22:27:22 +00:00
|
|
|
return tsdb.EOF, nil
|
2015-09-02 17:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
k, v := c.items[c.index].Key, c.items[c.index].Value
|
|
|
|
|
2015-09-16 21:04:37 +00:00
|
|
|
if c.ascending {
|
2015-09-02 17:25:42 +00:00
|
|
|
c.index++
|
|
|
|
} else {
|
|
|
|
c.index--
|
|
|
|
}
|
|
|
|
return k, v
|
2015-08-07 23:02:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate returns a randomly generated cursor. Implements quick.Generator.
|
|
|
|
func (c Cursor) Generate(rand *rand.Rand, size int) reflect.Value {
|
|
|
|
c.index = 0
|
2015-09-16 21:04:37 +00:00
|
|
|
c.ascending = true
|
2015-08-07 23:02:29 +00:00
|
|
|
|
|
|
|
c.items = make([]CursorItem, rand.Intn(size))
|
|
|
|
for i := range c.items {
|
|
|
|
c.items[i] = CursorItem{
|
2015-09-20 22:27:22 +00:00
|
|
|
Key: rand.Int63n(int64(size)),
|
|
|
|
Value: rand.Int(),
|
2015-08-07 23:02:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort items by key.
|
|
|
|
sort.Sort(CursorItems(c.items))
|
|
|
|
|
|
|
|
return reflect.ValueOf(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
// tsdbCursorSlice converts a Cursor slice to a tsdb.Cursor slice.
|
|
|
|
func tsdbCursorSlice(a []Cursor) []tsdb.Cursor {
|
|
|
|
var other []tsdb.Cursor
|
|
|
|
for i := range a {
|
|
|
|
other = append(other, &a[i])
|
|
|
|
}
|
|
|
|
return other
|
|
|
|
}
|
|
|
|
|
|
|
|
// CursorItem represents a key/value pair in a cursor.
|
|
|
|
type CursorItem struct {
|
2015-09-20 22:27:22 +00:00
|
|
|
Key int64
|
|
|
|
Value int
|
2015-08-07 23:02:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type CursorItems []CursorItem
|
|
|
|
|
|
|
|
func (a CursorItems) Len() int { return len(a) }
|
|
|
|
func (a CursorItems) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
2015-09-20 22:27:22 +00:00
|
|
|
func (a CursorItems) Less(i, j int) bool { return a[i].Key < a[j].Key }
|
2015-08-07 23:02:29 +00:00
|
|
|
|
|
|
|
// byteSlices represents a sortable slice of byte slices.
|
|
|
|
type byteSlices [][]byte
|
|
|
|
|
|
|
|
func (a byteSlices) Len() int { return len(a) }
|
|
|
|
func (a byteSlices) Less(i, j int) bool { return bytes.Compare(a[i], a[j]) == -1 }
|
|
|
|
func (a byteSlices) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
|
|
|
|
// u64tob converts a uint64 into an 8-byte slice.
|
|
|
|
func u64tob(v uint64) []byte {
|
|
|
|
b := make([]byte, 8)
|
|
|
|
binary.BigEndian.PutUint64(b, v)
|
|
|
|
return b
|
|
|
|
}
|