The delete operation would fail if the key was located outside of a leaf node (i.e. it was being used as a median.) This was the broken code:
if pos > 0 && node.Values[pos-1].Key == index {
// Found the delete value
if node.Nodes != nil {
// Lost median, will balance
tree.balance(node, pos)
}else{
The problem is that when I lost a median I just rebalanced two subtrees, resulting in no change. What was I thinking?
if pos > 0 && node.Values[pos-1].Key == index {
// Found the delete value
tree.Stats.Size--
if node.Nodes != nil {
// Lost median, must borrow
var remaining int
node.Values[pos-1], remaining = tree.borrow(node.Nodes[pos-1])
if remaining < (tree.N/2) {
tree.balance(node, pos-1)
}
The new code implements the borrow() routine. That operates similar to the del() routine except that it always descends down the right-most tree or value, returning the largest value in the subtree.
Wednesday, November 20, 2013
Tuesday, November 19, 2013
Better testing reveals broken delete...
The latest commit on GitHub has an updated test implementation. It's a big improvement over what I had before because it uses Go's map type as a reference implementation. First I created an interface "TreeLike" that defines the basic operations on a tree:
type Treelike interface {
Insert(uint64, interface{})
Fetch(uint64) interface{}
Delete(uint64)
Iterate() chan uint64
}
type Treelike interface {
Insert(uint64, interface{})
Fetch(uint64) interface{}
Delete(uint64)
Iterate() chan uint64
}
I know Iterate() is basically useless, it's still a work in progress. TreeLike is already implemented by Btree. Now I make a struct that implements checking.
type BtreeTest struct {
test *testing.T
tree Treelike
reference map[uint64] interface{}
}
The class contains a test so that I can testing.T.Fail() when a problem is detected. The TreeLike is kept in sync with the map[] by implementing methods that operate on both structures. This is the implementation of Insert():
func (self *BtreeTest) Insert(key uint64, value interface{}) {
self.reference[key] = value
self.tree.Insert(key, value)
}
The Insert() function does no checking. The Delete() function does:
func (self *BtreeTest) Delete(key uint64) {
delete(self.reference, key)
self.tree.Delete(key)
verify := self.tree.Fetch(key)
if (verify != nil) {
self.test.Fail()
}
}
The struct BtreeTest itself implements TreeLike so any code that requires TreeLike can be easily instrumented to be testing code. Thanks to that I found a serious bug in Delete(). My delete function fails to delete values that are not stored in leaf nodes. I'm fixing it.
Tuesday, November 5, 2013
Adding Persistence to Kali Linux on a USB Key
The official Kali Linux USB Live Install guide mentions how to partition your USB stick to add a persistence partition. The persistence partition is very useful because it give Kali state. The instructions are tell you how to do the procedure on a non-Kali Linux. What's the use in that.
Here's the procedure for adding persistence from within Kali Linux:
1. Figure out what the device your flash key is using. The 'df' command gave me this output:
4. Proceed to format a new partition of your desired size to be used for persistence. In our example, we used all the remaining space available. Make sure the volume label of the newly created partition is persistence, and format it using the ext4 filesystem.
5. Once the process is complete, mount your persistence USB partition using the following commands:
Plug the USB stick into the computer you want to boot up. Make sure your BIOS is set to boot from your USB device. When the Kali Linux boot screen is displayed, select “Live boot” from the menu (don’t press enter), and press the tab button. This will allow you to edit the boot parameters. Add the word “persistence” to the end of the boot parameter line each time you want to mount your persistent storage.
Maybe I'll blog about how to fix that one day...
Here's the procedure for adding persistence from within Kali Linux:
1. Figure out what the device your flash key is using. The 'df' command gave me this output:
root@kali:~# df2. The flash device is /dev/sdb. Start gparted on /dev/sdb:
Filesystem 1K-blocks Used Available Use% Mounted on
rootfs 2050320 39228 2011092 2% /
udev 10240 0 10240 0% /dev
tmpfs 410068 716 409352 1% /run
/dev/sdb1 2553376 2553376 0 100% /lib/live/mount/medium
/dev/loop0 2354304 2354304 0 100% /lib/live/mount/rootfs/filesystem.squashfs
tmpfs 2050320 0 2050320 0% /lib/live/mount/overlay
tmpfs 2050320 0 2050320 0% /lib/live/mount/overlay
aufs 2050320 39228 2011092 2% /
tmpfs 5120 0 5120 0% /run/lock
tmpfs 820120 224 819896 1% /run/shm
gparted /dev/sdb3. Your current partitioning scheme should look similar to this:
4. Proceed to format a new partition of your desired size to be used for persistence. In our example, we used all the remaining space available. Make sure the volume label of the newly created partition is persistence, and format it using the ext4 filesystem.
mkdir /mnt/usb mount /dev/sdb2 /mnt/usb echo "/ union" >> /mnt/usb/persistence.conf umount /mnt/usb6. The default instructions make you add the "persistence" parameter to the kernel every time you want your persistent storage. That's cumbersome. Unfortunately we're stuck with that problem because the file that controls the bootup is on an ISO9660 filesystem, therefore it cannot be changed without re-imaging the key. Bummer.
Plug the USB stick into the computer you want to boot up. Make sure your BIOS is set to boot from your USB device. When the Kali Linux boot screen is displayed, select “Live boot” from the menu (don’t press enter), and press the tab button. This will allow you to edit the boot parameters. Add the word “persistence” to the end of the boot parameter line each time you want to mount your persistent storage.
Maybe I'll blog about how to fix that one day...
Subscribe to:
Posts (Atom)