From 0ed3dc3071d7ef0503f3fcbd015b63bbd6eae93e Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Wed, 5 Feb 2014 22:15:47 -0700 Subject: Rename sys ☞ buckets. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- buckets.go | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ buckets_test.go | 70 ++++++++++++++++++++++++++++++++++++ db.go | 13 +++++-- meta.go | 4 +-- page.go | 6 ++-- rwtransaction.go | 24 +++++-------- stat.go | 10 ------ sys.go | 98 -------------------------------------------------- sys_test.go | 70 ------------------------------------ transaction.go | 9 +++-- 10 files changed, 206 insertions(+), 205 deletions(-) create mode 100644 buckets.go create mode 100644 buckets_test.go delete mode 100644 stat.go delete mode 100644 sys.go delete mode 100644 sys_test.go diff --git a/buckets.go b/buckets.go new file mode 100644 index 0000000..59a82d8 --- /dev/null +++ b/buckets.go @@ -0,0 +1,107 @@ +package bolt + +import ( + "sort" + "unsafe" +) + +// buckets represents a in-memory buckets page. +type buckets struct { + pgid pgid + items map[string]*bucket +} + +// size returns the size of the page after serialization. +func (b *buckets) size() int { + var size int = pageHeaderSize + for key, _ := range b.items { + size += int(unsafe.Sizeof(bucket{})) + len(key) + } + return size +} + +// get retrieves a bucket by name. +func (b *buckets) get(key string) *bucket { + return b.items[key] +} + +// put sets a new value for a bucket. +func (b *buckets) put(key string, item *bucket) { + b.items[key] = item +} + +// del deletes a bucket by name. +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 (b *buckets) read(p *page) { + b.pgid = p.id + b.items = make(map[string]*bucket) + + var items []*bucket + var keys []string + + // Read items. + nodes := (*[maxNodesPerPage]bucket)(unsafe.Pointer(&p.ptr)) + for i := 0; i < int(p.count); i++ { + node := &nodes[i] + items = append(items, node) + } + + // Read keys. + buf := (*[maxAllocSize]byte)(unsafe.Pointer(&nodes[p.count]))[:] + for i := 0; i < int(p.count); i++ { + size := int(buf[0]) + buf = buf[1:] + keys = append(keys, string(buf[:size])) + buf = buf[size:] + } + + // Associate keys and items. + for index, key := range keys { + b.items[key] = &bucket{items[index].root} + } +} + +// write writes the items onto a page. +func (b *buckets) write(p *page) { + // Initialize page. + p.flags |= p_buckets + p.count = uint16(len(b.items)) + + // Sort keys. + var keys []string + for key, _ := range b.items { + keys = append(keys, key) + } + sort.StringSlice(keys).Sort() + + // Write each bucket to the page. + items := (*[maxNodesPerPage]bucket)(unsafe.Pointer(&p.ptr)) + for index, key := range keys { + items[index] = *b.items[key] + } + + // Write each key to the page. + 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 + } + } +} diff --git a/buckets_test.go b/buckets_test.go new file mode 100644 index 0000000..0fc6288 --- /dev/null +++ b/buckets_test.go @@ -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)) +} diff --git a/db.go b/db.go index 41fbc64..d62114a 100644 --- a/db.go +++ b/db.go @@ -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) } @@ -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. @@ -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 +} diff --git a/meta.go b/meta.go index 33f45d4..5c68fa7 100644 --- a/meta.go +++ b/meta.go @@ -7,7 +7,7 @@ type meta struct { version uint32 pageSize uint32 flags uint32 - sys pgid + buckets pgid free pgid pgid pgid txnid txnid @@ -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. diff --git a/page.go b/page.go index d05dce6..7c5a91c 100644 --- a/page.go +++ b/page.go @@ -19,7 +19,7 @@ const ( p_branch = 0x01 p_leaf = 0x02 p_meta = 0x04 - p_sys = 0x08 + p_buckets = 0x08 p_freelist = 0x10 ) @@ -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" } diff --git a/rwtransaction.go b/rwtransaction.go index 93e544b..505d107 100644 --- a/rwtransaction.go +++ b/rwtransaction.go @@ -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 @@ -105,9 +104,9 @@ 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 { @@ -115,7 +114,7 @@ func (t *RWTransaction) Commit() error { } // Update the meta. - t.meta.sys = p.id + t.meta.buckets = p.id // Write meta to disk. if err := t.writeMeta(); err != nil { @@ -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) } } diff --git a/stat.go b/stat.go deleted file mode 100644 index b01fa99..0000000 --- a/stat.go +++ /dev/null @@ -1,10 +0,0 @@ -package bolt - -type Stat struct { - PageSize int - Depth int - BranchPageCount int - LeafPageCount int - OverflowPageCount int - EntryCount int -} diff --git a/sys.go b/sys.go deleted file mode 100644 index ec1b858..0000000 --- a/sys.go +++ /dev/null @@ -1,98 +0,0 @@ -package bolt - -import ( - "sort" - "unsafe" -) - -// sys represents a in-memory system page. -type sys struct { - pgid pgid - buckets map[string]*bucket -} - -// size returns the size of the page after serialization. -func (s *sys) size() int { - var size int = pageHeaderSize - for key, _ := range s.buckets { - 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] -} - -// put sets a new value for a bucket. -func (s *sys) put(key string, b *bucket) { - s.buckets[key] = b -} - -// del deletes a bucket by name. -func (s *sys) del(key string) { - if b := s.buckets[key]; b != nil { - delete(s.buckets, 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) - - var buckets []*bucket - var keys []string - - // Read buckets. - nodes := (*[maxNodesPerPage]bucket)(unsafe.Pointer(&p.ptr)) - for i := 0; i < int(p.count); i++ { - node := &nodes[i] - buckets = append(buckets, node) - } - - // Read keys. - buf := (*[maxAllocSize]byte)(unsafe.Pointer(&nodes[p.count]))[:] - for i := 0; i < int(p.count); i++ { - size := int(buf[0]) - buf = buf[1:] - keys = append(keys, string(buf[:size])) - buf = buf[size:] - } - - // Associate keys and buckets. - for index, key := range keys { - b := &bucket{buckets[index].root} - s.buckets[key] = b - } -} - -// write writes the items onto a page. -func (s *sys) write(p *page) { - // Initialize page. - p.flags |= p_sys - p.count = uint16(len(s.buckets)) - - // Sort keys. - var keys []string - for key, _ := range s.buckets { - keys = append(keys, key) - } - sort.StringSlice(keys).Sort() - - // Write each bucket to the page. - buckets := (*[maxNodesPerPage]bucket)(unsafe.Pointer(&p.ptr)) - for index, key := range keys { - buckets[index] = *s.buckets[key] - } - - // Write each key to the page. - buf := (*[maxAllocSize]byte)(unsafe.Pointer(&buckets[p.count]))[:] - for _, key := range keys { - buf[0] = byte(len(key)) - buf = buf[1:] - copy(buf, []byte(key)) - buf = buf[len(key):] - } -} diff --git a/sys_test.go b/sys_test.go deleted file mode 100644 index 0dcae66..0000000 --- a/sys_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package bolt - -import ( - "testing" - "unsafe" - - "github.com/stretchr/testify/assert" -) - -// Ensure that a system page can set a bucket. -func TestSysPut(t *testing.T) { - s := &sys{buckets: make(map[string]*bucket)} - s.put("foo", &bucket{root: 2}) - s.put("bar", &bucket{root: 3}) - s.put("foo", &bucket{root: 4}) - assert.Equal(t, len(s.buckets), 2) - assert.Equal(t, s.get("foo").root, pgid(4)) - assert.Equal(t, s.get("bar").root, pgid(3)) - assert.Nil(t, s.get("no_such_bucket")) -} - -// Ensure that a system page can deserialize from a page. -func TestSysRead(t *testing.T) { - // Create a page. - var buf [4096]byte - page := (*page)(unsafe.Pointer(&buf[0])) - page.count = 2 - - // Insert 2 buckets at the beginning. - buckets := (*[3]bucket)(unsafe.Pointer(&page.ptr)) - buckets[0] = bucket{root: 3} - buckets[1] = bucket{root: 4} - - // Write data for the nodes at the end. - data := (*[4096]byte)(unsafe.Pointer(&buckets[2])) - data[0] = 3 - copy(data[1:], []byte("bar")) - data[4] = 10 - copy(data[5:], []byte("helloworld")) - - // Deserialize page into a system page. - s := &sys{buckets: make(map[string]*bucket)} - s.read(page) - - // Check that there are two items with correct data. - assert.Equal(t, len(s.buckets), 2) - assert.Equal(t, s.get("bar").root, pgid(3)) - assert.Equal(t, s.get("helloworld").root, pgid(4)) -} - -// Ensure that a system page can serialize itself. -func TestSysWrite(t *testing.T) { - s := &sys{buckets: make(map[string]*bucket)} - s.put("foo", &bucket{root: 2}) - s.put("bar", &bucket{root: 3}) - - // Write it to a page. - var buf [4096]byte - p := (*page)(unsafe.Pointer(&buf[0])) - s.write(p) - - // Read the page back in. - s2 := &sys{buckets: make(map[string]*bucket)} - s2.read(p) - - // Check that the two pages are the same. - assert.Equal(t, len(s.buckets), 2) - assert.Equal(t, s.get("foo").root, pgid(2)) - assert.Equal(t, s.get("bar").root, pgid(3)) -} diff --git a/transaction.go b/transaction.go index 29337c6..5733e9e 100644 --- a/transaction.go +++ b/transaction.go @@ -13,7 +13,7 @@ type Transaction struct { id int db *DB meta *meta - sys *sys + buckets *buckets pages map[pgid]*page } @@ -23,8 +23,8 @@ func (t *Transaction) init(db *DB) { t.meta = db.meta() t.pages = nil - t.sys = &sys{} - t.sys.read(t.page(t.meta.sys)) + t.buckets = &buckets{} + t.buckets.read(t.page(t.meta.buckets)) } func (t *Transaction) Close() { @@ -37,8 +37,7 @@ func (t *Transaction) DB() *DB { // Bucket retrieves a bucket by name. func (t *Transaction) Bucket(name string) *Bucket { - // Lookup bucket from the system page. - b := t.sys.get(name) + b := t.buckets.get(name) if b == nil { return nil } -- cgit v1.2.3