Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
194 lines
3.7 KiB
Go
194 lines
3.7 KiB
Go
package hasher
|
|
|
|
// Implementation borrowed from https://github.com/spaolacci/murmur3,
|
|
// distributed under BSD-3 license.
|
|
|
|
import (
|
|
"hash"
|
|
"unsafe"
|
|
)
|
|
|
|
type bmixer interface {
|
|
bmix(p []byte) (tail []byte)
|
|
Size() (n int)
|
|
reset()
|
|
}
|
|
|
|
type digest struct {
|
|
clen int // Digested input cumulative length.
|
|
tail []byte // 0 to Size()-1 bytes view of `buf'.
|
|
buf [16]byte // Expected (but not required) to be Size() large.
|
|
seed uint32 // Seed for initializing the hash.
|
|
bmixer
|
|
}
|
|
|
|
func (d *digest) BlockSize() int { return 1 }
|
|
|
|
func (d *digest) Write(p []byte) (n int, err error) {
|
|
n = len(p)
|
|
d.clen += n
|
|
|
|
if len(d.tail) > 0 {
|
|
// Stick back pending bytes.
|
|
nfree := d.Size() - len(d.tail) // nfree ∈ [1, d.Size()-1].
|
|
if nfree < len(p) {
|
|
// One full block can be formed.
|
|
block := append(d.tail, p[:nfree]...)
|
|
p = p[nfree:]
|
|
_ = d.bmix(block) // No tail.
|
|
} else {
|
|
// Tail's buf is large enough to prevent reallocs.
|
|
p = append(d.tail, p...)
|
|
}
|
|
}
|
|
|
|
d.tail = d.bmix(p)
|
|
|
|
// Keep own copy of the 0 to Size()-1 pending bytes.
|
|
nn := copy(d.buf[:], d.tail)
|
|
d.tail = d.buf[:nn]
|
|
|
|
return n, nil
|
|
}
|
|
|
|
func (d *digest) Reset() {
|
|
d.clen = 0
|
|
d.tail = nil
|
|
d.bmixer.reset()
|
|
}
|
|
|
|
// Make sure interfaces are correctly implemented.
|
|
var (
|
|
_ hash.Hash = new(digest32)
|
|
_ hash.Hash32 = new(digest32)
|
|
_ bmixer = new(digest32)
|
|
)
|
|
|
|
const (
|
|
c1_32 uint32 = 0xcc9e2d51
|
|
c2_32 uint32 = 0x1b873593
|
|
)
|
|
|
|
// digest32 represents a partial evaluation of a 32 bites hash.
|
|
type digest32 struct {
|
|
digest
|
|
h1 uint32 // Unfinalized running hash.
|
|
}
|
|
|
|
func (d *digest32) Size() int { return 4 }
|
|
|
|
func (d *digest32) reset() { d.h1 = d.seed }
|
|
|
|
func (d *digest32) Sum(b []byte) []byte {
|
|
h := d.Sum32()
|
|
return append(b, byte(h>>24), byte(h>>16), byte(h>>8), byte(h))
|
|
}
|
|
|
|
// Digest as many blocks as possible.
|
|
func (d *digest32) bmix(p []byte) (tail []byte) {
|
|
h1 := d.h1
|
|
|
|
nblocks := len(p) / 4
|
|
for i := 0; i < nblocks; i++ {
|
|
k1 := *(*uint32)(unsafe.Pointer(&p[i*4]))
|
|
|
|
k1 *= c1_32
|
|
k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
|
|
k1 *= c2_32
|
|
|
|
h1 ^= k1
|
|
h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13)
|
|
h1 = h1*4 + h1 + 0xe6546b64
|
|
}
|
|
d.h1 = h1
|
|
return p[nblocks*d.Size():]
|
|
}
|
|
|
|
func (d *digest32) Sum32() (h1 uint32) {
|
|
|
|
h1 = d.h1
|
|
|
|
var k1 uint32
|
|
switch len(d.tail) & 3 {
|
|
case 3:
|
|
k1 ^= uint32(d.tail[2]) << 16
|
|
fallthrough
|
|
case 2:
|
|
k1 ^= uint32(d.tail[1]) << 8
|
|
fallthrough
|
|
case 1:
|
|
k1 ^= uint32(d.tail[0])
|
|
k1 *= c1_32
|
|
k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
|
|
k1 *= c2_32
|
|
h1 ^= k1
|
|
}
|
|
|
|
h1 ^= uint32(d.clen)
|
|
|
|
h1 ^= h1 >> 16
|
|
h1 *= 0x85ebca6b
|
|
h1 ^= h1 >> 13
|
|
h1 *= 0xc2b2ae35
|
|
h1 ^= h1 >> 16
|
|
|
|
return h1
|
|
}
|
|
|
|
/*
|
|
func rotl32(x uint32, r byte) uint32 {
|
|
return (x << r) | (x >> (32 - r))
|
|
}
|
|
*/
|
|
|
|
// sum32WithSeed returns the MurmurHash3 sum of data. It is equivalent to the
|
|
// following sequence (without the extra burden and the extra allocation):
|
|
// hasher := New32WithSeed(seed)
|
|
// hasher.Write(data)
|
|
// return hasher.Sum32()
|
|
func sum32WithSeed(data []byte, seed uint32) uint32 {
|
|
|
|
h1 := seed
|
|
|
|
nblocks := len(data) / 4
|
|
for blocks := 0; blocks < nblocks; blocks++ {
|
|
k1 := *(*uint32)(unsafe.Pointer(&data[blocks*4]))
|
|
|
|
k1 *= c1_32
|
|
k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
|
|
k1 *= c2_32
|
|
|
|
h1 ^= k1
|
|
h1 = (h1 << 13) | (h1 >> 19) // rotl32(h1, 13)
|
|
h1 = h1*4 + h1 + 0xe6546b64
|
|
}
|
|
|
|
tail := data[nblocks*4:]
|
|
|
|
var k1 uint32
|
|
switch len(tail) & 3 {
|
|
case 3:
|
|
k1 ^= uint32(tail[2]) << 16
|
|
fallthrough
|
|
case 2:
|
|
k1 ^= uint32(tail[1]) << 8
|
|
fallthrough
|
|
case 1:
|
|
k1 ^= uint32(tail[0])
|
|
k1 *= c1_32
|
|
k1 = (k1 << 15) | (k1 >> 17) // rotl32(k1, 15)
|
|
k1 *= c2_32
|
|
h1 ^= k1
|
|
}
|
|
|
|
h1 ^= uint32(len(data))
|
|
|
|
h1 ^= h1 >> 16
|
|
h1 *= 0x85ebca6b
|
|
h1 ^= h1 >> 13
|
|
h1 *= 0xc2b2ae35
|
|
h1 ^= h1 >> 16
|
|
|
|
return h1
|
|
}
|