├── go.mod ├── go.sum ├── crc64_other.go ├── crc64_arm64.go ├── crc64_amd64.go ├── README.md ├── .github └── workflows │ ├── go.yml │ └── codeql-analysis.yml ├── crc64_arm64.s ├── crc64.go ├── crc64_test.go ├── crc64_amd64.s └── LICENSE /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/minio/crc64nvme 2 | 3 | go 1.22 4 | 5 | require github.com/klauspost/cpuid/v2 v2.2.9 6 | 7 | require golang.org/x/sys v0.28.0 // indirect 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= 2 | github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= 3 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 4 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 5 | -------------------------------------------------------------------------------- /crc64_other.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Minio Inc. All rights reserved. 2 | // Use of this source code is governed by a license that can be 3 | // found in the LICENSE file. 4 | 5 | //go:build (!amd64 || noasm || appengine || gccgo) && (!arm64 || noasm || appengine || gccgo) 6 | 7 | package crc64nvme 8 | 9 | var hasAsm = false 10 | var hasAsm512 = false 11 | 12 | func updateAsm(crc uint64, p []byte) (checksum uint64) { panic("should not be reached") } 13 | func updateAsm512(crc uint64, p []byte) (checksum uint64) { panic("should not be reached") } 14 | -------------------------------------------------------------------------------- /crc64_arm64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Minio Inc. All rights reserved. 2 | // Use of this source code is governed by a license that can be 3 | // found in the LICENSE file. 4 | 5 | //go:build !noasm && !appengine && !gccgo 6 | 7 | package crc64nvme 8 | 9 | import ( 10 | "github.com/klauspost/cpuid/v2" 11 | ) 12 | 13 | var hasAsm = cpuid.CPU.Supports(cpuid.ASIMD, cpuid.PMULL, cpuid.SHA3) 14 | var hasAsm512 = false 15 | 16 | func updateAsm(crc uint64, p []byte) (checksum uint64) 17 | func updateAsm512(crc uint64, p []byte) (checksum uint64) { panic("should not be reached") } 18 | -------------------------------------------------------------------------------- /crc64_amd64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Minio Inc. All rights reserved. 2 | // Use of this source code is governed by a license that can be 3 | // found in the LICENSE file. 4 | 5 | //go:build !noasm && !appengine && !gccgo 6 | 7 | package crc64nvme 8 | 9 | import ( 10 | "github.com/klauspost/cpuid/v2" 11 | ) 12 | 13 | var hasAsm = cpuid.CPU.Supports(cpuid.SSE2, cpuid.CLMUL, cpuid.SSE4) 14 | var hasAsm512 = cpuid.CPU.Supports(cpuid.AVX512F, cpuid.VPCLMULQDQ, cpuid.AVX512VL, cpuid.CLMUL) 15 | 16 | func updateAsm(crc uint64, p []byte) (checksum uint64) 17 | func updateAsm512(crc uint64, p []byte) (checksum uint64) 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## crc64nvme 3 | 4 | This Golang package calculates CRC64 checksums using carryless-multiplication accelerated with SIMD instructions for both ARM and x86. It is based on the NVME polynomial as specified in the [NVM Express® NVM Command Set Specification](https://nvmexpress.org/wp-content/uploads/NVM-Express-NVM-Command-Set-Specification-1.0d-2023.12.28-Ratified.pdf). 5 | 6 | The code is based on the [crc64fast-nvme](https://github.com/awesomized/crc64fast-nvme.git) package in Rust and is released under the Apache 2.0 license. 7 | 8 | For more background on the exact technique used, see this [Fast CRC Computation for Generic Polynomials Using PCLMULQDQ Instruction](https://web.archive.org/web/20131224125630/https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf) paper. 9 | 10 | ### Performance 11 | 12 | To follow. 13 | 14 | ### Requirements 15 | 16 | All Go versions >= 1.22 are supported. 17 | 18 | ### Contributing 19 | 20 | Contributions are welcome, please send PRs for any enhancements. 21 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | matrix: 14 | go-version: [1.22.x, 1.23.x, 1.24.x] 15 | os: [ubuntu-latest, macos-latest, windows-latest] 16 | env: 17 | CGO_ENABLED: 0 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - name: Set up Go 21 | uses: actions/setup-go@v2 22 | with: 23 | go-version: ${{ matrix.go-version }} 24 | 25 | - name: Checkout code 26 | uses: actions/checkout@v2 27 | 28 | - name: Vet 29 | run: go vet ./... 30 | 31 | - name: Test 32 | run: go test 33 | 34 | - name: Test Noasm 35 | run: go test -tags=noasm 36 | 37 | - name: Test Race 38 | env: 39 | CGO_ENABLED: 1 40 | run: go test -cpu="1,4" -short -race -v . 41 | 42 | build-special: 43 | env: 44 | CGO_ENABLED: 0 45 | runs-on: ubuntu-latest 46 | steps: 47 | - name: Set up Go 48 | uses: actions/setup-go@v2 49 | with: 50 | go-version: 1.24.x 51 | 52 | - name: Checkout code 53 | uses: actions/checkout@v2 54 | 55 | - name: fmt 56 | run: diff <(gofmt -d .) <(printf "") 57 | 58 | - name: Test 386 59 | run: GOOS=linux GOARCH=386 go test -short ./... 60 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "main" ] 20 | schedule: 21 | - cron: '36 20 * * 3' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'go' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v2 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | -------------------------------------------------------------------------------- /crc64_arm64.s: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Minio Inc. All rights reserved. 2 | // Use of this source code is governed by a license that can be 3 | // found in the LICENSE file. 4 | 5 | //go:build !noasm && !appengine && !gccgo 6 | 7 | #include "textflag.h" 8 | 9 | TEXT ·updateAsm(SB), $0-40 10 | MOVD crc+0(FP), R0 // checksum 11 | MOVD p_base+8(FP), R1 // start pointer 12 | MOVD p_len+16(FP), R2 // length of buffer 13 | MOVD $·const(SB), R3 // constants 14 | MVN R0, R0 15 | LSR $7, R2, R2 16 | CMP $1, R2 17 | BLT skip128 18 | 19 | FLDPQ (R1), (F0, F1) 20 | FLDPQ 32(R1), (F2, F3) 21 | FLDPQ 64(R1), (F4, F5) 22 | FLDPQ 96(R1), (F6, F7) 23 | FMOVD R0, F8 24 | VMOVI $0, V9.B16 25 | VMOV V9.D[0], V8.D[1] 26 | VEOR V8.B16, V0.B16, V0.B16 27 | CMP $1, R2 28 | BEQ tail128 29 | 30 | MOVD 112(R3), R4 31 | MOVD 120(R3), R5 32 | FMOVD R4, F8 33 | VDUP R5, V9.D2 34 | 35 | loop128: 36 | ADD $128, R1, R1 37 | SUB $1, R2, R2 38 | VPMULL V0.D1, V8.D1, V10.Q1 39 | VPMULL2 V0.D2, V9.D2, V0.Q1 40 | FLDPQ (R1), (F11, F12) 41 | VEOR3 V0.B16, V11.B16, V10.B16, V0.B16 42 | VPMULL V1.D1, V8.D1, V10.Q1 43 | VPMULL2 V1.D2, V9.D2, V1.Q1 44 | VEOR3 V1.B16, V12.B16, V10.B16, V1.B16 45 | VPMULL V2.D1, V8.D1, V10.Q1 46 | VPMULL2 V2.D2, V9.D2, V2.Q1 47 | FLDPQ 32(R1), (F11, F12) 48 | VEOR3 V2.B16, V11.B16, V10.B16, V2.B16 49 | VPMULL V3.D1, V8.D1, V10.Q1 50 | VPMULL2 V3.D2, V9.D2, V3.Q1 51 | VEOR3 V3.B16, V12.B16, V10.B16, V3.B16 52 | VPMULL V4.D1, V8.D1, V10.Q1 53 | VPMULL2 V4.D2, V9.D2, V4.Q1 54 | FLDPQ 64(R1), (F11, F12) 55 | VEOR3 V4.B16, V11.B16, V10.B16, V4.B16 56 | VPMULL V5.D1, V8.D1, V10.Q1 57 | VPMULL2 V5.D2, V9.D2, V5.Q1 58 | VEOR3 V5.B16, V12.B16, V10.B16, V5.B16 59 | VPMULL V6.D1, V8.D1, V10.Q1 60 | VPMULL2 V6.D2, V9.D2, V6.Q1 61 | FLDPQ 96(R1), (F11, F12) 62 | VEOR3 V6.B16, V11.B16, V10.B16, V6.B16 63 | VPMULL V7.D1, V8.D1, V10.Q1 64 | VPMULL2 V7.D2, V9.D2, V7.Q1 65 | VEOR3 V7.B16, V12.B16, V10.B16, V7.B16 66 | CMP $1, R2 67 | BHI loop128 68 | 69 | tail128: 70 | MOVD (R3), R4 71 | FMOVD R4, F11 72 | VPMULL V0.D1, V11.D1, V11.Q1 73 | MOVD 8(R3), R4 74 | VDUP R4, V12.D2 75 | VPMULL2 V0.D2, V12.D2, V0.Q1 76 | VEOR3 V0.B16, V7.B16, V11.B16, V7.B16 77 | MOVD 16(R3), R4 78 | FMOVD R4, F11 79 | VPMULL V1.D1, V11.D1, V11.Q1 80 | MOVD 24(R3), R4 81 | VDUP R4, V12.D2 82 | VPMULL2 V1.D2, V12.D2, V1.Q1 83 | VEOR3 V1.B16, V11.B16, V7.B16, V1.B16 84 | MOVD 32(R3), R4 85 | FMOVD R4, F11 86 | VPMULL V2.D1, V11.D1, V11.Q1 87 | MOVD 40(R3), R4 88 | VDUP R4, V12.D2 89 | VPMULL2 V2.D2, V12.D2, V2.Q1 90 | VEOR3 V2.B16, V11.B16, V1.B16, V2.B16 91 | MOVD 48(R3), R4 92 | FMOVD R4, F11 93 | VPMULL V3.D1, V11.D1, V11.Q1 94 | MOVD 56(R3), R4 95 | VDUP R4, V12.D2 96 | VPMULL2 V3.D2, V12.D2, V3.Q1 97 | VEOR3 V3.B16, V11.B16, V2.B16, V3.B16 98 | MOVD 64(R3), R4 99 | FMOVD R4, F11 100 | VPMULL V4.D1, V11.D1, V11.Q1 101 | MOVD 72(R3), R4 102 | VDUP R4, V12.D2 103 | VPMULL2 V4.D2, V12.D2, V4.Q1 104 | VEOR3 V4.B16, V11.B16, V3.B16, V4.B16 105 | MOVD 80(R3), R4 106 | FMOVD R4, F11 107 | VPMULL V5.D1, V11.D1, V11.Q1 108 | MOVD 88(R3), R4 109 | VDUP R4, V12.D2 110 | VPMULL2 V5.D2, V12.D2, V5.Q1 111 | VEOR3 V5.B16, V11.B16, V4.B16, V5.B16 112 | MOVD 96(R3), R4 113 | FMOVD R4, F11 114 | VPMULL V6.D1, V11.D1, V11.Q1 115 | MOVD 104(R3), R4 116 | VDUP R4, V12.D2 117 | VPMULL2 V6.D2, V12.D2, V6.Q1 118 | VEOR3 V6.B16, V11.B16, V5.B16, V6.B16 119 | FMOVD R4, F5 120 | VPMULL V6.D1, V5.D1, V5.Q1 121 | VDUP V6.D[1], V6.D2 122 | VEOR V5.B8, V6.B8, V6.B8 123 | MOVD 128(R3), R4 124 | FMOVD R4, F4 125 | VPMULL V4.D1, V6.D1, V6.Q1 126 | FMOVD F6, R4 127 | MOVD 136(R3), R5 128 | FMOVD R5, F4 129 | VPMULL V4.D1, V6.D1, V6.Q1 130 | VEOR V6.B16, V5.B16, V6.B16 131 | VMOV V6.D[1], R5 132 | EOR R4, R5, R0 133 | 134 | skip128: 135 | MVN R0, R0 136 | MOVD R0, checksum+32(FP) 137 | RET 138 | 139 | DATA ·const+0x000(SB)/8, $0xd083dd594d96319d // K_959 140 | DATA ·const+0x008(SB)/8, $0x946588403d4adcbc // K_895 141 | DATA ·const+0x010(SB)/8, $0x3c255f5ebc414423 // K_831 142 | DATA ·const+0x018(SB)/8, $0x34f5a24e22d66e90 // K_767 143 | DATA ·const+0x020(SB)/8, $0x7b0ab10dd0f809fe // K_703 144 | DATA ·const+0x028(SB)/8, $0x03363823e6e791e5 // K_639 145 | DATA ·const+0x030(SB)/8, $0x0c32cdb31e18a84a // K_575 146 | DATA ·const+0x038(SB)/8, $0x62242240ace5045a // K_511 147 | DATA ·const+0x040(SB)/8, $0xbdd7ac0ee1a4a0f0 // K_447 148 | DATA ·const+0x048(SB)/8, $0xa3ffdc1fe8e82a8b // K_383 149 | DATA ·const+0x050(SB)/8, $0xb0bc2e589204f500 // K_319 150 | DATA ·const+0x058(SB)/8, $0xe1e0bb9d45d7a44c // K_255 151 | DATA ·const+0x060(SB)/8, $0xeadc41fd2ba3d420 // K_191 152 | DATA ·const+0x068(SB)/8, $0x21e9761e252621ac // K_127 153 | DATA ·const+0x070(SB)/8, $0xa1ca681e733f9c40 // K_1087 154 | DATA ·const+0x078(SB)/8, $0x5f852fb61e8d92dc // K_1023 155 | DATA ·const+0x080(SB)/8, $0x27ecfa329aef9f77 // MU 156 | DATA ·const+0x088(SB)/8, $0x34d926535897936b // POLY 157 | GLOBL ·const(SB), (NOPTR+RODATA), $144 158 | -------------------------------------------------------------------------------- /crc64.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Minio Inc. All rights reserved. 2 | // Use of this source code is governed by a license that can be 3 | // found in the LICENSE file. 4 | 5 | // Package crc64nvme implements the 64-bit cyclic redundancy check with NVME polynomial. 6 | package crc64nvme 7 | 8 | import ( 9 | "encoding/binary" 10 | "errors" 11 | "hash" 12 | "sync" 13 | "unsafe" 14 | ) 15 | 16 | const ( 17 | // The size of a CRC-64 checksum in bytes. 18 | Size = 8 19 | 20 | // The NVME polynoimial (reversed, as used by Go) 21 | NVME = 0x9a6c9329ac4bc9b5 22 | ) 23 | 24 | var ( 25 | // precalculated table. 26 | nvmeTable = makeTable(NVME) 27 | ) 28 | 29 | // table is a 256-word table representing the polynomial for efficient processing. 30 | type table [256]uint64 31 | 32 | var ( 33 | slicing8TablesBuildOnce sync.Once 34 | slicing8TableNVME *[8]table 35 | ) 36 | 37 | func buildSlicing8TablesOnce() { 38 | slicing8TablesBuildOnce.Do(buildSlicing8Tables) 39 | } 40 | 41 | func buildSlicing8Tables() { 42 | slicing8TableNVME = makeSlicingBy8Table(makeTable(NVME)) 43 | } 44 | 45 | func makeTable(poly uint64) *table { 46 | t := new(table) 47 | for i := 0; i < 256; i++ { 48 | crc := uint64(i) 49 | for j := 0; j < 8; j++ { 50 | if crc&1 == 1 { 51 | crc = (crc >> 1) ^ poly 52 | } else { 53 | crc >>= 1 54 | } 55 | } 56 | t[i] = crc 57 | } 58 | return t 59 | } 60 | 61 | func makeSlicingBy8Table(t *table) *[8]table { 62 | var helperTable [8]table 63 | helperTable[0] = *t 64 | for i := 0; i < 256; i++ { 65 | crc := t[i] 66 | for j := 1; j < 8; j++ { 67 | crc = t[crc&0xff] ^ (crc >> 8) 68 | helperTable[j][i] = crc 69 | } 70 | } 71 | return &helperTable 72 | } 73 | 74 | // digest represents the partial evaluation of a checksum. 75 | type digest struct { 76 | crc uint64 77 | } 78 | 79 | // New creates a new hash.Hash64 computing the CRC-64 checksum using the 80 | // NVME polynomial. Its Sum method will lay the 81 | // value out in big-endian byte order. The returned Hash64 also 82 | // implements [encoding.BinaryMarshaler] and [encoding.BinaryUnmarshaler] to 83 | // marshal and unmarshal the internal state of the hash. 84 | func New() hash.Hash64 { return &digest{0} } 85 | 86 | func (d *digest) Size() int { return Size } 87 | 88 | func (d *digest) BlockSize() int { return 1 } 89 | 90 | func (d *digest) Reset() { d.crc = 0 } 91 | 92 | const ( 93 | magic = "crc\x02" 94 | marshaledSize = len(magic) + 8 + 8 95 | ) 96 | 97 | func (d *digest) MarshalBinary() ([]byte, error) { 98 | b := make([]byte, 0, marshaledSize) 99 | b = append(b, magic...) 100 | b = binary.BigEndian.AppendUint64(b, tableSum) 101 | b = binary.BigEndian.AppendUint64(b, d.crc) 102 | return b, nil 103 | } 104 | 105 | func (d *digest) UnmarshalBinary(b []byte) error { 106 | if len(b) < len(magic) || string(b[:len(magic)]) != magic { 107 | return errors.New("hash/crc64: invalid hash state identifier") 108 | } 109 | if len(b) != marshaledSize { 110 | return errors.New("hash/crc64: invalid hash state size") 111 | } 112 | if tableSum != binary.BigEndian.Uint64(b[4:]) { 113 | return errors.New("hash/crc64: tables do not match") 114 | } 115 | d.crc = binary.BigEndian.Uint64(b[12:]) 116 | return nil 117 | } 118 | 119 | func update(crc uint64, p []byte) uint64 { 120 | if hasAsm && len(p) > 127 { 121 | ptr := unsafe.Pointer(&p[0]) 122 | if align := (uintptr(ptr)+15)&^0xf - uintptr(ptr); align > 0 { 123 | // Align to 16-byte boundary. 124 | crc = update(crc, p[:align]) 125 | p = p[align:] 126 | } 127 | runs := len(p) / 128 128 | if hasAsm512 && runs >= 8 { 129 | // Use 512-bit wide instructions for >= 1KB. 130 | crc = updateAsm512(crc, p[:128*runs]) 131 | } else if runs > 0 { 132 | crc = updateAsm(crc, p[:128*runs]) 133 | } 134 | return update(crc, p[128*runs:]) 135 | } 136 | 137 | buildSlicing8TablesOnce() 138 | crc = ^crc 139 | // table comparison is somewhat expensive, so avoid it for small sizes 140 | if len(p) >= 64 { 141 | var helperTable = slicing8TableNVME 142 | // Update using slicing-by-8 143 | for len(p) > 8 { 144 | crc ^= binary.LittleEndian.Uint64(p) 145 | crc = helperTable[7][crc&0xff] ^ 146 | helperTable[6][(crc>>8)&0xff] ^ 147 | helperTable[5][(crc>>16)&0xff] ^ 148 | helperTable[4][(crc>>24)&0xff] ^ 149 | helperTable[3][(crc>>32)&0xff] ^ 150 | helperTable[2][(crc>>40)&0xff] ^ 151 | helperTable[1][(crc>>48)&0xff] ^ 152 | helperTable[0][crc>>56] 153 | p = p[8:] 154 | } 155 | } 156 | // For reminders or small sizes 157 | for _, v := range p { 158 | crc = nvmeTable[byte(crc)^v] ^ (crc >> 8) 159 | } 160 | return ^crc 161 | } 162 | 163 | // Update returns the result of adding the bytes in p to the crc. 164 | func Update(crc uint64, p []byte) uint64 { 165 | return update(crc, p) 166 | } 167 | 168 | func (d *digest) Write(p []byte) (n int, err error) { 169 | d.crc = update(d.crc, p) 170 | return len(p), nil 171 | } 172 | 173 | func (d *digest) Sum64() uint64 { return d.crc } 174 | 175 | func (d *digest) Sum(in []byte) []byte { 176 | s := d.Sum64() 177 | return append(in, byte(s>>56), byte(s>>48), byte(s>>40), byte(s>>32), byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) 178 | } 179 | 180 | // Checksum returns the CRC-64 checksum of data 181 | // using the NVME polynomial. 182 | func Checksum(data []byte) uint64 { return update(0, data) } 183 | 184 | // ISO tablesum of NVME poly 185 | const tableSum = 0x8ddd9ee4402c7163 186 | -------------------------------------------------------------------------------- /crc64_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Minio Inc. All rights reserved. 2 | // Use of this source code is governed by a license that can be 3 | // found in the LICENSE file. 4 | 5 | package crc64nvme 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "hash" 11 | "hash/crc64" 12 | "io" 13 | "math/rand" 14 | "runtime" 15 | "sync/atomic" 16 | "testing" 17 | ) 18 | 19 | var crc64Table = crc64.MakeTable(NVME) 20 | 21 | func TestChecksum(t *testing.T) { 22 | if hasAsm { 23 | if hasAsm512 { 24 | testChecksum(t, "asm512-") 25 | hasAsm512 = false 26 | defer func() { 27 | hasAsm512 = true 28 | }() 29 | } 30 | testChecksum(t, "asm-") 31 | hasAsm = false 32 | testChecksum(t, "") 33 | hasAsm = true 34 | } else { 35 | testChecksum(t, "") 36 | } 37 | } 38 | 39 | func testChecksum(t *testing.T, asm string) { 40 | sizes := []int{0, 1, 3, 7, 8, 9, 15, 17, 127, 128, 129, 255, 256, 257, 511, 512, 513, 1e3, 1e4, 1e5, 1e6} 41 | for _, size := range sizes { 42 | t.Run(fmt.Sprintf("%ssize=%d", asm, size), func(t *testing.T) { 43 | rng := rand.New(rand.NewSource(int64(size))) 44 | for align := range 16 { 45 | t.Run(fmt.Sprintf("align=%d", align), func(t *testing.T) { 46 | data := make([]byte, size+align)[align:] 47 | rng.Read(data) 48 | ref := crc64.Checksum(data, crc64Table) 49 | got := Checksum(data) 50 | if got != ref { 51 | t.Errorf("got 0x%x, want 0x%x", got, ref) 52 | } 53 | }) 54 | } 55 | }) 56 | } 57 | } 58 | 59 | func TestHasher(t *testing.T) { 60 | if hasAsm { 61 | if hasAsm512 { 62 | testChecksum(t, "asm512-") 63 | hasAsm512 = false 64 | defer func() { 65 | hasAsm512 = true 66 | }() 67 | } 68 | testHasher(t, "asm-") 69 | hasAsm = false 70 | testHasher(t, "") 71 | hasAsm = true 72 | } else { 73 | testHasher(t, "") 74 | } 75 | } 76 | 77 | func testHasher(t *testing.T, asm string) { 78 | sizes := []int{0, 1, 3, 7, 8, 9, 15, 17, 127, 128, 129, 255, 256, 257, 383, 384, 385, 1e3, 1e4, 1e5, 1e6} 79 | for _, size := range sizes { 80 | t.Run(fmt.Sprintf("%ssize=%d", asm, size), func(t *testing.T) { 81 | rng := rand.New(rand.NewSource(int64(size))) 82 | data := make([]byte, size) 83 | rng.Read(data) 84 | ref := crc64.Checksum(data, crc64Table) 85 | h := New() 86 | io.CopyBuffer(h, bytes.NewReader(data), make([]byte, 17)) 87 | got := h.Sum64() 88 | if got != ref { 89 | t.Errorf("got 0x%x, want 0x%x", got, ref) 90 | } 91 | }) 92 | } 93 | } 94 | 95 | func TestLoopAlignment(t *testing.T) { 96 | for l := 128; l <= 128*10; l++ { 97 | dataBlock := make([]byte, l) 98 | for i := range dataBlock { 99 | dataBlock[i] = byte(i + 1) 100 | } 101 | 102 | // make sure we don't start on an aligned boundary 103 | offset := rand.Intn(16) 104 | data := dataBlock[offset:] 105 | 106 | ref := crc64.Checksum(data, crc64Table) 107 | got := update(0, data) 108 | if got != ref { 109 | t.Errorf("got 0x%x, want 0x%x", got, ref) 110 | } 111 | } 112 | } 113 | 114 | func setAsm(asm, asm512 bool) (reset func()) { 115 | oldAsm := hasAsm 116 | oldAsm512 := hasAsm512 117 | hasAsm = asm 118 | hasAsm512 = asm512 119 | return func() { 120 | hasAsm = oldAsm 121 | hasAsm512 = oldAsm512 122 | } 123 | } 124 | 125 | func BenchmarkCrc64(b *testing.B) { 126 | for _, sz := range []int64{64 << 20, 4 << 20, 1 << 20, 64 << 10, 4 << 10, 1 << 10, 128, 10} { 127 | if hasAsm512 { 128 | b.Run(fmt.Sprint(sz, "-asm512"), func(b *testing.B) { 129 | defer setAsm(true, true)() 130 | bench(b, New(), sz) 131 | }) 132 | } 133 | if hasAsm { 134 | b.Run(fmt.Sprint(sz, "-asm"), func(b *testing.B) { 135 | defer setAsm(true, false)() 136 | bench(b, New(), sz) 137 | }) 138 | } 139 | b.Run(fmt.Sprint(sz, "-go"), func(b *testing.B) { 140 | defer setAsm(false, false)() 141 | bench(b, New(), sz) 142 | }) 143 | b.Run(fmt.Sprint(sz, "-stdlib"), func(b *testing.B) { 144 | bench(b, crc64.New(crc64Table), sz) 145 | }) 146 | } 147 | 148 | } 149 | 150 | func bench(b *testing.B, h hash.Hash64, size int64) { 151 | b.SetBytes(size) 152 | data := make([]byte, size) 153 | for i := range data { 154 | data[i] = byte(i) 155 | } 156 | in := make([]byte, 0, h.Size()) 157 | 158 | b.ResetTimer() 159 | for i := 0; i < b.N; i++ { 160 | h.Reset() 161 | h.Write(data) 162 | h.Sum(in) 163 | } 164 | } 165 | 166 | func benchmarkParallel(b *testing.B, size int) { 167 | hashes := make([]hash.Hash64, runtime.GOMAXPROCS(0)) 168 | for i := range hashes { 169 | hashes[i] = New() 170 | } 171 | 172 | rng := rand.New(rand.NewSource(0xabadc0cac01a)) 173 | data := make([][]byte, runtime.GOMAXPROCS(0)) 174 | for i := range data { 175 | data[i] = make([]byte, size) 176 | rng.Read(data[i]) 177 | } 178 | 179 | b.SetBytes(int64(size)) 180 | b.ResetTimer() 181 | 182 | counter := uint64(0) 183 | b.RunParallel(func(pb *testing.PB) { 184 | for pb.Next() { 185 | index := atomic.AddUint64(&counter, 1) 186 | index = index % uint64(len(data)) 187 | hashes[index].Reset() 188 | hashes[index].Write(data[index]) 189 | in := make([]byte, 0, hashes[index].Size()) 190 | hashes[index].Sum(in) 191 | } 192 | }) 193 | } 194 | 195 | func BenchmarkParallel(b *testing.B) { 196 | // go test -cpu=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 -bench=Parallel 197 | b.Run("1KB", func(b *testing.B) { 198 | benchmarkParallel(b, 1*1024) 199 | }) 200 | b.Run("10KB", func(b *testing.B) { 201 | benchmarkParallel(b, 10*1024) 202 | }) 203 | b.Run("1M", func(b *testing.B) { 204 | benchmarkParallel(b, 1*1024*1024) 205 | }) 206 | b.Run("50M", func(b *testing.B) { 207 | benchmarkParallel(b, 50*1024*1024) 208 | }) 209 | } 210 | -------------------------------------------------------------------------------- /crc64_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Minio Inc. All rights reserved. 2 | // Use of this source code is governed by a license that can be 3 | // found in the LICENSE file. 4 | 5 | //go:build !noasm && !appengine && !gccgo 6 | 7 | #include "textflag.h" 8 | 9 | TEXT ·updateAsm(SB), $0-40 10 | MOVQ crc+0(FP), AX // checksum 11 | MOVQ p_base+8(FP), SI // start pointer 12 | MOVQ p_len+16(FP), CX // length of buffer 13 | NOTQ AX 14 | SHRQ $7, CX 15 | CMPQ CX, $1 16 | JLT skip128 17 | 18 | MOVOA 0x00(SI), X0 19 | MOVOA 0x10(SI), X1 20 | MOVOA 0x20(SI), X2 21 | MOVOA 0x30(SI), X3 22 | MOVOA 0x40(SI), X4 23 | MOVOA 0x50(SI), X5 24 | MOVOA 0x60(SI), X6 25 | MOVOA 0x70(SI), X7 26 | MOVQ AX, X8 27 | PXOR X8, X0 28 | CMPQ CX, $1 29 | JE tail128 30 | 31 | MOVQ $0xa1ca681e733f9c40, AX 32 | MOVQ AX, X8 33 | MOVQ $0x5f852fb61e8d92dc, AX 34 | PINSRQ $0x1, AX, X9 35 | 36 | loop128: 37 | ADDQ $128, SI 38 | SUBQ $1, CX 39 | MOVOA X0, X10 40 | PCLMULQDQ $0x00, X8, X10 41 | PCLMULQDQ $0x11, X9, X0 42 | PXOR X10, X0 43 | PXOR 0(SI), X0 44 | MOVOA X1, X10 45 | PCLMULQDQ $0x00, X8, X10 46 | PCLMULQDQ $0x11, X9, X1 47 | PXOR X10, X1 48 | PXOR 0x10(SI), X1 49 | MOVOA X2, X10 50 | PCLMULQDQ $0x00, X8, X10 51 | PCLMULQDQ $0x11, X9, X2 52 | PXOR X10, X2 53 | PXOR 0x20(SI), X2 54 | MOVOA X3, X10 55 | PCLMULQDQ $0x00, X8, X10 56 | PCLMULQDQ $0x11, X9, X3 57 | PXOR X10, X3 58 | PXOR 0x30(SI), X3 59 | MOVOA X4, X10 60 | PCLMULQDQ $0x00, X8, X10 61 | PCLMULQDQ $0x11, X9, X4 62 | PXOR X10, X4 63 | PXOR 0x40(SI), X4 64 | MOVOA X5, X10 65 | PCLMULQDQ $0x00, X8, X10 66 | PCLMULQDQ $0x11, X9, X5 67 | PXOR X10, X5 68 | PXOR 0x50(SI), X5 69 | MOVOA X6, X10 70 | PCLMULQDQ $0x00, X8, X10 71 | PCLMULQDQ $0x11, X9, X6 72 | PXOR X10, X6 73 | PXOR 0x60(SI), X6 74 | MOVOA X7, X10 75 | PCLMULQDQ $0x00, X8, X10 76 | PCLMULQDQ $0x11, X9, X7 77 | PXOR X10, X7 78 | PXOR 0x70(SI), X7 79 | CMPQ CX, $1 80 | JGT loop128 81 | 82 | tail128: 83 | MOVQ $0xd083dd594d96319d, AX 84 | MOVQ AX, X11 85 | PCLMULQDQ $0x00, X0, X11 86 | MOVQ $0x946588403d4adcbc, AX 87 | PINSRQ $0x1, AX, X12 88 | PCLMULQDQ $0x11, X12, X0 89 | PXOR X11, X7 90 | PXOR X0, X7 91 | MOVQ $0x3c255f5ebc414423, AX 92 | MOVQ AX, X11 93 | PCLMULQDQ $0x00, X1, X11 94 | MOVQ $0x34f5a24e22d66e90, AX 95 | PINSRQ $0x1, AX, X12 96 | PCLMULQDQ $0x11, X12, X1 97 | PXOR X11, X1 98 | PXOR X7, X1 99 | MOVQ $0x7b0ab10dd0f809fe, AX 100 | MOVQ AX, X11 101 | PCLMULQDQ $0x00, X2, X11 102 | MOVQ $0x03363823e6e791e5, AX 103 | PINSRQ $0x1, AX, X12 104 | PCLMULQDQ $0x11, X12, X2 105 | PXOR X11, X2 106 | PXOR X1, X2 107 | MOVQ $0x0c32cdb31e18a84a, AX 108 | MOVQ AX, X11 109 | PCLMULQDQ $0x00, X3, X11 110 | MOVQ $0x62242240ace5045a, AX 111 | PINSRQ $0x1, AX, X12 112 | PCLMULQDQ $0x11, X12, X3 113 | PXOR X11, X3 114 | PXOR X2, X3 115 | MOVQ $0xbdd7ac0ee1a4a0f0, AX 116 | MOVQ AX, X11 117 | PCLMULQDQ $0x00, X4, X11 118 | MOVQ $0xa3ffdc1fe8e82a8b, AX 119 | PINSRQ $0x1, AX, X12 120 | PCLMULQDQ $0x11, X12, X4 121 | PXOR X11, X4 122 | PXOR X3, X4 123 | MOVQ $0xb0bc2e589204f500, AX 124 | MOVQ AX, X11 125 | PCLMULQDQ $0x00, X5, X11 126 | MOVQ $0xe1e0bb9d45d7a44c, AX 127 | PINSRQ $0x1, AX, X12 128 | PCLMULQDQ $0x11, X12, X5 129 | PXOR X11, X5 130 | PXOR X4, X5 131 | MOVQ $0xeadc41fd2ba3d420, AX 132 | MOVQ AX, X11 133 | PCLMULQDQ $0x00, X6, X11 134 | MOVQ $0x21e9761e252621ac, AX 135 | PINSRQ $0x1, AX, X12 136 | PCLMULQDQ $0x11, X12, X6 137 | PXOR X11, X6 138 | PXOR X5, X6 139 | MOVQ AX, X5 140 | PCLMULQDQ $0x00, X6, X5 141 | PSHUFD $0xee, X6, X6 142 | PXOR X5, X6 143 | MOVQ $0x27ecfa329aef9f77, AX 144 | MOVQ AX, X4 145 | PCLMULQDQ $0x00, X4, X6 146 | PEXTRQ $0, X6, BX 147 | MOVQ $0x34d926535897936b, AX 148 | MOVQ AX, X4 149 | PCLMULQDQ $0x00, X4, X6 150 | PXOR X5, X6 151 | PEXTRQ $1, X6, AX 152 | XORQ BX, AX 153 | 154 | skip128: 155 | NOTQ AX 156 | MOVQ AX, checksum+32(FP) 157 | RET 158 | 159 | // Constants, pre-splatted. 160 | DATA ·asmConstantsPoly<>+0x00(SB)/8, $0xa1ca681e733f9c40 161 | DATA ·asmConstantsPoly<>+0x08(SB)/8, $0 162 | DATA ·asmConstantsPoly<>+0x10(SB)/8, $0xa1ca681e733f9c40 163 | DATA ·asmConstantsPoly<>+0x18(SB)/8, $0 164 | DATA ·asmConstantsPoly<>+0x20(SB)/8, $0xa1ca681e733f9c40 165 | DATA ·asmConstantsPoly<>+0x28(SB)/8, $0 166 | DATA ·asmConstantsPoly<>+0x30(SB)/8, $0xa1ca681e733f9c40 167 | DATA ·asmConstantsPoly<>+0x38(SB)/8, $0 168 | // Upper 169 | DATA ·asmConstantsPoly<>+0x40(SB)/8, $0 170 | DATA ·asmConstantsPoly<>+0x48(SB)/8, $0x5f852fb61e8d92dc 171 | DATA ·asmConstantsPoly<>+0x50(SB)/8, $0 172 | DATA ·asmConstantsPoly<>+0x58(SB)/8, $0x5f852fb61e8d92dc 173 | DATA ·asmConstantsPoly<>+0x60(SB)/8, $0 174 | DATA ·asmConstantsPoly<>+0x68(SB)/8, $0x5f852fb61e8d92dc 175 | DATA ·asmConstantsPoly<>+0x70(SB)/8, $0 176 | DATA ·asmConstantsPoly<>+0x78(SB)/8, $0x5f852fb61e8d92dc 177 | GLOBL ·asmConstantsPoly<>(SB), (NOPTR+RODATA), $128 178 | 179 | TEXT ·updateAsm512(SB), $0-40 180 | MOVQ crc+0(FP), AX // checksum 181 | MOVQ p_base+8(FP), SI // start pointer 182 | MOVQ p_len+16(FP), CX // length of buffer 183 | NOTQ AX 184 | SHRQ $7, CX 185 | CMPQ CX, $1 186 | VPXORQ Z8, Z8, Z8 // Initialize ZMM8 to zero 187 | JLT skip128 188 | 189 | VMOVDQU64 0x00(SI), Z0 190 | VMOVDQU64 0x40(SI), Z4 191 | MOVQ $·asmConstantsPoly<>(SB), BX 192 | VMOVQ AX, X8 193 | 194 | // XOR initialization value into lower 64 bits of ZMM0 195 | VPXORQ Z8, Z0, Z0 196 | CMPQ CX, $1 197 | JE tail128 198 | 199 | VMOVDQU64 0(BX), Z8 200 | VMOVDQU64 64(BX), Z9 201 | 202 | PCALIGN $16 203 | 204 | loop128: 205 | PREFETCHT0 512(SI) 206 | VMOVDQU64 0x80(SI), Z1 207 | VMOVDQU64 0xc0(SI), Z5 208 | ADDQ $128, SI 209 | 210 | SUBQ $1, CX 211 | VPCLMULQDQ $0x00, Z8, Z0, Z10 212 | VPCLMULQDQ $0x11, Z9, Z0, Z0 213 | VPTERNLOGD $0x96, Z1, Z10, Z0 // Combine results with xor into Z0 214 | 215 | PREFETCHT0 512-64(SI) 216 | VPCLMULQDQ $0x00, Z8, Z4, Z10 217 | VPCLMULQDQ $0x11, Z9, Z4, Z4 218 | VPTERNLOGD $0x96, Z5, Z10, Z4 // Combine results with xor into Z4 219 | 220 | CMPQ CX, $1 221 | JGT loop128 222 | 223 | tail128: 224 | // Extract X0 to X3 from ZMM0 225 | VEXTRACTF32X4 $1, Z0, X1 // X1: Second 128-bit lane 226 | VEXTRACTF32X4 $2, Z0, X2 // X2: Third 128-bit lane 227 | VEXTRACTF32X4 $3, Z0, X3 // X3: Fourth 128-bit lane 228 | 229 | // Extract X4 to X7 from ZMM4 230 | VEXTRACTF32X4 $1, Z4, X5 // X5: Second 128-bit lane 231 | VEXTRACTF32X4 $2, Z4, X6 // X6: Third 128-bit lane 232 | VEXTRACTF32X4 $3, Z4, X7 // X7: Fourth 128-bit lane 233 | 234 | MOVQ $0xd083dd594d96319d, AX 235 | MOVQ AX, X11 236 | PCLMULQDQ $0x00, X0, X11 237 | MOVQ $0x946588403d4adcbc, AX 238 | PINSRQ $0x1, AX, X12 239 | PCLMULQDQ $0x11, X12, X0 240 | PXOR X11, X7 241 | PXOR X0, X7 242 | MOVQ $0x3c255f5ebc414423, AX 243 | MOVQ AX, X11 244 | PCLMULQDQ $0x00, X1, X11 245 | MOVQ $0x34f5a24e22d66e90, AX 246 | PINSRQ $0x1, AX, X12 247 | PCLMULQDQ $0x11, X12, X1 248 | PXOR X11, X1 249 | PXOR X7, X1 250 | MOVQ $0x7b0ab10dd0f809fe, AX 251 | MOVQ AX, X11 252 | PCLMULQDQ $0x00, X2, X11 253 | MOVQ $0x03363823e6e791e5, AX 254 | PINSRQ $0x1, AX, X12 255 | PCLMULQDQ $0x11, X12, X2 256 | PXOR X11, X2 257 | PXOR X1, X2 258 | MOVQ $0x0c32cdb31e18a84a, AX 259 | MOVQ AX, X11 260 | PCLMULQDQ $0x00, X3, X11 261 | MOVQ $0x62242240ace5045a, AX 262 | PINSRQ $0x1, AX, X12 263 | PCLMULQDQ $0x11, X12, X3 264 | PXOR X11, X3 265 | PXOR X2, X3 266 | MOVQ $0xbdd7ac0ee1a4a0f0, AX 267 | MOVQ AX, X11 268 | PCLMULQDQ $0x00, X4, X11 269 | MOVQ $0xa3ffdc1fe8e82a8b, AX 270 | PINSRQ $0x1, AX, X12 271 | PCLMULQDQ $0x11, X12, X4 272 | PXOR X11, X4 273 | PXOR X3, X4 274 | MOVQ $0xb0bc2e589204f500, AX 275 | MOVQ AX, X11 276 | PCLMULQDQ $0x00, X5, X11 277 | MOVQ $0xe1e0bb9d45d7a44c, AX 278 | PINSRQ $0x1, AX, X12 279 | PCLMULQDQ $0x11, X12, X5 280 | PXOR X11, X5 281 | PXOR X4, X5 282 | MOVQ $0xeadc41fd2ba3d420, AX 283 | MOVQ AX, X11 284 | PCLMULQDQ $0x00, X6, X11 285 | MOVQ $0x21e9761e252621ac, AX 286 | PINSRQ $0x1, AX, X12 287 | PCLMULQDQ $0x11, X12, X6 288 | PXOR X11, X6 289 | PXOR X5, X6 290 | MOVQ AX, X5 291 | PCLMULQDQ $0x00, X6, X5 292 | PSHUFD $0xee, X6, X6 293 | PXOR X5, X6 294 | MOVQ $0x27ecfa329aef9f77, AX 295 | MOVQ AX, X4 296 | PCLMULQDQ $0x00, X4, X6 297 | PEXTRQ $0, X6, BX 298 | MOVQ $0x34d926535897936b, AX 299 | MOVQ AX, X4 300 | PCLMULQDQ $0x00, X4, X6 301 | PXOR X5, X6 302 | PEXTRQ $1, X6, AX 303 | XORQ BX, AX 304 | 305 | skip128: 306 | NOTQ AX 307 | MOVQ AX, checksum+32(FP) 308 | VZEROUPPER 309 | RET 310 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------