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.
172 lines
4.6 KiB
172 lines
4.6 KiB
2 years ago
|
// 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.
|
||
|
|
||
|
//TODO https://blog.dgraph.io/post/badger-lmdb-boltdb/
|
||
|
|
||
|
// Package db implements selected data structures found in database
|
||
|
// implementations.
|
||
|
//
|
||
|
// Build status
|
||
|
//
|
||
|
// available at https://modern-c.appspot.com/-/builder/?importpath=modernc.org%2fdb
|
||
|
package db // import "modernc.org/db"
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"os"
|
||
|
|
||
|
"modernc.org/internal/buffer"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
_ Storage = (*DB)(nil)
|
||
|
)
|
||
|
|
||
|
// Storage represents a database back end.
|
||
|
type Storage interface {
|
||
|
// Alloc allocates a storage block large enough for storing size bytes
|
||
|
// and returns its offset or an error, if any.
|
||
|
Alloc(size int64) (int64, error)
|
||
|
|
||
|
// Calloc is like Alloc but the allocated storage block is zeroed up
|
||
|
// to size.
|
||
|
Calloc(size int64) (int64, error)
|
||
|
|
||
|
// Close finishes storage use.
|
||
|
Close() error
|
||
|
|
||
|
// Free recycles the allocated storage block at off.
|
||
|
Free(off int64) error
|
||
|
|
||
|
// ReadAt reads len(p) bytes into p starting at offset off in the
|
||
|
// storage. It returns the number of bytes read (0 <= n <= len(p)) and
|
||
|
// any error encountered.
|
||
|
//
|
||
|
// When ReadAt returns n < len(p), it returns a non-nil error
|
||
|
// explaining why more bytes were not returned.
|
||
|
//
|
||
|
// Even if ReadAt returns n < len(p), it may use all of p as scratch
|
||
|
// space during the call.
|
||
|
//
|
||
|
// If the n = len(p) bytes returned by ReadAt are at the end of the
|
||
|
// storage, ReadAt may return either err == EOF or err == nil.
|
||
|
ReadAt(p []byte, off int64) (n int, err error)
|
||
|
|
||
|
// Realloc changes the size of the file block allocated at off, which
|
||
|
// must have been returned from Alloc or Realloc, to size and returns
|
||
|
// the offset of the relocated file block or an error, if any. The
|
||
|
// contents will be unchanged in the range from the start of the region
|
||
|
// up to the minimum of the old and new sizes. Realloc(off, 0) is equal
|
||
|
// to Free(off). If the file block was moved, a Free(off) is done.
|
||
|
Realloc(off, size int64) (int64, error)
|
||
|
|
||
|
// Root returns the offset of the database root object or an error, if
|
||
|
// any. It's not an error if a newly created or empty database has no
|
||
|
// root yet. The returned offset in that case will be zero.
|
||
|
Root() (int64, error)
|
||
|
|
||
|
// SetRoot sets the offset of the database root object.
|
||
|
SetRoot(root int64) error
|
||
|
|
||
|
// Stat returns the os.FileInfo structure describing the storage. If
|
||
|
// there is an error, it will be of type *os.PathError.
|
||
|
Stat() (os.FileInfo, error)
|
||
|
|
||
|
// Sync commits the current contents of the database to stable storage.
|
||
|
// Typically, this means flushing the file system's in-memory copy of
|
||
|
// recently written data to disk.
|
||
|
Sync() error
|
||
|
|
||
|
// Truncate changes the size of the storage. If there is an error, it
|
||
|
// will be of type *os.PathError.
|
||
|
Truncate(int64) error
|
||
|
|
||
|
// WriteAt writes len(p) bytes from p to the storage at offset off. It
|
||
|
// returns the number of bytes written from p (0 <= n <= len(p)) and
|
||
|
// any error encountered that caused the write to stop early. WriteAt
|
||
|
// must return a non-nil error if it returns n < len(p).
|
||
|
WriteAt(p []byte, off int64) (n int, err error)
|
||
|
}
|
||
|
|
||
|
// DB represents a database.
|
||
|
type DB struct {
|
||
|
Storage
|
||
|
}
|
||
|
|
||
|
// NewDB returns a newly created DB backed by s or an error, if any.
|
||
|
func NewDB(s Storage) (*DB, error) { return &DB{s}, nil }
|
||
|
|
||
|
func (db *DB) r4(off int64) (int, error) { return r4(db, off) }
|
||
|
func (db *DB) r8(off int64) (int64, error) { return r8(db, off) }
|
||
|
func (db *DB) w4(off int64, n int) error { return w4(db, off, n) }
|
||
|
func (db *DB) w8(off, n int64) error { return w8(db, off, n) }
|
||
|
|
||
|
func r4(s Storage, off int64) (int, error) {
|
||
|
p := buffer.Get(4)
|
||
|
b := *p
|
||
|
if n, err := s.ReadAt(b, off); n != 4 {
|
||
|
if err == nil {
|
||
|
err = fmt.Errorf("short storage read")
|
||
|
}
|
||
|
return 0, err
|
||
|
}
|
||
|
|
||
|
var n uint32
|
||
|
for _, v := range b {
|
||
|
n = n<<8 | uint32(v)
|
||
|
}
|
||
|
buffer.Put(p)
|
||
|
return int(int32(n)), nil
|
||
|
}
|
||
|
|
||
|
func r8(s Storage, off int64) (int64, error) {
|
||
|
p := buffer.Get(8)
|
||
|
b := *p
|
||
|
if n, err := s.ReadAt(b, off); n != 8 {
|
||
|
if err == nil {
|
||
|
err = fmt.Errorf("short storage read")
|
||
|
}
|
||
|
return 0, err
|
||
|
}
|
||
|
|
||
|
var n uint64
|
||
|
for _, v := range b {
|
||
|
n = n<<8 | uint64(v)
|
||
|
}
|
||
|
buffer.Put(p)
|
||
|
return int64(n), nil
|
||
|
}
|
||
|
|
||
|
func r8b(b []byte) int64 {
|
||
|
var n uint64
|
||
|
for _, v := range b {
|
||
|
n = n<<8 | uint64(v)
|
||
|
}
|
||
|
return int64(n)
|
||
|
}
|
||
|
|
||
|
func w4(s Storage, off int64, n int) error {
|
||
|
p := buffer.Get(4)
|
||
|
b := *p
|
||
|
for i := range b {
|
||
|
b[i] = byte(n >> 24)
|
||
|
n <<= 8
|
||
|
}
|
||
|
_, err := s.WriteAt(b, off)
|
||
|
buffer.Put(p)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func w8(s Storage, off, n int64) error {
|
||
|
p := buffer.Get(8)
|
||
|
b := *p
|
||
|
for i := range b {
|
||
|
b[i] = byte(n >> 56)
|
||
|
n <<= 8
|
||
|
}
|
||
|
_, err := s.WriteAt(b, off)
|
||
|
buffer.Put(p)
|
||
|
return err
|
||
|
}
|