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.
dorm/vendor/modernc.org/db/slice.go

184 lines
4.3 KiB

// Copyright 2017 The DB 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 db // import "modernc.org/db"
import (
"fmt"
"modernc.org/internal/buffer"
"modernc.org/mathutil"
)
const (
oSliceLen = 8 * iota // int64 0 8
oSliceSzItem // int64 8 8
oSliceTable // [64]int64 16 512
/*
index tabIndex N index+1
0: tab[0] 1 1: 2^0
1-2: tab[1] 2 2-3: 2^1
3-6: tab[2] 4 4-7: 2^2
7-14: tab[3] 8 8-15: 2^3
...
tab[63] 2^63
*/
szSlice = oSliceTable + 64*8
)
// Slice is a numbered sequence of items.
type Slice struct {
*DB
Len int64 // The number of items in the slice. R/O
Off int64 // Location in the database. R/O
SzItem int64 // The szItem argument of NewSlice. R/O
table [64]int64
}
// NewSlice allocates and returns a new Slice or an error, if any. The szItem
// argument is the size of an item. The resulting slice has zero length.
func (db *DB) NewSlice(szItem int64) (s *Slice, err error) {
if szItem < 0 {
panic(fmt.Errorf("%T.NewSlice: invalid argument", db))
}
off, err := db.Alloc(szSlice)
if err != nil {
return nil, fmt.Errorf("%T.NewSlice: %v", db, err)
}
if err = db.w8(off+oSliceSzItem, szItem); err != nil {
db.Free(off)
return nil, fmt.Errorf("%T.NewSlice: %v", db, err)
}
return &Slice{
DB: db,
Off: off,
SzItem: szItem,
}, nil
}
// OpenSlice returns an existing Slice found at offset off or an error, if any.
// The off argument must have been acquired from NewSlice.
func (db *DB) OpenSlice(off int64) (*Slice, error) {
szItem, err := db.r8(off + oSliceSzItem)
if err != nil {
return nil, err
}
if szItem < 0 {
return nil, fmt.Errorf("%T.OpenSlice: corrupted database", db)
}
len, err := db.r8(off + oSliceLen)
if err != nil {
return nil, err
}
if len < 0 {
return nil, fmt.Errorf("%T.OpenSlice: corrupted database", db)
}
p := buffer.Get(64 * 8)
defer buffer.Put(p)
b := *p
if _, err := db.ReadAt(b, off+oSliceTable); err != nil {
return nil, fmt.Errorf("%T.OpenSlice: corrupted database", db)
}
r := &Slice{
DB: db,
Len: len,
Off: off,
SzItem: szItem,
}
for i := range r.table {
o := 8 * i
r.table[i] = r8b(b[o : o+8])
}
return r, nil
}
// Append returns the offset of item s[s.Len] and increments s.Len. The storage
// space at the returned offset is not touched and may contain garbage. Callers
// are expected to initialize/set the item after calling Append.
func (s *Slice) Append() (off int64, err error) {
si := s.slotIndex(s.Len)
block := s.table[si]
if block == 0 {
bsz := s.SzItem << uint(si)
if block, err = s.Alloc(bsz); err != nil {
return 0, fmt.Errorf("%T.Append: %v", s, err)
}
if err = s.w8(s.Off+oSliceTable+int64(si)*8, block); err != nil {
return 0, fmt.Errorf("%T.Append: %v", s, err)
}
s.table[si] = block
}
bItems := int64(1) << uint(si)
ix := s.Len - bItems + 1
s.Len++
return block + ix*s.SzItem, s.w8(s.Off+oSliceLen, s.Len)
}
func (s *Slice) slotIndex(index int64) int { return mathutil.Log2Uint64(uint64(index + 1)) }
// RemoveLast removes the last item of s and decrements s.Len.
//
// The free function may be nil, otherwise it's called with offset of the last
// item in s before removing it.
func (s *Slice) RemoveLast(free func(off int64) error) (err error) {
if s.Len == 0 {
return fmt.Errorf("%T.RemoveLast: no items to remove", s)
}
freeBlock := s.Len&(s.Len-1) == 0
s.Len--
si := s.slotIndex(s.Len)
block := s.table[si]
if free != nil {
bItems := int64(1) << uint(si)
ix := s.Len - bItems + 1
if err = free(block + ix*s.SzItem); err != nil {
return fmt.Errorf("%T.RemoveLast: %v", s, err)
}
}
if freeBlock {
if err = s.Free(block); err != nil {
return fmt.Errorf("%T.RemoveLast: %v", s, err)
}
if err = s.w8(s.Off+oSliceTable+int64(si)*8, block); err != nil {
return fmt.Errorf("%T.RemoveLast: %v", s, err)
}
s.table[si] = block
}
return s.w8(s.Off+oSliceLen, s.Len)
}
// At returns the offset if item at index i.
func (s *Slice) At(i int64) (int64, error) {
if i < 0 || i >= s.Len {
return 0, fmt.Errorf("%T.At: index out of bounds", s)
}
si := s.slotIndex(i)
block := s.table[si]
bItems := int64(1) << uint(si)
ix := i - bItems + 1
return block + ix*s.SzItem, nil
}