Compare commits

...

No commits in common. "master" and "empty" have entirely different histories.

36 changed files with 2 additions and 2399 deletions

1
.github/CODEOWNERS vendored
View file

@ -1 +0,0 @@
* @TrueCloudLab/storage-core @TrueCloudLab/storage-services @TrueCloudLab/committers

27
.gitignore vendored
View file

@ -1,27 +0,0 @@
# IDE
.idea
.vscode
# Vendoring
vendor
# tempfiles
.DS_Store
*~
.cache
temp
tmp
# binary
bin/
release/
# coverage
coverage.txt
coverage.html
# testing
cmd/test
/plugins/
testfile

View file

@ -1,10 +0,0 @@
[general]
fail-without-commits=true
contrib=CC1
[title-match-regex]
regex=^\[\#[0-9]+\]\s
[ignore-by-title]
regex=^Release(.*)
ignore=title-match-regex

View file

@ -1,30 +0,0 @@
ci:
autofix_prs: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-executables-have-shebangs
- id: check-shebang-scripts-are-executable
- id: check-merge-conflict
- id: check-json
- id: check-xml
- id: check-yaml
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
- id: end-of-file-fixer
exclude: ".key$"
- repo: https://github.com/golangci/golangci-lint
rev: v1.51.2
hooks:
- id: golangci-lint
- repo: https://github.com/jorisroovers/gitlint
rev: v0.18.0
hooks:
- id: gitlint
stages: [commit-msg]

View file

@ -1,155 +0,0 @@
# Contribution guide
First, thank you for contributing! We love and encourage pull requests from
everyone. Please follow the guidelines:
- Check the open [issues](https://git.frostfs.info/TrueCloudLab/tzhash/issues) and
[pull requests](https://git.frostfs.info/TrueCloudLab/tzhash/pulls) for existing
discussions.
- Open an issue first, to discuss a new feature or enhancement.
- Write tests, and make sure the test suite passes locally and on CI.
- Open a pull request, and reference the relevant issue(s).
- Make sure your commits are logically separated and have good comments
explaining the details of your change.
- After receiving feedback, amend your commits or add new ones as
appropriate.
- **Have fun!**
## Development Workflow
Start by forking the `tzhash` repository, make changes in a branch and then
send a pull request. We encourage pull requests to discuss code changes. Here
are the steps in details:
### Set up your GitHub Repository
Fork [TZHash upstream](https://git.frostfs.info/TrueCloudLab/tzhash/fork) source
repository to your own personal repository. Copy the URL of your fork (you will
need it for the `git clone` command below).
```sh
$ git clone https://git.frostfs.info/TrueCloudLab/tzhash
```
### Set up git remote as ``upstream``
```sh
$ cd tzhash
$ git remote add upstream https://git.frostfs.info/TrueCloudLab/tzhash
$ git fetch upstream
$ git merge upstream/master
...
```
### Create your feature branch
Before making code changes, make sure you create a separate branch for these
changes. Maybe you will find it convenient to name branch in
`<type>/<Issue>-<changes_topic>` format.
```
$ git checkout -b feature/123-something_awesome
```
### Test your changes
After your code changes, make sure
- To add test cases for the new code.
- To squash your commits into a single commit or a series of logically separated
commits run `git rebase -i`. It's okay to force update your pull request.
- To run `make test` and `make all` completes.
### Commit changes
After verification, commit your changes. This is a [great
post](https://chris.beams.io/posts/git-commit/) on how to write useful commit
messages. Try following this template:
```
[#Issue] <component> Summary
Description
<Macros>
<Sign-Off>
```
```
$ git commit -sam '[#123] Add some feature'
```
### Push to the branch
Push your locally committed changes to the remote origin (your fork)
```
$ git push origin feature/123-something_awesome
```
### Create a Pull Request
Pull requests can be created via GitHub. Refer to [this
document](https://help.github.com/articles/creating-a-pull-request/) for
detailed steps on how to create a pull request. After a Pull Request gets peer
reviewed and approved, it will be merged.
## DCO Sign off
All authors to the project retain copyright to their work. However, to ensure
that they are only submitting work that they have rights to, we are requiring
everyone to acknowledge this by signing their work.
Any copyright notices in this repository should specify the authors as "the
contributors".
To sign your work, just add a line like this at the end of your commit message:
```
Signed-off-by: Samii Sakisaka <samii@ivunojikan.co.jp>
```
This can easily be done with the `--signoff` option to `git commit`.
By doing this you state that you can certify the following (from [The Developer
Certificate of Origin](https://developercertificate.org/)):
```
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```

View file

@ -1,36 +0,0 @@
FROM golang:1-alpine as builder
RUN set -x \
&& apk add --no-cache \
git \
curl \
&& mkdir -p /tmp \
&& mkdir -p /fixtures \
&& curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/01.txt \
&& curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/02.txt \
&& curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/03.txt \
&& curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/04.txt \
&& curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/05.txt \
&& curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/06.txt \
&& curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/07.txt \
&& curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/08.txt \
&& curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/09.txt \
&& curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/10.txt
COPY . /tzhash
WORKDIR /tzhash
# https://github.com/golang/go/wiki/Modules#how-do-i-use-vendoring-with-modules-is-vendoring-going-away
# go build -mod=vendor
RUN set -x \
&& export CGO_ENABLED=0 \
&& go build -mod=vendor -o /go/bin/homo ./cmd/homo/main.go
# Executable image
FROM alpine:3.11
WORKDIR /fixtures
COPY --from=builder /fixtures /fixtures
COPY --from=builder /go/bin/homo /usr/local/sbin/homo

201
LICENSE
View file

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View file

@ -1,61 +0,0 @@
#!/usr/bin/make -f
SHELL = bash
REPO ?= $(shell go list -m)
VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop")
BIN = bin
DIRS = $(BIN)
# List of binaries to build.
CMDS = $(notdir $(basename $(wildcard cmd/*)))
BINS = $(addprefix $(BIN)/, $(CMDS))
.PHONY: all help clean
# To build a specific binary, use it's name prefix with bin/ as a target
# For example `make bin/tzsum` will build only storage node binary
# Just `make` will build all possible binaries
all: $(DIRS) $(BINS)
# help target
include help.mk
$(BINS): $(DIRS) dep
@echo "⇒ Build $@"
CGO_ENABLED=0 \
go build -v -trimpath \
-ldflags "-X $(REPO)/misc.Version=$(VERSION)" \
-o $@ ./cmd/$(notdir $@)
$(DIRS):
@echo "⇒ Ensure dir: $@"
@mkdir -p $@
# Pull go dependencies
dep:
@printf "⇒ Download requirements: "
CGO_ENABLED=0 \
go mod download && echo OK
@printf "⇒ Tidy requirements : "
CGO_ENABLED=0 \
go mod tidy -v && echo OK
# Run Unit Test with go test
test:
@echo "⇒ Running go test"
@go test ./...
# Run Unit Test with go test
test.generic:
@echo "⇒ Running go test with generic tag"
@go test ./... --tags=generic
# Print version
version:
@echo $(VERSION)
clean:
rm -rf vendor
rm -rf .cache
rm -rf $(BIN)

View file

@ -1,78 +1,3 @@
# Demo # WIP area: this repo is just a fork!
[![asciicast](https://asciinema.org/a/IArEDLTrQyabI3agSSpINoqNu.svg)](https://asciinema.org/a/IArEDLTrQyabI3agSSpINoqNu) Useful things may be published only in [other branches](../../../branches)
**In project root:**
```bash
$ make
...
$ ./demo.sh
```
# Homomorphic hashing in golang
Package `tz` contains pure-Go (with some Assembly) implementation of hashing
function described by [Tillich and
Zémor](https://link.springer.com/content/pdf/10.1007/3-540-48658-5_5.pdf).
There are [existing implementations](https://github.com/srijs/hwsl2-core)
already, however they are written in C.
Package `gf127` contains arithmetic in `GF(2^127)` with `x^127+x^63+1` as reduction polynomial.
# Description
TZ Hash can be used instead of Merkle-tree for data-validation, because
homomorphic hashes are concatenable: hash sum of data can be calculated based on
hashes of chunks.
The example of how it works can be seen in tests and demo.
# Benchmarks
## go vs AVX vs AVX2 version
```
BenchmarkSum/AVX_digest-8 308 3889484 ns/op 25.71 MB/s 5 allocs/op
BenchmarkSum/AVXInline_digest-8 457 2455437 ns/op 40.73 MB/s 5 allocs/op
BenchmarkSum/AVX2_digest-8 399 3031102 ns/op 32.99 MB/s 3 allocs/op
BenchmarkSum/AVX2Inline_digest-8 602 2077719 ns/op 48.13 MB/s 3 allocs/op
BenchmarkSum/PureGo_digest-8 68 17795480 ns/op 5.62 MB/s 5 allocs/op
```
# Makefile
``` bash
Usage:
make <target>
Targets:
all Just `make` will build all possible binaries
clean Print version
dep Pull go dependencies
help Show this help prompt
test Run Unit Test with go test
version Print version
```
# Contributing
Feel free to contribute to this project after reading the [contributing
guidelines](CONTRIBUTING.md).
Before starting to work on a certain topic, create a new issue first, describing
the feature/topic you are going to implement.
# License
This project is licensed under the Apache 2.0 License -
see the [LICENSE](LICENSE) file for details
# References
- [https://link.springer.com/content/pdf/10.1007/3-540-48658-5_5.pdf](https://link.springer.com/content/pdf/10.1007/3-540-48658-5_5.pdf)
- [https://github.com/srijs/hwsl2-core](https://github.com/srijs/hwsl2-core)

View file

@ -1,12 +0,0 @@
#!/bin/bash
BLOCK_SIZE=${1:-1G} # gigabyte by default
OUT="${OUT:-$(mktemp /tmp/random-file.XXXXXX)}"
dd if=/dev/urandom of="$OUT" bs="$BLOCK_SIZE" count=1
for impl in avx avx2 generic; do
echo $impl implementation:
time ./bin/tzsum -name "$OUT" -impl $impl
echo
done

View file

@ -1,63 +0,0 @@
package main
import (
"bufio"
"encoding/hex"
"flag"
"fmt"
"os"
"git.frostfs.info/TrueCloudLab/tzhash/tz"
)
var (
concat = flag.Bool("concat", false, "Concatenate hashes")
filename = flag.String("file", "", "File to read from")
)
func main() {
var (
err error
file = os.Stdin
lines = make([]string, 0, 10)
)
flag.Parse()
if *filename != "" {
if file, err = os.Open(*filename); err != nil {
fatal("error while opening file: %v", err)
}
}
for f := bufio.NewScanner(file); f.Scan(); {
lines = append(lines, f.Text())
}
if *concat {
var (
h []byte
hashes = make([][]byte, len(lines))
)
for i := range lines {
if hashes[i], err = hex.DecodeString(lines[i]); err != nil {
fatal("error while decoding hex-string: %v", err)
}
}
h, err := tz.Concat(hashes)
if err != nil {
fatal("error while concatenating hashes: %v", err)
}
fmt.Println(hex.EncodeToString(h))
return
}
for i := range lines {
h := tz.Sum([]byte(lines[i]))
fmt.Println(hex.EncodeToString(h[:]))
}
}
func fatal(msg string, args ...interface{}) {
fmt.Printf(msg+"\n", args...)
os.Exit(1)
}

View file

@ -1,85 +0,0 @@
package main
import (
"flag"
"fmt"
"hash"
"io"
"log"
"os"
"runtime"
"runtime/pprof"
"git.frostfs.info/TrueCloudLab/tzhash/tz"
"golang.org/x/sys/cpu"
)
var (
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
memprofile = flag.String("memprofile", "", "write memory profile to `file`")
filename = flag.String("name", "-", "file to use")
hashimpl = flag.String("impl", "", "implementation to use")
)
func main() {
var (
f io.Reader
err error
)
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
if *filename != "-" {
if f, err = os.Open(*filename); err != nil {
log.Fatal("could not open file: ", err)
}
} else {
f = os.Stdin
}
// Override CPU feature flags to make sure a proper backend is used.
var h hash.Hash
switch *hashimpl {
case "avx":
cpu.X86.HasAVX = true
cpu.X86.HasAVX2 = false
h = tz.New()
case "avx2":
cpu.X86.HasAVX = true
cpu.X86.HasAVX2 = true
h = tz.New()
case "generic":
cpu.X86.HasAVX = false
cpu.X86.HasAVX2 = false
h = tz.New()
default:
log.Fatalf("Invalid backend: %s", *hashimpl)
}
if _, err := io.Copy(h, f); err != nil {
log.Fatal("error while reading file: ", err)
}
fmt.Printf("%x\t%s\n", h.Sum(nil), *filename)
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err)
}
f.Close()
}
}

38
demo.sh
View file

@ -1,38 +0,0 @@
#!/usr/bin/env bash
#set -x
BLOCK_SIZE=${1:-100M} # 100Mb by default
TMPDIR="${TMPDIR:-$(mktemp -d)}"
OUT="${OUT:-"${TMPDIR}/bighash"}"
echo "Preparing big file at ${OUT}..."
dd if=/dev/urandom of="$OUT" bs="$BLOCK_SIZE" count=1
echo "Make 4 smaller parts from ${OUT}..."
split -dn 4 "${OUT}" "${TMPDIR}/"
echo -n "Big file hash: "
TZALL=$(./bin/tzsum -impl avx2 -name "${OUT}" | awk '{print $1}')
echo "${TZALL}"
for i in $(seq -f "%02g" 0 3)
do
echo -n "Part ${i} hash: "
PART=$(./bin/tzsum -impl avx2 -name "${TMPDIR}/${i}" | awk '{print $1}')
echo "${PART}" | tee -a "${TMPDIR}/part.hashes"
done
echo -n "Cumulative: "
TZCUM=$(./bin/homo -concat -file "${TMPDIR}/part.hashes")
echo "${TZCUM}"
if [[ "$TZCUM" == "$TZALL" ]]; then
echo "Original and cumulative hashes are equal!"
else
echo "Original and cumulative hashes are NOT equal!"
fi
echo -ne "Cleaning up .. "
rm -rf "${TMPDIR}"
echo "Done!"

View file

@ -1,7 +0,0 @@
// Package gf127 implements the GF(2^127) arithmetic
// modulo reduction polynomial x^127 + x^63 + 1 .
// gf127.go contains common definitions.
// Other files contain architecture-specific implementations.
//
// Copyright 2019 (c) NSPCC
package gf127

View file

@ -1,181 +0,0 @@
package gf127
import (
"encoding/binary"
"encoding/hex"
"errors"
"math/bits"
"math/rand"
)
// GF127 represents element of GF(2^127)
type GF127 [2]uint64
const (
byteSize = 16
maxUint64 = ^uint64(0)
msb64 = uint64(1) << 63
)
// x127x631 is reduction polynomial x^127 + x^63 + 1
var x127x631 = GF127{msb64 + 1, msb64}
// New constructs new element of GF(2^127) as hi*x^64 + lo.
// It is assumed that hi has zero MSB.
func New(lo, hi uint64) *GF127 {
return &GF127{lo, hi}
}
func addGeneric(a, b, c *GF127) {
c[0] = a[0] ^ b[0]
c[1] = a[1] ^ b[1]
}
func mulGeneric(a, b, c *GF127) {
r := new(GF127)
d := *a
for i := uint(0); i < 64; i++ {
if b[0]&(1<<i) != 0 {
addGeneric(r, &d, r)
}
mul10Generic(&d, &d)
}
for i := uint(0); i < 63; i++ {
if b[1]&(1<<i) != 0 {
addGeneric(r, &d, r)
}
mul10Generic(&d, &d)
}
*c = *r
}
func mul10Generic(a, b *GF127) {
c := a[0] >> 63
b[0] = a[0] << 1
b[1] = (a[1] << 1) ^ c
mask := b[1] & msb64
b[0] ^= mask | (mask >> 63)
b[1] ^= mask
}
func mul11Generic(a, b *GF127) {
c := a[0] >> 63
b[0] = a[0] ^ (a[0] << 1)
b[1] = a[1] ^ (a[1] << 1) ^ c
mask := b[1] & msb64
b[0] ^= mask | (mask >> 63)
b[1] ^= mask
}
// Inv sets b to a^-1
// Algorithm is based on Extended Euclidean Algorithm
// and is described by Hankerson, Hernandez, Menezes in
// https://link.springer.com/content/pdf/10.1007/3-540-44499-8_1.pdf
func Inv(a, b *GF127) {
var (
v = x127x631
u = *a
c, d = &GF127{1, 0}, &GF127{0, 0}
t = new(GF127)
x *GF127
)
// degree of polynomial is a position of most significant bit
for du, dv := msb(&u), msb(&v); du != 0; du, dv = msb(&u), msb(&v) {
if du < dv {
v, u = u, v
dv, du = du, dv
d, c = c, d
}
x = xN(du - dv)
Mul(x, &v, t)
Add(&u, t, &u)
// becasuse mulAVX performs reduction on t, we need
// manually reduce u at first step
if msb(&u) == 127 {
Add(&u, &x127x631, &u)
}
Mul(x, d, t)
Add(c, t, c)
}
*b = *c
}
func xN(n int) *GF127 {
if n < 64 {
return &GF127{1 << uint(n), 0}
}
return &GF127{0, 1 << uint(n-64)}
}
func msb(a *GF127) (x int) {
x = bits.LeadingZeros64(a[1])
if x == 64 {
x = bits.LeadingZeros64(a[0]) + 64
}
return 127 - x
}
// Mul1 copies b into a.
func Mul1(a, b *GF127) {
a[0] = b[0]
a[1] = b[1]
}
// And sets c to a & b (bitwise-and).
func And(a, b, c *GF127) {
c[0] = a[0] & b[0]
c[1] = a[1] & b[1]
}
// Random returns random element from GF(2^127).
// Is used mostly for testing.
func Random() *GF127 {
return &GF127{rand.Uint64(), rand.Uint64() >> 1}
}
// String returns hex-encoded representation, starting with MSB.
func (c *GF127) String() string {
buf := c.Bytes()
return hex.EncodeToString(buf[:])
}
// Equals checks if two reduced (zero MSB) elements of GF(2^127) are equal
func (c *GF127) Equals(b *GF127) bool {
return c[0] == b[0] && c[1] == b[1]
}
// Bytes represents element of GF(2^127) as byte array of length 16.
func (c *GF127) Bytes() [16]byte {
var buf [16]byte
binary.BigEndian.PutUint64(buf[:8], c[1])
binary.BigEndian.PutUint64(buf[8:], c[0])
return buf
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (c *GF127) MarshalBinary() (data []byte, err error) {
buf := c.Bytes()
return buf[:], nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (c *GF127) UnmarshalBinary(data []byte) error {
if len(data) != byteSize {
return errors.New("data must be 16-bytes long")
}
c[0] = binary.BigEndian.Uint64(data[8:])
c[1] = binary.BigEndian.Uint64(data[:8])
if c[1]&msb64 != 0 {
return errors.New("MSB must be zero")
}
return nil
}

View file

@ -1,55 +0,0 @@
//go:build amd64 && !generic
// +build amd64,!generic
// Package gf127 implements the GF(2^127) arithmetic
// modulo reduction polynomial x^127 + x^63 + 1 .
// This is rather straight-forward re-implementation of C library
// available here https://github.com/srijs/hwsl2-core .
// Interfaces are highly influenced by math/big .
package gf127
import "golang.org/x/sys/cpu"
// x127x63 represents x^127 + x^63
var x127x63 = GF127{msb64, msb64} //nolint:unused
// Add sets c to a+b.
func Add(a, b, c *GF127) {
if cpu.X86.HasAVX {
addAVX(a, b, c)
} else {
addGeneric(a, b, c)
}
}
// Mul sets c to a*b.
func Mul(a, b, c *GF127) {
if cpu.X86.HasAVX {
mulAVX(a, b, c)
} else {
mulGeneric(a, b, c)
}
}
// Mul10 sets b to a*x.
func Mul10(a, b *GF127) {
if cpu.X86.HasAVX {
mul10AVX(a, b)
} else {
mul10Generic(a, b)
}
}
// Mul11 sets b to a*(x+1).
func Mul11(a, b *GF127) {
if cpu.X86.HasAVX {
mul11AVX(a, b)
} else {
mul11Generic(a, b)
}
}
func addAVX(a, b, c *GF127)
func mulAVX(a, b, c *GF127)
func mul10AVX(a, b *GF127)
func mul11AVX(a, b *GF127)

View file

@ -1,81 +0,0 @@
#include "textflag.h"
// func Add(a, b, c *[2]uint64)
TEXT ·addAVX(SB), NOSPLIT, $0
MOVQ a+0(FP), AX
MOVUPD (AX), X0
MOVQ b+8(FP), BX
MOVUPD (BX), X1
XORPD X1, X0
MOVQ c+16(FP), CX
MOVUPD X0, (CX)
RET
// func Mul10(a, b *[2]uint64)
TEXT ·mul10AVX(SB), NOSPLIT, $0
MOVQ a+0(FP), AX
MOVUPD (AX), X0
VPSLLQ $1, X0, X1
VPALIGNR $8, X1, X0, X2
PSRLQ $63, X2
MOVUPD ·x127x63(SB), X3
ANDPD X1, X3
VPUNPCKHQDQ X3, X3, X3
XORPD X2, X1
XORPD X3, X1
MOVQ b+8(FP), AX
MOVUPD X1, (AX)
RET
// func Mul11(a, b *[2]uint64)
TEXT ·mul11AVX(SB), NOSPLIT, $0
MOVQ a+0(FP), AX
MOVUPD (AX), X0
VPSLLQ $1, X0, X1
VPALIGNR $8, X1, X0, X2
PSRLQ $63, X2
MOVUPD ·x127x63(SB), X3
ANDPD X1, X3
VPUNPCKHQDQ X3, X3, X3
XORPD X2, X1
XORPD X3, X1
XORPD X0, X1
MOVQ b+8(FP), AX
MOVUPD X1, (AX)
RET
// func Mul(a, b, c *[2]uint64)
TEXT ·mulAVX(SB), NOSPLIT, $0
MOVQ a+0(FP), AX // X0 = a0 . a1
MOVUPD (AX), X0 // X0 = a0 . a1
MOVQ b+8(FP), BX // X1 = b0 . b1
MOVUPD (BX), X1 // X1 = b0 . b1
VPUNPCKLQDQ X1, X0, X2 // X2 = a0 . b0
VPUNPCKHQDQ X1, X0, X3 // X3 = a1 . b1
XORPD X2, X3 // X3 = (a0 + a1) . (b0 + b1)
PCLMULQDQ $0x10, X3, X3 // X3 = (a0 + a1) * (b0 + b1)
VPCLMULQDQ $0x00, X0, X1, X4 // X4 = a0 * b0
VPCLMULQDQ $0x11, X0, X1, X5 // X5 = a1 * b1
XORPD X4, X3
XORPD X5, X3 // X3 = a0 * b1 + a1 * b0
VPSLLDQ $8, X3, X2
XORPD X2, X4 // X4 = a0 * b0 + lo(X3)
VPSRLDQ $8, X3, X6
XORPD X6, X5 // X5 = a1 * b1 + hi(X3)
// at this point, a * b = X4 . X5 (as 256-bit number)
// reduction modulo x^127 + x^63 + 1
VPALIGNR $8, X4, X5, X3
XORPD X5, X3
PSLLQ $1, X5
XORPD X5, X4
VPUNPCKHQDQ X3, X5, X5
XORPD X5, X4
PSRLQ $63, X3
XORPD X3, X4
VPUNPCKLQDQ X3, X3, X5
PSLLQ $63, X5
XORPD X5, X4
MOVQ c+16(FP), CX
MOVUPD X4, (CX)
RET

View file

@ -1,24 +0,0 @@
//go:build !amd64 || generic
// +build !amd64 generic
package gf127
// Add sets c to a+b.
func Add(a, b, c *GF127) {
addGeneric(a, b, c)
}
// Mul sets c to a*b.
func Mul(a, b, c *GF127) {
mulGeneric(a, b, c)
}
// Mul10 sets b to a*x.
func Mul10(a, b *GF127) {
mul10Generic(a, b)
}
// Mul11 sets b to a*(x+1).
func Mul11(a, b *GF127) {
mul11Generic(a, b)
}

View file

@ -1,123 +0,0 @@
package gf127
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestAdd(t *testing.T) {
var (
a = Random()
b = Random()
e = &GF127{a[0] ^ b[0], a[1] ^ b[1]}
c = new(GF127)
)
Add(a, b, c)
require.Equal(t, e, c)
}
var testCasesMul = [][3]*GF127{
// (x+1)*(x^63+x^62+...+1) == x^64+1
{&GF127{3, 0}, &GF127{maxUint64, 0}, &GF127{1, 1}},
// x^126 * x^2 == x^128 == x^64 + x
{&GF127{0, 1 << 62}, &GF127{4, 0}, &GF127{2, 1}},
// (x^64+x^63+1) * (x^64+x) == x^128+x^65+x^127+x^64+x^64+x == x^65+x^64+x^63+1
{&GF127{1 + 1<<63, 1}, &GF127{2, 1}, &GF127{0x8000000000000001, 3}},
}
func TestMul(t *testing.T) {
c := new(GF127)
for _, tc := range testCasesMul {
Mul(tc[0], tc[1], c)
require.Equal(t, tc[2], c)
}
}
func TestMulInPlace(t *testing.T) {
for _, tc := range testCasesMul {
a := *tc[0]
b := *tc[1]
Mul(&a, &b, &b)
require.Equal(t, a, *tc[0])
require.Equal(t, b, *tc[2])
b = *tc[1]
Mul(&a, &b, &a)
require.Equal(t, b, *tc[1])
require.Equal(t, a, *tc[2])
}
}
var testCasesMul10 = [][2]*GF127{
{&GF127{123, 0}, &GF127{246, 0}},
{&GF127{maxUint64, 2}, &GF127{maxUint64 - 1, 5}},
{&GF127{0, maxUint64 >> 1}, &GF127{1 + 1<<63, maxUint64>>1 - 1}},
}
func TestMul10(t *testing.T) {
c := new(GF127)
for _, tc := range testCasesMul10 {
Mul10(tc[0], c)
require.Equal(t, tc[1], c)
}
}
var testCasesMul11 = [][2]*GF127{
{&GF127{123, 0}, &GF127{141, 0}},
{&GF127{maxUint64, 2}, &GF127{1, 7}},
{&GF127{0, maxUint64 >> 1}, &GF127{1 + 1<<63, 1}},
}
func TestMul11(t *testing.T) {
c := new(GF127)
for _, tc := range testCasesMul11 {
Mul11(tc[0], c)
require.Equal(t, tc[1], c)
}
}
var testCasesInv = [][2]*GF127{
{&GF127{1, 0}, &GF127{1, 0}},
{&GF127{3, 0}, &GF127{msb64, ^msb64}},
{&GF127{54321, 12345}, &GF127{8230555108620784737, 3929873967650665114}},
}
func TestInv(t *testing.T) {
var a, b, c = new(GF127), new(GF127), new(GF127)
for _, tc := range testCasesInv {
Inv(tc[0], c)
require.Equal(t, tc[1], c)
}
for i := 0; i < 3; i++ {
// 0 has no inverse
if a = Random(); a.Equals(&GF127{0, 0}) {
continue
}
Inv(a, b)
Mul(a, b, c)
require.Equal(t, &GF127{1, 0}, c)
}
}
func TestGF127_MarshalBinary(t *testing.T) {
a := New(0xFF, 0xEE)
data, err := a.MarshalBinary()
require.NoError(t, err)
require.Equal(t, data, []byte{0, 0, 0, 0, 0, 0, 0, 0xEE, 0, 0, 0, 0, 0, 0, 0, 0xFF})
a = Random()
data, err = a.MarshalBinary()
require.NoError(t, err)
b := new(GF127)
err = b.UnmarshalBinary(data)
require.NoError(t, err)
require.Equal(t, a, b)
err = b.UnmarshalBinary([]byte{0, 1, 2, 3})
require.Error(t, err)
}

View file

@ -1,51 +0,0 @@
package gf127
import (
"encoding/binary"
"encoding/hex"
)
// GF127x2 represents a pair of elements of GF(2^127) stored together.
type GF127x2 [2]GF127
func mul10x2Generic(a, b *GF127x2) {
mul10Generic(&a[0], &b[0])
mul10Generic(&a[1], &b[1])
}
func mul11x2Generic(a, b *GF127x2) {
mul11Generic(&a[0], &b[0])
mul11Generic(&a[1], &b[1])
}
// Split returns 2 components of pair without additional allocations.
func Split(a *GF127x2) (*GF127, *GF127) {
return &a[0], &a[1]
}
// CombineTo 2 elements of GF(2^127) to the respective components of pair.
func CombineTo(a *GF127, b *GF127, c *GF127x2) {
c[0] = *a
c[1] = *b
}
// Equal checks if both elements of GF(2^127) pair are equal.
func (a *GF127x2) Equal(b *GF127x2) bool {
return a[0] == b[0] && a[1] == b[1]
}
// String returns hex-encoded representation, starting with MSB.
// Elements of pair are separated by comma.
func (a *GF127x2) String() string {
b := a.Bytes()
return hex.EncodeToString(b[:16]) + " , " + hex.EncodeToString(b[16:])
}
// Bytes represents element of GF(2^127) as byte array of length 32.
func (a *GF127x2) Bytes() (buf [32]byte) {
binary.BigEndian.PutUint64(buf[:], a[0][1])
binary.BigEndian.PutUint64(buf[8:], a[0][0])
binary.BigEndian.PutUint64(buf[16:], a[1][1])
binary.BigEndian.PutUint64(buf[24:], a[1][0])
return
}

View file

@ -1,27 +0,0 @@
//go:build amd64 && !generic
// +build amd64,!generic
package gf127
import "golang.org/x/sys/cpu"
// Mul10x2 sets (b1, b2) to (a1*x, a2*x)
func Mul10x2(a, b *GF127x2) {
if cpu.X86.HasAVX && cpu.X86.HasAVX2 {
mul10x2AVX2(a, b)
} else {
mul10x2Generic(a, b)
}
}
// Mul11x2 sets (b1, b2) to (a1*(x+1), a2*(x+1))
func Mul11x2(a, b *GF127x2) {
if cpu.X86.HasAVX && cpu.X86.HasAVX2 {
mul11x2AVX2(a, b)
} else {
mul11x2Generic(a, b)
}
}
func mul10x2AVX2(a, b *GF127x2)
func mul11x2AVX2(a, b *GF127x2)

View file

@ -1,34 +0,0 @@
#include "textflag.h"
// func Mul10x2(a, b) *[4]uint64
TEXT ·mul10x2AVX2(SB), NOSPLIT, $0
MOVQ a+0(FP), AX
VMOVDQA (AX), Y0
VPSLLQ $1, Y0, Y1
VPALIGNR $8, Y1, Y0, Y2
VPSRLQ $63, Y2, Y2
VPXOR Y1, Y2, Y2
VPSRLQ $63, Y1, Y3
VPSLLQ $63, Y3, Y3
VPUNPCKHQDQ Y3, Y3, Y3
VPXOR Y2, Y3, Y3
MOVQ b+8(FP), AX
VMOVDQA Y3, (AX)
RET
// func Mul11x2(a, b) *[4]uint64
TEXT ·mul11x2AVX2(SB), NOSPLIT, $0
MOVQ a+0(FP), AX
VMOVDQA (AX), Y0
VPSLLQ $1, Y0, Y1
VPALIGNR $8, Y1, Y0, Y2
VPSRLQ $63, Y2, Y2
VPXOR Y1, Y2, Y2
VPSRLQ $63, Y1, Y3
VPSLLQ $63, Y3, Y3
VPUNPCKHQDQ Y3, Y3, Y3
VPXOR Y2, Y3, Y3
VPXOR Y0, Y3, Y3
MOVQ b+8(FP), AX
VMOVDQA Y3, (AX)
RET

View file

@ -1,14 +0,0 @@
//go:build !(amd64 && !generic)
// +build !amd64 generic
package gf127
// Mul10x2 sets (b1, b2) to (a1*x, a2*x)
func Mul10x2(a, b *GF127x2) {
mul10x2Generic(a, b)
}
// Mul11x2 sets (b1, b2) to (a1*(x+1), a2*(x+1))
func Mul11x2(a, b *GF127x2) {
mul11x2Generic(a, b)
}

View file

@ -1,78 +0,0 @@
package gf127
import (
"testing"
"github.com/stretchr/testify/require"
)
var testCasesSplit = []struct {
num *GF127x2
h1 *GF127
h2 *GF127
}{
{&GF127x2{GF127{123, 31}, GF127{141, 9}}, &GF127{123, 31}, &GF127{141, 9}},
{&GF127x2{GF127{maxUint64, 0}, GF127{0, maxUint64}}, &GF127{maxUint64, 0}, &GF127{0, maxUint64}},
}
func TestSplit(t *testing.T) {
for _, tc := range testCasesSplit {
a, b := Split(tc.num)
require.Equal(t, tc.h1, a)
require.Equal(t, tc.h2, b)
}
}
func TestCombineTo(t *testing.T) {
c := new(GF127x2)
for _, tc := range testCasesSplit {
CombineTo(tc.h1, tc.h2, c)
require.Equal(t, tc.num, c)
}
}
var testCasesMul10x2 = [][2]*GF127x2{
{
&GF127x2{GF127{123, 0}, GF127{123, 0}},
&GF127x2{GF127{246, 0}, GF127{246, 0}},
},
{
&GF127x2{GF127{maxUint64, 2}, GF127{0, 1}},
&GF127x2{GF127{maxUint64 - 1, 5}, GF127{0, 2}},
},
{
&GF127x2{GF127{0, maxUint64 >> 1}, GF127{maxUint64, 2}},
&GF127x2{GF127{1 + 1<<63, maxUint64>>1 - 1}, GF127{maxUint64 - 1, 5}},
},
}
func TestMul10x2(t *testing.T) {
c := new(GF127x2)
for _, tc := range testCasesMul10x2 {
Mul10x2(tc[0], c)
require.Equal(t, tc[1], c)
}
}
var testCasesMul11x2 = [][2]*GF127x2{
{
&GF127x2{GF127{123, 0}, GF127{123, 0}},
&GF127x2{GF127{141, 0}, GF127{141, 0}},
},
{
&GF127x2{GF127{maxUint64, 2}, GF127{0, 1}},
&GF127x2{GF127{1, 7}, GF127{0, 3}},
},
{
&GF127x2{GF127{0, maxUint64 >> 1}, GF127{maxUint64, 2}},
&GF127x2{GF127{1 + 1<<63, 1}, GF127{1, 7}},
},
}
func TestMul11x2(t *testing.T) {
c := new(GF127x2)
for _, tc := range testCasesMul11x2 {
Mul11x2(tc[0], c)
require.Equal(t, tc[1], c)
}
}

10
go.mod
View file

@ -1,10 +0,0 @@
module git.frostfs.info/TrueCloudLab/tzhash
go 1.16
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/stretchr/testify v1.7.0
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

15
go.sum
View file

@ -1,15 +0,0 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

11
help.mk
View file

@ -1,11 +0,0 @@
.PHONY: help
# Show this help prompt
help:
@echo ' Usage:'
@echo ''
@echo ' make <target>'
@echo ''
@echo ' Targets:'
@echo ''
@awk '/^#/{ comment = substr($$0,3) } comment && /^[a-zA-Z][a-zA-Z0-9_-]+ ?:/{ print " ", $$1, comment }' $(MAKEFILE_LIST) | column -t -s ':' | grep -v 'IGNORE' | sort | uniq

View file

@ -1,124 +0,0 @@
package tz
import (
"git.frostfs.info/TrueCloudLab/tzhash/gf127"
)
const (
// Size is the size of a Tillich-Zémor hash sum in bytes.
Size = 64
hashBlockSize = 128
)
type digest struct {
// Stores matrix cells in the following order:
// [ 0 2 ]
// [ 1 3 ]
// This is done to reuse the same digest between generic
// and AVX2 implementation.
x [4]GF127
}
// New returns a new hash.Hash computing the Tillich-Zémor checksum.
func New() *digest {
d := new(digest)
d.Reset()
return d
}
// Sum returns Tillich-Zémor checksum of data.
func Sum(data []byte) [Size]byte {
d := new(digest)
d.Reset()
_, _ = d.Write(data) // no errors
return d.checkSum()
}
// Sum implements hash.Hash.
func (d *digest) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
d0 := *d
h := d0.checkSum()
return append(in, h[:]...)
}
func (d *digest) checkSum() (b [Size]byte) {
t := d.x[0].Bytes()
copy(b[:], t[:])
t = d.x[2].Bytes()
copy(b[16:], t[:])
t = d.x[1].Bytes()
copy(b[32:], t[:])
t = d.x[3].Bytes()
copy(b[48:], t[:])
return
}
// Reset implements hash.Hash.
func (d *digest) Reset() {
d.x[0] = GF127{1, 0}
d.x[1] = GF127{0, 0}
d.x[2] = GF127{0, 0}
d.x[3] = GF127{1, 0}
}
// Write implements hash.Hash.
func (d *digest) Write(data []byte) (n int, err error) {
return write(d, data)
}
func writeGeneric(d *digest, data []byte) (n int, err error) {
n = len(data)
tmp := new(GF127)
for _, b := range data {
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x80 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x40 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x20 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x10 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x08 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x04 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x02 != 0, tmp)
mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x01 != 0, tmp)
}
return
}
// Size implements hash.Hash.
func (d *digest) Size() int {
return Size
}
// BlockSize implements hash.Hash.
func (d *digest) BlockSize() int {
return hashBlockSize
}
func mulBitRightGeneric(c00, c10, c01, c11 *GF127, bit bool, tmp *GF127) {
if bit {
*tmp = *c00
gf127.Mul10(c00, c00)
gf127.Add(c00, c01, c00)
gf127.Mul11(tmp, tmp)
gf127.Add(c01, tmp, c01)
*tmp = *c10
gf127.Mul10(c10, c10)
gf127.Add(c10, c11, c10)
gf127.Mul11(tmp, tmp)
gf127.Add(c11, tmp, c11)
} else {
*tmp = *c00
gf127.Mul10(c00, c00)
gf127.Add(c00, c01, c00)
*c01 = *tmp
*tmp = *c10
gf127.Mul10(c10, c10)
gf127.Add(c10, c11, c10)
*c11 = *tmp
}
}

View file

@ -1,56 +0,0 @@
#include "textflag.h"
#define mulBit(bit, in_1, in_2, out_1, out_2) \
VPSLLW bit, Y10, Y11 \
VPSLLQ $1, in_1, Y1 \
VPSRAW $15, Y11, Y12 \
VPALIGNR $8, Y1, in_1, Y2 \
VPAND Y1, Y14, Y3 \
VPSRLQ $63, Y2, Y2 \
VPUNPCKHQDQ Y3, Y3, Y3 \
VPXOR Y1, Y2, Y7 \
VPXOR Y3, in_2, out_1 \
VPXOR Y7, out_1, out_1 \
VPAND out_1, Y12, Y4 \
VPXOR Y4, in_1, out_2 \
// func mulByteSliceRightx2(c00c10, c01c11 *[4]uint64, n int, data *byte)
TEXT ·mulByteSliceRightx2(SB), NOSPLIT, $0
MOVQ c00c10+0(FP), AX
MOVQ c01c11+8(FP), BX
VPXOR Y13, Y13, Y13 // Y13 = 0x0000...
VPCMPEQB Y14, Y14, Y14 // Y14 = 0xFFFF...
VPSUBQ Y14, Y13, Y10
VPSLLQ $63, Y10, Y14 // Y14 = 0x10000000... (packed quad-words with HSB set)
MOVQ n+16(FP), CX
MOVQ data+24(FP), DX
VMOVDQU (AX), Y0
VMOVDQU (BX), Y8
loop:
CMPQ CX, $0
JEQ finish
VPBROADCASTB (DX), Y10
ADDQ $1, DX
SUBQ $1, CX
mulBit($8, Y0, Y8, Y5, Y6)
mulBit($9, Y5, Y6, Y0, Y8)
mulBit($10, Y0, Y8, Y5, Y6)
mulBit($11, Y5, Y6, Y0, Y8)
mulBit($12, Y0, Y8, Y5, Y6)
mulBit($13, Y5, Y6, Y0, Y8)
mulBit($14, Y0, Y8, Y5, Y6)
mulBit($15, Y5, Y6, Y0, Y8)
JMP loop
finish:
VMOVDQU Y0, (AX)
VMOVDQU Y8, (BX)
RET

View file

@ -1,70 +0,0 @@
#include "textflag.h"
// mul2 multiplicates FROM by 2, stores result in R1
// and uses R1, R2 and R3 for internal computations.
#define mul2(FROM, TO, R2, R3) \
VPSLLQ $1, FROM, TO \
VPALIGNR $8, TO, FROM, R2 \
VPSRLQ $63, R2, R2 \
VANDPD TO, X14, R3 \
VPUNPCKHQDQ R3, R3, R3 \
VXORPD R2, TO, TO \
VXORPD R3, TO, TO
#define mask(bit, tmp, to) \
VPSRLW bit, X10, tmp \
VPAND X12, tmp, to \ // to = 0x000<bit>000<bit>...
VPSUBW to, X13, to // to = 0xFFFF.. or 0x0000 depending on bit
#define mulBit(bit) \
VMOVDQU X0, X8 \
VMOVDQU X2, X9 \
mul2(X0, X5, X6, X7) \
VXORPD X1, X5, X0 \
mul2(X2, X5, X6, X7) \
VXORPD X3, X5, X2 \
mask(bit, X6, X5) \
VANDPD X0, X5, X1 \
VXORPD X8, X1, X1 \
VANDPD X2, X5, X3 \
VXORPD X9, X3, X3
TEXT ·mulByteRight(SB), NOSPLIT, $0
MOVQ c00+0(FP), AX
VMOVDQU (AX), X0
MOVQ c10+8(FP), CX
VMOVDQU (CX), X2
MOVQ c01+16(FP), BX
VMOVDQU (BX), X1
MOVQ c11+24(FP), DX
VMOVDQU (DX), X3
MOVQ $0, CX
MOVB b+32(FP), CX
VPXOR X13, X13, X13 // X13 = 0x0000...
VPCMPEQB X14, X14, X14 // X14 = 0xFFFF...
VPSUBQ X14, X13, X10
VPSUBW X14, X13, X12 // X12 = 0x00010001... (packed words of 1)
VPSLLQ $63, X10, X14 // X14 = 0x10000000... (packed quad-words with HSB set)
MOVQ CX, X10
VPSHUFLW $0, X10, X11
VPSHUFD $0, X11, X10
mulBit($7)
mulBit($6)
mulBit($5)
mulBit($4)
mulBit($3)
mulBit($2)
mulBit($1)
mulBit($0)
VMOVDQU X0, (AX)
MOVQ c10+8(FP), CX
VMOVDQU X2, (CX)
VMOVDQU X1, (BX)
MOVQ c11+24(FP), DX
VMOVDQU X3, (DX)
RET

View file

@ -1,8 +0,0 @@
//go:build !(amd64 && !generic)
// +build !amd64 generic
package tz
func write(d *digest, data []byte) (int, error) {
return writeGeneric(d, data)
}

View file

@ -1,39 +0,0 @@
//go:build amd64 && !generic
// +build amd64,!generic
package tz
import (
"git.frostfs.info/TrueCloudLab/tzhash/gf127"
"golang.org/x/sys/cpu"
)
func write(d *digest, data []byte) (n int, err error) {
switch {
case cpu.X86.HasAVX && cpu.X86.HasAVX2:
return writeAVX2(d, data)
case cpu.X86.HasAVX:
return writeAVX(d, data)
default:
return writeGeneric(d, data)
}
}
func writeAVX2(d *digest, data []byte) (n int, err error) {
n = len(data)
if len(data) != 0 {
mulByteSliceRightx2(&d.x[0], &d.x[2], n, &data[0])
}
return
}
func writeAVX(d *digest, data []byte) (n int, err error) {
n = len(data)
for _, b := range data {
mulByteRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b)
}
return
}
func mulByteRight(c00, c01, c10, c11 *GF127, b byte)
func mulByteSliceRightx2(c00c10 *gf127.GF127, c01c11 *gf127.GF127, n int, data *byte)

View file

@ -1,87 +0,0 @@
// Package tz contains Tillich-Zemor checksum implementations
// using different backends.
//
// Copyright 2022 (c) NSPCC
package tz
import (
"errors"
)
// Concat performs combining of hashes based on homomorphic property.
func Concat(hs [][]byte) ([]byte, error) {
var b, c sl2
b = id
for i := range hs {
if err := c.UnmarshalBinary(hs[i]); err != nil {
return nil, err
}
b.Mul(&b, &c)
}
return b.MarshalBinary()
}
// Validate checks if hashes in hs combined are equal to h.
func Validate(h []byte, hs [][]byte) (bool, error) {
var (
b []byte
got, expected [Size]byte
err error
)
if len(h) != Size {
return false, errors.New("invalid hash")
} else if len(hs) == 0 {
return false, errors.New("empty slice")
}
copy(expected[:], h)
b, err = Concat(hs)
if err != nil {
return false, errors.New("cant concatenate hashes")
}
copy(got[:], b)
return expected == got, nil
}
// SubtractR returns hash a, such that Concat(a, b) == c
// This is possible, because Tillich-Zemor hash is actually a matrix
// which can be inversed.
func SubtractR(c, b []byte) (a []byte, err error) {
var p1, p2, r sl2
if err = r.UnmarshalBinary(c); err != nil {
return nil, err
}
if err = p2.UnmarshalBinary(b); err != nil {
return nil, err
}
p1 = *Inv(&p2)
p1.Mul(&r, &p1)
return p1.MarshalBinary()
}
// SubtractL returns hash b, such that Concat(a, b) == c
// This is possible, because Tillich-Zemor hash is actually a matrix
// which can be inversed.
func SubtractL(c, a []byte) (b []byte, err error) {
var p1, p2, r sl2
if err = r.UnmarshalBinary(c); err != nil {
return nil, err
}
if err = p1.UnmarshalBinary(a); err != nil {
return nil, err
}
p2 = *Inv(&p1)
p2.Mul(&p2, &r)
return p2.MarshalBinary()
}

View file

@ -1,272 +0,0 @@
package tz
import (
"encoding/hex"
"fmt"
"io"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/sys/cpu"
)
const benchDataSize = 100000
type arch struct {
HasAVX bool
HasAVX2 bool
}
var backends = []struct {
Name string
arch
}{
{"AVX", arch{true, false}},
{"AVX2", arch{true, true}},
{"Generic", arch{false, false}},
}
var testCases = []struct {
input []byte
hash string
}{
{
[]byte{},
"00000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
},
{
[]byte{0},
"00000000000000000000000000000151000000000000000000000000000000800000000000000000000000000000008000000000000000000000000000000051",
},
{
[]byte{1, 2},
"000000000000000000000000000139800000000000000000000000000000c0010000000000000000000000000000b98100000000000000000000000000007981",
},
{
[]byte{2, 0, 1},
"00000000000000000000000001f980d10000000000000000000000000139805100000000000000000000000000c001d100000000000000000000000000b98080",
},
{
[]byte{3, 2, 1, 0},
"0000000000000000000000015540398000000000000000000000000082a1a88100000000000000000000000082a1d10100000000000000000000000050006881",
},
{
[]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
"0000000000000000000001bb00ba00ba000000000000000000000101010101010000000000000000000000ff00ff00ff0000000000000000000000ba01bb01bb",
},
{
[]byte{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
"000000000000000000016ad06ad16bd100000000000000000000ff00ff00ff0000000000000000000000808080808080000000000000000000006bd16bd06ad1",
},
{
[]byte{0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55},
"0000000000000000018c8c118d9d009d00000000000000000169680169680168000000000000000000f0f000f0f000f00000000000000000009d9c109c8d018d",
},
{
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8},
"00000000000001e4a545e5b90fb6882b00000000000000c849cd88f79307f67100000000000000cd0c898cb68356e624000000000000007cbcdc7c5e89b16e4b",
},
{
[]byte{4, 8, 15, 16, 23, 42, 255, 0, 127, 65, 32, 123, 42, 45, 201, 210, 213, 244},
"4db8a8e253903c70ab0efb65fe6de05a36d1dc9f567a147152d0148a86817b2062908d9b026a506007c1118e86901b672a39317c55ee3c10ac8efafa79efe8ee",
},
}
func TestHash(t *testing.T) {
for i, b := range backends {
t.Run(b.Name+" digest", func(t *testing.T) {
prepareArch(t, backends[i].arch)
fmt.Println("FEATURES:", cpu.X86.HasAVX, cpu.X86.HasAVX2)
d := New()
for _, tc := range testCases {
d.Reset()
_, _ = d.Write(tc.input)
sum := d.Sum(nil)
require.Equal(t, tc.hash, hex.EncodeToString(sum[:]))
}
})
}
}
func prepareArch(t testing.TB, b arch) {
realCPU := cpu.X86
if !realCPU.HasAVX2 && b.HasAVX2 || !realCPU.HasAVX && b.HasAVX {
t.Skip("Underlying CPU doesn't support necessary features")
} else {
t.Cleanup(func() {
cpu.X86.HasAVX = realCPU.HasAVX
cpu.X86.HasAVX2 = realCPU.HasAVX2
})
cpu.X86.HasAVX = b.HasAVX
cpu.X86.HasAVX2 = b.HasAVX2
}
}
func newBuffer() (data []byte) {
data = make([]byte, benchDataSize)
r := rand.New(rand.NewSource(0))
_, err := io.ReadFull(r, data)
if err != nil {
panic("cant initialize buffer")
}
return
}
func BenchmarkSum(b *testing.B) {
data := newBuffer()
for i := range backends {
b.Run(backends[i].Name+" digest", func(b *testing.B) {
prepareArch(b, backends[i].arch)
b.ResetTimer()
b.ReportAllocs()
d := New()
for i := 0; i < b.N; i++ {
d.Reset()
_, _ = d.Write(data)
d.Sum(nil)
}
b.SetBytes(int64(len(data)))
})
}
}
func TestHomomorphism(t *testing.T) {
var (
c1, c2 sl2
n int
err error
h, h1, h2 [Size]byte
b []byte
)
b = make([]byte, 64)
n, err = rand.Read(b)
require.Equal(t, 64, n)
require.NoError(t, err)
// Test if our hashing is really homomorphic
h = Sum(b)
require.NotEqual(t, [64]byte{}, h)
h1 = Sum(b[:32])
h2 = Sum(b[32:])
err = c1.UnmarshalBinary(h1[:])
require.NoError(t, err)
err = c2.UnmarshalBinary(h2[:])
require.NoError(t, err)
c1.Mul(&c1, &c2)
require.Equal(t, h, c1.Bytes())
}
var testCasesConcat = []struct {
Hash string
Parts []string
}{{
Hash: "7f5c9280352a8debea738a74abd4ec787f2c5e556800525692f651087442f9883bb97a2c1bc72d12ba26e3df8dc0f670564292ebc984976a8e353ff69a5fb3cb",
Parts: []string{
"4275945919296224acd268456be23b8b2df931787a46716477e32cd991e98074029d4f03a0fedc09125ee4640d228d7d40d430659a0b2b70e9cd4d4c5361865a",
"2828661d1b1e77f21788d3b365f140a2395d57dc2083c33e60d9a80e69017d5016a249c7adfe1718a10ba887dedbdaec5c4c1fbecdb1f98776b43f1142c26a88",
"02310598b45dfa77db9f00eed6ab60773dd8bed7bdac431b42e441fae463f64c6e2688402cfdcec5def47a299b0651fb20878cf4410991bd57056d7b4b31635a",
"1ed7e0b065c060d915e7355cdcb4edc752c06d2a4b39d90c8985aeb58e08cb9e5bbe4b2b45524efbd68cd7e4081a1b8362941200a4c9f76a0a9f9ac9b7868c03",
"6f11e3dc4fff99ffa45e36e4655cfc657c29e950e598a90f426bf5710de9171323523db7636643b23892783f4fb3cf8e583d584c82d29558a105a615a668fc9e",
"1865dbdb4c849620fb2c4809d75d62490f83c11f2145abaabbdc9a66ae58ce1f2e42c34d3b380e5dea1b45217750b42d130f995b162afbd2e412b0d41ec8871b",
"5102dd1bd1f08f44dbf3f27ac895020d63f96044ce3b491aed3efbc7bbe363bc5d800101d63890f89a532427812c30c9674f37476ba44daf758afa88d4f91063",
"70cab735dad90164cc61f7411396221c4e549f12392c0d77728c89a9754f606c7d961169d4fa88133a1ba954bad616656c86f8fd1335a2f3428fd4dca3a3f5a5",
"430f3e92536ff9a50cbcdf08d8810a59786ca37e31d54293646117a93469f61c6cdd67933128407d77f3235293293ee86dbc759d12dfe470969eba1b4a373bd0",
"46e1d97912ca2cf92e6a9a63667676835d900cdb2fff062136a64d8d60a8e5aa644ccee3558900af8e77d56b013ed5da12d9d0b7de0f56976e040b3d01345c0d",
},
}}
func TestConcat(t *testing.T) {
var (
actual, expect []byte
ps [][]byte
err error
)
for _, tc := range testCasesConcat {
expect, err = hex.DecodeString(tc.Hash)
require.NoError(t, err)
ps = make([][]byte, len(tc.Parts))
for j := 0; j < len(tc.Parts); j++ {
ps[j], err = hex.DecodeString(tc.Parts[j])
require.NoError(t, err)
}
actual, err = Concat(ps)
require.NoError(t, err)
require.Equal(t, expect, actual)
}
}
func TestValidate(t *testing.T) {
var (
h []byte
ps [][]byte
got bool
err error
)
for _, tc := range testCasesConcat {
h, _ = hex.DecodeString(tc.Hash)
require.NoError(t, err)
ps = make([][]byte, len(tc.Parts))
for j := 0; j < len(tc.Parts); j++ {
ps[j], _ = hex.DecodeString(tc.Parts[j])
require.NoError(t, err)
}
got, err = Validate(h, ps)
require.NoError(t, err)
require.True(t, got)
}
}
var testCasesSubtract = []struct {
first, second, result string
}{
{
first: "4275945919296224acd268456be23b8b2df931787a46716477e32cd991e98074029d4f03a0fedc09125ee4640d228d7d40d430659a0b2b70e9cd4d4c5361865a",
second: "277c10e0d7c52fcc0b23ba7dbf2c3dde7dcfc1f7c0cc0d998b2de504b8c1e17c6f65ab1294aea676d4060ed2ca18c1c26fd7cec5012ab69a4ddb5e6555ac8a59",
result: "7f5c9280352a8debea738a74abd4ec787f2c5e556800525692f651087442f9883bb97a2c1bc72d12ba26e3df8dc0f670564292ebc984976a8e353ff69a5fb3cb",
},
{
first: "18e2ce290cc74998ebd0bef76454b52a40428f13bb612e40b5b96187e9cc813248a0ed5f7ec9fb205d55d3f243e2211363f171b19eb8acc7931cf33853a79069",
second: "73a0582fa7d00d62fd09c1cd18589cdb2b126cb58b3a022ae47a8a787dabe35c4388aaf0d8bb343b1e58ee8d267812d115f40a0da611f42458f452e102f60700",
result: "54ccaad1bb15b2989fa31109713bca955ea5d87bbd3113b3008cea167c00052266e9c9fcb73ece98c6c08cccb074ba3d39b5d8685f022fc388e2bf1997c5bd1d",
},
}
func TestSubtract(t *testing.T) {
var (
a, b, c, r []byte
err error
)
for _, tc := range testCasesSubtract {
a, err = hex.DecodeString(tc.first)
require.NoError(t, err)
b, err = hex.DecodeString(tc.second)
require.NoError(t, err)
c, err = hex.DecodeString(tc.result)
require.NoError(t, err)
r, err = SubtractR(c, b)
require.NoError(t, err)
require.Equal(t, a, r)
r, err = SubtractL(c, a)
require.NoError(t, err)
require.Equal(t, b, r)
}
}

176
tz/sl2.go
View file

@ -1,176 +0,0 @@
package tz
import (
"errors"
"git.frostfs.info/TrueCloudLab/tzhash/gf127"
)
type (
GF127 = gf127.GF127
sl2 [2][2]GF127
)
var id = sl2{
{GF127{1, 0}, GF127{0, 0}},
{GF127{0, 0}, GF127{1, 0}},
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (c *sl2) MarshalBinary() (data []byte, err error) {
s := c.Bytes()
return s[:], nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (c *sl2) UnmarshalBinary(data []byte) (err error) {
if len(data) != 64 {
return errors.New("data must be 64-bytes long")
}
if err = c[0][0].UnmarshalBinary(data[:16]); err != nil {
return
}
if err = c[0][1].UnmarshalBinary(data[16:32]); err != nil {
return
}
if err = c[1][0].UnmarshalBinary(data[32:48]); err != nil {
return
}
if err = c[1][1].UnmarshalBinary(data[48:64]); err != nil {
return
}
return
}
func (c *sl2) mulStrassen(a, b *sl2, x *[8]GF127) *sl2 { //nolint:unused
// strassen algorithm
gf127.Add(&a[0][0], &a[1][1], &x[0])
gf127.Add(&b[0][0], &b[1][1], &x[1])
gf127.Mul(&x[0], &x[1], &x[0])
gf127.Add(&a[1][0], &a[1][1], &x[1])
gf127.Mul(&x[1], &b[0][0], &x[1])
gf127.Add(&b[0][1], &b[1][1], &x[2])
gf127.Mul(&x[2], &a[0][0], &x[2])
gf127.Add(&b[1][0], &b[0][0], &x[3])
gf127.Mul(&x[3], &a[1][1], &x[3])
gf127.Add(&a[0][0], &a[0][1], &x[4])
gf127.Mul(&x[4], &b[1][1], &x[4])
gf127.Add(&a[1][0], &a[0][0], &x[5])
gf127.Add(&b[0][0], &b[0][1], &x[6])
gf127.Mul(&x[5], &x[6], &x[5])
gf127.Add(&a[0][1], &a[1][1], &x[6])
gf127.Add(&b[1][0], &b[1][1], &x[7])
gf127.Mul(&x[6], &x[7], &x[6])
gf127.Add(&x[2], &x[4], &c[0][1])
gf127.Add(&x[1], &x[3], &c[1][0])
gf127.Add(&x[4], &x[6], &x[4])
gf127.Add(&x[0], &x[3], &c[0][0])
gf127.Add(&c[0][0], &x[4], &c[0][0])
gf127.Add(&x[0], &x[1], &x[0])
gf127.Add(&x[2], &x[5], &c[1][1])
gf127.Add(&c[1][1], &x[0], &c[1][1])
return c
}
func (c *sl2) MulA() *sl2 {
var a GF127
gf127.Mul10(&c[0][0], &a)
gf127.Mul1(&c[0][0], &c[0][1])
gf127.Add(&a, &c[0][1], &c[0][0])
gf127.Mul10(&c[1][0], &a)
gf127.Mul1(&c[1][0], &c[1][1])
gf127.Add(&a, &c[1][1], &c[1][0])
return c
}
func (c *sl2) MulB() *sl2 {
var a GF127
gf127.Mul1(&c[0][0], &a)
gf127.Mul10(&c[0][0], &c[0][0])
gf127.Add(&c[0][1], &c[0][0], &c[0][0])
gf127.Add(&c[0][0], &a, &c[0][1])
gf127.Mul1(&c[1][0], &a)
gf127.Mul10(&c[1][0], &c[1][0])
gf127.Add(&c[1][1], &c[1][0], &c[1][0])
gf127.Add(&c[1][0], &a, &c[1][1])
return c
}
// Mul returns a * b in GL_2(GF(2^127))
func (c *sl2) Mul(a, b *sl2) *sl2 {
var x [4]GF127
gf127.Mul(&a[0][0], &b[0][0], &x[0])
gf127.Mul(&a[0][0], &b[0][1], &x[1])
gf127.Mul(&a[1][0], &b[0][0], &x[2])
gf127.Mul(&a[1][0], &b[0][1], &x[3])
gf127.Mul(&a[0][1], &b[1][0], &c[0][0])
gf127.Add(&c[0][0], &x[0], &c[0][0])
gf127.Mul(&a[0][1], &b[1][1], &c[0][1])
gf127.Add(&c[0][1], &x[1], &c[0][1])
gf127.Mul(&a[1][1], &b[1][0], &c[1][0])
gf127.Add(&c[1][0], &x[2], &c[1][0])
gf127.Mul(&a[1][1], &b[1][1], &c[1][1])
gf127.Add(&c[1][1], &x[3], &c[1][1])
return c
}
// Inv returns inverse of a in GL_2(GF(2^127))
func Inv(a *sl2) (b *sl2) {
b = new(sl2)
inv(a, b, new([2]GF127))
return
}
func inv(a, b *sl2, t *[2]GF127) {
gf127.Mul(&a[0][0], &a[1][1], &t[0])
gf127.Mul(&a[0][1], &a[1][0], &t[1])
gf127.Add(&t[0], &t[1], &t[0])
gf127.Inv(&t[0], &t[1])
gf127.Mul(&t[1], &a[0][0], &b[1][1])
gf127.Mul(&t[1], &a[0][1], &b[0][1])
gf127.Mul(&t[1], &a[1][0], &b[1][0])
gf127.Mul(&t[1], &a[1][1], &b[0][0])
}
func (c *sl2) String() string {
return c[0][0].String() + c[0][1].String() +
c[1][0].String() + c[1][1].String()
}
func (c *sl2) Bytes() (b [Size]byte) {
t := c[0][0].Bytes()
copy(b[:], t[:])
t = c[0][1].Bytes()
copy(b[16:], t[:])
t = c[1][0].Bytes()
copy(b[32:], t[:])
t = c[1][1].Bytes()
copy(b[48:], t[:])
return
}

View file

@ -1,60 +0,0 @@
package tz
import (
"math/rand"
"testing"
"time"
"git.frostfs.info/TrueCloudLab/tzhash/gf127"
"github.com/stretchr/testify/require"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
func random() (a *sl2) {
a = new(sl2)
a[0][0] = *gf127.Random()
a[0][1] = *gf127.Random()
a[1][0] = *gf127.Random()
// so that result is in SL2
// d = a^-1*(1+b*c)
gf127.Mul(&a[0][1], &a[1][0], &a[1][1])
gf127.Add(&a[1][1], gf127.New(1, 0), &a[1][1])
t := gf127.New(0, 0)
gf127.Inv(&a[0][0], t)
gf127.Mul(t, &a[1][1], &a[1][1])
return
}
func TestSL2_MarshalBinary(t *testing.T) {
var (
a = random()
b = new(sl2)
)
data, err := a.MarshalBinary()
require.NoError(t, err)
err = b.UnmarshalBinary(data)
require.NoError(t, err)
require.Equal(t, a, b)
}
func TestInv(t *testing.T) {
var a, b, c *sl2
c = new(sl2)
for i := 0; i < 5; i++ {
a = random()
b = Inv(a)
c = c.Mul(a, b)
require.Equal(t, id, *c)
}
}