func (ht *HashTable) Remove(key, val int) {
	for entry, bucket := 0, ht.HashKey(key); ; {
		entryAddr := bucket*ht.BucketSize + BucketHeader + entry*EntrySize
		entryKey, _ := binary.Varint(ht.Buf[entryAddr+1 : entryAddr+11])
		entryVal, _ := binary.Varint(ht.Buf[entryAddr+11 : entryAddr+21])
		if ht.Buf[entryAddr] == 1 {
			if int(entryKey) == key && int(entryVal) == val {
				ht.Buf[entryAddr] = 0
				return
			}
		} else if entryKey == 0 && entryVal == 0 {
			return
		}
		if entry++; entry == ht.PerBucket {
			entry = 0
			if bucket = ht.nextBucket(bucket); bucket == 0 {
				return
			}
		}
	}
}
Flag an entry as invalid, so that Get will not return it later on.
package data
import (
	"encoding/binary"
	"sync"
	"github.com/HouzuoGuo/tiedot/tdlog"
)
type HashTable struct {
	*Config
	*DataFile
	numBuckets int
	Lock       *sync.RWMutex
}
func (conf *Config) OpenHashTable(path string) (ht *HashTable, err error) {
	ht = &HashTable{Config: conf, Lock: new(sync.RWMutex)}
	if ht.DataFile, err = OpenDataFile(path, ht.HTFileGrowth); err != nil {
		return
	}
	conf.CalculateConfigConstants()
	ht.calculateNumBuckets()
	return
}
func (ht *HashTable) calculateNumBuckets() {
	ht.numBuckets = ht.Size / ht.BucketSize
	largestBucketNum := ht.InitialBuckets - 1
	for i := 0; i < ht.InitialBuckets; i++ {
		lastBucket := ht.lastBucket(i)
		if lastBucket > largestBucketNum && lastBucket < ht.numBuckets {
			largestBucketNum = lastBucket
		}
	}
	ht.numBuckets = largestBucketNum + 1
	usedSize := ht.numBuckets * ht.BucketSize
	if usedSize > ht.Size {
		ht.Used = ht.Size
		ht.EnsureSize(usedSize - ht.Used)
	}
	ht.Used = usedSize
	tdlog.Infof("%s: calculated used size is %d", ht.Path, usedSize)
}
func (ht *HashTable) nextBucket(bucket int) int {
	if bucket >= ht.numBuckets {
		return 0
	}
	bucketAddr := bucket * ht.BucketSize
	nextUint, err := binary.Varint(ht.Buf[bucketAddr : bucketAddr+10])
	next := int(nextUint)
	if next == 0 {
		return 0
	} else if err < 0 || next <= bucket || next >= ht.numBuckets || next < ht.InitialBuckets {
		tdlog.CritNoRepeat("Bad hash table - repair ASAP %s", ht.Path)
		return 0
	} else {
		return next
	}
}
func (ht *HashTable) lastBucket(bucket int) int {
	for curr := bucket; ; {
		next := ht.nextBucket(curr)
		if next == 0 {
			return curr
		}
		curr = next
	}
}
func (ht *HashTable) growBucket(bucket int) {
	ht.EnsureSize(ht.BucketSize)
	lastBucketAddr := ht.lastBucket(bucket) * ht.BucketSize
	binary.PutVarint(ht.Buf[lastBucketAddr:lastBucketAddr+10], int64(ht.numBuckets))
	ht.Used += ht.BucketSize
	ht.numBuckets++
}
func (ht *HashTable) Clear() (err error) {
	if err = ht.DataFile.Clear(); err != nil {
		return
	}
	ht.calculateNumBuckets()
	return
}
func (ht *HashTable) Put(key, val int) {
	for bucket, entry := ht.HashKey(key), 0; ; {
		entryAddr := bucket*ht.BucketSize + BucketHeader + entry*EntrySize
		if ht.Buf[entryAddr] != 1 {
			ht.Buf[entryAddr] = 1
			binary.PutVarint(ht.Buf[entryAddr+1:entryAddr+11], int64(key))
			binary.PutVarint(ht.Buf[entryAddr+11:entryAddr+21], int64(val))
			return
		}
		if entry++; entry == ht.PerBucket {
			entry = 0
			if bucket = ht.nextBucket(bucket); bucket == 0 {
				ht.growBucket(ht.HashKey(key))
				ht.Put(key, val)
				return
			}
		}
	}
}
func (ht *HashTable) Get(key, limit int) (vals []int) {
	if limit == 0 {
		vals = make([]int, 0, 10)
	} else {
		vals = make([]int, 0, limit)
	}
	for count, entry, bucket := 0, 0, ht.HashKey(key); ; {
		entryAddr := bucket*ht.BucketSize + BucketHeader + entry*EntrySize
		entryKey, _ := binary.Varint(ht.Buf[entryAddr+1 : entryAddr+11])
		entryVal, _ := binary.Varint(ht.Buf[entryAddr+11 : entryAddr+21])
		if ht.Buf[entryAddr] == 1 {
			if int(entryKey) == key {
				vals = append(vals, int(entryVal))
				if count++; count == limit {
					return
				}
			}
		} else if entryKey == 0 && entryVal == 0 {
			return
		}
		if entry++; entry == ht.PerBucket {
			entry = 0
			if bucket = ht.nextBucket(bucket); bucket == 0 {
				return
			}
		}
	}
}