forked from etcd-io/bbolt
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Points: 1. There are lots of duplicated definitions between bolt and guts_cli, which is definitely not good. 2. The implementation in guts_cli also has issue, please refer to etcd-io#391. This refactoring can fix the issue. Signed-off-by: Benjamin Wang <[email protected]>
- Loading branch information
1 parent
f87db0e
commit 4910a49
Showing
8 changed files
with
839 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package common | ||
|
||
import ( | ||
"fmt" | ||
"unsafe" | ||
) | ||
|
||
const BucketHeaderSize = int(unsafe.Sizeof(InBucket{})) | ||
|
||
// InBucket represents the on-file representation of a bucket. | ||
// This is stored as the "value" of a bucket key. If the bucket is small enough, | ||
// then its root page can be stored inline in the "value", after the bucket | ||
// header. In the case of inline buckets, the "root" will be 0. | ||
type InBucket struct { | ||
root Pgid // page id of the bucket's root-level page | ||
sequence uint64 // monotonically incrementing, used by NextSequence() | ||
} | ||
|
||
func NewInBucket(root Pgid, seq uint64) InBucket { | ||
return InBucket{ | ||
root: root, | ||
sequence: seq, | ||
} | ||
} | ||
|
||
func (b *InBucket) RootPage() Pgid { | ||
return b.root | ||
} | ||
|
||
func (b *InBucket) SetRootPage(id Pgid) { | ||
b.root = id | ||
} | ||
|
||
// InSequence returns the sequence. The reason why not naming it `Sequence` | ||
// is to avoid duplicated name as `(*Bucket) Sequence()` | ||
func (b *InBucket) InSequence() uint64 { | ||
return b.sequence | ||
} | ||
|
||
func (b *InBucket) SetInSequence(v uint64) { | ||
b.sequence = v | ||
} | ||
|
||
func (b *InBucket) IncSequence() { | ||
b.sequence++ | ||
} | ||
|
||
func (b *InBucket) InlinePage(v []byte) *Page { | ||
return (*Page)(unsafe.Pointer(&v[BucketHeaderSize])) | ||
} | ||
|
||
func (b *InBucket) String() string { | ||
return fmt.Sprintf("<pgid=%d,seq=%d>", b.root, b.sequence) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package common | ||
|
||
import "errors" | ||
|
||
// These errors can be returned when opening or calling methods on a DB. | ||
var ( | ||
// ErrDatabaseNotOpen is returned when a DB instance is accessed before it | ||
// is opened or after it is closed. | ||
ErrDatabaseNotOpen = errors.New("database not open") | ||
|
||
// ErrDatabaseOpen is returned when opening a database that is | ||
// already open. | ||
ErrDatabaseOpen = errors.New("database already open") | ||
|
||
// ErrInvalid is returned when both meta pages on a database are invalid. | ||
// This typically occurs when a file is not a bolt database. | ||
ErrInvalid = errors.New("invalid database") | ||
|
||
// ErrInvalidMapping is returned when the database file fails to get mapped. | ||
ErrInvalidMapping = errors.New("database isn't correctly mapped") | ||
|
||
// ErrVersionMismatch is returned when the data file was created with a | ||
// different version of Bolt. | ||
ErrVersionMismatch = errors.New("version mismatch") | ||
|
||
// ErrChecksum is returned when either meta page checksum does not match. | ||
ErrChecksum = errors.New("checksum error") | ||
|
||
// ErrTimeout is returned when a database cannot obtain an exclusive lock | ||
// on the data file after the timeout passed to Open(). | ||
ErrTimeout = errors.New("timeout") | ||
) | ||
|
||
// These errors can occur when beginning or committing a Tx. | ||
var ( | ||
// ErrTxNotWritable is returned when performing a write operation on a | ||
// read-only transaction. | ||
ErrTxNotWritable = errors.New("tx not writable") | ||
|
||
// ErrTxClosed is returned when committing or rolling back a transaction | ||
// that has already been committed or rolled back. | ||
ErrTxClosed = errors.New("tx closed") | ||
|
||
// ErrDatabaseReadOnly is returned when a mutating transaction is started on a | ||
// read-only database. | ||
ErrDatabaseReadOnly = errors.New("database is in read-only mode") | ||
|
||
// ErrFreePagesNotLoaded is returned when a readonly transaction without | ||
// preloading the free pages is trying to access the free pages. | ||
ErrFreePagesNotLoaded = errors.New("free pages are not pre-loaded") | ||
) | ||
|
||
// These errors can occur when putting or deleting a value or a bucket. | ||
var ( | ||
// ErrBucketNotFound is returned when trying to access a bucket that has | ||
// not been created yet. | ||
ErrBucketNotFound = errors.New("bucket not found") | ||
|
||
// ErrBucketExists is returned when creating a bucket that already exists. | ||
ErrBucketExists = errors.New("bucket already exists") | ||
|
||
// ErrBucketNameRequired is returned when creating a bucket with a blank name. | ||
ErrBucketNameRequired = errors.New("bucket name required") | ||
|
||
// ErrKeyRequired is returned when inserting a zero-length key. | ||
ErrKeyRequired = errors.New("key required") | ||
|
||
// ErrKeyTooLarge is returned when inserting a key that is larger than MaxKeySize. | ||
ErrKeyTooLarge = errors.New("key too large") | ||
|
||
// ErrValueTooLarge is returned when inserting a value that is larger than MaxValueSize. | ||
ErrValueTooLarge = errors.New("value too large") | ||
|
||
// ErrIncompatibleValue is returned when trying create or delete a bucket | ||
// on an existing non-bucket key or when trying to create or delete a | ||
// non-bucket key on an existing bucket key. | ||
ErrIncompatibleValue = errors.New("incompatible value") | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package common | ||
|
||
import ( | ||
"fmt" | ||
"hash/fnv" | ||
"io" | ||
"unsafe" | ||
) | ||
|
||
type Meta struct { | ||
magic uint32 | ||
version uint32 | ||
pageSize uint32 | ||
flags uint32 | ||
root InBucket | ||
freelist Pgid | ||
pgid Pgid | ||
txid Txid | ||
checksum uint64 | ||
} | ||
|
||
// Validate checks the marker bytes and version of the meta page to ensure it matches this binary. | ||
func (m *Meta) Validate() error { | ||
if m.magic != Magic { | ||
return ErrInvalid | ||
} else if m.version != Version { | ||
return ErrVersionMismatch | ||
} else if m.checksum != m.Sum64() { | ||
return ErrChecksum | ||
} | ||
return nil | ||
} | ||
|
||
// Copy copies one meta object to another. | ||
func (m *Meta) Copy(dest *Meta) { | ||
*dest = *m | ||
} | ||
|
||
// Write writes the meta onto a page. | ||
func (m *Meta) Write(p *Page) { | ||
if m.root.root >= m.pgid { | ||
panic(fmt.Sprintf("root bucket pgid (%d) above high water mark (%d)", m.root.root, m.pgid)) | ||
} else if m.freelist >= m.pgid && m.freelist != PgidNoFreelist { | ||
// TODO: reject pgidNoFreeList if !NoFreelistSync | ||
panic(fmt.Sprintf("freelist pgid (%d) above high water mark (%d)", m.freelist, m.pgid)) | ||
} | ||
|
||
// Page id is either going to be 0 or 1 which we can determine by the transaction ID. | ||
p.id = Pgid(m.txid % 2) | ||
p.flags |= MetaPageFlag | ||
|
||
// Calculate the checksum. | ||
m.checksum = m.Sum64() | ||
|
||
m.Copy(p.Meta()) | ||
} | ||
|
||
// Sum64 generates the checksum for the meta. | ||
func (m *Meta) Sum64() uint64 { | ||
var h = fnv.New64a() | ||
_, _ = h.Write((*[unsafe.Offsetof(Meta{}.checksum)]byte)(unsafe.Pointer(m))[:]) | ||
return h.Sum64() | ||
} | ||
|
||
func (m *Meta) Magic() uint32 { | ||
return m.magic | ||
} | ||
|
||
func (m *Meta) SetMagic(v uint32) { | ||
m.magic = v | ||
} | ||
|
||
func (m *Meta) SetVersion(v uint32) { | ||
m.version = v | ||
} | ||
|
||
func (m *Meta) PageSize() uint32 { | ||
return m.pageSize | ||
} | ||
|
||
func (m *Meta) SetPageSize(v uint32) { | ||
m.pageSize = v | ||
} | ||
|
||
func (m *Meta) Flags() uint32 { | ||
return m.flags | ||
} | ||
|
||
func (m *Meta) SetFlags(v uint32) { | ||
m.flags = v | ||
} | ||
|
||
func (m *Meta) SetRootBucket(b InBucket) { | ||
m.root = b | ||
} | ||
|
||
func (m *Meta) RootBucket() *InBucket { | ||
return &m.root | ||
} | ||
|
||
func (m *Meta) Freelist() Pgid { | ||
return m.freelist | ||
} | ||
|
||
func (m *Meta) SetFreelist(v Pgid) { | ||
m.freelist = v | ||
} | ||
|
||
func (m *Meta) Pgid() Pgid { | ||
return m.pgid | ||
} | ||
|
||
func (m *Meta) SetPgid(id Pgid) { | ||
m.pgid = id | ||
} | ||
|
||
func (m *Meta) Txid() Txid { | ||
return m.txid | ||
} | ||
|
||
func (m *Meta) SetTxid(id Txid) { | ||
m.txid = id | ||
} | ||
|
||
func (m *Meta) IncTxid() { | ||
m.txid += 1 | ||
} | ||
|
||
func (m *Meta) DecTxid() { | ||
m.txid -= 1 | ||
} | ||
|
||
func (m *Meta) SetChecksum(v uint64) { | ||
m.checksum = v | ||
} | ||
|
||
func (m *Meta) Print(w io.Writer) { | ||
fmt.Fprintf(w, "Version: %d\n", m.version) | ||
fmt.Fprintf(w, "Page Size: %d bytes\n", m.pageSize) | ||
fmt.Fprintf(w, "Flags: %08x\n", m.flags) | ||
fmt.Fprintf(w, "Root: <pgid=%d>\n", m.root.root) | ||
fmt.Fprintf(w, "Freelist: <pgid=%d>\n", m.freelist) | ||
fmt.Fprintf(w, "HWM: <pgid=%d>\n", m.pgid) | ||
fmt.Fprintf(w, "Txn ID: %d\n", m.txid) | ||
fmt.Fprintf(w, "Checksum: %016x\n", m.checksum) | ||
fmt.Fprintf(w, "\n") | ||
} |
Oops, something went wrong.