Documentation
¶
Overview ¶
Package rollinghash implements rolling versions of some hashes
Index ¶
Examples ¶
Constants ¶
const DefaultWindowCap = 64
DefaultWindowCap is the default capacity of the internal window of a new Hash.
Variables ¶
This section is empty.
Functions ¶
func WithBoundaries ¶ added in v4.2.0
func WithBoundaries(min, max int) chunkerOption
WithBoundaries sets the minimum and maximum chunk size. Chunks shorter than min bytes are extended to the next boundary; chunks that reach max bytes without a mask hit are cut there unconditionally. Defaults are 0 and math.MaxInt.
func WithBufferSize ¶ added in v4.2.0
func WithBufferSize(n int) batchRollerOption
WithBufferSize sets the internal batch buffer size in bytes. A larger buffer means larger batches and better amortization of the bulk fast path; it must be at least window bytes. The default is 64 KiB.
Example ¶
Buffer controls the batch size used by the BatchRoller. A larger buffer means fewer Next() calls and better amortization of the bulk fast path, at the cost of higher memory use. The default buffer is 64 KiB; here we use a small buffer to show that the BatchRoller produces correct results regardless of how the input is split across batches.
package main
import (
"bytes"
"fmt"
"log"
rollinghash "github.com/chmduquesne/rollinghash/v4"
"github.com/chmduquesne/rollinghash/v4/buzhash64"
)
func main() {
data := []byte("the quick brown fox jumps over the lazy dog")
needle := []byte("brown")
window := len(needle)
h := buzhash64.New()
if _, err := h.Write(needle); err != nil {
log.Fatal(err)
}
target := h.Sum64()
// Use the smallest valid buffer (window bytes) so every Next() call
// returns exactly one position, exercising the batch-boundary logic.
s := rollinghash.NewBatchRoller(bytes.NewReader(data), buzhash64.New(), window, rollinghash.WithBufferSize(window))
for s.Next() {
sums, buf := s.Sums(), s.Bytes()
for i, sum := range sums {
if sum == target && bytes.Equal(buf[i:i+window], needle) {
fmt.Printf("found %q at offset %d\n", needle, s.Offset()+i)
}
}
}
if err := s.Err(); err != nil {
log.Fatal(err)
}
}
Output: found "brown" at offset 10
Types ¶
type BatchRoller ¶ added in v4.2.0
type BatchRoller interface {
Next() bool
Bytes() []byte
Sums() []uint64
Offset() int
WindowSize() int
Err() error
Reset(r io.Reader)
}
BatchRoller walks an io.Reader and yields, per batch, the rolling checksum at every window position together with the bytes those checksums cover. The underlying hash must implement BatchRoll.
s := NewBatchRoller(r, h, window)
for s.Next() {
sums, data := s.Sums(), s.Bytes()
for i, sum := range sums {
// sum == rolling checksum of data[i:i+window]
}
}
if err := s.Err(); err != nil { ... }
Per-batch guarantee: len(Sums()) == len(Bytes())-window+1, and Sums()[i] is the checksum of Bytes()[i:i+window]. Consecutive batches overlap by window-1 bytes so no window position is skipped or duplicated.
Sums and Bytes are valid only until the next call to Next. Offset returns the stream position of Bytes()[0]; Sums()[i] is at Offset()+i. WindowSize returns the rolling window size; Sums()[i] covers Bytes()[i:i+WindowSize()]. Use WithBufferSize to control the batch size in bytes (default 64 KiB). Reset reuses internal allocations across streams.
Note: BatchRoll bypasses the hash's internal rolling window. After passing h to NewBatchRoller, h.WriteWindow() will not reflect the stream — use WindowSize() on the BatchRoller instead.
Example ¶
BatchRoller is designed to support users who want to search for a given block within a stream, rsync-style. The rolling checksum acts as a cheap filter, and another method (e.g. byte comparison) confirms the match. Because it batches computations, it can exploit instruction-level parallelism, achieving roughly twice the throughput of Roll().
package main
import (
"bytes"
"fmt"
"log"
rollinghash "github.com/chmduquesne/rollinghash/v4"
"github.com/chmduquesne/rollinghash/v4/buzhash64"
)
func main() {
data := []byte("the quick brown fox jumps over the lazy dog")
// The block we are looking for, and its rolling checksum.
needle := []byte("brown")
window := len(needle)
h := buzhash64.New()
if _, err := h.Write(needle); err != nil {
log.Fatal(err)
}
target := h.Sum64()
// Roll the stream. Within each batch, Sums()[i] is the checksum of
// Bytes()[i:i+window], at stream position Offset()+i.
s := rollinghash.NewBatchRoller(bytes.NewReader(data), buzhash64.New(), window)
for s.Next() {
sums, buf := s.Sums(), s.Bytes()
for i, sum := range sums {
if sum == target && bytes.Equal(buf[i:i+window], needle) {
fmt.Printf("found %q at offset %d\n", needle, s.Offset()+i)
}
}
}
if err := s.Err(); err != nil {
log.Fatal(err)
}
}
Output: found "brown" at offset 10
func NewBatchRoller ¶ added in v4.2.0
func NewBatchRoller(r io.Reader, h Hash, window int, opts ...batchRollerOption) BatchRoller
NewBatchRoller returns a BatchRoller over r. window must be >= 1. h must implement BatchRoll; NewBatchRoller panics otherwise. Pass nil for r and call Reset before the first Next to defer stream attachment. Use WithBufferSize to control the batch size (default 64 KiB).
type Chunker ¶ added in v4.2.0
type Chunker interface {
Next() bool
Bytes() []byte
ContentDefined() bool
Sum() uint64
Offset() int
WindowSize() int
Err() error
Reset(r io.Reader)
}
Chunker splits an io.Reader into content-defined chunks. The underlying hash must implement BatchBoundaries.
c := NewChunker(r, h, window, mask)
for c.Next() {
chunk := c.Bytes()
if c.ContentDefined() {
// content-defined boundary; Sum() is the hit value
} else {
// forced cut at max, or the final chunk
}
}
if err := c.Err(); err != nil { ... }
Bytes is valid only until the next call to Next. ContentDefined reports whether the current chunk ended at a mask hit (true) or was forced at max / end of stream (false). Sum returns the rolling checksum at a mask boundary; it returns 0 on forced cuts. Offset returns the start byte offset of the current chunk in the stream; the end offset is Offset()+len(Bytes()). WindowSize returns the rolling window size used for boundary detection. Use WithBoundaries to set minimum and maximum chunk sizes (defaults: 0 and math.MaxInt). Reset reuses internal allocations across streams.
Note: BatchBoundaries bypasses the hash's internal rolling window. After passing h to NewChunker, h.WriteWindow() will not reflect the stream — use WindowSize() on the Chunker instead.
Example ¶
The Chunker interface was designed to support users who want to use rolling hashes for Content Defined Chunking (CDC). It also operates on a stream, which allows for batch computation optimizations similar to the ones used with the BatchRoller. In this type of situation, the stream is split where the rolling checksum hits a mask. Use WithBoundaries to keep chunk sizes within a desired range.
package main
import (
"bytes"
"fmt"
"log"
rollinghash "github.com/chmduquesne/rollinghash/v4"
"github.com/chmduquesne/rollinghash/v4/buzhash64"
)
func main() {
// Repeatable pseudo-random data (xorshift), so the boundaries are stable.
data := make([]byte, 4096)
x := uint32(1)
for i := range data {
x ^= x << 13
x ^= x >> 17
x ^= x << 5
data[i] = byte(x)
}
// Cut where the low 8 bits of the rolling checksum are zero, keeping each
// chunk between 64 and 1024 bytes.
c := rollinghash.NewChunker(bytes.NewReader(data), buzhash64.New(), 56, 0xff, rollinghash.WithBoundaries(64, 1024))
var sizes []int
total := 0
for c.Next() {
chunk := c.Bytes()
sizes = append(sizes, len(chunk))
total += len(chunk)
if c.ContentDefined() {
fmt.Printf("boundary at %d: sum=0x%x\n", total, c.Sum())
} else {
fmt.Printf("max cut at %d\n", total)
}
}
if err := c.Err(); err != nil {
log.Fatal(err)
}
fmt.Printf("split %d bytes into %d chunks: %v\n", total, len(sizes), sizes)
}
Output: boundary at 123: sum=0xbd9f33d05f52e700 boundary at 277: sum=0x3a611bc3e53cf900 boundary at 651: sum=0xecb29647a13a3600 boundary at 769: sum=0x3109e14cbfa7da00 boundary at 1436: sum=0xb61cdeda53dac000 boundary at 1522: sum=0x8bef143657fed400 boundary at 1722: sum=0x35f525a03a01d000 boundary at 2173: sum=0xb168bf8f4418ee00 boundary at 2404: sum=0x6c13f4fb45436f00 boundary at 2647: sum=0x33695e700dcdf300 boundary at 3388: sum=0xe915cd64f38a9800 boundary at 3837: sum=0xdfc83351b3d06800 max cut at 4096 split 4096 bytes into 13 chunks: [123 154 374 118 667 86 200 451 231 243 741 449 259]
func NewChunker ¶ added in v4.2.0
NewChunker returns a chunker over r. A boundary is placed where the rolling checksum under h (over window bytes) satisfies checksum & mask == 0, with the chunk length kept in [min, max]. window must be >= 1. Use WithMinSize and WithMaxSize to set min (default 0) and max (default math.MaxInt). The hash must implement BatchBoundaries; NewChunker panics otherwise.
type Hash ¶
rollinghash.Hash extends hash.Hash by adding the method Roll. A rollinghash.Hash can be updated byte by byte, by specifying which byte enters the window. A rollinghash.Hash internally maintains a copy of the rolling window in order to keep track of the value of the byte exiting the window. This copy is updated with every call to Roll. The rolling window can be accessed through the io.Reader interface.
type Hash32 ¶
rollinghash.Hash32 extends hash.Hash by adding the method Roll. A rollinghash.Hash32 can be updated byte by byte, by specifying which byte enters the window. A rollinghash.Hash32 internally maintains a copy of the rolling window in order to keep track of the value of the byte exiting the window. This copy is updated with every call to Roll. The rolling window can be accessed through the io.Reader interface.
type Hash64 ¶
rollinghash.Hash64 extends hash.Hash by adding the method Roll. A rollinghash.Hash64 can be updated byte by byte, by specifying which byte enters the window. A rollinghash.Hash64 internally maintains a copy of the rolling window in order to keep track of the value of the byte exiting the window. This copy is updated with every call to Roll. The rolling window can be accessed through the io.Reader interface.
type Roller ¶
type Roller interface {
Roll(b byte)
// WriteWindow writes the contents of the current window to w.
//
// It returns the number of bytes written and any error returned by
// w.Write.
WriteWindow(w io.Writer) (int, error)
}
A Roller is a type that has the method Roll. Roll updates the hash of a rolling window from just the entering byte. You MUST call Write() BEFORE using this method and provide it with an initial window of size at least 1 byte. You can then call this method for every new byte entering the window. The byte leaving the window is automatically computed from a copy of the window internally kept in the checksum. This window is updated along with the internal state of the checksum every time Roll() is called.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package rollinghash/gearhash64 implements the Gear rolling hash (64-bit).
|
Package rollinghash/gearhash64 implements the Gear rolling hash (64-bit). |
|
internal
|
|
|
window
Package window provides common code for dealing with sliding windows.
|
Package window provides common code for dealing with sliding windows. |