mirror of https://github.com/harness/drone.git
97 lines
2.3 KiB
Go
97 lines
2.3 KiB
Go
// Copyright 2023 Harness, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package hash
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// xorAggregator is an implementation of the Aggregator interface
|
|
// that aggregates hashes by XORing them.
|
|
type xorAggregator struct {
|
|
hfm hashFactoryMethod
|
|
hashSize int
|
|
}
|
|
|
|
func (a *xorAggregator) Empty() []byte {
|
|
return make([]byte, a.hashSize)
|
|
}
|
|
|
|
func (a *xorAggregator) Hash(source Source) ([]byte, error) {
|
|
return a.append(a.Empty(), source)
|
|
}
|
|
|
|
func (a *xorAggregator) Append(hash []byte, source Source) ([]byte, error) {
|
|
// copy value to ensure we don't modify the original hash array
|
|
hashCopy := make([]byte, len(hash))
|
|
copy(hashCopy, hash)
|
|
|
|
return a.append(hashCopy, source)
|
|
}
|
|
|
|
func (a *xorAggregator) append(hash []byte, source Source) ([]byte, error) {
|
|
if len(hash) != a.hashSize {
|
|
return nil, fmt.Errorf(
|
|
"hash is of invalid length %d, aggregator works with hashes of length %d",
|
|
len(hash),
|
|
a.hashSize,
|
|
)
|
|
}
|
|
// create new hasher to allow asynchronous usage
|
|
hasher := a.hfm()
|
|
|
|
v, err := source.Next()
|
|
for err == nil {
|
|
// calculate hash of the value
|
|
hasher.Reset()
|
|
hasher.Write(v)
|
|
vHash := hasher.Sum(nil)
|
|
|
|
// combine the hash with the current hash
|
|
hash = xorInPlace(hash, vHash)
|
|
|
|
v, err = source.Next()
|
|
}
|
|
if !errors.Is(err, io.EOF) {
|
|
return nil, fmt.Errorf("failed getting the next element from source: %w", err)
|
|
}
|
|
|
|
return hash, nil
|
|
}
|
|
|
|
// xorInPlace XORs the provided byte arrays in place.
|
|
// If one slice is shorter, 0s will be used as replacement elements.
|
|
// WARNING: The method will taint the passed arrays!
|
|
func xorInPlace(a, b []byte) []byte {
|
|
// ensure len(a) >= len(b)
|
|
if len(b) > len(a) {
|
|
a, b = b, a
|
|
}
|
|
|
|
// xor all values from a with b (or 0)
|
|
for i := 0; i < len(a); i++ {
|
|
var bi byte
|
|
if i < len(b) {
|
|
bi = b[i]
|
|
}
|
|
|
|
a[i] ^= bi
|
|
}
|
|
|
|
return a
|
|
}
|