func (self *BTree) Insert(key ByteSlice, record []ByteSlice) bool { dirty := dirty.New(self.info.Height() * 4) // this is our buffer of "dirty" blocks that we will write back at the end if !self.ValidateKey(key) || !self.ValidateRecord(record) { return false } // makes the record rec := self.node.NewRecord(key) for i, f := range record { rec.Set(uint32(i), f) } // insert the block if split is true then we need to split the root if b, r, split := self.insert(self.getblock(self.info.Root()), rec, self.info.Height()-1, dirty); split { // root split // first allocate a new root then insert the key record and the associated pointers root := self.allocate() dirty.Insert(root) if i, ok := root.Add(r); ok { root.InsertPointer(i, self.info.Root()) root.InsertPointer(i+1, b.Position()) } else { fmt.Println("Could not insert into empty block PANIC") os.Exit(2) return false } // don't forget to update the height of the tree and the root self.info.SetRoot(root.Position()) self.info.SetHeight(self.info.Height() + 1) } dirty.Sync() // writes the dirty blocks to disk return true }
func constructCompleteLevel2(self *BTree, order, skip int) { dirty := dirty.New(100) n := order * (order + 2) if skip <= n { n++ } root := self.getblock(self.info.Root()) dirty.Insert(root) cur := self.allocate() dirty.Insert(cur) for i := 0; i < n; i++ { if i+1 == skip { continue } rec := makerec(self, ByteSlice32(uint32(i+1))) if cur.Full() { root.InsertPointer(int(root.PointerCount()), cur.Position()) cur = self.allocate() dirty.Insert(cur) root.Add(rec) } else { cur.Add(rec) } } root.InsertPointer(int(root.PointerCount()), cur.Position()) dirty.Sync() self.info.SetHeight(2) }
func TestGetBlock(t *testing.T) { t.Log("------- TestGetBlock -------") self := makebptree(BLOCKSIZE, t) defer cleanbptree(self) dirty := dirty.New(self.info.Height() * 4) b1 := self.allocate(self.internal) b2 := self.allocate(self.external) dirty.Insert(b1) dirty.Insert(b2) dirty.Sync() b1_ := self.getblock(b1.Position()) b2_ := self.getblock(b2.Position()) if b1_ == nil || b2_ == nil { t.Error("getblock return nil") } b1s, _ := b1.Serialize() b2s, _ := b2.Serialize() b1_s, _ := b1_.Serialize() b2_s, _ := b2_.Serialize() if !ByteSlice(b1s).Eq(ByteSlice(b1_s)) { t.Error("block read from file is not the same as the one written out for b1") } if !ByteSlice(b2s).Eq(ByteSlice(b2_s)) { t.Error("block read from file is not the same as the one written out for b2") } }
func TestSplit(t *testing.T) { for _, size := range sizes { var n int { self := makebptree(size, t) n = int(self.external.KeysPerBlock()) cleanbptree(self) } for i := 0; i <= n; i++ { self := makebptree(size, t) dirty := dirty.New(10) test_split(i, n, self, dirty, t) cleanbptree(self) } } }
func make_complete(self *BpTree, skip int, t *testing.T) { dirty := dirty.New(self.internal.KeysPerBlock() * 3) n := int(self.external.KeysPerBlock()) m := n * n if skip < m { m++ } c := self.getblock(self.info.Root()) root := self.allocate(self.internal) self.info.SetRoot(root.Position()) dirty.Insert(c) dirty.Insert(root) first := 0 if first == skip { first = 1 } r, _ := pkg_rec(self, ByteSlice32(uint32(first)), record) if p, ok := root.Add(r.internal()); ok { root.InsertPointer(p, c.Position()) } else { t.Fatal("could not add a record to the root") } for i := 0; i < m; i++ { if i == skip { continue } r, _ := pkg_rec(self, ByteSlice32(uint32(i)), record) if c.Full() { c = self.allocate(self.external) dirty.Insert(c) if p, ok := root.Add(r.internal()); ok { root.InsertPointer(p, c.Position()) } else { t.Fatal("could not add a record to the root") } } if _, ok := c.Add(r.external()); !ok { t.Fatal("could not add a record to the leaf") } } dirty.Sync() self.info.SetHeight(2) }
func testSimpleSplit(self *BTree, t *testing.T) { // fmt.Println("case 1") dirty := dirty.New(100) a := self.allocate() dirty.Insert(a) fill_block(self, a, t, self.node.KeysPerBlock()) validateSimpleSplit(self, a, makerec(self, ByteSlice32(uint32(self.node.KeysPerBlock()))), dirty, t) // fmt.Println("case 2") a = self.allocate() dirty.Insert(a) fill_block(self, a, t, self.node.KeysPerBlock()>>1) validateSimpleSplit(self, a, makerec(self, ByteSlice32(uint32(self.node.KeysPerBlock())>>1)), dirty, t) // fmt.Println("case 3") a = self.allocate() dirty.Insert(a) fill_block(self, a, t, 0) validateSimpleSplit(self, a, makerec(self, ByteSlice32(0)), dirty, t) }
func (self *BpTree) Insert(key ByteSlice, record []ByteSlice) bool { self.lock.Lock() defer self.lock.Unlock() dirty := dirty.New(self.info.Height() * 4) // package the temp rec rec, valid := pkg_rec(self, key, record) if !valid { fmt.Fprintln(os.Stderr, "key or record not valid") return false } // insert the block if split is true then we need to split the root if b, r, split := self.insert(self.getblock(self.info.Root()), rec, self.info.Height()-1, dirty); split { // This is where the root split goes. // we have to sync the blocks back because the first key in the root will have been // modified if the key we inserted was less than any key in the b+ tree dirty.Sync() // we get the oldroot so we can get the first key from it, this key becomes the first key in // the new root. oldroot := self.getblock(self.info.Root()) var first *tmprec if f, _, _, ok := oldroot.Get(0); ok { first = rec_to_tmp(self, f) } // first allocate a new root then insert the key record and the associated pointers root := self.allocate(self.internal) // the new root will always be an internal node dirty.Insert(root) // first we insert the first key from the old root into the new root and point it at the // old root if i, ok := root.Add(first.internal()); ok { root.InsertPointer(i, self.info.Root()) } else { fmt.Println("431 Could not insert into empty block PANIC") os.Exit(2) return false } // then we point the split rec's key at the the split block if i, ok := root.Add(r.internal()); ok { root.InsertPointer(i, b.Position()) } else { fmt.Println("440 Could not insert into empty block PANIC\n", r, "\n", root, oldroot) os.Exit(2) return false } // don't forget to update the height of the tree and the root self.info.SetRoot(root.Position()) self.info.SetHeight(self.info.Height() + 1) } // at the end of of the method sync back the dirty blocks self.info.IncEntries() self.info.Serialize() dirty.Sync() return true }