vendor: Update everything
GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4620
This commit is contained in:
318
vendor/github.com/chmduquesne/rollinghash/rabinkarp64/polynomials.go
generated
vendored
Normal file
318
vendor/github.com/chmduquesne/rollinghash/rabinkarp64/polynomials.go
generated
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
// Copyright (c) 2014, Alexander Neumann <alexander@bumpern.de>
|
||||
// Copyright (c) 2017, Christophe-Marie Duquesne <chmd@chmd.fr>
|
||||
//
|
||||
// This file was adapted from restic https://github.com/restic/chunker
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package rabinkarp64
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Pol is a polynomial from F_2[X].
|
||||
type Pol uint64
|
||||
|
||||
// Add returns x+y.
|
||||
func (x Pol) Add(y Pol) Pol {
|
||||
r := Pol(uint64(x) ^ uint64(y))
|
||||
return r
|
||||
}
|
||||
|
||||
// mulOverflows returns true if the multiplication would overflow uint64.
|
||||
// Code by Rob Pike, see
|
||||
// https://groups.google.com/d/msg/golang-nuts/h5oSN5t3Au4/KaNQREhZh0QJ
|
||||
func mulOverflows(a, b Pol) bool {
|
||||
if a <= 1 || b <= 1 {
|
||||
return false
|
||||
}
|
||||
c := a.mul(b)
|
||||
d := c.Div(b)
|
||||
if d != a {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (x Pol) mul(y Pol) Pol {
|
||||
if x == 0 || y == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var res Pol
|
||||
for i := 0; i <= y.Deg(); i++ {
|
||||
if (y & (1 << uint(i))) > 0 {
|
||||
res = res.Add(x << uint(i))
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Mul returns x*y. When an overflow occurs, Mul panics.
|
||||
func (x Pol) Mul(y Pol) Pol {
|
||||
if mulOverflows(x, y) {
|
||||
panic("multiplication would overflow uint64")
|
||||
}
|
||||
|
||||
return x.mul(y)
|
||||
}
|
||||
|
||||
// Deg returns the degree of the polynomial x. If x is zero, -1 is returned.
|
||||
func (x Pol) Deg() int {
|
||||
// the degree of 0 is -1
|
||||
if x == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
// see https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
|
||||
|
||||
r := 0
|
||||
if uint64(x)&0xffffffff00000000 > 0 {
|
||||
x >>= 32
|
||||
r |= 32
|
||||
}
|
||||
|
||||
if uint64(x)&0xffff0000 > 0 {
|
||||
x >>= 16
|
||||
r |= 16
|
||||
}
|
||||
|
||||
if uint64(x)&0xff00 > 0 {
|
||||
x >>= 8
|
||||
r |= 8
|
||||
}
|
||||
|
||||
if uint64(x)&0xf0 > 0 {
|
||||
x >>= 4
|
||||
r |= 4
|
||||
}
|
||||
|
||||
if uint64(x)&0xc > 0 {
|
||||
x >>= 2
|
||||
r |= 2
|
||||
}
|
||||
|
||||
if uint64(x)&0x2 > 0 {
|
||||
x >>= 1
|
||||
r |= 1
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// String returns the coefficients in hex.
|
||||
func (x Pol) String() string {
|
||||
return "0x" + strconv.FormatUint(uint64(x), 16)
|
||||
}
|
||||
|
||||
// Expand returns the string representation of the polynomial x.
|
||||
func (x Pol) Expand() string {
|
||||
if x == 0 {
|
||||
return "0"
|
||||
}
|
||||
|
||||
s := ""
|
||||
for i := x.Deg(); i > 1; i-- {
|
||||
if x&(1<<uint(i)) > 0 {
|
||||
s += fmt.Sprintf("+x^%d", i)
|
||||
}
|
||||
}
|
||||
|
||||
if x&2 > 0 {
|
||||
s += "+x"
|
||||
}
|
||||
|
||||
if x&1 > 0 {
|
||||
s += "+1"
|
||||
}
|
||||
|
||||
return s[1:]
|
||||
}
|
||||
|
||||
// DivMod returns x / d = q, and remainder r,
|
||||
// see https://en.wikipedia.org/wiki/Division_algorithm
|
||||
func (x Pol) DivMod(d Pol) (Pol, Pol) {
|
||||
if x == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
if d == 0 {
|
||||
panic("division by zero")
|
||||
}
|
||||
|
||||
D := d.Deg()
|
||||
diff := x.Deg() - D
|
||||
if diff < 0 {
|
||||
return 0, x
|
||||
}
|
||||
|
||||
var q Pol
|
||||
for diff >= 0 {
|
||||
m := d << uint(diff)
|
||||
q |= (1 << uint(diff))
|
||||
x = x.Add(m)
|
||||
|
||||
diff = x.Deg() - D
|
||||
}
|
||||
|
||||
return q, x
|
||||
}
|
||||
|
||||
// Div returns the integer division result x / d.
|
||||
func (x Pol) Div(d Pol) Pol {
|
||||
q, _ := x.DivMod(d)
|
||||
return q
|
||||
}
|
||||
|
||||
// Mod returns the remainder of x / d
|
||||
func (x Pol) Mod(d Pol) Pol {
|
||||
_, r := x.DivMod(d)
|
||||
return r
|
||||
}
|
||||
|
||||
// I really dislike having a function that does not terminate, so specify a
|
||||
// really large upper bound for finding a new irreducible polynomial, and
|
||||
// return an error when no irreducible polynomial has been found within
|
||||
// randPolMaxTries.
|
||||
const randPolMaxTries = 1e6
|
||||
|
||||
// RandomPolynomial returns a new random irreducible polynomial
|
||||
// of degree 53 using the input seed as a source.
|
||||
// It is equivalent to calling DerivePolynomial(rand.Reader).
|
||||
func RandomPolynomial(seed int64) (Pol, error) {
|
||||
return DerivePolynomial(rand.New(rand.NewSource(seed)))
|
||||
}
|
||||
|
||||
// DerivePolynomial returns an irreducible polynomial of degree 53
|
||||
// (largest prime number below 64-8) by reading bytes from source.
|
||||
// There are (2^53-2/53) irreducible polynomials of degree 53 in
|
||||
// F_2[X], c.f. Michael O. Rabin (1981): "Fingerprinting by Random
|
||||
// Polynomials", page 4. If no polynomial could be found in one
|
||||
// million tries, an error is returned.
|
||||
func DerivePolynomial(source io.Reader) (Pol, error) {
|
||||
for i := 0; i < randPolMaxTries; i++ {
|
||||
var f Pol
|
||||
|
||||
// choose polynomial at (pseudo)random
|
||||
err := binary.Read(source, binary.LittleEndian, &f)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// mask away bits above bit 53
|
||||
f &= Pol((1 << 54) - 1)
|
||||
|
||||
// set highest and lowest bit so that the degree is 53 and the
|
||||
// polynomial is not trivially reducible
|
||||
f |= (1 << 53) | 1
|
||||
|
||||
// test if f is irreducible
|
||||
if f.Irreducible() {
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If this is reached, we haven't found an irreducible polynomial in
|
||||
// randPolMaxTries. This error is very unlikely to occur.
|
||||
return 0, errors.New("unable to find new random irreducible polynomial")
|
||||
}
|
||||
|
||||
// GCD computes the Greatest Common Divisor x and f.
|
||||
func (x Pol) GCD(f Pol) Pol {
|
||||
if f == 0 {
|
||||
return x
|
||||
}
|
||||
|
||||
if x == 0 {
|
||||
return f
|
||||
}
|
||||
|
||||
if x.Deg() < f.Deg() {
|
||||
x, f = f, x
|
||||
}
|
||||
|
||||
return f.GCD(x.Mod(f))
|
||||
}
|
||||
|
||||
// Irreducible returns true iff x is irreducible over F_2. This function
|
||||
// uses Ben Or's reducibility test.
|
||||
//
|
||||
// For details see "Tests and Constructions of Irreducible Polynomials over
|
||||
// Finite Fields".
|
||||
func (x Pol) Irreducible() bool {
|
||||
for i := 1; i <= x.Deg()/2; i++ {
|
||||
if x.GCD(qp(uint(i), x)) != 1 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// MulMod computes x*f mod g
|
||||
func (x Pol) MulMod(f, g Pol) Pol {
|
||||
if x == 0 || f == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var res Pol
|
||||
for i := 0; i <= f.Deg(); i++ {
|
||||
if (f & (1 << uint(i))) > 0 {
|
||||
a := x
|
||||
for j := 0; j < i; j++ {
|
||||
a = a.Mul(2).Mod(g)
|
||||
}
|
||||
res = res.Add(a).Mod(g)
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// qp computes the polynomial (x^(2^p)-x) mod g. This is needed for the
|
||||
// reducibility test.
|
||||
func qp(p uint, g Pol) Pol {
|
||||
num := (1 << p)
|
||||
i := 1
|
||||
|
||||
// start with x
|
||||
res := Pol(2)
|
||||
|
||||
for i < num {
|
||||
// repeatedly square res
|
||||
res = res.MulMod(res, g)
|
||||
i *= 2
|
||||
}
|
||||
|
||||
// add x
|
||||
return res.Add(2).Mod(g)
|
||||
}
|
||||
224
vendor/github.com/chmduquesne/rollinghash/rabinkarp64/rabinkarp64.go
generated
vendored
Normal file
224
vendor/github.com/chmduquesne/rollinghash/rabinkarp64/rabinkarp64.go
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
// Copyright (c) 2014, Alexander Neumann <alexander@bumpern.de>
|
||||
// Copyright (c) 2017, Christophe-Marie Duquesne <chmd@chmd.fr>
|
||||
//
|
||||
// This file was adapted from restic https://github.com/restic/chunker
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package rabinkarp64
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/chmduquesne/rollinghash"
|
||||
)
|
||||
|
||||
const Size = 8
|
||||
|
||||
type tables struct {
|
||||
out [256]Pol
|
||||
mod [256]Pol
|
||||
}
|
||||
|
||||
// tables are cacheable for a given pol and windowsize
|
||||
type index struct {
|
||||
pol Pol
|
||||
windowsize int
|
||||
}
|
||||
|
||||
type RabinKarp64 struct {
|
||||
pol Pol
|
||||
tables *tables
|
||||
polShift uint
|
||||
value Pol
|
||||
|
||||
// window is treated like a circular buffer, where the oldest element
|
||||
// is indicated by d.oldest
|
||||
window []byte
|
||||
oldest int
|
||||
}
|
||||
|
||||
// cache precomputed tables, these are read-only anyway
|
||||
var cache struct {
|
||||
// For a given polynom and a given window size, we get a table
|
||||
entries map[index]*tables
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func init() {
|
||||
cache.entries = make(map[index]*tables)
|
||||
}
|
||||
|
||||
func (d *RabinKarp64) buildTables() {
|
||||
windowsize := len(d.window)
|
||||
idx := index{d.pol, windowsize}
|
||||
|
||||
cache.Lock()
|
||||
t, ok := cache.entries[idx]
|
||||
cache.Unlock()
|
||||
if ok {
|
||||
d.tables = t
|
||||
return
|
||||
}
|
||||
|
||||
t = &tables{}
|
||||
|
||||
// calculate table for sliding out bytes. The byte to slide out is used as
|
||||
// the index for the table, the value contains the following:
|
||||
// out_table[b] = Hash(b || 0 || ... || 0)
|
||||
// \ windowsize-1 zero bytes /
|
||||
// To slide out byte b_0 for window size w with known hash
|
||||
// H := H(b_0 || ... || b_w), it is sufficient to add out_table[b_0]:
|
||||
// H(b_0 || ... || b_w) + H(b_0 || 0 || ... || 0)
|
||||
// = H(b_0 + b_0 || b_1 + 0 || ... || b_w + 0)
|
||||
// = H( 0 || b_1 || ... || b_w)
|
||||
//
|
||||
// Afterwards a new byte can be shifted in.
|
||||
for b := 0; b < 256; b++ {
|
||||
var h Pol
|
||||
h <<= 8
|
||||
h |= Pol(b)
|
||||
h = h.Mod(d.pol)
|
||||
for i := 0; i < windowsize-1; i++ {
|
||||
h <<= 8
|
||||
h |= Pol(0)
|
||||
h = h.Mod(d.pol)
|
||||
}
|
||||
t.out[b] = h
|
||||
}
|
||||
|
||||
// calculate table for reduction mod Polynomial
|
||||
k := d.pol.Deg()
|
||||
for b := 0; b < 256; b++ {
|
||||
// mod_table[b] = A | B, where A = (b(x) * x^k mod pol) and B = b(x) * x^k
|
||||
//
|
||||
// The 8 bits above deg(Polynomial) determine what happens next and so
|
||||
// these bits are used as a lookup to this table. The value is split in
|
||||
// two parts: Part A contains the result of the modulus operation, part
|
||||
// B is used to cancel out the 8 top bits so that one XOR operation is
|
||||
// enough to reduce modulo Polynomial
|
||||
t.mod[b] = Pol(uint64(b)<<uint(k)).Mod(d.pol) | (Pol(b) << uint(k))
|
||||
}
|
||||
|
||||
d.tables = t
|
||||
cache.Lock()
|
||||
cache.entries[idx] = d.tables
|
||||
cache.Unlock()
|
||||
}
|
||||
|
||||
// NewFromPol returns a RabinKarp64 digest from a polynomial over GF(2).
|
||||
// It is assumed that the input polynomial is irreducible. You can obtain
|
||||
// such a polynomial using the RandomPolynomial function.
|
||||
func NewFromPol(p Pol) *RabinKarp64 {
|
||||
res := &RabinKarp64{
|
||||
pol: p,
|
||||
tables: nil,
|
||||
polShift: uint(p.Deg() - 8),
|
||||
value: 0,
|
||||
window: make([]byte, 0, rollinghash.DefaultWindowCap),
|
||||
oldest: 0,
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// New returns a RabinKarp64 digest from the default polynomial obtained
|
||||
// when using RandomPolynomial with the seed 1.
|
||||
func New() *RabinKarp64 {
|
||||
p, err := RandomPolynomial(1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return NewFromPol(p)
|
||||
}
|
||||
|
||||
// Reset resets the running hash to its initial state
|
||||
func (d *RabinKarp64) Reset() {
|
||||
d.tables = nil
|
||||
d.value = 0
|
||||
d.window = d.window[:1]
|
||||
d.window[0] = 0
|
||||
d.oldest = 0
|
||||
}
|
||||
|
||||
// Size is 8 bytes
|
||||
func (d *RabinKarp64) Size() int { return Size }
|
||||
|
||||
// BlockSize is 1 byte
|
||||
func (d *RabinKarp64) BlockSize() int { return 1 }
|
||||
|
||||
// Write (re)initializes the rolling window with the input byte slice and
|
||||
// adds its data to the digest. It never returns an error.
|
||||
func (d *RabinKarp64) Write(data []byte) (int, error) {
|
||||
// Copy the window
|
||||
l := len(data)
|
||||
if l == 0 {
|
||||
l = 1
|
||||
}
|
||||
if len(d.window) >= l {
|
||||
d.window = d.window[:l]
|
||||
} else {
|
||||
d.window = make([]byte, l)
|
||||
}
|
||||
copy(d.window, data)
|
||||
|
||||
for _, b := range d.window {
|
||||
d.value <<= 8
|
||||
d.value |= Pol(b)
|
||||
d.value = d.value.Mod(d.pol)
|
||||
}
|
||||
|
||||
d.buildTables()
|
||||
|
||||
return len(d.window), nil
|
||||
}
|
||||
|
||||
// Sum64 returns the hash as a uint64
|
||||
func (d *RabinKarp64) Sum64() uint64 {
|
||||
return uint64(d.value)
|
||||
}
|
||||
|
||||
// Sum returns the hash as byte slice
|
||||
func (d *RabinKarp64) Sum(b []byte) []byte {
|
||||
v := d.Sum64()
|
||||
return append(b, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
||||
}
|
||||
|
||||
// Roll updates the checksum of the window from the entering byte. You
|
||||
// MUST initialize a window with Write() before calling this method.
|
||||
func (d *RabinKarp64) Roll(c byte) {
|
||||
// extract the entering/leaving bytes and update the circular buffer.
|
||||
enter := c
|
||||
leave := uint64(d.window[d.oldest])
|
||||
d.window[d.oldest] = c
|
||||
d.oldest += 1
|
||||
if d.oldest >= len(d.window) {
|
||||
d.oldest = 0
|
||||
}
|
||||
|
||||
d.value ^= d.tables.out[leave]
|
||||
index := byte(d.value >> d.polShift)
|
||||
d.value <<= 8
|
||||
d.value |= Pol(enter)
|
||||
d.value ^= d.tables.mod[index]
|
||||
}
|
||||
Reference in New Issue
Block a user