Compare commits

..

50 commits

Author SHA1 Message Date
bd2fc65b5c [#9] Update obsolete URLs
Signed-off-by: Vitaliy Potyarkin <v.potyarkin@yadro.com>
2024-09-11 14:10:28 +03:00
124ec832b1 [#7] .github: Remove directory
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-09 23:00:06 +03:00
5869aeada5 [#7] pre-commit: Add gitlint hook
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-09 23:00:06 +03:00
feab143fe5 [#7] pre-commit: Add golangci-lint hook
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-09 23:00:06 +03:00
d6b4e69399 [#7] pre-commit: Add initial configuration
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-03-09 23:00:06 +03:00
63e884006d Rename package name
Due to source code relocation from GitHub.

Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2023-03-07 13:47:29 +03:00
e5c13d22e1 [TrueCloudLab#6] .github: Fix CODEOWNERS
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-02-28 16:42:49 +03:00
489dcf4d8b [#3] Change license to Apache 2.0
Written approvals from all original authors and contributors were
received.

Signed-off-by: Stanislav Bogatyrev <s.bogatyrev@yadro.com>
2023-02-13 15:16:25 +03:00
3a7bdcc020 [#3] Simplify demo and benchmark
No need to build everything every time and no need to do it in docker.

Signed-off-by: Stanislav Bogatyrev <s.bogatyrev@yadro.com>
2023-02-13 15:16:25 +03:00
a5347ee68e [#3] Simplify Makefile
Signed-off-by: Stanislav Bogatyrev <s.bogatyrev@yadro.com>
2023-02-13 15:16:25 +03:00
9f80f99aed [#3] Update README and Contributing guide
Preparing to start accepting PRs from everybody.

Signed-off-by: Stanislav Bogatyrev <s.bogatyrev@yadro.com>
2023-02-13 15:16:25 +03:00
4d1b95c926 Move from nspcc-dev to TrueCloudLab
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2022-12-12 21:40:06 +03:00
Evgenii Stratonikov
85abb43253 tz: initialize digest in Sum
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-07-06 18:05:21 +03:00
Evgenii Stratonikov
3de3046074 tz: optimize AVX2 implementation
1. Perform masking with 2 instructions instead of 3 (use arithmetic
   shift).
2. Broadcast data byte in one instruction at the start of byte-processing
3. Reorder instructions to reduce the amount of data hazards and resources
   contention.

```
name               old time/op    new time/op    delta
Sum/AVX2_digest-8    1.39ms ± 0%    1.22ms ± 0%  -12.18%  (p=0.000 n=9+7)

name               old speed      new speed      delta
Sum/AVX2_digest-8  71.7MB/s ± 0%  81.7MB/s ± 0%  +13.87%  (p=0.000 n=9+7)
```

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-22 12:25:13 +03:00
Evgenii Stratonikov
defa61ce8f go.mod: bump to go1.16
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-21 12:30:08 +03:00
Evgenii Stratonikov
f4cc7726e9 benchmark: fix shellcheck issues
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-21 12:30:08 +03:00
Evgenii Stratonikov
83ba541725 tz: add comments to public functions
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-21 12:30:08 +03:00
Evgenii Stratonikov
0d764a51b7 *: rename ByteArray to Bytes
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-21 12:30:08 +03:00
Evgenii Stratonikov
0e0d28e82f tz: use build tags for different implemenations
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-21 12:30:08 +03:00
Evgenii Stratonikov
3491e7c5ea Makefile: add target for testing generic implementation
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-21 12:30:08 +03:00
Evgenii Stratonikov
026731b260 gf127: use build tags for different implemenations
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-21 12:30:08 +03:00
Evgenii Stratonikov
0fa6b1314e *: format assembly code with asmfmt
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-03-21 12:30:08 +03:00
Evgenii Stratonikov
1520cde665 tz: fix package comments
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-02-25 14:53:59 +03:00
Evgenii Stratonikov
337819d130 tz: export checksum size
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-02-25 14:53:59 +03:00
Evgenii Stratonikov
0078ce6e1d go.mod: update dependencies
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-24 13:58:13 +03:00
Evgenii Stratonikov
3a90fa7a76 Add more tests
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-17 17:18:36 +03:00
Evgenii Stratonikov
73d978c31e Rewrite AVX2 loop in assembly
Helps to get rid of MOV and generating constants for each iteration.

```
name                     old time/op    new time/op    delta
Sum/AVX2Inline_digest-8    1.57ms ± 2%    1.41ms ± 0%  -10.52%  (p=0.000 n=9+9)

name                     old speed      new speed      delta
Sum/AVX2Inline_digest-8  63.6MB/s ± 1%  71.1MB/s ± 0%  +11.76%  (p=0.000 n=9+9)
```

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-17 17:18:36 +03:00
Evgenii Stratonikov
d7c96f5d2e Fix comments
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-17 17:18:36 +03:00
Evgenii Stratonikov
8dd24d0195 Interleave carry registers for successive bits
8 instructions less per byte.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-17 17:18:36 +03:00
Evgenii Stratonikov
921f8b0579 Optimize AVX implementation
1. Do the same mask trick as with AVX2.
2. Get rid of load, generate constant on the fly.

```
name                    old time/op    new time/op    delta
Sum/AVXInline_digest-8    2.26ms ± 4%    2.17ms ± 5%  -4.05%  (p=0.000 n=19+17)

name                    old speed      new speed      delta
Sum/AVXInline_digest-8  44.3MB/s ± 4%  46.2MB/s ± 5%  +4.25%  (p=0.000 n=19+17)
```

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-17 17:18:36 +03:00
Evgenii Stratonikov
d4cb61e470 Replace two shifts with a single AND
We need to isolate HSB in every quad-word, this can be done with a
simple mask.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-01-17 17:18:36 +03:00
Evgenii Stratonikov
a7201418ab Fix linter issues
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2021-12-29 13:23:05 +03:00
Evgenii Stratonikov
2e922115d8 Replace CircleCI with Github actions
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2021-12-29 13:23:05 +03:00
Evgenii Stratonikov
bbbcf3fa5c Use unaligned move in AVX2 implementation
Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2021-12-29 13:23:05 +03:00
Evgenii Stratonikov
c8a32b25ec Optimize AVX2 implementation
We use 6 instructions only to calculate mask based on single bit value.
Use only 3 now and calculate multiple masks in parallel.

Also `VPSUB*` is faster than VPBROADCAST*,
see https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html .

```
name                     old time/op    new time/op    delta
Sum/AVX2Inline_digest-8    1.83ms ± 0%    1.62ms ± 1%  -11.23%  (p=0.000 n=46+42)

name                     old speed      new speed      delta
Sum/AVX2Inline_digest-8  54.7MB/s ± 0%  61.6MB/s ± 1%  +12.65%  (p=0.000 n=46+42)
```

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2021-12-29 13:23:05 +03:00
Evgenii Stratonikov
a370c525ba Replace all SSE instructions with AVX ones
Also use integer MOV* variant instead of floating-point one.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2021-12-29 13:23:05 +03:00
Evgenii Stratonikov
9b3f45993f gf127: remove branch in pure Go operations
```
name                 old time/op    new time/op    delta
Sum/PureGo_digest-8    16.1ms ± 3%    10.4ms ± 3%  -35.53%  (p=0.000 n=10+10)

name                 old speed      new speed      delta
Sum/PureGo_digest-8  6.22MB/s ± 3%  9.65MB/s ± 3%  +55.12%  (p=0.000 n=10+10)
```

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2021-12-29 11:00:27 +03:00
fyrchik
33bf778066
Merge pull request #20 from nspcc-dev/use-x-sys-cpu-instead-of-self-implemented
Use golang.org/x/sys instead of self-implemented detector
2020-01-16 11:32:58 +03:00
Evgeniy Kulikov
77b7d87549
Use golang.org/x/sys instead of self-implemented detector 2020-01-16 11:30:46 +03:00
Evgeniy Kulikov
d4b45131cd
Update alpine image, fixup for Makefile, fixup for benchmark 2020-01-16 11:30:46 +03:00
Evgeniy Kulikov
9789dcb2b6
Ignore vendor and binary 2020-01-16 11:30:45 +03:00
fyrchik
3d96a71c03
Merge pull request #19 from nspcc-dev/feat/avx_inline
Speed up AVX implementation
2019-10-17 17:53:41 +03:00
Evgenii Stratonikov
a8357fda0e Change default AVX implementation 2019-10-16 15:11:57 +03:00
Evgenii Stratonikov
5f74bbc979 Update benchmark result in README.md
Also simplify test's and benchmark's names.
2019-10-16 15:11:57 +03:00
Evgenii Stratonikov
4b7f39cd1d Move mulBitRightx2 to avx2 assembly file 2019-10-16 15:11:57 +03:00
Evgenii Stratonikov
3191f1b3fd Add AVX implementation with inlined multiplication
Perform multiplication by-byte instead of by-bit as
in AVX2Inline implementation.
2019-10-16 15:11:53 +03:00
fyrchik
702d2553ba
Merge pull request #18 from nspcc-dev/feat/interface
Restructure code layout in gf127/
2019-10-15 14:19:13 +03:00
Evgenii Stratonikov
63834fe8c1 Remove non-AVX parts from avx package
Remove Inv(), Mul1(), And() because right now
they have no AVX optimizations.
2019-10-15 13:22:36 +03:00
Evgenii Stratonikov
0f8b498b58 Alias gf127.GF127 2019-10-15 13:22:36 +03:00
Evgenii Stratonikov
d891a9c591 Restructure code layout
Provide default implementations in gf127 package and
all optimizations in subpackages. This way it will be easier
to use from a client.
2019-10-15 13:22:31 +03:00
48 changed files with 1378 additions and 2122 deletions

View file

@ -1,12 +0,0 @@
# Golang CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-go/ for more details
version: 2
jobs:
build:
docker:
- image: circleci/golang:1.11
working_directory: ~/{{ORG_NAME}}/{{REPO_NAME}}
steps:
- checkout
- run: go test -v ./...

1
.github/CODEOWNERS vendored Normal file
View file

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

35
.gitignore vendored
View file

@ -1,12 +1,27 @@
# Binaries for programs and plugins # IDE
*.exe .idea
*.exe~ .vscode
*.dll
*.so
*.dylib
# Test binary, build with `go test -c` # Vendoring
*.test vendor
# Output of the go coverage tool, specifically when used with LiteIDE # tempfiles
*.out .DS_Store
*~
.cache
temp
tmp
# binary
bin/
release/
# coverage
coverage.txt
coverage.html
# testing
cmd/test
/plugins/
testfile

10
.gitlint Normal file
View file

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

30
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,30 @@
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]

155
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,155 @@
# 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,4 +1,4 @@
FROM golang:1.11-alpine3.8 as builder FROM golang:1-alpine as builder
RUN set -x \ RUN set -x \
&& apk add --no-cache \ && apk add --no-cache \
@ -28,7 +28,7 @@ RUN set -x \
&& go build -mod=vendor -o /go/bin/homo ./cmd/homo/main.go && go build -mod=vendor -o /go/bin/homo ./cmd/homo/main.go
# Executable image # Executable image
FROM alpine:3.8 FROM alpine:3.11
WORKDIR /fixtures WORKDIR /fixtures

859
LICENSE
View file

@ -1,674 +1,201 @@
GNU GENERAL PUBLIC LICENSE Apache License
Version 3, 29 June 2007 Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
of this license document, but changing it is not allowed.
1. Definitions.
Preamble
"License" shall mean the terms and conditions for use, reproduction,
The GNU General Public License is a free, copyleft license for and distribution as defined by Sections 1 through 9 of this document.
software and other kinds of works.
"Licensor" shall mean the copyright owner or entity authorized by
The licenses for most software and other practical works are designed the copyright owner that is granting the License.
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to "Legal Entity" shall mean the union of the acting entity and all
share and change all versions of a program--to make sure it remains free other entities that control, are controlled by, or are under common
software for all its users. We, the Free Software Foundation, use the control with that entity. For the purposes of this definition,
GNU General Public License for most of our software; it applies also to "control" means (i) the power, direct or indirect, to cause the
any other work released this way by its authors. You can apply it to direction or management of such entity, whether by contract or
your programs, too. otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you "You" (or "Your") shall mean an individual or Legal Entity
have the freedom to distribute copies of free software (and charge for exercising permissions granted by this License.
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new "Source" form shall mean the preferred form for making modifications,
free programs, and that you know you can do these things. including but not limited to software source code, documentation
source, and configuration files.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have "Object" form shall mean any form resulting from mechanical
certain responsibilities if you distribute copies of the software, or if transformation or translation of a Source form, including but
you modify it: responsibilities to respect the freedom of others. not limited to compiled object code, generated documentation,
and conversions to other media types.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same "Work" shall mean the work of authorship, whether in Source or
freedoms that you received. You must make sure that they, too, receive Object form, made available under the License, as indicated by a
or can get the source code. And you must show them these terms so they copyright notice that is included in or attached to the work
know their rights. (an example is provided in the Appendix below).
Developers that use the GNU GPL protect your rights with two steps: "Derivative Works" shall mean any work, whether in Source or Object
(1) assert copyright on the software, and (2) offer you this License form, that is based on (or derived from) the Work and for which the
giving you legal permission to copy, distribute and/or modify it. editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
For the developers' and authors' protection, the GPL clearly explains of this License, Derivative Works shall not include works that remain
that there is no warranty for this free software. For both users' and separable from, or merely link (or bind by name) to the interfaces of,
authors' sake, the GPL requires that modified versions be marked as the Work and Derivative Works thereof.
changed, so that their problems will not be attributed erroneously to
authors of previous versions. "Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
Some devices are designed to deny users access to install or run to that Work or Derivative Works thereof, that is intentionally
modified versions of the software inside them, although the manufacturer submitted to Licensor for inclusion in the Work by the copyright owner
can do so. This is fundamentally incompatible with the aim of or by an individual or Legal Entity authorized to submit on behalf of
protecting users' freedom to change the software. The systematic the copyright owner. For the purposes of this definition, "submitted"
pattern of such abuse occurs in the area of products for individuals to means any form of electronic, verbal, or written communication sent
use, which is precisely where it is most unacceptable. Therefore, we to the Licensor or its representatives, including but not limited to
have designed this version of the GPL to prohibit the practice for those communication on electronic mailing lists, source code control systems,
products. If such problems arise substantially in other domains, we and issue tracking systems that are managed by, or on behalf of, the
stand ready to extend this provision to those domains in future versions Licensor for the purpose of discussing and improving the Work, but
of the GPL, as needed to protect the freedom of users. excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of "Contributor" shall mean Licensor and any individual or Legal Entity
software on general-purpose computers, but in those that do, we wish to on behalf of whom a Contribution has been received by Licensor and
avoid the special danger that patents applied to a free program could subsequently incorporated within the Work.
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free. 2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
The precise terms and conditions for copying, distribution and worldwide, non-exclusive, no-charge, royalty-free, irrevocable
modification follow. copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
TERMS AND CONDITIONS Work and such Derivative Works in Source or Object form.
0. Definitions. 3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
"This License" refers to version 3 of the GNU General Public License. worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
"Copyright" also means copyright-like laws that apply to other kinds of use, offer to sell, sell, import, and otherwise transfer the Work,
works, such as semiconductor masks. where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
"The Program" refers to any copyrightable work licensed under this Contribution(s) alone or by combination of their Contribution(s)
License. Each licensee is addressed as "you". "Licensees" and with the Work to which such Contribution(s) was submitted. If You
"recipients" may be individuals or organizations. institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
To "modify" a work means to copy from or adapt all or part of the work or a Contribution incorporated within the Work constitutes direct
in a fashion requiring copyright permission, other than the making of an or contributory patent infringement, then any patent licenses
exact copy. The resulting work is called a "modified version" of the granted to You under this License for that Work shall terminate
earlier work or a work "based on" the earlier work. as of the date such litigation is filed.
A "covered work" means either the unmodified Program or a work based 4. Redistribution. You may reproduce and distribute copies of the
on the Program. Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
To "propagate" a work means to do anything with it that, without meet the following conditions:
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a (a) You must give any other recipients of the Work or
computer or modifying a private copy. Propagation includes copying, Derivative Works a copy of this License; and
distribution (with or without modification), making available to the
public, and in some countries other activities as well. (b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through (c) You must retain, in the Source form of any Derivative Works
a computer network, with no transfer of a copy, is not conveying. that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
An interactive user interface displays "Appropriate Legal Notices" excluding those notices that do not pertain to any part of
to the extent that it includes a convenient and prominently visible the Derivative Works; and
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the (d) If the Work includes a "NOTICE" text file as part of its
extent that warranties are provided), that licensees may convey the distribution, then any Derivative Works that You distribute must
work under this License, and how to view a copy of this License. If include a readable copy of the attribution notices contained
the interface presents a list of user commands or options, such as a within such NOTICE file, excluding those notices that do not
menu, a prominent item in the list meets this criterion. pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
1. Source Code. as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
The "source code" for a work means the preferred form of the work within a display generated by the Derivative Works, if and
for making modifications to it. "Object code" means any non-source wherever such third-party notices normally appear. The contents
form of a work. of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
A "Standard Interface" means an interface that either is an official notices within Derivative Works that You distribute, alongside
standard defined by a recognized standards body, or, in the case of or as an addendum to the NOTICE text from the Work, provided
interfaces specified for a particular programming language, one that that such additional attribution notices cannot be construed
is widely used among developers working in that language. as modifying the License.
The "System Libraries" of an executable work include anything, other You may add Your own copyright statement to Your modifications and
than the work as a whole, that (a) is included in the normal form of may provide additional or different license terms and conditions
packaging a Major Component, but which is not part of that Major for use, reproduction, or distribution of Your modifications, or
Component, and (b) serves only to enable use of the work with that for any such Derivative Works as a whole, provided Your use,
Major Component, or to implement a Standard Interface for which an reproduction, and distribution of the Work otherwise complies with
implementation is available to the public in source code form. A the conditions stated in this License.
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system 5. Submission of Contributions. Unless You explicitly state otherwise,
(if any) on which the executable work runs, or a compiler used to any Contribution intentionally submitted for inclusion in the Work
produce the work, or an object code interpreter used to run it. by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
The "Corresponding Source" for a work in object code form means all Notwithstanding the above, nothing herein shall supersede or modify
the source code needed to generate, install, and (for an executable the terms of any separate license agreement you may have executed
work) run the object code and to modify the work, including scripts to with Licensor regarding such Contributions.
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free 6. Trademarks. This License does not grant permission to use the trade
programs which are used unmodified in performing those activities but names, trademarks, service marks, or product names of the Licensor,
which are not part of the work. For example, Corresponding Source except as required for reasonable and customary use in describing the
includes interface definition files associated with source files for origin of the Work and reproducing the content of the NOTICE file.
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require, 7. Disclaimer of Warranty. Unless required by applicable law or
such as by intimate data communication or control flow between those agreed to in writing, Licensor provides the Work (and each
subprograms and other parts of the work. Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
The Corresponding Source need not include anything that users implied, including, without limitation, any warranties or conditions
can regenerate automatically from other parts of the Corresponding of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
Source. PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
The Corresponding Source for a work in source code form is that risks associated with Your exercise of permissions under this License.
same work.
8. Limitation of Liability. In no event and under no legal theory,
2. Basic Permissions. whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
All rights granted under this License are granted for the term of negligent acts) or agreed to in writing, shall any Contributor be
copyright on the Program, and are irrevocable provided the stated liable to You for damages, including any direct, indirect, special,
conditions are met. This License explicitly affirms your unlimited incidental, or consequential damages of any character arising as a
permission to run the unmodified Program. The output from running a result of this License or out of the use or inability to use the
covered work is covered by this License only if the output, given its Work (including but not limited to damages for loss of goodwill,
content, constitutes a covered work. This License acknowledges your work stoppage, computer failure or malfunction, or any and all
rights of fair use or other equivalent, as provided by copyright law. other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains 9. Accepting Warranty or Additional Liability. While redistributing
in force. You may convey covered works to others for the sole purpose the Work or Derivative Works thereof, You may choose to offer,
of having them make modifications exclusively for you, or provide you and charge a fee for, acceptance of support, warranty, indemnity,
with facilities for running those works, provided that you comply with or other liability obligations and/or rights consistent with this
the terms of this License in conveying all material for which you do License. However, in accepting such obligations, You may act only
not control copyright. Those thus making or running the covered works on Your own behalf and on Your sole responsibility, not on behalf
for you must do so exclusively on your behalf, under your direction of any other Contributor, and only if You agree to indemnify,
and control, on terms that prohibit them from making any copies of defend, and hold each Contributor harmless for any liability
your copyrighted material outside their relationship with you. incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs APPENDIX: How to apply the Apache License to your work.
If you develop a new program, and you want it to be of the greatest To apply the Apache License to your work, attach the following
possible use to the public, the best way to achieve this is to make it boilerplate notice, with the fields enclosed by brackets "[]"
free software which everyone can redistribute and change under these terms. 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.
To do so, attach the following notices to the program. It is safest Copyright [yyyy] [name of copyright owner]
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> Licensed under the Apache License, Version 2.0 (the "License");
Copyright (C) <year> <name of author> you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is free software: you can redistribute it and/or modify http://www.apache.org/licenses/LICENSE-2.0
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, Unless required by applicable law or agreed to in writing, software
but WITHOUT ANY WARRANTY; without even the implied warranty of distributed under the License is distributed on an "AS IS" BASIS,
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
GNU General Public License for more details. See the License for the specific language governing permissions and
limitations under the License.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

91
Makefile Normal file → Executable file
View file

@ -1,42 +1,61 @@
B=\033[0;1m #!/usr/bin/make -f
G=\033[0;92m SHELL = bash
R=\033[0m
NAME ?= homo 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")
.PHONY: help attach auto up down BIN = bin
# Show this help prompt DIRS = $(BIN)
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
# Auto Tillich-Zémor hasher demo # List of binaries to build.
auto: down CMDS = $(notdir $(basename $(wildcard cmd/*)))
@echo "\n${B}${G}build container${R}\n" BINS = $(addprefix $(BIN)/, $(CMDS))
@time docker build -t poc-demo .
@echo "\n${B}${G}Bootup container:${R}\n"
@time docker run -d --rm -it --name hash-demo poc-demo:latest sh
@bash ./auto.sh
# Stop demo container .PHONY: all help clean
down:
@echo "\n${B}${G}Stop container${R}\n"
@docker stop hash-demo || true
@docker rm hash-demo || true
# Run Tillich-Zémor hasher demo # To build a specific binary, use it's name prefix with bin/ as a target
up: down # For example `make bin/tzsum` will build only storage node binary
@echo "\n${B}${G}build container${R}\n" # Just `make` will build all possible binaries
@time docker build -t poc-demo . all: $(DIRS) $(BINS)
@echo "\n${B}${G}enter inside container:${R}\n"
@time docker run --rm -it --name hash-demo poc-demo:latest sh
# Attach to existing container # help target
attach: include help.mk
@echo "\n${B}${G} attach to hash-container ${R}\n"
@time docker exec -it --name hash-demo /bin/sh $(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

@ -5,60 +5,74 @@
**In project root:** **In project root:**
```bash ```bash
# show help $ make
make ...
# run auto demo $ ./demo.sh
make auto
``` ```
# Homomorphic hashing in golang # Homomorphic hashing in golang
Package `tz` containts pure-Go implementation of hashing function described by Tillich and Źemor in [1] . 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 already (e.g. [2]), however they are written in C. 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. Package `gf127` contains arithmetic in `GF(2^127)` with `x^127+x^63+1` as reduction polynomial.
# Description # Description
It can be used instead of Merkle-tree for data-validation, because homomorphic hashes TZ Hash can be used instead of Merkle-tree for data-validation, because
are concatenable: hash sum of data can be calculated based on hashes of chunks. 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. The example of how it works can be seen in tests and demo.
# Benchmarks # Benchmarks
## AVX vs AVX2 version ## go vs AVX vs AVX2 version
``` ```
BenchmarkAVX-8 500 3492019 ns/op 28.64 MB/s 64 B/op 4 allocs/op BenchmarkSum/AVX_digest-8 308 3889484 ns/op 25.71 MB/s 5 allocs/op
BenchmarkAVX2-8 500 2752693 ns/op 36.33 MB/s 64 B/op 2 allocs/op BenchmarkSum/AVXInline_digest-8 457 2455437 ns/op 40.73 MB/s 5 allocs/op
BenchmarkAVX2Inline-8 1000 1877260 ns/op 53.27 MB/s 64 B/op 2 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
``` ```
# Contributing
At this moment, we do not accept contributions. Follow us.
# Makefile # Makefile
``` ``` bash
→ make
Usage: Usage:
make <target> make <target>
Targets: Targets:
attach Attach to existing container all Just `make` will build all possible binaries
auto Auto Tillich-Zémor hasher demo clean Print version
down Stop demo container dep Pull go dependencies
help Show this help prompt help Show this help prompt
up Run Tillich-Zémor hasher demo test Run Unit Test with go test
version Print version
``` ```
# Links # Contributing
[1] https://link.springer.com/content/pdf/10.1007/3-540-48658-5_5.pdf Feel free to contribute to this project after reading the [contributing
guidelines](CONTRIBUTING.md).
[2] https://github.com/srijs/hwsl2-core 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)

35
auto.sh
View file

@ -1,35 +0,0 @@
#!/usr/bin/env bash
B="\033[0;1m"
G="\033[0;92m"
R="\033[0m"
echo -e "${B}${G}Let's make some hash${R}"
echo -e "\n${B}${G} - cleanup environment${R}"
echo -e "remove files: small.hash, large.hash, large.txt"
docker exec -it hash-demo sh -c "rm -rf small.hash"
docker exec -it hash-demo sh -c "rm -rf large.hash"
docker exec -it hash-demo sh -c "rm -rf large.txt"
echo -e "\n${B}${G} - make large file (concat small files)${R}"
for i in $(seq -f "%02g" 10)
do
echo " #> cat $i.txt >> large.txt"
docker exec -it hash-demo sh -c "cat $i.txt >> large.txt"
done
echo -e "\n${B}${G} - make hash of small files${R}"
for i in $(seq -f "%02g" 10)
do
echo -e " #> homo -file $i.txt | tee -a small.hash"
docker exec -it hash-demo sh -c "homo -file $i.txt | tee -a small.hash"
done
echo -e "\n${B}${G} - make hash of large${R}"
echo -e " #> homo -file large.txt | homo -concat"
docker exec -it hash-demo sh -c 'homo -file large.txt | homo -concat'
echo -e "\n${B}${G} - make hash of pieces${R}"
echo -e " #> cat small.hash | homo -concat"
docker exec -it hash-demo sh -c 'cat small.hash | homo -concat '

View file

@ -1,13 +0,0 @@
#!/bin/bash
#tmpfile=$(mktemp /tmp/random-file.XXXXXX)
#size=$1
#dd if=/dev/urandom of=$tmpfile bs=$size count=1
tmpfile=$1
go build ./cmd/tzsum && \
for impl in avx avx2 avx2inline purego; do
echo $impl implementation:
/usr/bin/env time -f "time: %e seconds" ./tzsum -name $tmpfile -impl $impl
echo
done

12
benchmark.sh Executable file
View file

@ -0,0 +1,12 @@
#!/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

@ -7,7 +7,7 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/nspcc-dev/tzhash/tz" "git.frostfs.info/TrueCloudLab/tzhash/tz"
) )
var ( var (

View file

@ -10,7 +10,8 @@ import (
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
"github.com/nspcc-dev/tzhash/tz" "git.frostfs.info/TrueCloudLab/tzhash/tz"
"golang.org/x/sys/cpu"
) )
var ( var (
@ -46,18 +47,23 @@ func main() {
f = os.Stdin f = os.Stdin
} }
// Override CPU feature flags to make sure a proper backend is used.
var h hash.Hash var h hash.Hash
switch *hashimpl { switch *hashimpl {
case "avx": case "avx":
h = tz.NewWith(tz.AVX) cpu.X86.HasAVX = true
case "avx2": cpu.X86.HasAVX2 = false
h = tz.NewWith(tz.AVX2)
case "avx2inline":
h = tz.NewWith(tz.AVX2Inline)
case "purego":
h = tz.NewWith(tz.PureGo)
default:
h = tz.New() 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 { if _, err := io.Copy(h, f); err != nil {

38
demo.sh Executable file
View file

@ -0,0 +1,38 @@
#!/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!"

7
gf127/doc.go Normal file
View file

@ -0,0 +1,7 @@
// 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,10 +1,3 @@
// Copyright 2018 (c) NSPCC
//
// 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 package gf127
import ( import (
@ -19,17 +12,13 @@ import (
type GF127 [2]uint64 type GF127 [2]uint64
const ( const (
msb64 = uint64(0x8000000000000000)
byteSize = 16 byteSize = 16
maxUint64 = ^uint64(0)
msb64 = uint64(1) << 63
) )
var ( // x127x631 is reduction polynomial x^127 + x^63 + 1
// x127x63 represents x^127 + x^63. Used in assembly file. var x127x631 = GF127{msb64 + 1, msb64}
x127x63 = GF127{msb64, msb64}
// x126x631 is reduction polynomial x^127+x^63+1
x127x631 = GF127{msb64 + 1, msb64}
)
// New constructs new element of GF(2^127) as hi*x^64 + lo. // New constructs new element of GF(2^127) as hi*x^64 + lo.
// It is assumed that hi has zero MSB. // It is assumed that hi has zero MSB.
@ -37,48 +26,47 @@ func New(lo, hi uint64) *GF127 {
return &GF127{lo, hi} return &GF127{lo, hi}
} }
// Random returns random element from GF(2^127). func addGeneric(a, b, c *GF127) {
// Is used mostly for testing. c[0] = a[0] ^ b[0]
func Random() *GF127 { c[1] = a[1] ^ b[1]
return &GF127{rand.Uint64(), rand.Uint64() >> 1}
} }
// String returns hex-encoded representation, starting with MSB. func mulGeneric(a, b, c *GF127) {
func (c *GF127) String() string { r := new(GF127)
return hex.EncodeToString(c.ByteArray()) 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
} }
// Equals checks if two reduced (zero MSB) elements of GF(2^127) are equal func mul10Generic(a, b *GF127) {
func (c *GF127) Equals(b *GF127) bool { c := a[0] >> 63
return c[0] == b[0] && c[1] == b[1] b[0] = a[0] << 1
b[1] = (a[1] << 1) ^ c
mask := b[1] & msb64
b[0] ^= mask | (mask >> 63)
b[1] ^= mask
} }
// ByteArray represents element of GF(2^127) as byte array of length 16. func mul11Generic(a, b *GF127) {
func (c *GF127) ByteArray() (buf []byte) { c := a[0] >> 63
buf = make([]byte, 16) b[0] = a[0] ^ (a[0] << 1)
binary.BigEndian.PutUint64(buf[:8], c[1]) b[1] = a[1] ^ (a[1] << 1) ^ c
binary.BigEndian.PutUint64(buf[8:], c[0])
return
}
// MarshalBinary implements encoding.BinaryMarshaler. mask := b[1] & msb64
func (c *GF127) MarshalBinary() (data []byte, err error) { b[0] ^= mask | (mask >> 63)
return c.ByteArray(), nil b[1] ^= mask
}
// 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
} }
// Inv sets b to a^-1 // Inv sets b to a^-1
@ -107,7 +95,7 @@ func Inv(a, b *GF127) {
Mul(x, &v, t) Mul(x, &v, t)
Add(&u, t, &u) Add(&u, t, &u)
// becasuse mul performs reduction on t, we need // becasuse mulAVX performs reduction on t, we need
// manually reduce u at first step // manually reduce u at first step
if msb(&u) == 127 { if msb(&u) == 127 {
Add(&u, &x127x631, &u) Add(&u, &x127x631, &u)
@ -134,22 +122,10 @@ func msb(a *GF127) (x int) {
return 127 - x return 127 - x
} }
// Mul sets c to the product a*b and returns c. // Mul1 copies b into a.
func (c *GF127) Mul(a, b *GF127) *GF127 {
Mul(a, b, c)
return c
}
// Add sets c to the sum a+b and returns c.
func (c *GF127) Add(a, b *GF127) *GF127 {
Add(a, b, c)
return c
}
// Mul1 copies a to b.
func Mul1(a, b *GF127) { func Mul1(a, b *GF127) {
b[0] = a[0] a[0] = b[0]
b[1] = a[1] a[1] = b[1]
} }
// And sets c to a & b (bitwise-and). // And sets c to a & b (bitwise-and).
@ -158,14 +134,48 @@ func And(a, b, c *GF127) {
c[1] = a[1] & b[1] c[1] = a[1] & b[1]
} }
// Add sets c to a+b. // Random returns random element from GF(2^127).
func Add(a, b, c *GF127) // Is used mostly for testing.
func Random() *GF127 {
return &GF127{rand.Uint64(), rand.Uint64() >> 1}
}
// Mul sets c to a*b. // String returns hex-encoded representation, starting with MSB.
func Mul(a, b, c *GF127) func (c *GF127) String() string {
buf := c.Bytes()
return hex.EncodeToString(buf[:])
}
// Mul10 sets b to a*x. // Equals checks if two reduced (zero MSB) elements of GF(2^127) are equal
func Mul10(a, b *GF127) func (c *GF127) Equals(b *GF127) bool {
return c[0] == b[0] && c[1] == b[1]
}
// Mul11 sets b to a*(x+1). // Bytes represents element of GF(2^127) as byte array of length 16.
func Mul11(a, b *GF127) 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
}

55
gf127/gf127_amd64.go Normal file
View file

@ -0,0 +1,55 @@
//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,7 +1,7 @@
#include "textflag.h" #include "textflag.h"
// func Add(a, b, c *[2]uint64) // func Add(a, b, c *[2]uint64)
TEXT ·Add(SB),NOSPLIT,$0 TEXT ·addAVX(SB), NOSPLIT, $0
MOVQ a+0(FP), AX MOVQ a+0(FP), AX
MOVUPD (AX), X0 MOVUPD (AX), X0
MOVQ b+8(FP), BX MOVQ b+8(FP), BX
@ -12,7 +12,7 @@ TEXT ·Add(SB),NOSPLIT,$0
RET RET
// func Mul10(a, b *[2]uint64) // func Mul10(a, b *[2]uint64)
TEXT ·Mul10(SB),NOSPLIT,$0 TEXT ·mul10AVX(SB), NOSPLIT, $0
MOVQ a+0(FP), AX MOVQ a+0(FP), AX
MOVUPD (AX), X0 MOVUPD (AX), X0
VPSLLQ $1, X0, X1 VPSLLQ $1, X0, X1
@ -27,24 +27,8 @@ TEXT ·Mul10(SB),NOSPLIT,$0
MOVUPD X1, (AX) MOVUPD X1, (AX)
RET RET
// func Mul10x2(a, b) *[4]uint64
TEXT ·Mul10x2(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 Mul11(a, b *[2]uint64) // func Mul11(a, b *[2]uint64)
TEXT ·Mul11(SB),NOSPLIT,$0 TEXT ·mul11AVX(SB), NOSPLIT, $0
MOVQ a+0(FP), AX MOVQ a+0(FP), AX
MOVUPD (AX), X0 MOVUPD (AX), X0
VPSLLQ $1, X0, X1 VPSLLQ $1, X0, X1
@ -60,25 +44,8 @@ TEXT ·Mul11(SB),NOSPLIT,$0
MOVUPD X1, (AX) MOVUPD X1, (AX)
RET RET
// func Mul11x2(a, b) *[4]uint64
TEXT ·Mul11x2(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
// func Mul(a, b, c *[2]uint64) // func Mul(a, b, c *[2]uint64)
TEXT ·Mul(SB),NOSPLIT,$0 TEXT ·mulAVX(SB), NOSPLIT, $0
MOVQ a+0(FP), AX // X0 = a0 . a1 MOVQ a+0(FP), AX // X0 = a0 . a1
MOVUPD (AX), X0 // X0 = a0 . a1 MOVUPD (AX), X0 // X0 = a0 . a1
MOVQ b+8(FP), BX // X1 = b0 . b1 MOVQ b+8(FP), BX // X1 = b0 . b1
@ -89,11 +56,11 @@ TEXT ·Mul(SB),NOSPLIT,$0
PCLMULQDQ $0x10, X3, X3 // X3 = (a0 + a1) * (b0 + b1) PCLMULQDQ $0x10, X3, X3 // X3 = (a0 + a1) * (b0 + b1)
VPCLMULQDQ $0x00, X0, X1, X4 // X4 = a0 * b0 VPCLMULQDQ $0x00, X0, X1, X4 // X4 = a0 * b0
VPCLMULQDQ $0x11, X0, X1, X5 // X5 = a1 * b1 VPCLMULQDQ $0x11, X0, X1, X5 // X5 = a1 * b1
XORPD X4, X3 // XORPD X4, X3
XORPD X5, X3 // X3 = a0 * b1 + a1 * b0 XORPD X5, X3 // X3 = a0 * b1 + a1 * b0
VPSLLDQ $8, X3, X2 // VPSLLDQ $8, X3, X2
XORPD X2, X4 // X4 = a0 * b0 + lo(X3) XORPD X2, X4 // X4 = a0 * b0 + lo(X3)
VPSRLDQ $8, X3, X6 // VPSRLDQ $8, X3, X6
XORPD X6, X5 // X5 = a1 * b1 + hi(X3) XORPD X6, X5 // X5 = a1 * b1 + hi(X3)
// at this point, a * b = X4 . X5 (as 256-bit number) // at this point, a * b = X4 . X5 (as 256-bit number)

24
gf127/gf127_generic.go Normal file
View file

@ -0,0 +1,24 @@
//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

@ -6,8 +6,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
const maxUint64 = ^uint64(0)
func TestAdd(t *testing.T) { func TestAdd(t *testing.T) {
var ( var (
a = Random() a = Random()
@ -15,7 +13,7 @@ func TestAdd(t *testing.T) {
e = &GF127{a[0] ^ b[0], a[1] ^ b[1]} e = &GF127{a[0] ^ b[0], a[1] ^ b[1]}
c = new(GF127) c = new(GF127)
) )
c.Add(a, b) Add(a, b, c)
require.Equal(t, e, c) require.Equal(t, e, c)
} }
@ -38,6 +36,21 @@ func TestMul(t *testing.T) {
} }
} }
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{ var testCasesMul10 = [][2]*GF127{
{&GF127{123, 0}, &GF127{246, 0}}, {&GF127{123, 0}, &GF127{246, 0}},
{&GF127{maxUint64, 2}, &GF127{maxUint64 - 1, 5}}, {&GF127{maxUint64, 2}, &GF127{maxUint64 - 1, 5}},

View file

@ -8,6 +8,16 @@ import (
// GF127x2 represents a pair of elements of GF(2^127) stored together. // GF127x2 represents a pair of elements of GF(2^127) stored together.
type GF127x2 [2]GF127 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. // Split returns 2 components of pair without additional allocations.
func Split(a *GF127x2) (*GF127, *GF127) { func Split(a *GF127x2) (*GF127, *GF127) {
return &a[0], &a[1] return &a[0], &a[1]
@ -27,22 +37,15 @@ func (a *GF127x2) Equal(b *GF127x2) bool {
// String returns hex-encoded representation, starting with MSB. // String returns hex-encoded representation, starting with MSB.
// Elements of pair are separated by comma. // Elements of pair are separated by comma.
func (a *GF127x2) String() string { func (a *GF127x2) String() string {
b := a.ByteArray() b := a.Bytes()
return hex.EncodeToString(b[:16]) + " , " + hex.EncodeToString(b[16:]) return hex.EncodeToString(b[:16]) + " , " + hex.EncodeToString(b[16:])
} }
// ByteArray represents element of GF(2^127) as byte array of length 32. // Bytes represents element of GF(2^127) as byte array of length 32.
func (a *GF127x2) ByteArray() (buf []byte) { func (a *GF127x2) Bytes() (buf [32]byte) {
buf = make([]byte, 32) binary.BigEndian.PutUint64(buf[:], a[0][1])
binary.BigEndian.PutUint64(buf, a[0][1])
binary.BigEndian.PutUint64(buf[8:], a[0][0]) binary.BigEndian.PutUint64(buf[8:], a[0][0])
binary.BigEndian.PutUint64(buf[16:], a[1][1]) binary.BigEndian.PutUint64(buf[16:], a[1][1])
binary.BigEndian.PutUint64(buf[24:], a[1][0]) binary.BigEndian.PutUint64(buf[24:], a[1][0])
return return
} }
// Mul10x2 sets (b1, b2) to (a1*x, a2*x)
func Mul10x2(a, b *GF127x2)
// Mul10x2 sets (b1, b2) to (a1*(x+1), a2*(x+1))
func Mul11x2(a, b *GF127x2)

27
gf127/gf127x2_amd64.go Normal file
View file

@ -0,0 +1,27 @@
//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)

34
gf127/gf127x2_amd64.s Normal file
View file

@ -0,0 +1,34 @@
#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

14
gf127/gf127x2_generic.go Normal file
View file

@ -0,0 +1,14 @@
//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)
}

11
go.mod
View file

@ -1,5 +1,10 @@
module github.com/nspcc-dev/tzhash module git.frostfs.info/TrueCloudLab/tzhash
go 1.12 go 1.16
require github.com/stretchr/testify v1.3.0 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,8 +1,15 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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=

View file

@ -1,125 +0,0 @@
package gogf127
import (
"testing"
"github.com/stretchr/testify/require"
)
const maxUint64 = ^uint64(0)
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,201 +0,0 @@
// Copyright 2019 (c) NSPCC
//
// Package gf127 implements the GF(2^127) arithmetic
// modulo reduction polynomial x^127 + x^63 + 1 .
// Implementation is in pure Go.
package gogf127
import (
"encoding/binary"
"encoding/hex"
"errors"
"math/bits"
"math/rand"
)
// GF127 represents element of GF(2^127)
type GF127 [2]uint64
const (
msb64 = uint64(0x8000000000000000)
byteSize = 16
)
var (
// x126x631 is reduction polynomial x^127+x^63+1
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}
}
// 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 {
return hex.EncodeToString(c.ByteArray())
}
// 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]
}
// ByteArray represents element of GF(2^127) as byte array of length 16.
func (c *GF127) ByteArray() (buf []byte) {
buf = make([]byte, 16)
binary.BigEndian.PutUint64(buf[:8], c[1])
binary.BigEndian.PutUint64(buf[8:], c[0])
return
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (c *GF127) MarshalBinary() (data []byte, err error) {
return c.ByteArray(), 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
}
// 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 mul 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
}
// Mul sets c to the product a*b and returns c.
func (c *GF127) Mul(a, b *GF127) *GF127 {
Mul(a, b, c)
return c
}
// Add sets c to the sum a+b and returns c.
func (c *GF127) Add(a, b *GF127) *GF127 {
Add(a, b, c)
return c
}
// Mul1 copies a to b.
func Mul1(a, b *GF127) {
b[0] = a[0]
b[1] = a[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]
}
// Add sets c to a+b.
func Add(a, b, c *GF127) {
c[0] = a[0] ^ b[0]
c[1] = a[1] ^ b[1]
}
// Mul sets c to a*b.
func Mul(a, b, c *GF127) {
r := new(GF127)
d := *a
for i := uint(0); i < 64; i++ {
if b[0]&(1<<i) != 0 {
Add(r, &d, r)
}
Mul10(&d, &d)
}
for i := uint(0); i < 63; i++ {
if b[1]&(1<<i) != 0 {
Add(r, &d, r)
}
Mul10(&d, &d)
}
*c = *r
}
// Mul10 sets b to a*x.
func Mul10(a, b *GF127) {
c := (a[0] & msb64) >> 63
b[0] = a[0] << 1
b[1] = (a[1] << 1) ^ c
if b[1]&msb64 != 0 {
b[0] ^= x127x631[0]
b[1] ^= x127x631[1]
}
}
// Mul11 sets b to a*(x+1).
func Mul11(a, b *GF127) {
c := (a[0] & msb64) >> 63
b[0] = a[0] ^ (a[0] << 1)
b[1] = a[1] ^ (a[1] << 1) ^ c
if b[1]&msb64 != 0 {
b[0] ^= x127x631[0]
b[1] ^= x127x631[1]
}
}

11
help.mk Normal file
View file

@ -0,0 +1,11 @@
.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,77 +0,0 @@
// Copyright (c) 2009 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// 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
// OWNER 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.
// +build 386 amd64 amd64p32
package cpuid
const (
bitOSXSAVE = 1 << 27
bitAVX = 1 << 28
bitAVX2 = 1 << 5
)
var (
hasAVX bool
hasAVX2 bool
)
func init() {
maxID, _, _, _ := cpuid(0, 0)
if maxID < 1 {
return
}
_, _, ecx1, _ := cpuid(1, 0)
hasOSXSAVE := isSet(ecx1, bitOSXSAVE)
osSupportsAVX := false
if hasOSXSAVE {
eax, _ := xgetbv()
osSupportsAVX = isSet(eax, 1<<1) && isSet(eax, 1<<2)
}
hasAVX = isSet(ecx1, bitAVX) && osSupportsAVX
if maxID < 7 {
return
}
_, ebx7, _, _ := cpuid(7, 0)
hasAVX2 = isSet(ebx7, bitAVX2) && osSupportsAVX
}
func HasAVX() bool { return hasAVX }
func HasAVX2() bool { return hasAVX2 }
func isSet(hwc uint32, value uint32) bool {
return hwc&value != 0
}
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
func xgetbv() (eax, edx uint32)

View file

@ -1,48 +0,0 @@
// Copyright (c) 2009 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * 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.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// 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
// OWNER 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.
#include "textflag.h"
// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
TEXT ·cpuid(SB), NOSPLIT, $0-24
MOVL eaxArg+0(FP), AX
MOVL ecxArg+4(FP), CX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func xgetbv() (eax, edx uint32)
TEXT ·xgetbv(SB),NOSPLIT,$0-8
MOVL $0, CX
XGETBV
MOVL AX, eax+0(FP)
MOVL DX, edx+4(FP)
RET

View file

@ -1,83 +0,0 @@
// Copyright 2018 (c) NSPCC
//
// This file contains AVX implementation.
package tz
import (
"hash"
"math"
"github.com/nspcc-dev/tzhash/gf127"
)
type digest struct {
x [4]gf127.GF127
}
// type assertion
var _ hash.Hash = (*digest)(nil)
var (
minmax = [2]gf127.GF127{{0, 0}, {math.MaxUint64, math.MaxUint64}}
x127x63 = gf127.GF127{1 << 63, 1 << 63}
)
func newAVX() *digest {
d := new(digest)
d.Reset()
return d
}
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() [hashSize]byte {
return d.byteArray()
}
func (d *digest) byteArray() (b [hashSize]byte) {
copy(b[:], d.x[0].ByteArray())
copy(b[16:], d.x[1].ByteArray())
copy(b[32:], d.x[2].ByteArray())
copy(b[48:], d.x[3].ByteArray())
return
}
func (d *digest) Reset() {
d.x[0] = gf127.GF127{1, 0}
d.x[1] = gf127.GF127{0, 0}
d.x[2] = gf127.GF127{0, 0}
d.x[3] = gf127.GF127{1, 0}
}
func (d *digest) Write(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)
// mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>7)&1])
// mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>6)&1])
// mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>5)&1])
// mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>4)&1])
// mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>3)&1])
// mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>2)&1])
// mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>1)&1])
// mulBitRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], &minmax[(b>>0)&1])
}
return
}
func (d *digest) Size() int {
return hashSize
}
func (d *digest) BlockSize() int {
return hashBlockSize
}
func mulBitRight(c00, c01, c10, c11, e *gf127.GF127)
func mulByteRight(c00, c01, c10, c11 *gf127.GF127, b byte)

View file

@ -1,65 +0,0 @@
// Copyright 2019 (c) NSPCC
//
// This file contains AVX2 implementation.
package tz
import (
"hash"
"github.com/nspcc-dev/tzhash/gf127"
)
type digest2 struct {
x [2]gf127.GF127x2
}
// type assertion
var _ hash.Hash = (*digest2)(nil)
func newAVX2() *digest2 {
d := new(digest2)
d.Reset()
return d
}
func (d *digest2) Write(data []byte) (n int, err error) {
n = len(data)
for _, b := range data {
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>7)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>6)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>5)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>4)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>3)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>2)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>1)&1])
mulBitRightx2(&d.x[0], &d.x[1], &minmax[(b>>0)&1])
}
return
}
func (d *digest2) 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 *digest2) Reset() {
d.x[0] = gf127.GF127x2{gf127.GF127{1, 0}, gf127.GF127{0, 0}}
d.x[1] = gf127.GF127x2{gf127.GF127{0, 0}, gf127.GF127{1, 0}}
}
func (d *digest2) Size() int { return hashSize }
func (d *digest2) BlockSize() int { return hashBlockSize }
func (d *digest2) checkSum() (b [hashSize]byte) {
// Matrix is stored transposed,
// but we need to use order consistent with digest.
h := d.x[0].ByteArray()
copy(b[:], h[:16])
copy(b[32:], h[16:])
h = d.x[1].ByteArray()
copy(b[16:], h[:16])
copy(b[48:], h[16:])
return
}
func mulBitRightx2(c00c10 *gf127.GF127x2, c01c11 *gf127.GF127x2, e *gf127.GF127)

View file

@ -1,59 +0,0 @@
// Copyright 2019 (c) NSPCC
//
// This file contains AVX2 implementation with inlined
// assembly calls.
package tz
import (
"hash"
"github.com/nspcc-dev/tzhash/gf127"
)
type digest3 struct {
x [2]gf127.GF127x2
}
// type assertion
var _ hash.Hash = (*digest3)(nil)
func newAVX2Inline() *digest3 {
d := new(digest3)
d.Reset()
return d
}
func (d *digest3) Write(data []byte) (n int, err error) {
n = len(data)
for _, b := range data {
mulByteRightx2(&d.x[0], &d.x[1], b)
}
return
}
func (d *digest3) 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 *digest3) Reset() {
d.x[0] = gf127.GF127x2{gf127.GF127{1, 0}, gf127.GF127{0, 0}}
d.x[1] = gf127.GF127x2{gf127.GF127{0, 0}, gf127.GF127{1, 0}}
}
func (d *digest3) Size() int { return hashSize }
func (d *digest3) BlockSize() int { return hashBlockSize }
func (d *digest3) checkSum() (b [hashSize]byte) {
// Matrix is stored transposed,
// but we need to use order consistent with digest.
h := d.x[0].ByteArray()
copy(b[:], h[:16])
copy(b[32:], h[16:])
h = d.x[1].ByteArray()
copy(b[16:], h[:16])
copy(b[48:], h[16:])
return
}
func mulByteRightx2(c00c10 *gf127.GF127x2, c01c11 *gf127.GF127x2, b byte)

View file

@ -1,46 +0,0 @@
#include "textflag.h"
#define mask(bit, src, tmp, to1, to2) \
MOVQ src, tmp \
SHRQ bit, tmp \
ANDQ $1, tmp \
NEGQ tmp \
MOVQ tmp, to1 \
VPBROADCASTB to1, to2
#define mulBit(bit) \
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 \
mask(bit, CX, DX, X1, Y2) \
VPXOR Y3, Y8, Y3 \
VPAND Y3, Y2, Y4 \
VPXOR Y4, Y0, Y8 \
VMOVDQA Y3, Y0
// func mulByteRightx2(c00c10, c01c11 *[4]uint64, b byte)
TEXT ·mulByteRightx2(SB),NOSPLIT,$0
MOVQ c00c10+0(FP), AX
VMOVDQA (AX), Y0
MOVQ c01c11+8(FP), BX
VMOVDQA (BX), Y8
MOVB b+16(FP), CX
mulBit($7)
mulBit($6)
mulBit($5)
mulBit($4)
mulBit($3)
mulBit($2)
mulBit($1)
mulBit($0)
VMOVDQA Y8, (BX)
VMOVDQA Y0, (AX)
RET

View file

@ -1,127 +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 \
PSRLQ $63, R2 \
MOVUPD ·x127x63(SB), R3 \
ANDPD TO, R3 \
VPUNPCKHQDQ R3, R3, R3 \
XORPD R2, TO \
XORPD R3, TO
#define mask(bit, src, tmp, to1, to2) \
MOVQ src, tmp \
SHRQ bit, tmp \
ANDQ $1, tmp \
NEGQ tmp \
MOVQ tmp, to1 \
VPBROADCASTB to1, to2
// FIXME VPBROADCASTB is AVX2 instruction
//https://software.intel.com/en-us/forums/intel-isa-extensions/topic/301461
#define mulBit(bit) \
MOVUPD X0, X8 \
MOVUPD X2, X9 \
mul2(X0, X5, X6, X7) \
VXORPD X1, X5, X0 \
mul2(X2, X5, X6, X7) \
VXORPD X3, X5, X2 \
mask(bit, CX, DX, X6, X5) \
VANDPD X0, X5, X1 \
XORPD X8, X1 \
VANDPD X2, X5, X3 \
XORPD X9, X3
// func mulBitRight(c00, c01, c10, c11 *[2]uint64, byte)
TEXT ·mulByteRight(SB),NOSPLIT,$0
MOVQ c00+0(FP), AX
MOVUPD (AX), X0
MOVQ c01+8(FP), BX
MOVUPD (BX), X1
MOVQ c10+16(FP), CX
MOVUPD (CX), X2
MOVQ c11+24(FP), DX
MOVUPD (DX), X3
MOVB e+32(FP), CX
mulBit($7)
mulBit($6)
mulBit($5)
mulBit($4)
mulBit($3)
mulBit($2)
mulBit($1)
mulBit($0)
MOVUPD X0, (AX)
MOVQ c10+16(FP), CX
MOVUPD X2, (CX)
MOVUPD X1, (BX)
MOVQ c11+24(FP), DX
MOVUPD X3, (DX)
RET
// func mulBitRight(c00, c01, c10, c11, e *[2]uint64)
TEXT ·mulBitRight(SB),NOSPLIT,$0
MOVQ c00+0(FP), AX
MOVUPD (AX), X0
MOVUPD X0, X8 // remember c00 value
MOVQ c01+8(FP), BX
MOVUPD (BX), X1
MOVQ c10+16(FP), CX
MOVUPD (CX), X2
MOVUPD X2, X9 // remember c10 value
MOVQ c11+24(FP), DX
MOVUPD (DX), X3
mul2(X0, X5, X6, X7)
VXORPD X1, X5, X0
mul2(X2, X5, X6, X7)
VXORPD X3, X5, X2
MOVQ e+32(FP), CX
MOVUPD (CX), X5
VANDPD X0, X5, X1
XORPD X8, X1
VANDPD X2, X5, X3
XORPD X9, X3
MOVUPD X0, (AX)
MOVQ c10+16(FP), CX
MOVUPD X2, (CX)
MOVUPD X1, (BX)
MOVUPD X3, (DX)
RET
// func mulBitRightx2(c00c10, c01c11 *[4]uint64, e *[2]uint64)
TEXT ·mulBitRightx2(SB),NOSPLIT,$0
MOVQ c00c10+0(FP), AX
VMOVDQA (AX), Y0
MOVQ c01c11+8(FP), BX
VMOVDQA (BX), Y8
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 e+16(FP), CX
VBROADCASTI128 (CX), Y2
VPXOR Y3, Y8, Y3
VPAND Y3, Y2, Y4
VPXOR Y4, Y0, Y8
VMOVDQA Y8, (BX)
VMOVDQA Y3, (AX)
RET

124
tz/digest.go Normal file
View file

@ -0,0 +1,124 @@
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
}
}

56
tz/digest_avx2_amd64.s Normal file
View file

@ -0,0 +1,56 @@
#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

70
tz/digest_avx_amd64.s Normal file
View file

@ -0,0 +1,70 @@
#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

8
tz/digest_generic.go Normal file
View file

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

39
tz/digets_amd64.go Normal file
View file

@ -0,0 +1,39 @@
//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,93 +1,13 @@
// Copyright 2018 (c) NSPCC // Package tz contains Tillich-Zemor checksum implementations
// using different backends.
// //
// Package tz implements general Tillich-Zemo // Copyright 2022 (c) NSPCC
package tz package tz
import ( import (
"errors" "errors"
"hash"
"github.com/nspcc-dev/tzhash/internal/cpuid"
) )
type Implementation int
const (
hashSize = 64
hashBlockSize = 128
_ Implementation = iota
AVX
AVX2
AVX2Inline
PureGo
)
var (
hasAVX = cpuid.HasAVX()
// Having AVX2 does not guarantee
// that AVX is also present.
hasAVX2 = cpuid.HasAVX2() && hasAVX
)
func (impl Implementation) String() string {
switch impl {
case AVX:
return "AVX"
case AVX2:
return "AVX2"
case AVX2Inline:
return "AVX2Inline"
case PureGo:
return "PureGo"
default:
return "UNKNOWN"
}
}
func NewWith(impl Implementation) hash.Hash {
switch impl {
case AVX:
return newAVX()
case AVX2:
return newAVX2()
case AVX2Inline:
return newAVX2Inline()
case PureGo:
return newPure()
default:
return New()
}
}
// New returns a new hash.Hash computing the Tillich-Zémor checksum.
func New() hash.Hash {
if hasAVX2 {
return newAVX2Inline()
} else if hasAVX {
return newAVX()
} else {
return newPure()
}
}
// Sum returns Tillich-Zémor checksum of data.
func Sum(data []byte) [hashSize]byte {
if hasAVX2 {
d := newAVX2Inline()
_, _ = d.Write(data) // no errors
return d.checkSum()
} else if hasAVX {
d := newAVX()
_, _ = d.Write(data) // no errors
return d.checkSum()
} else {
d := newPure()
_, _ = d.Write(data) // no errors
return d.checkSum()
}
}
// Concat performs combining of hashes based on homomorphic property. // Concat performs combining of hashes based on homomorphic property.
func Concat(hs [][]byte) ([]byte, error) { func Concat(hs [][]byte) ([]byte, error) {
var b, c sl2 var b, c sl2
@ -106,11 +26,11 @@ func Concat(hs [][]byte) ([]byte, error) {
func Validate(h []byte, hs [][]byte) (bool, error) { func Validate(h []byte, hs [][]byte) (bool, error) {
var ( var (
b []byte b []byte
got, expected [hashSize]byte got, expected [Size]byte
err error err error
) )
if len(h) != hashSize { if len(h) != Size {
return false, errors.New("invalid hash") return false, errors.New("invalid hash")
} else if len(hs) == 0 { } else if len(hs) == 0 {
return false, errors.New("empty slice") return false, errors.New("empty slice")

View file

@ -2,34 +2,29 @@ package tz
import ( import (
"encoding/hex" "encoding/hex"
"fmt"
"io" "io"
"math/rand" "math/rand"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/sys/cpu"
) )
const benchDataSize = 100000 const benchDataSize = 100000
var providers = []Implementation{ type arch struct {
AVX, HasAVX bool
AVX2, HasAVX2 bool
AVX2Inline,
PureGo,
} }
func TestNewWith(t *testing.T) { var backends = []struct {
d := NewWith(AVX) Name string
require.IsType(t, (*digest)(nil), d) arch
}{
d = NewWith(AVX2) {"AVX", arch{true, false}},
require.IsType(t, (*digest2)(nil), d) {"AVX2", arch{true, true}},
{"Generic", arch{false, false}},
d = NewWith(AVX2Inline)
require.IsType(t, (*digest3)(nil), d)
d = NewWith(PureGo)
require.IsType(t, (*digestp)(nil), d)
} }
var testCases = []struct { var testCases = []struct {
@ -40,6 +35,34 @@ var testCases = []struct {
[]byte{}, []byte{},
"00000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "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}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8},
"00000000000001e4a545e5b90fb6882b00000000000000c849cd88f79307f67100000000000000cd0c898cb68356e624000000000000007cbcdc7c5e89b16e4b", "00000000000001e4a545e5b90fb6882b00000000000000c849cd88f79307f67100000000000000cd0c898cb68356e624000000000000007cbcdc7c5e89b16e4b",
@ -51,10 +74,12 @@ var testCases = []struct {
} }
func TestHash(t *testing.T) { func TestHash(t *testing.T) {
for i := range providers { for i, b := range backends {
p := providers[i] t.Run(b.Name+" digest", func(t *testing.T) {
t.Run("test "+p.String()+" digest", func(t *testing.T) { prepareArch(t, backends[i].arch)
d := NewWith(p)
fmt.Println("FEATURES:", cpu.X86.HasAVX, cpu.X86.HasAVX2)
d := New()
for _, tc := range testCases { for _, tc := range testCases {
d.Reset() d.Reset()
_, _ = d.Write(tc.input) _, _ = d.Write(tc.input)
@ -65,6 +90,20 @@ func TestHash(t *testing.T) {
} }
} }
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) { func newBuffer() (data []byte) {
data = make([]byte, benchDataSize) data = make([]byte, benchDataSize)
@ -78,20 +117,20 @@ func newBuffer() (data []byte) {
func BenchmarkSum(b *testing.B) { func BenchmarkSum(b *testing.B) {
data := newBuffer() data := newBuffer()
size := int64(len(data))
for i := range providers { for i := range backends {
p := providers[i] b.Run(backends[i].Name+" digest", func(b *testing.B) {
b.Run("bench"+p.String()+"digest", func(b *testing.B) { prepareArch(b, backends[i].arch)
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
d := NewWith(p) d := New()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
d.Reset() d.Reset()
_, _ = d.Write(data) _, _ = d.Write(data)
d.Sum(nil) d.Sum(nil)
} }
b.SetBytes(size) b.SetBytes(int64(len(data)))
}) })
} }
} }
@ -101,7 +140,7 @@ func TestHomomorphism(t *testing.T) {
c1, c2 sl2 c1, c2 sl2
n int n int
err error err error
h, h1, h2 [hashSize]byte h, h1, h2 [Size]byte
b []byte b []byte
) )
@ -112,6 +151,7 @@ func TestHomomorphism(t *testing.T) {
// Test if our hashing is really homomorphic // Test if our hashing is really homomorphic
h = Sum(b) h = Sum(b)
require.NotEqual(t, [64]byte{}, h)
h1 = Sum(b[:32]) h1 = Sum(b[:32])
h2 = Sum(b[32:]) h2 = Sum(b[32:])
@ -121,7 +161,7 @@ func TestHomomorphism(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
c1.Mul(&c1, &c2) c1.Mul(&c1, &c2)
require.Equal(t, h, c1.ByteArray()) require.Equal(t, h, c1.Bytes())
} }
var testCasesConcat = []struct { var testCasesConcat = []struct {

View file

@ -1,92 +0,0 @@
package tz
import (
"github.com/nspcc-dev/tzhash/gogf127"
)
type digestp struct {
x [4]gogf127.GF127
}
// New returns a new hash.Hash computing the Tillich-Zémor checksum.
func newPure() *digestp {
d := new(digestp)
d.Reset()
return d
}
func (d *digestp) 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 *digestp) checkSum() [hashSize]byte {
return d.byteArray()
}
func (d *digestp) byteArray() (b [hashSize]byte) {
for i := 0; i < 4; i++ {
t := d.x[i].ByteArray()
copy(b[i*16:], t[:])
}
return
}
func (d *digestp) Reset() {
d.x[0] = gogf127.GF127{1, 0}
d.x[1] = gogf127.GF127{0, 0}
d.x[2] = gogf127.GF127{0, 0}
d.x[3] = gogf127.GF127{1, 0}
}
func (d *digestp) Write(data []byte) (n int, err error) {
n = len(data)
tmp := new(gogf127.GF127)
for _, b := range data {
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x80 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x40 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x20 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x10 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x08 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x04 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x02 != 0, tmp)
mulBitRightPure(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x01 != 0, tmp)
}
return
}
func (d *digestp) Size() int {
return hashSize
}
func (d *digestp) BlockSize() int {
return hashBlockSize
}
func mulBitRightPure(c00, c01, c10, c11 *gogf127.GF127, bit bool, tmp *gogf127.GF127) {
if bit {
*tmp = *c00
gogf127.Mul10(c00, c00)
gogf127.Add(c00, c01, c00)
gogf127.Mul11(tmp, tmp)
gogf127.Add(c01, tmp, c01)
*tmp = *c10
gogf127.Mul10(c10, c10)
gogf127.Add(c10, c11, c10)
gogf127.Mul11(tmp, tmp)
gogf127.Add(c11, tmp, c11)
} else {
*tmp = *c00
gogf127.Mul10(c00, c00)
gogf127.Add(c00, c01, c00)
*c01 = *tmp
*tmp = *c10
gogf127.Mul10(c10, c10)
gogf127.Add(c10, c11, c10)
*c11 = *tmp
}
}

104
tz/sl2.go
View file

@ -3,32 +3,27 @@ package tz
import ( import (
"errors" "errors"
"github.com/nspcc-dev/tzhash/gf127" "git.frostfs.info/TrueCloudLab/tzhash/gf127"
"github.com/nspcc-dev/tzhash/gogf127"
) )
type sl2 [2][2]gf127.GF127 type (
GF127 = gf127.GF127
sl2 [2][2]GF127
)
var id = sl2{ var id = sl2{
{gf127.GF127{1, 0}, gf127.GF127{0, 0}}, {GF127{1, 0}, GF127{0, 0}},
{gf127.GF127{0, 0}, gf127.GF127{1, 0}}, {GF127{0, 0}, GF127{1, 0}},
}
var mul func(a, b, c *sl2, x *[4]gf127.GF127)
func init() {
if hasAVX {
mul = mulSL2AVX
} else {
mul = mulSL2Pure
}
} }
// MarshalBinary implements encoding.BinaryMarshaler.
func (c *sl2) MarshalBinary() (data []byte, err error) { func (c *sl2) MarshalBinary() (data []byte, err error) {
s := c.ByteArray() s := c.Bytes()
return s[:], nil return s[:], nil
} }
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (c *sl2) UnmarshalBinary(data []byte) (err error) { func (c *sl2) UnmarshalBinary(data []byte) (err error) {
if len(data) != 64 { if len(data) != 64 {
return errors.New("data must be 64-bytes long") return errors.New("data must be 64-bytes long")
@ -50,7 +45,7 @@ func (c *sl2) UnmarshalBinary(data []byte) (err error) {
return return
} }
func (c *sl2) mulStrassen(a, b *sl2, x *[8]gf127.GF127) *sl2 { func (c *sl2) mulStrassen(a, b *sl2, x *[8]GF127) *sl2 { //nolint:unused
// strassen algorithm // strassen algorithm
gf127.Add(&a[0][0], &a[1][1], &x[0]) gf127.Add(&a[0][0], &a[1][1], &x[0])
gf127.Add(&b[0][0], &b[1][1], &x[1]) gf127.Add(&b[0][0], &b[1][1], &x[1])
@ -90,40 +85,8 @@ func (c *sl2) mulStrassen(a, b *sl2, x *[8]gf127.GF127) *sl2 {
return c return c
} }
func mulSL2AVX(a, b, c *sl2, x *[4]gf127.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])
}
func mulSL2Pure(a, b, c *sl2, x *[4]gf127.GF127) {
gogf127.Mul((*gogf127.GF127)(&a[0][0]), (*gogf127.GF127)(&b[0][0]), (*gogf127.GF127)(&x[0]))
gogf127.Mul((*gogf127.GF127)(&a[0][0]), (*gogf127.GF127)(&b[0][1]), (*gogf127.GF127)(&x[1]))
gogf127.Mul((*gogf127.GF127)(&a[1][0]), (*gogf127.GF127)(&b[0][0]), (*gogf127.GF127)(&x[2]))
gogf127.Mul((*gogf127.GF127)(&a[1][0]), (*gogf127.GF127)(&b[0][1]), (*gogf127.GF127)(&x[3]))
gogf127.Mul((*gogf127.GF127)(&a[0][1]), (*gogf127.GF127)(&b[1][0]), (*gogf127.GF127)(&c[0][0]))
gogf127.Add((*gogf127.GF127)(&c[0][0]), (*gogf127.GF127)(&x[0]), (*gogf127.GF127)(&c[0][0]))
gogf127.Mul((*gogf127.GF127)(&a[0][1]), (*gogf127.GF127)(&b[1][1]), (*gogf127.GF127)(&c[0][1]))
gogf127.Add((*gogf127.GF127)(&c[0][1]), (*gogf127.GF127)(&x[1]), (*gogf127.GF127)(&c[0][1]))
gogf127.Mul((*gogf127.GF127)(&a[1][1]), (*gogf127.GF127)(&b[1][0]), (*gogf127.GF127)(&c[1][0]))
gogf127.Add((*gogf127.GF127)(&c[1][0]), (*gogf127.GF127)(&x[2]), (*gogf127.GF127)(&c[1][0]))
gogf127.Mul((*gogf127.GF127)(&a[1][1]), (*gogf127.GF127)(&b[1][1]), (*gogf127.GF127)(&c[1][1]))
gogf127.Add((*gogf127.GF127)(&c[1][1]), (*gogf127.GF127)(&x[3]), (*gogf127.GF127)(&c[1][1]))
}
func (c *sl2) MulA() *sl2 { func (c *sl2) MulA() *sl2 {
var a gf127.GF127 var a GF127
gf127.Mul10(&c[0][0], &a) gf127.Mul10(&c[0][0], &a)
gf127.Mul1(&c[0][0], &c[0][1]) gf127.Mul1(&c[0][0], &c[0][1])
@ -137,7 +100,7 @@ func (c *sl2) MulA() *sl2 {
} }
func (c *sl2) MulB() *sl2 { func (c *sl2) MulB() *sl2 {
var a gf127.GF127 var a GF127
gf127.Mul1(&c[0][0], &a) gf127.Mul1(&c[0][0], &a)
gf127.Mul10(&c[0][0], &c[0][0]) gf127.Mul10(&c[0][0], &c[0][0])
@ -152,19 +115,34 @@ func (c *sl2) MulB() *sl2 {
return c return c
} }
// Mul returns a * b in GL_2(GF(2^127))
func (c *sl2) Mul(a, b *sl2) *sl2 { func (c *sl2) Mul(a, b *sl2) *sl2 {
mul(a, b, c, new([4]gf127.GF127)) 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 return c
} }
// Inv returns inverse of a in GL_2(GF(2^127)) // Inv returns inverse of a in GL_2(GF(2^127))
func Inv(a *sl2) (b *sl2) { func Inv(a *sl2) (b *sl2) {
b = new(sl2) b = new(sl2)
inv(a, b, new([2]gf127.GF127)) inv(a, b, new([2]GF127))
return return
} }
func inv(a, b *sl2, t *[2]gf127.GF127) { func inv(a, b *sl2, t *[2]GF127) {
gf127.Mul(&a[0][0], &a[1][1], &t[0]) gf127.Mul(&a[0][0], &a[1][1], &t[0])
gf127.Mul(&a[0][1], &a[1][0], &t[1]) gf127.Mul(&a[0][1], &a[1][0], &t[1])
gf127.Add(&t[0], &t[1], &t[0]) gf127.Add(&t[0], &t[1], &t[0])
@ -181,18 +159,18 @@ func (c *sl2) String() string {
c[1][0].String() + c[1][1].String() c[1][0].String() + c[1][1].String()
} }
func (c *sl2) ByteArray() (b [hashSize]byte) { func (c *sl2) Bytes() (b [Size]byte) {
t := c[0][0].ByteArray() t := c[0][0].Bytes()
copy(b[:], t) copy(b[:], t[:])
t = c[0][1].ByteArray() t = c[0][1].Bytes()
copy(b[16:], t) copy(b[16:], t[:])
t = c[1][0].ByteArray() t = c[1][0].Bytes()
copy(b[32:], t) copy(b[32:], t[:])
t = c[1][1].ByteArray() t = c[1][1].Bytes()
copy(b[48:], t) copy(b[48:], t[:])
return return
} }

View file

@ -5,7 +5,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/nspcc-dev/tzhash/gf127" "git.frostfs.info/TrueCloudLab/tzhash/gf127"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )