Example #1
0
func TestInsert(t *testing.T) {

	h := offheap.NewHashTable(8)

	cv.Convey("inserting a non-zero key should enable retrieving them with Lookup", t, func() {
		cv.So(h.Population, cv.ShouldEqual, 0)
		cv.So(h.Lookup(23), cv.ShouldEqual, nil)
		c, ok := h.Insert(23)
		c.SetInt(55)
		cv.So(c, cv.ShouldNotEqual, nil)
		cv.So(ok, cv.ShouldEqual, true)
		cv.So(h.Population, cv.ShouldEqual, 1)
		cv.So(h.Lookup(23), cv.ShouldNotEqual, nil)
		cell := h.Lookup(23)
		cv.So(cell.Value[0], cv.ShouldEqual, 55)
	})

	h.Clear()
	cv.Convey("inserting a zero key should also be retrievable with Lookup", t, func() {
		cv.So(h.Population, cv.ShouldEqual, 0)
		cv.So(h.Lookup(0), cv.ShouldEqual, nil)
		c, ok := h.Insert(0)
		c.SetInt(55)
		cv.So(c, cv.ShouldNotEqual, nil)
		cv.So(ok, cv.ShouldEqual, true)
		cv.So(h.Population, cv.ShouldEqual, 1)
		cv.So(h.Lookup(0), cv.ShouldNotEqual, nil)
		cell := h.Lookup(0)
		cv.So(cell.Value[0], cv.ShouldEqual, 55)
	})

	h.Clear()
	cv.Convey("Insert()-ing the same key twice should return false for the 2nd param on encountering the same key again. For 0 key.", t, func() {
		cv.So(h.Population, cv.ShouldEqual, 0)
		c, ok := h.Insert(0)
		cv.So(c, cv.ShouldNotEqual, nil)
		cv.So(c.UnHashedKey, cv.ShouldEqual, 0)
		cv.So(ok, cv.ShouldEqual, true)

		c, ok = h.Insert(0)
		cv.So(c, cv.ShouldNotEqual, nil)
		cv.So(c.UnHashedKey, cv.ShouldEqual, 0)
		cv.So(ok, cv.ShouldEqual, false)
	})

	h.Clear()
	cv.Convey("Insert()-ing the same key twice should return false for the 2nd param on encountering the same key again. For not-zero key.", t, func() {
		cv.So(h.Population, cv.ShouldEqual, 0)
		c, ok := h.Insert(1)
		cv.So(c, cv.ShouldNotEqual, nil)
		cv.So(c.UnHashedKey, cv.ShouldEqual, 1)
		cv.So(ok, cv.ShouldEqual, true)

		c, ok = h.Insert(1)
		cv.So(c, cv.ShouldNotEqual, nil)
		cv.So(c.UnHashedKey, cv.ShouldEqual, 1)
		cv.So(ok, cv.ShouldEqual, false)
	})

}
Example #2
0
func TestCompact(t *testing.T) {

	cv.Convey("Given a big table with no values it, Compact() should re-allocate it to be smaller", t, func() {
		h := offheap.NewHashTable(128)
		cv.So(h.ArraySize, cv.ShouldEqual, 128)
		cv.So(h.Population, cv.ShouldEqual, 0)

		h.Compact()

		cv.So(h.ArraySize, cv.ShouldEqual, 1)
		cv.So(h.Population, cv.ShouldEqual, 0)
	})

}
Example #3
0
func TestDelete(t *testing.T) {

	h := offheap.NewHashTable(2)

	h.Clear()
	cv.Convey("delete should eliminate an element, leaving the rest", t, func() {
		N := 5

		// fill up a table
		cv.So(h.Population, cv.ShouldEqual, 0)
		for i := 0; i < N; i++ {
			a, ok := h.Insert(uint64(i))
			cv.So(ok, cv.ShouldEqual, true)
			cell := h.Lookup(uint64(i))
			cv.So(cell, cv.ShouldNotEqual, nil)
			cv.So(cell, cv.ShouldEqual, a)

			for j := i + 1; j < N+10; j++ {
				cell = h.Lookup(uint64(j))
				cv.So(cell, cv.ShouldEqual, nil)
			}
		}

		// now delete from it, checking all the way down for correctness
		for i := -10; i < N; i++ {
			h.DeleteKey(uint64(i))
			if i >= 0 {
				cell := h.Lookup(uint64(i))
				cv.So(cell, cv.ShouldEqual, nil)
				for j := i + 1; j < N; j++ {
					cell = h.Lookup(uint64(j))
					cv.So(cell, cv.ShouldNotEqual, nil)
					cv.So(cell.UnHashedKey, cv.ShouldEqual, j)
				}
			} else {
				cv.So(h.Population, cv.ShouldEqual, N)
			}
		}
		cell := h.Lookup(uint64(N + 1))
		cv.So(cell, cv.ShouldEqual, nil)

	})
}
Example #4
0
func TestCompatAfterDelete(t *testing.T) {

	h := offheap.NewHashTable(2)

	h.Clear()
	cv.Convey("after being filled up and then deleted down to just 2 elements, Compact() should reduce table size to 4", t, func() {
		N := 100

		// fill up a table
		cv.So(h.Population, cv.ShouldEqual, 0)
		for i := 0; i < N; i++ {
			h.Insert(uint64(i))
		}

		// now delete from it
		for i := 0; i < N-2; i++ {
			h.DeleteKey(uint64(i))
		}
		cv.So(h.ArraySize, cv.ShouldEqual, 256)
		h.Compact()
		cv.So(h.ArraySize, cv.ShouldEqual, 4)

	})
}
Example #5
0
func TestGrowth(t *testing.T) {

	h := offheap.NewHashTable(2)

	cv.Convey("Given a size 2 table, inserting 0 and 1 should retain and recall the 0 and the 1 keys", t, func() {
		cv.So(h.Population, cv.ShouldEqual, 0)
		for i := 0; i < 2; i++ {
			_, ok := h.Insert(uint64(i))
			cv.So(ok, cv.ShouldEqual, true)
		}
		cv.So(h.Population, cv.ShouldEqual, 2)
		cv.So(h.ArraySize, cv.ShouldEqual, 4)
	})

	h.Clear()
	cv.Convey("inserting more than the current size should automatically grow the table", t, func() {
		N := 100

		cv.So(h.Population, cv.ShouldEqual, 0)
		for i := 0; i < N; i++ {
			_, ok := h.Insert(uint64(i))
			cv.So(ok, cv.ShouldEqual, true)
		}
		cv.So(h.Population, cv.ShouldEqual, N)
		cv.So(h.ArraySize, cv.ShouldEqual, upper_power_of_two(uint64(float64(N)*4.0/3.0)))

		for i := 0; i < N; i++ {
			cell := h.Lookup(uint64(i))
			cv.So(cell, cv.ShouldNotEqual, nil)
			cv.So(cell.UnHashedKey, cv.ShouldEqual, i)
		}
		cell := h.Lookup(uint64(N + 1))
		cv.So(cell, cv.ShouldEqual, nil)

	})
}
Example #6
0
func TestIterator(t *testing.T) {

	cv.Convey("Given a table with 0,1,2 in it, the Iterator should give all three values back", t, func() {
		h := offheap.NewHashTable(8)
		cv.So(h.Population, cv.ShouldEqual, 0)
		for i := 0; i < 3; i++ {
			_, ok := h.Insert(uint64(i))
			cv.So(ok, cv.ShouldEqual, true)
			if i == 0 {
				// iterator should start with the zero value
				it := h.NewIterator()
				cv.So(it.Cur.UnHashedKey, cv.ShouldEqual, 0)
			}
		}
		cv.So(h.Population, cv.ShouldEqual, 3)

		found := []uint64{}
		for it := h.NewIterator(); it.Cur != nil; it.Next() {
			found = append(found, it.Cur.UnHashedKey)
		}
		cv.So(len(found), cv.ShouldEqual, 3)
		cv.So(found, cv.ShouldContain, 0)
		cv.So(found, cv.ShouldContain, 1)
		cv.So(found, cv.ShouldContain, 2)
	})

	cv.Convey("Given a table with 1,2,3 in it, the Iterator should give all three values back", t, func() {
		h := offheap.NewHashTable(8)
		cv.So(h.Population, cv.ShouldEqual, 0)
		for i := 1; i < 4; i++ {
			_, ok := h.Insert(uint64(i))
			cv.So(ok, cv.ShouldEqual, true)
			if i == 0 {
				// iterator should not start with the zero value, not inserted.
				it := h.NewIterator()
				cv.So(it.Cur.UnHashedKey, cv.ShouldEqual, 1)
			}
		}
		cv.So(h.Population, cv.ShouldEqual, 3)

		found := []uint64{}
		for it := h.NewIterator(); it.Cur != nil; it.Next() {
			found = append(found, it.Cur.UnHashedKey)
		}
		cv.So(len(found), cv.ShouldEqual, 3)
		cv.So(found, cv.ShouldContain, 3)
		cv.So(found, cv.ShouldContain, 1)
		cv.So(found, cv.ShouldContain, 2)
	})

	cv.Convey("Given a table with the regular 0-th slot and the special zero-location spot occupied, then the the Iterator should still give all the values back", t, func() {
		h := offheap.NewHashTable(4)
		cv.So(h.Population, cv.ShouldEqual, 0)
		for i := 0; i < 2; i++ {
			_, ok := h.Insert(uint64(i))
			cv.So(ok, cv.ShouldEqual, true)
			if i == 0 {
				// iterator should start with the zero value
				it := h.NewIterator()
				cv.So(it.Cur.UnHashedKey, cv.ShouldEqual, 0)
			}
		}
		cv.So(h.Population, cv.ShouldEqual, 2)
		cv.So(h.CellAt(0).UnHashedKey, cv.ShouldEqual, 1) // important for this test that the regular 0-th (first) cell slot be occupied.

		found := []uint64{}
		for it := h.NewIterator(); it.Cur != nil; it.Next() {
			found = append(found, it.Cur.UnHashedKey)
		}
		cv.So(len(found), cv.ShouldEqual, 2)
		cv.So(found, cv.ShouldContain, 0)
		cv.So(found, cv.ShouldContain, 1)
	})

	cv.Convey("Given a table with the regular 0-th slot *empty* and the special zero-location spot occupied, then the the Iterator should still give all the values back", t, func() {
		h := offheap.NewHashTable(8)
		cv.So(h.Population, cv.ShouldEqual, 0)
		for i := 0; i < 2; i++ {
			_, ok := h.Insert(uint64(i))
			cv.So(ok, cv.ShouldEqual, true)
			if i == 0 {
				// iterator should start with the zero value
				it := h.NewIterator()
				cv.So(it.Cur.UnHashedKey, cv.ShouldEqual, 0)
				cv.So(it.Pos, cv.ShouldEqual, -1)
			}
		}
		cv.So(h.Population, cv.ShouldEqual, 2)
		cv.So(h.CellAt(0).UnHashedKey, cv.ShouldEqual, 0) // important for this test that the regular 0-th (first) cell slot be *empty*.

		found := []uint64{}
		for it := h.NewIterator(); it.Cur != nil; it.Next() {
			found = append(found, it.Cur.UnHashedKey)
		}
		cv.So(len(found), cv.ShouldEqual, 2)
		cv.So(found, cv.ShouldContain, 0)
		cv.So(found, cv.ShouldContain, 1)
	})

	cv.Convey("Given a table with the regular 0-th slot *filled* and the special zero-location spot *empty*, then the the Iterator should still give the one value back", t, func() {
		// size 4 just happens to generate an occupation at h.Cells[0]
		h := offheap.NewHashTable(4)
		cv.So(h.Population, cv.ShouldEqual, 0)
		i := 1
		_, ok := h.Insert(uint64(i))
		cv.So(ok, cv.ShouldEqual, true)

		// iterator should start with the zero value
		it := h.NewIterator()
		cv.So(it.Cur.UnHashedKey, cv.ShouldEqual, 1)
		cv.So(it.Pos, cv.ShouldEqual, 0)

		cv.So(h.Population, cv.ShouldEqual, 1)
		cv.So(h.CellAt(0).UnHashedKey, cv.ShouldEqual, 1) // important for this test that the regular 0-th (first) cell slot be *filled*.

		found := []uint64{}
		for it := h.NewIterator(); it.Cur != nil; it.Next() {
			found = append(found, it.Cur.UnHashedKey)
		}
		cv.So(len(found), cv.ShouldEqual, 1)
		cv.So(found, cv.ShouldContain, 1)
	})

	cv.Convey("Given an empty table, an Iterator should still work fine, without crashing", t, func() {
		h := offheap.NewHashTable(4)
		cv.So(h.Population, cv.ShouldEqual, 0)

		found := []uint64{}
		for it := h.NewIterator(); it.Cur != nil; it.Next() {
			found = append(found, it.Cur.UnHashedKey)
		}
		cv.So(len(found), cv.ShouldEqual, 0)
	})

}
Example #7
0
func TestRandomOperationsOrder(t *testing.T) {

	h := offheap.NewHashTable(2)

	m := make(map[uint64]int)

	cv.Convey("given a sequence of random operations, the result should match what Go's builtin map does", t, func() {
		cv.So(hashEqualsMap(h, m), cv.ShouldEqual, true)

		// basic insert
		m[1] = 2
		h.InsertIntValue(1, 2)
		cv.So(hashEqualsMap(h, m), cv.ShouldEqual, true)

		m[3] = 4
		h.InsertIntValue(3, 4)
		cv.So(hashEqualsMap(h, m), cv.ShouldEqual, true)

		// basic delete
		delete(m, 1)
		h.DeleteKey(1)
		cv.So(hashEqualsMap(h, m), cv.ShouldEqual, true)

		delete(m, 3)
		h.DeleteKey(3)
		cv.So(hashEqualsMap(h, m), cv.ShouldEqual, true)

		// now do random operations
		N := 1000
		seed := time.Now().UnixNano()
		fmt.Printf("\n TestRandomOperationsOrder() using seed = '%v'\n", seed)
		gen := rand.New(rand.NewSource(seed))

		for i := 0; i < N; i++ {

			op := gen.Int() % 4
			k := uint64(gen.Int() % (N / 4))
			v := gen.Int() % (N / 4)

			switch op {
			case 0, 1, 2:
				h.InsertIntValue(k, v)
				m[k] = v
				cv.So(hashEqualsMap(h, m), cv.ShouldEqual, true)
			case 3:
				h.DeleteKey(uint64(k))
				delete(m, k)
				cv.So(hashEqualsMap(h, m), cv.ShouldEqual, true)

			}
		}

		// distribution more emphasizing deletes

		for i := 0; i < N; i++ {

			op := gen.Int() % 2
			k := uint64(gen.Int() % (N / 5))
			v := gen.Int() % (N / 2)

			switch op {
			case 0:
				h.InsertIntValue(k, v)
				m[k] = v
				cv.So(hashEqualsMap(h, m), cv.ShouldEqual, true)
			case 1:
				h.DeleteKey(uint64(k))
				delete(m, k)
				cv.So(hashEqualsMap(h, m), cv.ShouldEqual, true)

			}
		}
	})
}