Skip to content
This repository has been archived by the owner on Mar 9, 2019. It is now read-only.

Commit

Permalink
Merge pull request #8 from benbjohnson/master
Browse files Browse the repository at this point in the history
Rename sys ☞ buckets.
  • Loading branch information
benbjohnson committed Feb 6, 2014
2 parents 4820312 + 0ed3dc3 commit cf46437
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 135 deletions.
65 changes: 37 additions & 28 deletions sys.go → buckets.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,51 @@ import (
"unsafe"
)

// sys represents a in-memory system page.
type sys struct {
// buckets represents a in-memory buckets page.
type buckets struct {
pgid pgid
buckets map[string]*bucket
items map[string]*bucket
}

// size returns the size of the page after serialization.
func (s *sys) size() int {
func (b *buckets) size() int {
var size int = pageHeaderSize
for key, _ := range s.buckets {
for key, _ := range b.items {
size += int(unsafe.Sizeof(bucket{})) + len(key)
}
return size
}

// get retrieves a bucket by name.
func (s *sys) get(key string) *bucket {
return s.buckets[key]
func (b *buckets) get(key string) *bucket {
return b.items[key]
}

// put sets a new value for a bucket.
func (s *sys) put(key string, b *bucket) {
s.buckets[key] = b
func (b *buckets) put(key string, item *bucket) {
b.items[key] = item
}

// del deletes a bucket by name.
func (s *sys) del(key string) {
if b := s.buckets[key]; b != nil {
delete(s.buckets, key)
func (b *buckets) del(key string) {
if item := b.items[key]; item != nil {
delete(b.items, key)
}
}

// read initializes the data from an on-disk page.
func (s *sys) read(p *page) {
s.pgid = p.id
s.buckets = make(map[string]*bucket)
func (b *buckets) read(p *page) {
b.pgid = p.id
b.items = make(map[string]*bucket)

var buckets []*bucket
var items []*bucket
var keys []string

// Read buckets.
// Read items.
nodes := (*[maxNodesPerPage]bucket)(unsafe.Pointer(&p.ptr))
for i := 0; i < int(p.count); i++ {
node := &nodes[i]
buckets = append(buckets, node)
items = append(items, node)
}

// Read keys.
Expand All @@ -61,38 +61,47 @@ func (s *sys) read(p *page) {
buf = buf[size:]
}

// Associate keys and buckets.
// Associate keys and items.
for index, key := range keys {
b := &bucket{buckets[index].root}
s.buckets[key] = b
b.items[key] = &bucket{items[index].root}
}
}

// write writes the items onto a page.
func (s *sys) write(p *page) {
func (b *buckets) write(p *page) {
// Initialize page.
p.flags |= p_sys
p.count = uint16(len(s.buckets))
p.flags |= p_buckets
p.count = uint16(len(b.items))

// Sort keys.
var keys []string
for key, _ := range s.buckets {
for key, _ := range b.items {
keys = append(keys, key)
}
sort.StringSlice(keys).Sort()

// Write each bucket to the page.
buckets := (*[maxNodesPerPage]bucket)(unsafe.Pointer(&p.ptr))
items := (*[maxNodesPerPage]bucket)(unsafe.Pointer(&p.ptr))
for index, key := range keys {
buckets[index] = *s.buckets[key]
items[index] = *b.items[key]
}

// Write each key to the page.
buf := (*[maxAllocSize]byte)(unsafe.Pointer(&buckets[p.count]))[:]
buf := (*[maxAllocSize]byte)(unsafe.Pointer(&items[p.count]))[:]
for _, key := range keys {
buf[0] = byte(len(key))
buf = buf[1:]
copy(buf, []byte(key))
buf = buf[len(key):]
}
}

// updateRoot finds a bucket by root id and then updates it to point to a new root.
func (b *buckets) updateRoot(oldid, newid pgid) {
for _, b := range b.items {
if b.root == oldid {
b.root = newid
return
}
}
}
70 changes: 70 additions & 0 deletions buckets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package bolt

import (
"testing"
"unsafe"

"github.com/stretchr/testify/assert"
)

// Ensure that a buckets page can set a bucket.
func TestBucketsPut(t *testing.T) {
b := &buckets{items: make(map[string]*bucket)}
b.put("foo", &bucket{root: 2})
b.put("bar", &bucket{root: 3})
b.put("foo", &bucket{root: 4})
assert.Equal(t, len(b.items), 2)
assert.Equal(t, b.get("foo").root, pgid(4))
assert.Equal(t, b.get("bar").root, pgid(3))
assert.Nil(t, b.get("no_such_bucket"))
}

// Ensure that a buckets page can deserialize from a page.
func TestBucketsRead(t *testing.T) {
// Create a page.
var buf [4096]byte
page := (*page)(unsafe.Pointer(&buf[0]))
page.count = 2

// Insert 2 items at the beginning.
s := (*[3]bucket)(unsafe.Pointer(&page.ptr))
s[0] = bucket{root: 3}
s[1] = bucket{root: 4}

// Write data for the nodes at the end.
data := (*[4096]byte)(unsafe.Pointer(&s[2]))
data[0] = 3
copy(data[1:], []byte("bar"))
data[4] = 10
copy(data[5:], []byte("helloworld"))

// Deserialize page into a buckets page.
b := &buckets{items: make(map[string]*bucket)}
b.read(page)

// Check that there are two items with correct data.
assert.Equal(t, len(b.items), 2)
assert.Equal(t, b.get("bar").root, pgid(3))
assert.Equal(t, b.get("helloworld").root, pgid(4))
}

// Ensure that a buckets page can serialize itself.
func TestBucketsWrite(t *testing.T) {
b := &buckets{items: make(map[string]*bucket)}
b.put("foo", &bucket{root: 2})
b.put("bar", &bucket{root: 3})

// Write it to a page.
var buf [4096]byte
p := (*page)(unsafe.Pointer(&buf[0]))
b.write(p)

// Read the page back in.
b2 := &buckets{items: make(map[string]*bucket)}
b2.read(p)

// Check that the two pages are the same.
assert.Equal(t, len(b.items), 2)
assert.Equal(t, b.get("foo").root, pgid(2))
assert.Equal(t, b.get("bar").root, pgid(3))
}
13 changes: 11 additions & 2 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (db *DB) init() error {
m.pageSize = uint32(db.pageSize)
m.version = version
m.free = 2
m.sys = 3
m.buckets = 3
m.pgid = 4
m.txnid = txnid(i)
}
Expand All @@ -170,7 +170,7 @@ func (db *DB) init() error {
// Write an empty leaf page at page 4.
p = db.pageInBuffer(buf[:], pgid(3))
p.id = pgid(3)
p.flags = p_sys
p.flags = p_buckets
p.count = 0

// Write the buffer to our data file.
Expand Down Expand Up @@ -385,3 +385,12 @@ func (db *DB) Stat() *Stat {
// TODO: Calculate size, depth, page count (by type), entry count, readers, etc.
return nil
}

type Stat struct {
PageSize int
Depth int
BranchPageCount int
LeafPageCount int
OverflowPageCount int
EntryCount int
}
4 changes: 2 additions & 2 deletions meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type meta struct {
version uint32
pageSize uint32
flags uint32
sys pgid
buckets pgid
free pgid
pgid pgid
txnid txnid
Expand All @@ -31,7 +31,7 @@ func (m *meta) copy(dest *meta) {
dest.pgid = m.pgid
dest.free = m.free
dest.txnid = m.txnid
dest.sys = m.sys
dest.buckets = m.buckets
}

// write writes the meta onto a page.
Expand Down
6 changes: 3 additions & 3 deletions page.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const (
p_branch = 0x01
p_leaf = 0x02
p_meta = 0x04
p_sys = 0x08
p_buckets = 0x08
p_freelist = 0x10
)

Expand All @@ -41,8 +41,8 @@ func (p *page) typ() string {
return "leaf"
} else if (p.flags & p_meta) != 0 {
return "meta"
} else if (p.flags & p_sys) != 0 {
return "system"
} else if (p.flags & p_buckets) != 0 {
return "buckets"
} else if (p.flags & p_freelist) != 0 {
return "freelist"
}
Expand Down
24 changes: 9 additions & 15 deletions rwtransaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,17 @@ func (t *RWTransaction) CreateBucket(name string) error {
p := t.allocate(1)
p.flags = p_leaf

// Add bucket to system page.
t.sys.put(name, &bucket{root: p.id})
// Add bucket to buckets page.
t.buckets.put(name, &bucket{root: p.id})

return nil
}

// DropBucket deletes a bucket.
func (t *RWTransaction) DeleteBucket(name string) error {
// Remove from system page.
t.sys.del(name)
// Remove from buckets page.
t.buckets.del(name)

// TODO: Delete entry from system bucket.
// TODO: Free all pages.
// TODO: Remove cursor.
return nil
Expand Down Expand Up @@ -105,17 +104,17 @@ func (t *RWTransaction) Commit() error {
// Spill data onto dirty pages.
t.spill()

// Spill system page.
p := t.allocate((t.sys.size() / t.db.pageSize) + 1)
t.sys.write(p)
// Spill buckets page.
p := t.allocate((t.buckets.size() / t.db.pageSize) + 1)
t.buckets.write(p)

// Write dirty pages to disk.
if err := t.write(); err != nil {
return err
}

// Update the meta.
t.meta.sys = p.id
t.meta.buckets = p.id

// Write meta to disk.
if err := t.writeMeta(); err != nil {
Expand Down Expand Up @@ -223,12 +222,7 @@ func (t *RWTransaction) spill() {

// Update roots with new roots.
for _, root := range roots {
for _, b := range t.sys.buckets {
if b.root == root.pgid {
b.root = root.node.root().pgid
break
}
}
t.buckets.updateRoot(root.pgid, root.node.root().pgid)
}
}

Expand Down
10 changes: 0 additions & 10 deletions stat.go

This file was deleted.

Loading

0 comments on commit cf46437

Please sign in to comment.