disk.go (107 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
package qf
import (
"encoding/binary"
"fmt"
"os"
"unsafe"
)
type extReader interface {
Read(ix uint64) (val uint64, err error)
}
// Disk is a read-only quotient filter that interacts with a
// quotient filter on disk without loading it into RAM
type Disk struct {
entries uint64
size uint64
hashfn HashFn
rBits uint
rMask uint64
f *os.File
filterRead, storageRead extReader
storageBits uint
}
// OpenReadOnlyFromFile initializes a read only quotient filter
// from disk
func OpenReadOnlyFromPath(path string) (*Disk, error) {
rdr, err := os.Open(path)
if err != nil {
return nil, err
}
// read header
var h QFHeader
if err = binary.Read(rdr, binary.LittleEndian, &h); err != nil {
return nil, err
}
var ext Disk
ext.f = rdr
ext.entries = h.Entries
ext.rBits, ext.rMask, ext.size = initForQuotientBits(uint(h.QBits))
ext.storageBits = uint(h.StorageBits)
if h.BitPacked {
ext.filterRead, err = initPackedDiskReader(rdr)
if err != nil {
return nil, err
}
if h.StorageBits > 0 {
ext.storageRead, err = initPackedDiskReader(rdr)
if err != nil {
return nil, err
}
}
} else {
ext.filterRead, err = initUnpackedDiskReader(rdr)
if err != nil {
return nil, err
}
if h.StorageBits > 0 {
ext.storageRead, err = initUnpackedDiskReader(rdr)
if err != nil {
return nil, err
}
}
}
// XXX: handle variable hash functions
ext.hashfn = murmurhash64
return &ext, nil
}
// BitsOfStoragePerEntry reports the number of bits of integer storage associated
// with each entry in the quotient filter
func (ext *Disk) BitsOfStoragePerEntry() uint {
return ext.storageBits
}
// HasStorage is true when a non-zero amount of integer storage is tracked
// along with each entry in the quotient filter
func (ext *Disk) HasStorage() bool {
return ext.storageBits > 0
}
// Close the file handle associated with the disk based quotient filter
func (ext *Disk) Close() error {
if ext.f != nil {
return ext.f.Close()
}
return nil
}
// Len reports the number of entries in the quotient filter
func (ext *Disk) Len() uint64 {
return ext.entries
}
// Contains checks whether the byte string is stored within the quotient filter
func (ext *Disk) Contains(v []byte) bool {
found, _ := ext.Lookup(v)
return found
}
// Contains checks whether the string is stored within the quotient filter
func (ext *Disk) ContainsString(s string) bool {
found, _ := ext.Lookup(*(*[]byte)(unsafe.Pointer(&s)))
return found
}
// Lookup checks whether the byte string is stored within the quotient filter and
// returns a boolean indicating its presence and external integer data if applicable
func (ext *Disk) Lookup(key []byte) (bool, uint64) {
dq, dr := hash(ext.hashfn, key, ext.rBits, ext.rMask)
var filterFn, storageFn readFn
filterFn = func(v uint64) uint64 {
x, err := ext.filterRead.Read(v)
if err != nil {
panic(fmt.Sprintf("error: %s", err))
}
return x
}
if ext.storageRead != nil {
storageFn = func(v uint64) uint64 {
x, err := ext.storageRead.Read(v)
if err != nil {
panic(fmt.Sprintf("error: %s", err))
}
return x
}
}
return lookupByHash(dq, dr, ext.size, filterFn, storageFn)
}
// LookupString is like Lookup, but for strings
func (ext *Disk) LookupString(key string) (bool, uint64) {
return ext.Lookup(*(*[]byte)(unsafe.Pointer(&key)))
}