packages feed

blake3 (empty) → 0.1

raw patch · 15 files changed

+2970/−0 lines, 15 filesdep +basedep +blake3dep +memory

Dependencies added: base, blake3, memory, tasty, tasty-hunit

Files

+ CHANGELOG.md view
@@ -0,0 +1,7 @@+# Version 0.1++* Initial version.++* Using upstream C code from+  [https://github.com/BLAKE3-team/BLAKE3](https://github.com/BLAKE3-team/BLAKE3)+  version `5651ce7ee0b0ad2f577beef7efcef87e2f39fbe2`.
+ LICENSE view
@@ -0,0 +1,219 @@+Copyright 2020, Renzo Carbonara.++Except where otherwise explicitly mentioned, all of contents of the+Haskell "blake3" project are licensed under the Apache License,+Version 2.0 (the "License"); you may not use this file except in+compliance with the License. You may obtain a copy of the License at the+end of this file or at http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+++-------------------------------------------------------------------------------+++                                 Apache License+                           Version 2.0, January 2004+                        http://www.apache.org/licenses/++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++   1. Definitions.++      "License" shall mean the terms and conditions for use, reproduction,+      and distribution as defined by Sections 1 through 9 of this document.++      "Licensor" shall mean the copyright owner or entity authorized by+      the copyright owner that is granting the License.++      "Legal Entity" shall mean the union of the acting entity and all+      other entities that control, are controlled by, or are under common+      control with that entity. For the purposes of this definition,+      "control" means (i) the power, direct or indirect, to cause the+      direction or management of such entity, whether by contract or+      otherwise, or (ii) ownership of fifty percent (50%) or more of the+      outstanding shares, or (iii) beneficial ownership of such entity.++      "You" (or "Your") shall mean an individual or Legal Entity+      exercising permissions granted by this License.++      "Source" form shall mean the preferred form for making modifications,+      including but not limited to software source code, documentation+      source, and configuration files.++      "Object" form shall mean any form resulting from mechanical+      transformation or translation of a Source form, including but+      not limited to compiled object code, generated documentation,+      and conversions to other media types.++      "Work" shall mean the work of authorship, whether in Source or+      Object form, made available under the License, as indicated by a+      copyright notice that is included in or attached to the work+      (an example is provided in the Appendix below).++      "Derivative Works" shall mean any work, whether in Source or Object+      form, that is based on (or derived from) the Work and for which the+      editorial revisions, annotations, elaborations, or other modifications+      represent, as a whole, an original work of authorship. For the purposes+      of this License, Derivative Works shall not include works that remain+      separable from, or merely link (or bind by name) to the interfaces of,+      the Work and Derivative Works thereof.++      "Contribution" shall mean any work of authorship, including+      the original version of the Work and any modifications or additions+      to that Work or Derivative Works thereof, that is intentionally+      submitted to Licensor for inclusion in the Work by the copyright owner+      or by an individual or Legal Entity authorized to submit on behalf of+      the copyright owner. For the purposes of this definition, "submitted"+      means any form of electronic, verbal, or written communication sent+      to the Licensor or its representatives, including but not limited to+      communication on electronic mailing lists, source code control systems,+      and issue tracking systems that are managed by, or on behalf of, the+      Licensor for the purpose of discussing and improving the Work, but+      excluding communication that is conspicuously marked or otherwise+      designated in writing by the copyright owner as "Not a Contribution."++      "Contributor" shall mean Licensor and any individual or Legal Entity+      on behalf of whom a Contribution has been received by Licensor and+      subsequently incorporated within the Work.++   2. Grant of Copyright License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      copyright license to reproduce, prepare Derivative Works of,+      publicly display, publicly perform, sublicense, and distribute the+      Work and such Derivative Works in Source or Object form.++   3. Grant of Patent License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      (except as stated in this section) patent license to make, have made,+      use, offer to sell, sell, import, and otherwise transfer the Work,+      where such license applies only to those patent claims licensable+      by such Contributor that are necessarily infringed by their+      Contribution(s) alone or by combination of their Contribution(s)+      with the Work to which such Contribution(s) was submitted. If You+      institute patent litigation against any entity (including a+      cross-claim or counterclaim in a lawsuit) alleging that the Work+      or a Contribution incorporated within the Work constitutes direct+      or contributory patent infringement, then any patent licenses+      granted to You under this License for that Work shall terminate+      as of the date such litigation is filed.++   4. Redistribution. You may reproduce and distribute copies of the+      Work or Derivative Works thereof in any medium, with or without+      modifications, and in Source or Object form, provided that You+      meet the following conditions:++      (a) You must give any other recipients of the Work or+          Derivative Works a copy of this License; and++      (b) You must cause any modified files to carry prominent notices+          stating that You changed the files; and++      (c) You must retain, in the Source form of any Derivative Works+          that You distribute, all copyright, patent, trademark, and+          attribution notices from the Source form of the Work,+          excluding those notices that do not pertain to any part of+          the Derivative Works; and++      (d) If the Work includes a "NOTICE" text file as part of its+          distribution, then any Derivative Works that You distribute must+          include a readable copy of the attribution notices contained+          within such NOTICE file, excluding those notices that do not+          pertain to any part of the Derivative Works, in at least one+          of the following places: within a NOTICE text file distributed+          as part of the Derivative Works; within the Source form or+          documentation, if provided along with the Derivative Works; or,+          within a display generated by the Derivative Works, if and+          wherever such third-party notices normally appear. The contents+          of the NOTICE file are for informational purposes only and+          do not modify the License. You may add Your own attribution+          notices within Derivative Works that You distribute, alongside+          or as an addendum to the NOTICE text from the Work, provided+          that such additional attribution notices cannot be construed+          as modifying the License.++      You may add Your own copyright statement to Your modifications and+      may provide additional or different license terms and conditions+      for use, reproduction, or distribution of Your modifications, or+      for any such Derivative Works as a whole, provided Your use,+      reproduction, and distribution of the Work otherwise complies with+      the conditions stated in this License.++   5. Submission of Contributions. Unless You explicitly state otherwise,+      any Contribution intentionally submitted for inclusion in the Work+      by You to the Licensor shall be under the terms and conditions of+      this License, without any additional terms or conditions.+      Notwithstanding the above, nothing herein shall supersede or modify+      the terms of any separate license agreement you may have executed+      with Licensor regarding such Contributions.++   6. Trademarks. This License does not grant permission to use the trade+      names, trademarks, service marks, or product names of the Licensor,+      except as required for reasonable and customary use in describing the+      origin of the Work and reproducing the content of the NOTICE file.++   7. Disclaimer of Warranty. Unless required by applicable law or+      agreed to in writing, Licensor provides the Work (and each+      Contributor provides its Contributions) on an "AS IS" BASIS,+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+      implied, including, without limitation, any warranties or conditions+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+      PARTICULAR PURPOSE. You are solely responsible for determining the+      appropriateness of using or redistributing the Work and assume any+      risks associated with Your exercise of permissions under this License.++   8. Limitation of Liability. In no event and under no legal theory,+      whether in tort (including negligence), contract, or otherwise,+      unless required by applicable law (such as deliberate and grossly+      negligent acts) or agreed to in writing, shall any Contributor be+      liable to You for damages, including any direct, indirect, special,+      incidental, or consequential damages of any character arising as a+      result of this License or out of the use or inability to use the+      Work (including but not limited to damages for loss of goodwill,+      work stoppage, computer failure or malfunction, or any and all+      other commercial damages or losses), even if such Contributor+      has been advised of the possibility of such damages.++   9. Accepting Warranty or Additional Liability. While redistributing+      the Work or Derivative Works thereof, You may choose to offer,+      and charge a fee for, acceptance of support, warranty, indemnity,+      or other liability obligations and/or rights consistent with this+      License. However, in accepting such obligations, You may act only+      on Your own behalf and on Your sole responsibility, not on behalf+      of any other Contributor, and only if You agree to indemnify,+      defend, and hold each Contributor harmless for any liability+      incurred by, or claims asserted against, such Contributor by reason+      of your accepting any such warranty or additional liability.++   END OF TERMS AND CONDITIONS++   APPENDIX: How to apply the Apache License to your work.++      To apply the Apache License to your work, attach the following+      boilerplate notice, with the fields enclosed by brackets "[]"+      replaced with your own identifying information. (Don't include+      the brackets!)  The text should be enclosed in the appropriate+      comment syntax for the file format. We also recommend that a+      file or class name and description of purpose be included on the+      same "printed page" as the copyright notice for easier+      identification within third-party archives.++   Copyright [yyyy] [name of copyright owner]++   Licensed under the Apache License, Version 2.0 (the "License");+   you may not use this file except in compliance with the License.+   You may obtain a copy of the License at++       http://www.apache.org/licenses/LICENSE-2.0++   Unless required by applicable law or agreed to in writing, software+   distributed under the License is distributed on an "AS IS" BASIS,+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+   See the License for the specific language governing permissions and+   limitations under the License.
+ README.md view
@@ -0,0 +1,5 @@+# Haskell BLAKE3++Haskell bindings to the official C implementation at+[https://github.com/BLAKE3-team/BLAKE3](https://github.com/BLAKE3-team/BLAKE3).+
+ blake3.cabal view
@@ -0,0 +1,68 @@+cabal-version: 2.4+name: blake3+version: 0.1+license: Apache-2.0+license-file: LICENSE+extra-source-files: README.md CHANGELOG.md+author: Renzo Carbonara+maintainer: renλren.zone+copyright: Copyright (c) Renzo Carbonara 2020+category: Cryptography+build-type: Simple+synopsis: BLAKE3 hashing algorithm+description: Bindings to the official BLAKE3 implementation in C+homepage: https://github.com/k0001/blake3+bug-reports: https://github.com/k0001/blake3/issues+tested-with: GHC == 8.6.5, GHC == 8.8.3+extra-source-files:+  cbits/*.h+  cbits/README.md+  cbits/LICENSE++common basic+  default-language: Haskell2010+  ghc-options: -Wall -Werror=incomplete-patterns+  build-depends:+    base == 4.*,+    memory++library+  import: basic+  hs-source-dirs: lib+  exposed-modules:+    BLAKE3+    BLAKE3.IO+    BLAKE3.Raw+  cc-options: -O3 -DBLAKE3_NO_SSE41 -DBLAKE3_NO_AVX2 -DBLAKE3_NO_AVX512+  include-dirs: cbits/+  c-sources:+    cbits/blake3.c+    cbits/blake3_dispatch.c+    cbits/blake3_portable.c+-- TODO ENABLE THESE+--   cbits/blake3_avx2.c+--   cbits/blake3_avx512.c+--   cbits/blake3_neon.c+--   cbits/blake3_sse41.c+-- asm-sources:+--   cbits/blake3_avx2_x86-64_unix.S+--   cbits/blake3_avx2_x86-64_windows_gnu.S+--   cbits/blake3_avx2_x86-64_windows_msvc.asm+--   cbits/blake3_avx512_x86-64_unix.S+--   cbits/blake3_avx512_x86-64_windows_gnu.S+--   cbits/blake3_avx512_x86-64_windows_msvc.asm+--   cbits/blake3_sse41_x86-64_unix.S+--   cbits/blake3_sse41_x86-64_windows_gnu.S+--   cbits/blake3_sse41_x86-64_windows_msvc.asm++test-suite test+  import: basic+  ghc-options: -Wall -Werror=incomplete-patterns -O2+  type: exitcode-stdio-1.0+  hs-source-dirs: test+  main-is: Main.hs+  build-depends:+    blake3,+    memory,+    tasty,+    tasty-hunit,
+ cbits/LICENSE view
@@ -0,0 +1,330 @@+This work is released into the public domain with CC0 1.0. Alternatively, it is+licensed under the Apache License 2.0.++-------------------------------------------------------------------------------++Creative Commons Legal Code++CC0 1.0 Universal++    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED+    HEREUNDER.++Statement of Purpose++The laws of most jurisdictions throughout the world automatically confer+exclusive Copyright and Related Rights (defined below) upon the creator+and subsequent owner(s) (each and all, an "owner") of an original work of+authorship and/or a database (each, a "Work").++Certain owners wish to permanently relinquish those rights to a Work for+the purpose of contributing to a commons of creative, cultural and+scientific works ("Commons") that the public can reliably and without fear+of later claims of infringement build upon, modify, incorporate in other+works, reuse and redistribute as freely as possible in any form whatsoever+and for any purposes, including without limitation commercial purposes.+These owners may contribute to the Commons to promote the ideal of a free+culture and the further production of creative, cultural and scientific+works, or to gain reputation or greater distribution for their Work in+part through the use and efforts of others.++For these and/or other purposes and motivations, and without any+expectation of additional consideration or compensation, the person+associating CC0 with a Work (the "Affirmer"), to the extent that he or she+is an owner of Copyright and Related Rights in the Work, voluntarily+elects to apply CC0 to the Work and publicly distribute the Work under its+terms, with knowledge of his or her Copyright and Related Rights in the+Work and the meaning and intended legal effect of CC0 on those rights.++1. Copyright and Related Rights. A Work made available under CC0 may be+protected by copyright and related or neighboring rights ("Copyright and+Related Rights"). Copyright and Related Rights include, but are not+limited to, the following:++  i. the right to reproduce, adapt, distribute, perform, display,+     communicate, and translate a Work;+ ii. moral rights retained by the original author(s) and/or performer(s);+iii. publicity and privacy rights pertaining to a person's image or+     likeness depicted in a Work;+ iv. rights protecting against unfair competition in regards to a Work,+     subject to the limitations in paragraph 4(a), below;+  v. rights protecting the extraction, dissemination, use and reuse of data+     in a Work;+ vi. database rights (such as those arising under Directive 96/9/EC of the+     European Parliament and of the Council of 11 March 1996 on the legal+     protection of databases, and under any national implementation+     thereof, including any amended or successor version of such+     directive); and+vii. other similar, equivalent or corresponding rights throughout the+     world based on applicable law or treaty, and any national+     implementations thereof.++2. Waiver. To the greatest extent permitted by, but not in contravention+of, applicable law, Affirmer hereby overtly, fully, permanently,+irrevocably and unconditionally waives, abandons, and surrenders all of+Affirmer's Copyright and Related Rights and associated claims and causes+of action, whether now known or unknown (including existing as well as+future claims and causes of action), in the Work (i) in all territories+worldwide, (ii) for the maximum duration provided by applicable law or+treaty (including future time extensions), (iii) in any current or future+medium and for any number of copies, and (iv) for any purpose whatsoever,+including without limitation commercial, advertising or promotional+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each+member of the public at large and to the detriment of Affirmer's heirs and+successors, fully intending that such Waiver shall not be subject to+revocation, rescission, cancellation, termination, or any other legal or+equitable action to disrupt the quiet enjoyment of the Work by the public+as contemplated by Affirmer's express Statement of Purpose.++3. Public License Fallback. Should any part of the Waiver for any reason+be judged legally invalid or ineffective under applicable law, then the+Waiver shall be preserved to the maximum extent permitted taking into+account Affirmer's express Statement of Purpose. In addition, to the+extent the Waiver is so judged Affirmer hereby grants to each affected+person a royalty-free, non transferable, non sublicensable, non exclusive,+irrevocable and unconditional license to exercise Affirmer's Copyright and+Related Rights in the Work (i) in all territories worldwide, (ii) for the+maximum duration provided by applicable law or treaty (including future+time extensions), (iii) in any current or future medium and for any number+of copies, and (iv) for any purpose whatsoever, including without+limitation commercial, advertising or promotional purposes (the+"License"). The License shall be deemed effective as of the date CC0 was+applied by Affirmer to the Work. Should any part of the License for any+reason be judged legally invalid or ineffective under applicable law, such+partial invalidity or ineffectiveness shall not invalidate the remainder+of the License, and in such case Affirmer hereby affirms that he or she+will not (i) exercise any of his or her remaining Copyright and Related+Rights in the Work or (ii) assert any associated claims and causes of+action with respect to the Work, in either case contrary to Affirmer's+express Statement of Purpose.++4. Limitations and Disclaimers.++ a. No trademark or patent rights held by Affirmer are waived, abandoned,+    surrendered, licensed or otherwise affected by this document.+ b. Affirmer offers the Work as-is and makes no representations or+    warranties of any kind concerning the Work, express, implied,+    statutory or otherwise, including without limitation warranties of+    title, merchantability, fitness for a particular purpose, non+    infringement, or the absence of latent or other defects, accuracy, or+    the present or absence of errors, whether or not discoverable, all to+    the greatest extent permissible under applicable law.+ c. Affirmer disclaims responsibility for clearing rights of other persons+    that may apply to the Work or any use thereof, including without+    limitation any person's Copyright and Related Rights in the Work.+    Further, Affirmer disclaims responsibility for obtaining any necessary+    consents, permissions or other rights required for any use of the+    Work.+ d. Affirmer understands and acknowledges that Creative Commons is not a+    party to this document and has no duty or obligation with respect to+    this CC0 or use of the Work.++-------------------------------------------------------------------------------++                                 Apache License+                           Version 2.0, January 2004+                        http://www.apache.org/licenses/++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++   1. Definitions.++      "License" shall mean the terms and conditions for use, reproduction,+      and distribution as defined by Sections 1 through 9 of this document.++      "Licensor" shall mean the copyright owner or entity authorized by+      the copyright owner that is granting the License.++      "Legal Entity" shall mean the union of the acting entity and all+      other entities that control, are controlled by, or are under common+      control with that entity. For the purposes of this definition,+      "control" means (i) the power, direct or indirect, to cause the+      direction or management of such entity, whether by contract or+      otherwise, or (ii) ownership of fifty percent (50%) or more of the+      outstanding shares, or (iii) beneficial ownership of such entity.++      "You" (or "Your") shall mean an individual or Legal Entity+      exercising permissions granted by this License.++      "Source" form shall mean the preferred form for making modifications,+      including but not limited to software source code, documentation+      source, and configuration files.++      "Object" form shall mean any form resulting from mechanical+      transformation or translation of a Source form, including but+      not limited to compiled object code, generated documentation,+      and conversions to other media types.++      "Work" shall mean the work of authorship, whether in Source or+      Object form, made available under the License, as indicated by a+      copyright notice that is included in or attached to the work+      (an example is provided in the Appendix below).++      "Derivative Works" shall mean any work, whether in Source or Object+      form, that is based on (or derived from) the Work and for which the+      editorial revisions, annotations, elaborations, or other modifications+      represent, as a whole, an original work of authorship. For the purposes+      of this License, Derivative Works shall not include works that remain+      separable from, or merely link (or bind by name) to the interfaces of,+      the Work and Derivative Works thereof.++      "Contribution" shall mean any work of authorship, including+      the original version of the Work and any modifications or additions+      to that Work or Derivative Works thereof, that is intentionally+      submitted to Licensor for inclusion in the Work by the copyright owner+      or by an individual or Legal Entity authorized to submit on behalf of+      the copyright owner. For the purposes of this definition, "submitted"+      means any form of electronic, verbal, or written communication sent+      to the Licensor or its representatives, including but not limited to+      communication on electronic mailing lists, source code control systems,+      and issue tracking systems that are managed by, or on behalf of, the+      Licensor for the purpose of discussing and improving the Work, but+      excluding communication that is conspicuously marked or otherwise+      designated in writing by the copyright owner as "Not a Contribution."++      "Contributor" shall mean Licensor and any individual or Legal Entity+      on behalf of whom a Contribution has been received by Licensor and+      subsequently incorporated within the Work.++   2. Grant of Copyright License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      copyright license to reproduce, prepare Derivative Works of,+      publicly display, publicly perform, sublicense, and distribute the+      Work and such Derivative Works in Source or Object form.++   3. Grant of Patent License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      (except as stated in this section) patent license to make, have made,+      use, offer to sell, sell, import, and otherwise transfer the Work,+      where such license applies only to those patent claims licensable+      by such Contributor that are necessarily infringed by their+      Contribution(s) alone or by combination of their Contribution(s)+      with the Work to which such Contribution(s) was submitted. If You+      institute patent litigation against any entity (including a+      cross-claim or counterclaim in a lawsuit) alleging that the Work+      or a Contribution incorporated within the Work constitutes direct+      or contributory patent infringement, then any patent licenses+      granted to You under this License for that Work shall terminate+      as of the date such litigation is filed.++   4. Redistribution. You may reproduce and distribute copies of the+      Work or Derivative Works thereof in any medium, with or without+      modifications, and in Source or Object form, provided that You+      meet the following conditions:++      (a) You must give any other recipients of the Work or+          Derivative Works a copy of this License; and++      (b) You must cause any modified files to carry prominent notices+          stating that You changed the files; and++      (c) You must retain, in the Source form of any Derivative Works+          that You distribute, all copyright, patent, trademark, and+          attribution notices from the Source form of the Work,+          excluding those notices that do not pertain to any part of+          the Derivative Works; and++      (d) If the Work includes a "NOTICE" text file as part of its+          distribution, then any Derivative Works that You distribute must+          include a readable copy of the attribution notices contained+          within such NOTICE file, excluding those notices that do not+          pertain to any part of the Derivative Works, in at least one+          of the following places: within a NOTICE text file distributed+          as part of the Derivative Works; within the Source form or+          documentation, if provided along with the Derivative Works; or,+          within a display generated by the Derivative Works, if and+          wherever such third-party notices normally appear. The contents+          of the NOTICE file are for informational purposes only and+          do not modify the License. You may add Your own attribution+          notices within Derivative Works that You distribute, alongside+          or as an addendum to the NOTICE text from the Work, provided+          that such additional attribution notices cannot be construed+          as modifying the License.++      You may add Your own copyright statement to Your modifications and+      may provide additional or different license terms and conditions+      for use, reproduction, or distribution of Your modifications, or+      for any such Derivative Works as a whole, provided Your use,+      reproduction, and distribution of the Work otherwise complies with+      the conditions stated in this License.++   5. Submission of Contributions. Unless You explicitly state otherwise,+      any Contribution intentionally submitted for inclusion in the Work+      by You to the Licensor shall be under the terms and conditions of+      this License, without any additional terms or conditions.+      Notwithstanding the above, nothing herein shall supersede or modify+      the terms of any separate license agreement you may have executed+      with Licensor regarding such Contributions.++   6. Trademarks. This License does not grant permission to use the trade+      names, trademarks, service marks, or product names of the Licensor,+      except as required for reasonable and customary use in describing the+      origin of the Work and reproducing the content of the NOTICE file.++   7. Disclaimer of Warranty. Unless required by applicable law or+      agreed to in writing, Licensor provides the Work (and each+      Contributor provides its Contributions) on an "AS IS" BASIS,+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+      implied, including, without limitation, any warranties or conditions+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+      PARTICULAR PURPOSE. You are solely responsible for determining the+      appropriateness of using or redistributing the Work and assume any+      risks associated with Your exercise of permissions under this License.++   8. Limitation of Liability. In no event and under no legal theory,+      whether in tort (including negligence), contract, or otherwise,+      unless required by applicable law (such as deliberate and grossly+      negligent acts) or agreed to in writing, shall any Contributor be+      liable to You for damages, including any direct, indirect, special,+      incidental, or consequential damages of any character arising as a+      result of this License or out of the use or inability to use the+      Work (including but not limited to damages for loss of goodwill,+      work stoppage, computer failure or malfunction, or any and all+      other commercial damages or losses), even if such Contributor+      has been advised of the possibility of such damages.++   9. Accepting Warranty or Additional Liability. While redistributing+      the Work or Derivative Works thereof, You may choose to offer,+      and charge a fee for, acceptance of support, warranty, indemnity,+      or other liability obligations and/or rights consistent with this+      License. However, in accepting such obligations, You may act only+      on Your own behalf and on Your sole responsibility, not on behalf+      of any other Contributor, and only if You agree to indemnify,+      defend, and hold each Contributor harmless for any liability+      incurred by, or claims asserted against, such Contributor by reason+      of your accepting any such warranty or additional liability.++   END OF TERMS AND CONDITIONS++   APPENDIX: How to apply the Apache License to your work.++      To apply the Apache License to your work, attach the following+      boilerplate notice, with the fields enclosed by brackets "[]"+      replaced with your own identifying information. (Don't include+      the brackets!)  The text should be enclosed in the appropriate+      comment syntax for the file format. We also recommend that a+      file or class name and description of purpose be included on the+      same "printed page" as the copyright notice for easier+      identification within third-party archives.++   Copyright 2019 Jack O'Connor and Samuel Neves++   Licensed under the Apache License, Version 2.0 (the "License");+   you may not use this file except in compliance with the License.+   You may obtain a copy of the License at++       http://www.apache.org/licenses/LICENSE-2.0++   Unless required by applicable law or agreed to in writing, software+   distributed under the License is distributed on an "AS IS" BASIS,+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+   See the License for the specific language governing permissions and+   limitations under the License.
+ cbits/README.md view
@@ -0,0 +1,234 @@+The official C implementation of BLAKE3.++# Example++An example program that hashes bytes from standard input and prints the+result:++```c+#include "blake3.h"+#include <stdio.h>+#include <unistd.h>++int main() {+  // Initialize the hasher.+  blake3_hasher hasher;+  blake3_hasher_init(&hasher);++  // Read input bytes from stdin.+  unsigned char buf[65536];+  ssize_t n;+  while ((n = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {+    blake3_hasher_update(&hasher, buf, n);+  }++  // Finalize the hash. BLAKE3_OUT_LEN is the default output length, 32 bytes.+  uint8_t output[BLAKE3_OUT_LEN];+  blake3_hasher_finalize(&hasher, output, BLAKE3_OUT_LEN);++  // Print the hash as hexadecimal.+  for (size_t i = 0; i < BLAKE3_OUT_LEN; i++) {+    printf("%02x", output[i]);+  }+  printf("\n");+  return 0;+}+```++If you save the example code above as `example.c`, and you're on x86\_64+with a Unix-like OS, you can compile a working binary like this:++```bash+gcc -O3 -o example example.c blake3.c blake3_dispatch.c blake3_portable.c \+    blake3_sse41_x86-64_unix.S blake3_avx2_x86-64_unix.S blake3_avx512_x86-64_unix.S+```++# API++## The Struct++```c+typedef struct {+  // private fields+} blake3_hasher;+```++An incremental BLAKE3 hashing state, which can accept any number of+updates. This implementation doesn't allocate any heap memory, but+`sizeof(blake3_hasher)` itself is relatively large, currently 1912 bytes+on x86-64. This size can be reduced by restricting the maximum input+length, as described in Section 5.4 of [the BLAKE3+spec](https://github.com/BLAKE3-team/BLAKE3-specs/blob/master/blake3.pdf),+but this implementation doesn't currently support that strategy.++## Common API Functions++```c+void blake3_hasher_init(+  blake3_hasher *self);+```++Initialize a `blake3_hasher` in the default hashing mode.++```c+void blake3_hasher_update(+  blake3_hasher *self,+  const void *input,+  size_t input_len);+```++Add input to the hasher. This can be called any number of times.++```c+void blake3_hasher_finalize(+  const blake3_hasher *self,+  uint8_t *out,+  size_t out_len);+```++Finalize the hasher and emit an output of any length. This doesn't+modify the hasher itself, and it's possible to finalize again after+adding more input. The constant `BLAKE3_OUT_LEN` provides the default+output length, 32 bytes.++## Less Common API Functions++```c+void blake3_hasher_init_keyed(+  blake3_hasher *self,+  const uint8_t key[BLAKE3_KEY_LEN]);+```++Initialize a `blake3_hasher` in the keyed hashing mode. The key must be+exactly 32 bytes.++```c+void blake3_hasher_init_derive_key(+  blake3_hasher *self,+  const char *context);+```++Initialize a `blake3_hasher` in the key derivation mode. Key material+should be given as input after initialization, using+`blake3_hasher_update`. `context` is a standard C string of any length,+and the terminating null byte is not included. The context string should+be hardcoded, globally unique, and application-specific. A good default+format for the context string is `"[application] [commit timestamp]+[purpose]"`, e.g., `"example.com 2019-12-25 16:18:03 session tokens+v1"`.++```c+void blake3_hasher_finalize_seek(+  const blake3_hasher *self,+  uint64_t seek,+  uint8_t *out,+  size_t out_len);+```++The same as `blake3_hasher_finalize`, but with an additional `seek`+parameter for the starting byte position in the output stream. To+efficiently stream a large output without allocating memory, call this+function in a loop, incrementing `seek` by the output length each time.++# Building++This implementation is just C and assembly files. It doesn't include a+public-facing build system. (The `Makefile` in this directory is only+for testing.) Instead, the intention is that you can include these files+in whatever build system you're already using. This section describes+the commands your build system should execute, or which you can execute+by hand. Note that these steps may change in future versions.++## x86++Dynamic dispatch is enabled by default on x86. The implementation will+query the CPU at runtime to detect SIMD support, and it will use the+widest instruction set available. By default, `blake3_dispatch.c`+expects to be linked with code for four different instruction sets:+portable C, SSE4.1, AVX2, and AVX-512.++For each of the x86 SIMD instruction sets, two versions are available,+one in assembly (with three flavors: Unix, Windows MSVC, and Windows+GNU) and one using C intrinsics. The assembly versions are generally+preferred: they perform better, they perform more consistently across+different compilers, and they build more quickly. On the other hand, the+assembly versions are x86\_64-only, and you need to select the right+flavor for your target platform.++Here's an example of building a shared library on x86\_64 Linux using+the assembly implementations:++```bash+gcc -shared -O3 -o libblake3.so blake3.c blake3_dispatch.c blake3_portable.c \+    blake3_sse41_x86-64_unix.S blake3_avx2_x86-64_unix.S blake3_avx512_x86-64_unix.S+```++When building the intrinsics-based implementations, you need to build+each implementation separately, with the corresponding instruction set+explicitly enabled in the compiler. Here's the same shared library using+the intrinsics-based implementations:++```bash+gcc -c -fPIC -O3 -msse4.1 blake3_sse41.c -o blake3_sse41.o+gcc -c -fPIC -O3 -mavx2 blake3_avx2.c -o blake3_avx2.o+gcc -c -fPIC -O3 -mavx512f -mavx512vl blake3_avx512.c -o blake3_avx512.o+gcc -shared -O3 -o libblake3.so blake3.c blake3_dispatch.c blake3_portable.c \+    blake3_avx2.o blake3_avx512.o blake3_sse41.o+```++Note above that building `blake3_avx512.c` requires both `-mavx512f` and+`-mavx512vl` under GCC and Clang, as shown above. Under MSVC, the single+`/arch:AVX512` flag is sufficient. The MSVC equivalent of `-mavx2` is+`/arch:AVX2`. MSVC enables SSE4.1 by defaut, and it doesn't have a+corresponding flag.++If you want to omit SIMD code on x86, you need to explicitly disable+each instruction set. Here's an example of building a shared library on+x86 with only portable code:++```bash+gcc -shared -O3 -o libblake3.so -DBLAKE3_NO_SSE41 -DBLAKE3_NO_AVX2 -DBLAKE3_NO_AVX512 \+    blake3.c blake3_dispatch.c blake3_portable.c+```++## ARM NEON++The NEON implementation is not enabled by default on ARM, since not all+ARM targets support it. To enable it, set `BLAKE3_USE_NEON=1`. Here's an+example of building a shared library on ARM Linux with NEON support:++```bash+gcc -shared -O3 -o libblake3.so -DBLAKE3_USE_NEON blake3.c blake3_dispatch.c \+    blake3_portable.c blake3_neon.c+```++Note that on some targets (ARMv7 in particular), extra flags may be+required to activate NEON support in the compiler. If you see an error+like...++```+/usr/lib/gcc/armv7l-unknown-linux-gnueabihf/9.2.0/include/arm_neon.h:635:1: error: inlining failed+in call to always_inline ‘vaddq_u32’: target specific option mismatch+```++...then you may need to add something like `-mfpu=neon-vfpv4+-mfloat-abi=hard`.++## Other Platforms++The portable implementation should work on most other architectures. For+example:++```bash+gcc -shared -O3 -o libblake3.so blake3.c blake3_dispatch.c blake3_portable.c+```++# Differences from the Rust Implementation++The single-threaded Rust and C implementations use the same algorithms,+and their performance is the same if you use the assembly+implementations or if you compile the intrinsics-based implementations+with Clang. (Both Clang and rustc are LLVM-based.)++The C implementation doesn't currently support multi-threading. OpenMP+support or similar might be added in the future.
+ cbits/blake3.c view
@@ -0,0 +1,598 @@+#include <assert.h>+#include <stdbool.h>+#include <string.h>++#include "blake3.h"+#include "blake3_impl.h"++INLINE void chunk_state_init(blake3_chunk_state *self, const uint32_t key[8],+                             uint8_t flags) {+  memcpy(self->cv, key, BLAKE3_KEY_LEN);+  self->chunk_counter = 0;+  memset(self->buf, 0, BLAKE3_BLOCK_LEN);+  self->buf_len = 0;+  self->blocks_compressed = 0;+  self->flags = flags;+}++INLINE void chunk_state_reset(blake3_chunk_state *self, const uint32_t key[8],+                              uint64_t chunk_counter) {+  memcpy(self->cv, key, BLAKE3_KEY_LEN);+  self->chunk_counter = chunk_counter;+  self->blocks_compressed = 0;+  memset(self->buf, 0, BLAKE3_BLOCK_LEN);+  self->buf_len = 0;+}++INLINE size_t chunk_state_len(const blake3_chunk_state *self) {+  return (BLAKE3_BLOCK_LEN * (size_t)self->blocks_compressed) ++         ((size_t)self->buf_len);+}++INLINE size_t chunk_state_fill_buf(blake3_chunk_state *self,+                                   const uint8_t *input, size_t input_len) {+  size_t take = BLAKE3_BLOCK_LEN - ((size_t)self->buf_len);+  if (take > input_len) {+    take = input_len;+  }+  uint8_t *dest = self->buf + ((size_t)self->buf_len);+  memcpy(dest, input, take);+  self->buf_len += (uint8_t)take;+  return take;+}++INLINE uint8_t chunk_state_maybe_start_flag(const blake3_chunk_state *self) {+  if (self->blocks_compressed == 0) {+    return CHUNK_START;+  } else {+    return 0;+  }+}++typedef struct {+  uint32_t input_cv[8];+  uint64_t counter;+  uint8_t block[BLAKE3_BLOCK_LEN];+  uint8_t block_len;+  uint8_t flags;+} output_t;++INLINE output_t make_output(const uint32_t input_cv[8],+                            const uint8_t block[BLAKE3_BLOCK_LEN],+                            uint8_t block_len, uint64_t counter,+                            uint8_t flags) {+  output_t ret;+  memcpy(ret.input_cv, input_cv, 32);+  memcpy(ret.block, block, BLAKE3_BLOCK_LEN);+  ret.block_len = block_len;+  ret.counter = counter;+  ret.flags = flags;+  return ret;+}++// Chaining values within a given chunk (specifically the compress_in_place+// interface) are represented as words. This avoids unnecessary bytes<->words+// conversion overhead in the portable implementation. However, the hash_many+// interface handles both user input and parent node blocks, so it accepts+// bytes. For that reason, chaining values in the CV stack are represented as+// bytes.+INLINE void output_chaining_value(const output_t *self, uint8_t cv[32]) {+  uint32_t cv_words[8];+  memcpy(cv_words, self->input_cv, 32);+  blake3_compress_in_place(cv_words, self->block, self->block_len,+                           self->counter, self->flags);+  memcpy(cv, cv_words, 32);+}++INLINE void output_root_bytes(const output_t *self, uint64_t seek, uint8_t *out,+                              size_t out_len) {+  uint64_t output_block_counter = seek / 64;+  size_t offset_within_block = seek % 64;+  uint8_t wide_buf[64];+  while (out_len > 0) {+    blake3_compress_xof(self->input_cv, self->block, self->block_len,+                        output_block_counter, self->flags | ROOT, wide_buf);+    size_t available_bytes = 64 - offset_within_block;+    size_t memcpy_len;+    if (out_len > available_bytes) {+      memcpy_len = available_bytes;+    } else {+      memcpy_len = out_len;+    }+    memcpy(out, wide_buf + offset_within_block, memcpy_len);+    out += memcpy_len;+    out_len -= memcpy_len;+    output_block_counter += 1;+    offset_within_block = 0;+  }+}++INLINE void chunk_state_update(blake3_chunk_state *self, const uint8_t *input,+                               size_t input_len) {+  if (self->buf_len > 0) {+    size_t take = chunk_state_fill_buf(self, input, input_len);+    input += take;+    input_len -= take;+    if (input_len > 0) {+      blake3_compress_in_place(+          self->cv, self->buf, BLAKE3_BLOCK_LEN, self->chunk_counter,+          self->flags | chunk_state_maybe_start_flag(self));+      self->blocks_compressed += 1;+      self->buf_len = 0;+      memset(self->buf, 0, BLAKE3_BLOCK_LEN);+    }+  }++  while (input_len > BLAKE3_BLOCK_LEN) {+    blake3_compress_in_place(self->cv, input, BLAKE3_BLOCK_LEN,+                             self->chunk_counter,+                             self->flags | chunk_state_maybe_start_flag(self));+    self->blocks_compressed += 1;+    input += BLAKE3_BLOCK_LEN;+    input_len -= BLAKE3_BLOCK_LEN;+  }++  size_t take = chunk_state_fill_buf(self, input, input_len);+  input += take;+  input_len -= take;+}++INLINE output_t chunk_state_output(const blake3_chunk_state *self) {+  uint8_t block_flags =+      self->flags | chunk_state_maybe_start_flag(self) | CHUNK_END;+  return make_output(self->cv, self->buf, self->buf_len, self->chunk_counter,+                     block_flags);+}++INLINE output_t parent_output(const uint8_t block[BLAKE3_BLOCK_LEN],+                              const uint32_t key[8], uint8_t flags) {+  return make_output(key, block, BLAKE3_BLOCK_LEN, 0, flags | PARENT);+}++// Given some input larger than one chunk, return the number of bytes that+// should go in the left subtree. This is the largest power-of-2 number of+// chunks that leaves at least 1 byte for the right subtree.+INLINE size_t left_len(size_t content_len) {+  // Subtract 1 to reserve at least one byte for the right side. content_len+  // should always be greater than BLAKE3_CHUNK_LEN.+  size_t full_chunks = (content_len - 1) / BLAKE3_CHUNK_LEN;+  return round_down_to_power_of_2(full_chunks) * BLAKE3_CHUNK_LEN;+}++// Use SIMD parallelism to hash up to MAX_SIMD_DEGREE chunks at the same time+// on a single thread. Write out the chunk chaining values and return the+// number of chunks hashed. These chunks are never the root and never empty;+// those cases use a different codepath.+INLINE size_t compress_chunks_parallel(const uint8_t *input, size_t input_len,+                                       const uint32_t key[8],+                                       uint64_t chunk_counter, uint8_t flags,+                                       uint8_t *out) {+#if defined(BLAKE3_TESTING)+  assert(0 < input_len);+  assert(input_len <= MAX_SIMD_DEGREE * BLAKE3_CHUNK_LEN);+#endif++  const uint8_t *chunks_array[MAX_SIMD_DEGREE];+  size_t input_position = 0;+  size_t chunks_array_len = 0;+  while (input_len - input_position >= BLAKE3_CHUNK_LEN) {+    chunks_array[chunks_array_len] = &input[input_position];+    input_position += BLAKE3_CHUNK_LEN;+    chunks_array_len += 1;+  }++  blake3_hash_many(chunks_array, chunks_array_len,+                   BLAKE3_CHUNK_LEN / BLAKE3_BLOCK_LEN, key, chunk_counter,+                   true, flags, CHUNK_START, CHUNK_END, out);++  // Hash the remaining partial chunk, if there is one. Note that the empty+  // chunk (meaning the empty message) is a different codepath.+  if (input_len > input_position) {+    uint64_t counter = chunk_counter + (uint64_t)chunks_array_len;+    blake3_chunk_state chunk_state;+    chunk_state_init(&chunk_state, key, flags);+    chunk_state.chunk_counter = counter;+    chunk_state_update(&chunk_state, &input[input_position],+                       input_len - input_position);+    output_t output = chunk_state_output(&chunk_state);+    output_chaining_value(&output, &out[chunks_array_len * BLAKE3_OUT_LEN]);+    return chunks_array_len + 1;+  } else {+    return chunks_array_len;+  }+}++// Use SIMD parallelism to hash up to MAX_SIMD_DEGREE parents at the same time+// on a single thread. Write out the parent chaining values and return the+// number of parents hashed. (If there's an odd input chaining value left over,+// return it as an additional output.) These parents are never the root and+// never empty; those cases use a different codepath.+INLINE size_t compress_parents_parallel(const uint8_t *child_chaining_values,+                                        size_t num_chaining_values,+                                        const uint32_t key[8], uint8_t flags,+                                        uint8_t *out) {+#if defined(BLAKE3_TESTING)+  assert(2 <= num_chaining_values);+  assert(num_chaining_values <= 2 * MAX_SIMD_DEGREE_OR_2);+#endif++  const uint8_t *parents_array[MAX_SIMD_DEGREE_OR_2];+  size_t parents_array_len = 0;+  while (num_chaining_values - (2 * parents_array_len) >= 2) {+    parents_array[parents_array_len] =+        &child_chaining_values[2 * parents_array_len * BLAKE3_OUT_LEN];+    parents_array_len += 1;+  }++  blake3_hash_many(parents_array, parents_array_len, 1, key,+                   0, // Parents always use counter 0.+                   false, flags | PARENT,+                   0, // Parents have no start flags.+                   0, // Parents have no end flags.+                   out);++  // If there's an odd child left over, it becomes an output.+  if (num_chaining_values > 2 * parents_array_len) {+    memcpy(&out[parents_array_len * BLAKE3_OUT_LEN],+           &child_chaining_values[2 * parents_array_len * BLAKE3_OUT_LEN],+           BLAKE3_OUT_LEN);+    return parents_array_len + 1;+  } else {+    return parents_array_len;+  }+}++// The wide helper function returns (writes out) an array of chaining values+// and returns the length of that array. The number of chaining values returned+// is the dyanmically detected SIMD degree, at most MAX_SIMD_DEGREE. Or fewer,+// if the input is shorter than that many chunks. The reason for maintaining a+// wide array of chaining values going back up the tree, is to allow the+// implementation to hash as many parents in parallel as possible.+//+// As a special case when the SIMD degree is 1, this function will still return+// at least 2 outputs. This guarantees that this function doesn't perform the+// root compression. (If it did, it would use the wrong flags, and also we+// wouldn't be able to implement exendable ouput.) Note that this function is+// not used when the whole input is only 1 chunk long; that's a different+// codepath.+//+// Why not just have the caller split the input on the first update(), instead+// of implementing this special rule? Because we don't want to limit SIMD or+// multi-threading parallelism for that update().+static size_t blake3_compress_subtree_wide(const uint8_t *input,+                                           size_t input_len,+                                           const uint32_t key[8],+                                           uint64_t chunk_counter,+                                           uint8_t flags, uint8_t *out) {+  // Note that the single chunk case does *not* bump the SIMD degree up to 2+  // when it is 1. If this implementation adds multi-threading in the future,+  // this gives us the option of multi-threading even the 2-chunk case, which+  // can help performance on smaller platforms.+  if (input_len <= blake3_simd_degree() * BLAKE3_CHUNK_LEN) {+    return compress_chunks_parallel(input, input_len, key, chunk_counter, flags,+                                    out);+  }++  // With more than simd_degree chunks, we need to recurse. Start by dividing+  // the input into left and right subtrees. (Note that this is only optimal+  // as long as the SIMD degree is a power of 2. If we ever get a SIMD degree+  // of 3 or something, we'll need a more complicated strategy.)+  size_t left_input_len = left_len(input_len);+  size_t right_input_len = input_len - left_input_len;+  const uint8_t *right_input = &input[left_input_len];+  uint64_t right_chunk_counter =+      chunk_counter + (uint64_t)(left_input_len / BLAKE3_CHUNK_LEN);++  // Make space for the child outputs. Here we use MAX_SIMD_DEGREE_OR_2 to+  // account for the special case of returning 2 outputs when the SIMD degree+  // is 1.+  uint8_t cv_array[2 * MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN];+  size_t degree = blake3_simd_degree();+  if (left_input_len > BLAKE3_CHUNK_LEN && degree == 1) {+    // The special case: We always use a degree of at least two, to make+    // sure there are two outputs. Except, as noted above, at the chunk+    // level, where we allow degree=1. (Note that the 1-chunk-input case is+    // a different codepath.)+    degree = 2;+  }+  uint8_t *right_cvs = &cv_array[degree * BLAKE3_OUT_LEN];++  // Recurse! If this implementation adds multi-threading support in the+  // future, this is where it will go.+  size_t left_n = blake3_compress_subtree_wide(input, left_input_len, key,+                                               chunk_counter, flags, cv_array);+  size_t right_n = blake3_compress_subtree_wide(+      right_input, right_input_len, key, right_chunk_counter, flags, right_cvs);++  // The special case again. If simd_degree=1, then we'll have left_n=1 and+  // right_n=1. Rather than compressing them into a single output, return+  // them directly, to make sure we always have at least two outputs.+  if (left_n == 1) {+    memcpy(out, cv_array, 2 * BLAKE3_OUT_LEN);+    return 2;+  }++  // Otherwise, do one layer of parent node compression.+  size_t num_chaining_values = left_n + right_n;+  return compress_parents_parallel(cv_array, num_chaining_values, key, flags,+                                   out);+}++// Hash a subtree with compress_subtree_wide(), and then condense the resulting+// list of chaining values down to a single parent node. Don't compress that+// last parent node, however. Instead, return its message bytes (the+// concatenated chaining values of its children). This is necessary when the+// first call to update() supplies a complete subtree, because the topmost+// parent node of that subtree could end up being the root. It's also necessary+// for extended output in the general case.+//+// As with compress_subtree_wide(), this function is not used on inputs of 1+// chunk or less. That's a different codepath.+INLINE void compress_subtree_to_parent_node(+    const uint8_t *input, size_t input_len, const uint32_t key[8],+    uint64_t chunk_counter, uint8_t flags, uint8_t out[2 * BLAKE3_OUT_LEN]) {+#if defined(BLAKE3_TESTING)+  assert(input_len > BLAKE3_CHUNK_LEN);+#endif++  uint8_t cv_array[2 * MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN];+  size_t num_cvs = blake3_compress_subtree_wide(input, input_len, key,+                                                chunk_counter, flags, cv_array);++  // If MAX_SIMD_DEGREE is greater than 2 and there's enough input,+  // compress_subtree_wide() returns more than 2 chaining values. Condense+  // them into 2 by forming parent nodes repeatedly.+  uint8_t out_array[MAX_SIMD_DEGREE_OR_2 * BLAKE3_OUT_LEN / 2];+  while (num_cvs > 2) {+    num_cvs =+        compress_parents_parallel(cv_array, num_cvs, key, flags, out_array);+    memcpy(cv_array, out_array, num_cvs * BLAKE3_OUT_LEN);+  }+  memcpy(out, cv_array, 2 * BLAKE3_OUT_LEN);+}++INLINE void hasher_init_base(blake3_hasher *self, const uint32_t key[8],+                             uint8_t flags) {+  memcpy(self->key, key, BLAKE3_KEY_LEN);+  chunk_state_init(&self->chunk, key, flags);+  self->cv_stack_len = 0;+}++void blake3_hasher_init(blake3_hasher *self) { hasher_init_base(self, IV, 0); }++void blake3_hasher_init_keyed(blake3_hasher *self,+                              const uint8_t key[BLAKE3_KEY_LEN]) {+  uint32_t key_words[8];+  load_key_words(key, key_words);+  hasher_init_base(self, key_words, KEYED_HASH);+}++void blake3_hasher_init_derive_key(blake3_hasher *self, const char *context) {+  blake3_hasher context_hasher;+  hasher_init_base(&context_hasher, IV, DERIVE_KEY_CONTEXT);+  blake3_hasher_update(&context_hasher, context, strlen(context));+  uint8_t context_key[BLAKE3_KEY_LEN];+  blake3_hasher_finalize(&context_hasher, context_key, BLAKE3_KEY_LEN);+  uint32_t context_key_words[8];+  load_key_words(context_key, context_key_words);+  hasher_init_base(self, context_key_words, DERIVE_KEY_MATERIAL);+}++// As described in hasher_push_cv() below, we do "lazy merging", delaying+// merges until right before the next CV is about to be added. This is+// different from the reference implementation. Another difference is that we+// aren't always merging 1 chunk at a time. Instead, each CV might represent+// any power-of-two number of chunks, as long as the smaller-above-larger stack+// order is maintained. Instead of the "count the trailing 0-bits" algorithm+// described in the spec, we use a "count the total number of 1-bits" variant+// that doesn't require us to retain the subtree size of the CV on top of the+// stack. The principle is the same: each CV that should remain in the stack is+// represented by a 1-bit in the total number of chunks (or bytes) so far.+INLINE void hasher_merge_cv_stack(blake3_hasher *self, uint64_t total_len) {+  size_t post_merge_stack_len = (size_t)popcnt(total_len);+  while (self->cv_stack_len > post_merge_stack_len) {+    uint8_t *parent_node =+        &self->cv_stack[(self->cv_stack_len - 2) * BLAKE3_OUT_LEN];+    output_t output = parent_output(parent_node, self->key, self->chunk.flags);+    output_chaining_value(&output, parent_node);+    self->cv_stack_len -= 1;+  }+}++// In reference_impl.rs, we merge the new CV with existing CVs from the stack+// before pushing it. We can do that because we know more input is coming, so+// we know none of the merges are root.+//+// This setting is different. We want to feed as much input as possible to+// compress_subtree_wide(), without setting aside anything for the chunk_state.+// If the user gives us 64 KiB, we want to parallelize over all 64 KiB at once+// as a single subtree, if at all possible.+//+// This leads to two problems:+// 1) This 64 KiB input might be the only call that ever gets made to update.+//    In this case, the root node of the 64 KiB subtree would be the root node+//    of the whole tree, and it would need to be ROOT finalized. We can't+//    compress it until we know.+// 2) This 64 KiB input might complete a larger tree, whose root node is+//    similarly going to be the the root of the whole tree. For example, maybe+//    we have 196 KiB (that is, 128 + 64) hashed so far. We can't compress the+//    node at the root of the 256 KiB subtree until we know how to finalize it.+//+// The second problem is solved with "lazy merging". That is, when we're about+// to add a CV to the stack, we don't merge it with anything first, as the+// reference impl does. Instead we do merges using the *previous* CV that was+// added, which is sitting on top of the stack, and we put the new CV+// (unmerged) on top of the stack afterwards. This guarantees that we never+// merge the root node until finalize().+//+// Solving the first problem requires an additional tool,+// compress_subtree_to_parent_node(). That function always returns the top+// *two* chaining values of the subtree it's compressing. We then do lazy+// merging with each of them separately, so that the second CV will always+// remain unmerged. (That also helps us support extendable output when we're+// hashing an input all-at-once.)+INLINE void hasher_push_cv(blake3_hasher *self, uint8_t new_cv[BLAKE3_OUT_LEN],+                           uint64_t chunk_counter) {+  hasher_merge_cv_stack(self, chunk_counter);+  memcpy(&self->cv_stack[self->cv_stack_len * BLAKE3_OUT_LEN], new_cv,+         BLAKE3_OUT_LEN);+  self->cv_stack_len += 1;+}++void blake3_hasher_update(blake3_hasher *self, const void *input,+                          size_t input_len) {+  // Explicitly checking for zero avoids causing UB by passing a null pointer+  // to memcpy. This comes up in practice with things like:+  //   std::vector<uint8_t> v;+  //   blake3_hasher_update(&hasher, v.data(), v.size());+  if (input_len == 0) {+    return;+  }++  const uint8_t *input_bytes = (const uint8_t *)input;++  // If we have some partial chunk bytes in the internal chunk_state, we need+  // to finish that chunk first.+  if (chunk_state_len(&self->chunk) > 0) {+    size_t take = BLAKE3_CHUNK_LEN - chunk_state_len(&self->chunk);+    if (take > input_len) {+      take = input_len;+    }+    chunk_state_update(&self->chunk, input_bytes, take);+    input_bytes += take;+    input_len -= take;+    // If we've filled the current chunk and there's more coming, finalize this+    // chunk and proceed. In this case we know it's not the root.+    if (input_len > 0) {+      output_t output = chunk_state_output(&self->chunk);+      uint8_t chunk_cv[32];+      output_chaining_value(&output, chunk_cv);+      hasher_push_cv(self, chunk_cv, self->chunk.chunk_counter);+      chunk_state_reset(&self->chunk, self->key, self->chunk.chunk_counter + 1);+    } else {+      return;+    }+  }++  // Now the chunk_state is clear, and we have more input. If there's more than+  // a single chunk (so, definitely not the root chunk), hash the largest whole+  // subtree we can, with the full benefits of SIMD (and maybe in the future,+  // multi-threading) parallelism. Two restrictions:+  // - The subtree has to be a power-of-2 number of chunks. Only subtrees along+  //   the right edge can be incomplete, and we don't know where the right edge+  //   is going to be until we get to finalize().+  // - The subtree must evenly divide the total number of chunks up until this+  //   point (if total is not 0). If the current incomplete subtree is only+  //   waiting for 1 more chunk, we can't hash a subtree of 4 chunks. We have+  //   to complete the current subtree first.+  // Because we might need to break up the input to form powers of 2, or to+  // evenly divide what we already have, this part runs in a loop.+  while (input_len > BLAKE3_CHUNK_LEN) {+    size_t subtree_len = round_down_to_power_of_2(input_len);+    uint64_t count_so_far = self->chunk.chunk_counter * BLAKE3_CHUNK_LEN;+    // Shrink the subtree_len until it evenly divides the count so far. We know+    // that subtree_len itself is a power of 2, so we can use a bitmasking+    // trick instead of an actual remainder operation. (Note that if the caller+    // consistently passes power-of-2 inputs of the same size, as is hopefully+    // typical, this loop condition will always fail, and subtree_len will+    // always be the full length of the input.)+    //+    // An aside: We don't have to shrink subtree_len quite this much. For+    // example, if count_so_far is 1, we could pass 2 chunks to+    // compress_subtree_to_parent_node. Since we'll get 2 CVs back, we'll still+    // get the right answer in the end, and we might get to use 2-way SIMD+    // parallelism. The problem with this optimization, is that it gets us+    // stuck always hashing 2 chunks. The total number of chunks will remain+    // odd, and we'll never graduate to higher degrees of parallelism. See+    // https://github.com/BLAKE3-team/BLAKE3/issues/69.+    while ((((uint64_t)(subtree_len - 1)) & count_so_far) != 0) {+      subtree_len /= 2;+    }+    // The shrunken subtree_len might now be 1 chunk long. If so, hash that one+    // chunk by itself. Otherwise, compress the subtree into a pair of CVs.+    uint64_t subtree_chunks = subtree_len / BLAKE3_CHUNK_LEN;+    if (subtree_len <= BLAKE3_CHUNK_LEN) {+      blake3_chunk_state chunk_state;+      chunk_state_init(&chunk_state, self->key, self->chunk.flags);+      chunk_state.chunk_counter = self->chunk.chunk_counter;+      chunk_state_update(&chunk_state, input_bytes, subtree_len);+      output_t output = chunk_state_output(&chunk_state);+      uint8_t cv[BLAKE3_OUT_LEN];+      output_chaining_value(&output, cv);+      hasher_push_cv(self, cv, chunk_state.chunk_counter);+    } else {+      // This is the high-performance happy path, though getting here depends+      // on the caller giving us a long enough input.+      uint8_t cv_pair[2 * BLAKE3_OUT_LEN];+      compress_subtree_to_parent_node(input_bytes, subtree_len, self->key,+                                      self->chunk.chunk_counter,+                                      self->chunk.flags, cv_pair);+      hasher_push_cv(self, cv_pair, self->chunk.chunk_counter);+      hasher_push_cv(self, &cv_pair[BLAKE3_OUT_LEN],+                     self->chunk.chunk_counter + (subtree_chunks / 2));+    }+    self->chunk.chunk_counter += subtree_chunks;+    input_bytes += subtree_len;+    input_len -= subtree_len;+  }++  // If there's any remaining input less than a full chunk, add it to the chunk+  // state. In that case, also do a final merge loop to make sure the subtree+  // stack doesn't contain any unmerged pairs. The remaining input means we+  // know these merges are non-root. This merge loop isn't strictly necessary+  // here, because hasher_push_chunk_cv already does its own merge loop, but it+  // simplifies blake3_hasher_finalize below.+  if (input_len > 0) {+    chunk_state_update(&self->chunk, input_bytes, input_len);+    hasher_merge_cv_stack(self, self->chunk.chunk_counter);+  }+}++void blake3_hasher_finalize(const blake3_hasher *self, uint8_t *out,+                            size_t out_len) {+  blake3_hasher_finalize_seek(self, 0, out, out_len);+}++void blake3_hasher_finalize_seek(const blake3_hasher *self, uint64_t seek,+                                 uint8_t *out, size_t out_len) {+  // Explicitly checking for zero avoids causing UB by passing a null pointer+  // to memcpy. This comes up in practice with things like:+  //   std::vector<uint8_t> v;+  //   blake3_hasher_finalize(&hasher, v.data(), v.size());+  if (out_len == 0) {+    return;+  }++  // If the subtree stack is empty, then the current chunk is the root.+  if (self->cv_stack_len == 0) {+    output_t output = chunk_state_output(&self->chunk);+    output_root_bytes(&output, seek, out, out_len);+    return;+  }+  // If there are any bytes in the chunk state, finalize that chunk and do a+  // roll-up merge between that chunk hash and every subtree in the stack. In+  // this case, the extra merge loop at the end of blake3_hasher_update+  // guarantees that none of the subtrees in the stack need to be merged with+  // each other first. Otherwise, if there are no bytes in the chunk state,+  // then the top of the stack is a chunk hash, and we start the merge from+  // that.+  output_t output;+  size_t cvs_remaining;+  if (chunk_state_len(&self->chunk) > 0) {+    cvs_remaining = self->cv_stack_len;+    output = chunk_state_output(&self->chunk);+  } else {+    // There are always at least 2 CVs in the stack in this case.+    cvs_remaining = self->cv_stack_len - 2;+    output = parent_output(&self->cv_stack[cvs_remaining * 32], self->key,+                           self->chunk.flags);+  }+  while (cvs_remaining > 0) {+    cvs_remaining -= 1;+    uint8_t parent_block[BLAKE3_BLOCK_LEN];+    memcpy(parent_block, &self->cv_stack[cvs_remaining * 32], 32);+    output_chaining_value(&output, &parent_block[32]);+    output = parent_output(parent_block, self->key, self->chunk.flags);+  }+  output_root_bytes(&output, seek, out, out_len);+}
+ cbits/blake3.h view
@@ -0,0 +1,56 @@+#ifndef BLAKE3_H+#define BLAKE3_H++#include <stddef.h>+#include <stdint.h>++#ifdef __cplusplus+extern "C" {+#endif++#define BLAKE3_KEY_LEN 32+#define BLAKE3_OUT_LEN 32+#define BLAKE3_BLOCK_LEN 64+#define BLAKE3_CHUNK_LEN 1024+#define BLAKE3_MAX_DEPTH 54+#define BLAKE3_MAX_SIMD_DEGREE 16++// This struct is a private implementation detail. It has to be here because+// it's part of blake3_hasher below.+typedef struct {+  uint32_t cv[8];+  uint64_t chunk_counter;+  uint8_t buf[BLAKE3_BLOCK_LEN];+  uint8_t buf_len;+  uint8_t blocks_compressed;+  uint8_t flags;+} blake3_chunk_state;++typedef struct {+  uint32_t key[8];+  blake3_chunk_state chunk;+  uint8_t cv_stack_len;+  // The stack size is MAX_DEPTH + 1 because we do lazy merging. For example,+  // with 7 chunks, we have 3 entries in the stack. Adding an 8th chunk+  // requires a 4th entry, rather than merging everything down to 1, because we+  // don't know whether more input is coming. This is different from how the+  // reference implementation does things.+  uint8_t cv_stack[(BLAKE3_MAX_DEPTH + 1) * BLAKE3_OUT_LEN];+} blake3_hasher;++void blake3_hasher_init(blake3_hasher *self);+void blake3_hasher_init_keyed(blake3_hasher *self,+                              const uint8_t key[BLAKE3_KEY_LEN]);+void blake3_hasher_init_derive_key(blake3_hasher *self, const char *context);+void blake3_hasher_update(blake3_hasher *self, const void *input,+                          size_t input_len);+void blake3_hasher_finalize(const blake3_hasher *self, uint8_t *out,+                            size_t out_len);+void blake3_hasher_finalize_seek(const blake3_hasher *self, uint64_t seek,+                                 uint8_t *out, size_t out_len);++#ifdef __cplusplus+}+#endif++#endif /* BLAKE3_H */
+ cbits/blake3_dispatch.c view
@@ -0,0 +1,245 @@+#include <stdbool.h>+#include <stddef.h>+#include <stdint.h>++#include "blake3_impl.h"++#if defined(IS_X86)+#if defined(_MSC_VER)+#include <intrin.h>+#elif defined(__GNUC__)+#include <immintrin.h>+#else+#error "Unimplemented!"+#endif+#endif++#if defined(IS_X86)+static uint64_t xgetbv() {+#if defined(_MSC_VER)+  return _xgetbv(0);+#else+  uint32_t eax = 0, edx = 0;+  __asm__ __volatile__("xgetbv\n" : "=a"(eax), "=d"(edx) : "c"(0));+  return ((uint64_t)edx << 32) | eax;+#endif+}++static void cpuid(uint32_t out[4], uint32_t id) {+#if defined(_MSC_VER)+  __cpuid((int *)out, id);+#elif defined(__i386__) || defined(_M_IX86)+  __asm__ __volatile__("movl %%ebx, %1\n"+                       "cpuid\n"+                       "xchgl %1, %%ebx\n"+                       : "=a"(out[0]), "=r"(out[1]), "=c"(out[2]), "=d"(out[3])+                       : "a"(id));+#else+  __asm__ __volatile__("cpuid\n"+                       : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3])+                       : "a"(id));+#endif+}++static void cpuidex(uint32_t out[4], uint32_t id, uint32_t sid) {+#if defined(_MSC_VER)+  __cpuidex((int *)out, id, sid);+#elif defined(__i386__) || defined(_M_IX86)+  __asm__ __volatile__("movl %%ebx, %1\n"+                       "cpuid\n"+                       "xchgl %1, %%ebx\n"+                       : "=a"(out[0]), "=r"(out[1]), "=c"(out[2]), "=d"(out[3])+                       : "a"(id), "c"(sid));+#else+  __asm__ __volatile__("cpuid\n"+                       : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3])+                       : "a"(id), "c"(sid));+#endif+}++#endif++enum cpu_feature {+  SSE2 = 1 << 0,+  SSSE3 = 1 << 1,+  SSE41 = 1 << 2,+  AVX = 1 << 3,+  AVX2 = 1 << 4,+  AVX512F = 1 << 5,+  AVX512VL = 1 << 6,+  /* ... */+  UNDEFINED = 1 << 30+};++#if !defined(BLAKE3_TESTING)+static /* Allow the variable to be controlled manually for testing */+#endif+    enum cpu_feature g_cpu_features = UNDEFINED;++#if !defined(BLAKE3_TESTING)+static+#endif+    enum cpu_feature+    get_cpu_features() {++  if (g_cpu_features != UNDEFINED) {+    return g_cpu_features;+  } else {+#if defined(IS_X86)+    uint32_t regs[4] = {0};+    uint32_t *eax = &regs[0], *ebx = &regs[1], *ecx = &regs[2], *edx = &regs[3];+    (void)edx;+    enum cpu_feature features = 0;+    cpuid(regs, 0);+    const int max_id = *eax;+    cpuid(regs, 1);+#if defined(__amd64__) || defined(_M_X64)+    features |= SSE2;+#else+    if (*edx & (1UL << 26))+      features |= SSE2;+#endif+    if (*ecx & (1UL << 0))+      features |= SSSE3;+    if (*ecx & (1UL << 19))+      features |= SSE41;++    if (*ecx & (1UL << 27)) { // OSXSAVE+      const uint64_t mask = xgetbv();+      if ((mask & 6) == 6) { // SSE and AVX states+        if (*ecx & (1UL << 28))+          features |= AVX;+        if (max_id >= 7) {+          cpuidex(regs, 7, 0);+          if (*ebx & (1UL << 5))+            features |= AVX2;+          if ((mask & 224) == 224) { // Opmask, ZMM_Hi256, Hi16_Zmm+            if (*ebx & (1UL << 31))+              features |= AVX512VL;+            if (*ebx & (1UL << 16))+              features |= AVX512F;+          }+        }+      }+    }+    g_cpu_features = features;+    return features;+#else+    /* How to detect NEON? */+    return 0;+#endif+  }+}++void blake3_compress_in_place(uint32_t cv[8],+                              const uint8_t block[BLAKE3_BLOCK_LEN],+                              uint8_t block_len, uint64_t counter,+                              uint8_t flags) {+#if defined(IS_X86)+  const enum cpu_feature features = get_cpu_features();+#if !defined(BLAKE3_NO_AVX512)+  if (features & AVX512VL) {+    blake3_compress_in_place_avx512(cv, block, block_len, counter, flags);+    return;+  }+#endif+#if !defined(BLAKE3_NO_SSE41)+  if (features & SSE41) {+    blake3_compress_in_place_sse41(cv, block, block_len, counter, flags);+    return;+  }+#endif+#endif+  blake3_compress_in_place_portable(cv, block, block_len, counter, flags);+}++void blake3_compress_xof(const uint32_t cv[8],+                         const uint8_t block[BLAKE3_BLOCK_LEN],+                         uint8_t block_len, uint64_t counter, uint8_t flags,+                         uint8_t out[64]) {+#if defined(IS_X86)+  const enum cpu_feature features = get_cpu_features();+#if !defined(BLAKE3_NO_AVX512)+  if (features & AVX512VL) {+    blake3_compress_xof_avx512(cv, block, block_len, counter, flags, out);+    return;+  }+#endif+#if !defined(BLAKE3_NO_SSE41)+  if (features & SSE41) {+    blake3_compress_xof_sse41(cv, block, block_len, counter, flags, out);+    return;+  }+#endif+#endif+  blake3_compress_xof_portable(cv, block, block_len, counter, flags, out);+}++void blake3_hash_many(const uint8_t *const *inputs, size_t num_inputs,+                      size_t blocks, const uint32_t key[8], uint64_t counter,+                      bool increment_counter, uint8_t flags,+                      uint8_t flags_start, uint8_t flags_end, uint8_t *out) {+#if defined(IS_X86)+  const enum cpu_feature features = get_cpu_features();+#if !defined(BLAKE3_NO_AVX512)+  if ((features & (AVX512F|AVX512VL)) == (AVX512F|AVX512VL)) {+    blake3_hash_many_avx512(inputs, num_inputs, blocks, key, counter,+                            increment_counter, flags, flags_start, flags_end,+                            out);+    return;+  }+#endif+#if !defined(BLAKE3_NO_AVX2)+  if (features & AVX2) {+    blake3_hash_many_avx2(inputs, num_inputs, blocks, key, counter,+                          increment_counter, flags, flags_start, flags_end,+                          out);+    return;+  }+#endif+#if !defined(BLAKE3_NO_SSE41)+  if (features & SSE41) {+    blake3_hash_many_sse41(inputs, num_inputs, blocks, key, counter,+                           increment_counter, flags, flags_start, flags_end,+                           out);+    return;+  }+#endif+#endif++#if defined(BLAKE3_USE_NEON)+  blake3_hash_many_neon(inputs, num_inputs, blocks, key, counter,+                        increment_counter, flags, flags_start, flags_end, out);+  return;+#endif++  blake3_hash_many_portable(inputs, num_inputs, blocks, key, counter,+                            increment_counter, flags, flags_start, flags_end,+                            out);+}++// The dynamically detected SIMD degree of the current platform.+size_t blake3_simd_degree(void) {+#if defined(IS_X86)+  const enum cpu_feature features = get_cpu_features();+#if !defined(BLAKE3_NO_AVX512)+  if ((features & (AVX512F|AVX512VL)) == (AVX512F|AVX512VL)) {+    return 16;+  }+#endif+#if !defined(BLAKE3_NO_AVX2)+  if (features & AVX2) {+    return 8;+  }+#endif+#if !defined(BLAKE3_NO_SSE41)+  if (features & SSE41) {+    return 4;+  }+#endif+#endif+#if defined(BLAKE3_USE_NEON)+  return 4;+#endif+  return 1;+}
+ cbits/blake3_impl.h view
@@ -0,0 +1,235 @@+#ifndef BLAKE3_IMPL_H+#define BLAKE3_IMPL_H++#include <assert.h>+#include <stdbool.h>+#include <stddef.h>+#include <stdint.h>+#include <string.h>++#include "blake3.h"++// internal flags+enum blake3_flags {+  CHUNK_START         = 1 << 0,+  CHUNK_END           = 1 << 1,+  PARENT              = 1 << 2,+  ROOT                = 1 << 3,+  KEYED_HASH          = 1 << 4,+  DERIVE_KEY_CONTEXT  = 1 << 5,+  DERIVE_KEY_MATERIAL = 1 << 6,+};++// This C implementation tries to support recent versions of GCC, Clang, and+// MSVC.+#if defined(_MSC_VER)+#define INLINE static __forceinline+#else+#define INLINE static inline __attribute__((always_inline))+#endif++#if defined(__x86_64__) || defined(_M_X64) +#define IS_X86+#define IS_X86_64+#endif++#if defined(__i386__) || defined(_M_IX86)+#define IS_X86+#define IS_X86_32+#endif++#if defined(IS_X86)+#if defined(_MSC_VER)+#include <intrin.h>+#endif+#include <immintrin.h>+#endif++#if defined(IS_X86)+#define MAX_SIMD_DEGREE 16+#elif defined(BLAKE3_USE_NEON)+#define MAX_SIMD_DEGREE 4+#else+#define MAX_SIMD_DEGREE 1+#endif++// There are some places where we want a static size that's equal to the+// MAX_SIMD_DEGREE, but also at least 2.+#define MAX_SIMD_DEGREE_OR_2 (MAX_SIMD_DEGREE > 2 ? MAX_SIMD_DEGREE : 2)++static const uint32_t IV[8] = {0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL,+                               0xA54FF53AUL, 0x510E527FUL, 0x9B05688CUL,+                               0x1F83D9ABUL, 0x5BE0CD19UL};++static const uint8_t MSG_SCHEDULE[7][16] = {+    {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},+    {2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8},+    {3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1},+    {10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6},+    {12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4},+    {9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7},+    {11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13},+};++/* Find index of the highest set bit */+/* x is assumed to be nonzero.       */+static unsigned int highest_one(uint64_t x) {+#if defined(__GNUC__) || defined(__clang__)+  return 63 ^ __builtin_clzll(x);+#elif defined(_MSC_VER) && defined(IS_X86_64)+  unsigned long index;+  _BitScanReverse64(&index, x);+  return index;+#elif defined(_MSC_VER) && defined(IS_X86_32)+  if(x >> 32) {+    unsigned long index;+    _BitScanReverse(&index, x >> 32);+    return 32 + index;+  } else {+    unsigned long index;+    _BitScanReverse(&index, x);+    return index;+  }+#else+  unsigned int c = 0;+  if(x & 0xffffffff00000000ULL) { x >>= 32; c += 32; }+  if(x & 0x00000000ffff0000ULL) { x >>= 16; c += 16; }+  if(x & 0x000000000000ff00ULL) { x >>=  8; c +=  8; }+  if(x & 0x00000000000000f0ULL) { x >>=  4; c +=  4; }+  if(x & 0x000000000000000cULL) { x >>=  2; c +=  2; }+  if(x & 0x0000000000000002ULL) {           c +=  1; }+  return c;+#endif+}++// Count the number of 1 bits.+INLINE unsigned int popcnt(uint64_t x) {+#if defined(__GNUC__) || defined(__clang__)+  return __builtin_popcountll(x);+#else+  unsigned int count = 0;+  while (x != 0) {+    count += 1;+    x &= x - 1;+  }+  return count;+#endif+}++// Largest power of two less than or equal to x. As a special case, returns 1+// when x is 0. +INLINE uint64_t round_down_to_power_of_2(uint64_t x) {+  return 1ULL << highest_one(x | 1);+}++INLINE uint32_t counter_low(uint64_t counter) { return (uint32_t)counter; }++INLINE uint32_t counter_high(uint64_t counter) {+  return (uint32_t)(counter >> 32);+}++INLINE uint32_t load32(const void *src) {+  const uint8_t *p = (const uint8_t *)src;+  return ((uint32_t)(p[0]) << 0) | ((uint32_t)(p[1]) << 8) |+         ((uint32_t)(p[2]) << 16) | ((uint32_t)(p[3]) << 24);+}++INLINE void load_key_words(const uint8_t key[BLAKE3_KEY_LEN],+                           uint32_t key_words[8]) {+  key_words[0] = load32(&key[0 * 4]);+  key_words[1] = load32(&key[1 * 4]);+  key_words[2] = load32(&key[2 * 4]);+  key_words[3] = load32(&key[3 * 4]);+  key_words[4] = load32(&key[4 * 4]);+  key_words[5] = load32(&key[5 * 4]);+  key_words[6] = load32(&key[6 * 4]);+  key_words[7] = load32(&key[7 * 4]);+}++void blake3_compress_in_place(uint32_t cv[8],+                              const uint8_t block[BLAKE3_BLOCK_LEN],+                              uint8_t block_len, uint64_t counter,+                              uint8_t flags);++void blake3_compress_xof(const uint32_t cv[8],+                         const uint8_t block[BLAKE3_BLOCK_LEN],+                         uint8_t block_len, uint64_t counter, uint8_t flags,+                         uint8_t out[64]);++void blake3_hash_many(const uint8_t *const *inputs, size_t num_inputs,+                      size_t blocks, const uint32_t key[8], uint64_t counter,+                      bool increment_counter, uint8_t flags,+                      uint8_t flags_start, uint8_t flags_end, uint8_t *out);++size_t blake3_simd_degree(void);+++// Declarations for implementation-specific functions.+void blake3_compress_in_place_portable(uint32_t cv[8],+                                       const uint8_t block[BLAKE3_BLOCK_LEN],+                                       uint8_t block_len, uint64_t counter,+                                       uint8_t flags);++void blake3_compress_xof_portable(const uint32_t cv[8],+                                  const uint8_t block[BLAKE3_BLOCK_LEN],+                                  uint8_t block_len, uint64_t counter,+                                  uint8_t flags, uint8_t out[64]);++void blake3_hash_many_portable(const uint8_t *const *inputs, size_t num_inputs,+                               size_t blocks, const uint32_t key[8],+                               uint64_t counter, bool increment_counter,+                               uint8_t flags, uint8_t flags_start,+                               uint8_t flags_end, uint8_t *out);++#if defined(IS_X86)+#if !defined(BLAKE3_NO_SSE41)+void blake3_compress_in_place_sse41(uint32_t cv[8],+                                    const uint8_t block[BLAKE3_BLOCK_LEN],+                                    uint8_t block_len, uint64_t counter,+                                    uint8_t flags);+void blake3_compress_xof_sse41(const uint32_t cv[8],+                               const uint8_t block[BLAKE3_BLOCK_LEN],+                               uint8_t block_len, uint64_t counter,+                               uint8_t flags, uint8_t out[64]);+void blake3_hash_many_sse41(const uint8_t *const *inputs, size_t num_inputs,+                            size_t blocks, const uint32_t key[8],+                            uint64_t counter, bool increment_counter,+                            uint8_t flags, uint8_t flags_start,+                            uint8_t flags_end, uint8_t *out);+#endif+#if !defined(BLAKE3_NO_AVX2)+void blake3_hash_many_avx2(const uint8_t *const *inputs, size_t num_inputs,+                           size_t blocks, const uint32_t key[8],+                           uint64_t counter, bool increment_counter,+                           uint8_t flags, uint8_t flags_start,+                           uint8_t flags_end, uint8_t *out);+#endif+#if !defined(BLAKE3_NO_AVX512)+void blake3_compress_in_place_avx512(uint32_t cv[8],+                                     const uint8_t block[BLAKE3_BLOCK_LEN],+                                     uint8_t block_len, uint64_t counter,+                                     uint8_t flags);++void blake3_compress_xof_avx512(const uint32_t cv[8],+                                const uint8_t block[BLAKE3_BLOCK_LEN],+                                uint8_t block_len, uint64_t counter,+                                uint8_t flags, uint8_t out[64]);++void blake3_hash_many_avx512(const uint8_t *const *inputs, size_t num_inputs,+                             size_t blocks, const uint32_t key[8],+                             uint64_t counter, bool increment_counter,+                             uint8_t flags, uint8_t flags_start,+                             uint8_t flags_end, uint8_t *out);+#endif+#endif++#if defined(BLAKE3_USE_NEON)+void blake3_hash_many_neon(const uint8_t *const *inputs, size_t num_inputs,+                           size_t blocks, const uint32_t key[8],+                           uint64_t counter, bool increment_counter,+                           uint8_t flags, uint8_t flags_start,+                           uint8_t flags_end, uint8_t *out);+#endif+++#endif /* BLAKE3_IMPL_H */
+ cbits/blake3_portable.c view
@@ -0,0 +1,168 @@+#include "blake3_impl.h"+#include <string.h>++INLINE void store32(void *dst, uint32_t w) {+  uint8_t *p = (uint8_t *)dst;+  p[0] = (uint8_t)(w >> 0);+  p[1] = (uint8_t)(w >> 8);+  p[2] = (uint8_t)(w >> 16);+  p[3] = (uint8_t)(w >> 24);+}++INLINE uint32_t rotr32(uint32_t w, uint32_t c) {+  return (w >> c) | (w << (32 - c));+}++INLINE void g(uint32_t *state, size_t a, size_t b, size_t c, size_t d,+              uint32_t x, uint32_t y) {+  state[a] = state[a] + state[b] + x;+  state[d] = rotr32(state[d] ^ state[a], 16);+  state[c] = state[c] + state[d];+  state[b] = rotr32(state[b] ^ state[c], 12);+  state[a] = state[a] + state[b] + y;+  state[d] = rotr32(state[d] ^ state[a], 8);+  state[c] = state[c] + state[d];+  state[b] = rotr32(state[b] ^ state[c], 7);+}++INLINE void round_fn(uint32_t state[16], const uint32_t *msg, size_t round) {+  // Select the message schedule based on the round.+  const uint8_t *schedule = MSG_SCHEDULE[round];++  // Mix the columns.+  g(state, 0, 4, 8, 12, msg[schedule[0]], msg[schedule[1]]);+  g(state, 1, 5, 9, 13, msg[schedule[2]], msg[schedule[3]]);+  g(state, 2, 6, 10, 14, msg[schedule[4]], msg[schedule[5]]);+  g(state, 3, 7, 11, 15, msg[schedule[6]], msg[schedule[7]]);++  // Mix the rows.+  g(state, 0, 5, 10, 15, msg[schedule[8]], msg[schedule[9]]);+  g(state, 1, 6, 11, 12, msg[schedule[10]], msg[schedule[11]]);+  g(state, 2, 7, 8, 13, msg[schedule[12]], msg[schedule[13]]);+  g(state, 3, 4, 9, 14, msg[schedule[14]], msg[schedule[15]]);+}++INLINE void compress_pre(uint32_t state[16], const uint32_t cv[8],+                         const uint8_t block[BLAKE3_BLOCK_LEN],+                         uint8_t block_len, uint64_t counter, uint8_t flags) {+  uint32_t block_words[16];+  block_words[0] = load32(block + 4 * 0);+  block_words[1] = load32(block + 4 * 1);+  block_words[2] = load32(block + 4 * 2);+  block_words[3] = load32(block + 4 * 3);+  block_words[4] = load32(block + 4 * 4);+  block_words[5] = load32(block + 4 * 5);+  block_words[6] = load32(block + 4 * 6);+  block_words[7] = load32(block + 4 * 7);+  block_words[8] = load32(block + 4 * 8);+  block_words[9] = load32(block + 4 * 9);+  block_words[10] = load32(block + 4 * 10);+  block_words[11] = load32(block + 4 * 11);+  block_words[12] = load32(block + 4 * 12);+  block_words[13] = load32(block + 4 * 13);+  block_words[14] = load32(block + 4 * 14);+  block_words[15] = load32(block + 4 * 15);++  state[0] = cv[0];+  state[1] = cv[1];+  state[2] = cv[2];+  state[3] = cv[3];+  state[4] = cv[4];+  state[5] = cv[5];+  state[6] = cv[6];+  state[7] = cv[7];+  state[8] = IV[0];+  state[9] = IV[1];+  state[10] = IV[2];+  state[11] = IV[3];+  state[12] = counter_low(counter);+  state[13] = counter_high(counter);+  state[14] = (uint32_t)block_len;+  state[15] = (uint32_t)flags;++  round_fn(state, &block_words[0], 0);+  round_fn(state, &block_words[0], 1);+  round_fn(state, &block_words[0], 2);+  round_fn(state, &block_words[0], 3);+  round_fn(state, &block_words[0], 4);+  round_fn(state, &block_words[0], 5);+  round_fn(state, &block_words[0], 6);+}++void blake3_compress_in_place_portable(uint32_t cv[8],+                                       const uint8_t block[BLAKE3_BLOCK_LEN],+                                       uint8_t block_len, uint64_t counter,+                                       uint8_t flags) {+  uint32_t state[16];+  compress_pre(state, cv, block, block_len, counter, flags);+  cv[0] = state[0] ^ state[8];+  cv[1] = state[1] ^ state[9];+  cv[2] = state[2] ^ state[10];+  cv[3] = state[3] ^ state[11];+  cv[4] = state[4] ^ state[12];+  cv[5] = state[5] ^ state[13];+  cv[6] = state[6] ^ state[14];+  cv[7] = state[7] ^ state[15];+}++void blake3_compress_xof_portable(const uint32_t cv[8],+                                  const uint8_t block[BLAKE3_BLOCK_LEN],+                                  uint8_t block_len, uint64_t counter,+                                  uint8_t flags, uint8_t out[64]) {+  uint32_t state[16];+  compress_pre(state, cv, block, block_len, counter, flags);++  store32(&out[0 * 4], state[0] ^ state[8]);+  store32(&out[1 * 4], state[1] ^ state[9]);+  store32(&out[2 * 4], state[2] ^ state[10]);+  store32(&out[3 * 4], state[3] ^ state[11]);+  store32(&out[4 * 4], state[4] ^ state[12]);+  store32(&out[5 * 4], state[5] ^ state[13]);+  store32(&out[6 * 4], state[6] ^ state[14]);+  store32(&out[7 * 4], state[7] ^ state[15]);+  store32(&out[8 * 4], state[8] ^ cv[0]);+  store32(&out[9 * 4], state[9] ^ cv[1]);+  store32(&out[10 * 4], state[10] ^ cv[2]);+  store32(&out[11 * 4], state[11] ^ cv[3]);+  store32(&out[12 * 4], state[12] ^ cv[4]);+  store32(&out[13 * 4], state[13] ^ cv[5]);+  store32(&out[14 * 4], state[14] ^ cv[6]);+  store32(&out[15 * 4], state[15] ^ cv[7]);+}++INLINE void hash_one_portable(const uint8_t *input, size_t blocks,+                              const uint32_t key[8], uint64_t counter,+                              uint8_t flags, uint8_t flags_start,+                              uint8_t flags_end, uint8_t out[BLAKE3_OUT_LEN]) {+  uint32_t cv[8];+  memcpy(cv, key, BLAKE3_KEY_LEN);+  uint8_t block_flags = flags | flags_start;+  while (blocks > 0) {+    if (blocks == 1) {+      block_flags |= flags_end;+    }+    blake3_compress_in_place_portable(cv, input, BLAKE3_BLOCK_LEN, counter,+                                      block_flags);+    input = &input[BLAKE3_BLOCK_LEN];+    blocks -= 1;+    block_flags = flags;+  }+  memcpy(out, cv, 32);+}++void blake3_hash_many_portable(const uint8_t *const *inputs, size_t num_inputs,+                               size_t blocks, const uint32_t key[8],+                               uint64_t counter, bool increment_counter,+                               uint8_t flags, uint8_t flags_start,+                               uint8_t flags_end, uint8_t *out) {+  while (num_inputs > 0) {+    hash_one_portable(inputs[0], blocks, key, counter, flags, flags_start,+                      flags_end, out);+    if (increment_counter) {+      counter += 1;+    }+    inputs += 1;+    num_inputs -= 1;+    out = &out[BLAKE3_OUT_LEN];+  }+}
+ lib/BLAKE3.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}++-- | Haskell bindings to the [official BLAKE3 hashing+-- implementation in C](https://github.com/BLAKE3-team/BLAKE3).+--+-- The original C implementation is released into the public domain with CC0 1.0.+-- Alternatively, it is licensed under the Apache License 2.0, copyright of Jack+-- O'Connor and Samuel Neves. See its [LICENSE](https://github.com/BLAKE3-team/BLAKE3/blob/88dcee7005be962a81516f7863e70009d9caa2c9/LICENSE)+-- for details.+--+-- This Haskell library is the copyright of Renzo Carbonara,+-- licensed under the terms of+-- the [Apache License 2.0](https://github.com/k0001/hs-blake3/blob/master/blake3/LICENSE).+module BLAKE3+  ( -- * Hashing+    hash+  , BIO.Digest+    -- * Keyed hashing+  , hashKeyed+  , BIO.Key+  , BIO.key+    -- * Key derivation+  , derive+  , BIO.Context+  , BIO.context+    -- * Incremental hashing+  , BIO.Hasher+  , hasher+  , hasherKeyed+  , update+  , finalize+    -- * Constants+  , BIO.KEY_LEN+  , BIO.BLOCK_SIZE+  , BIO.DEFAULT_DIGEST_LEN+  )+  where++import qualified Data.ByteArray as BA+import GHC.TypeLits+import System.IO.Unsafe (unsafeDupablePerformIO)++import qualified BLAKE3.IO as BIO++--------------------------------------------------------------------------------++-- | BLAKE3 hashing.+--+-- For incremental hashing, see 'hasher', 'update' and 'finalize':+--+-- @+-- 'hash' = 'finalize' '.' 'update' 'hasher'+-- @+hash+  :: forall len bin+  .  (KnownNat len, BA.ByteArrayAccess bin)+  => [bin]           -- ^ Data to hash.+  -> BIO.Digest len  -- ^ Default digest length is 'BIO.DEFAULT_DIGEST_LEN'.+hash bins = unsafeDupablePerformIO $ do+  fmap fst $ BIO.allocRetHasher $ \ph -> do+    BIO.init ph+    BIO.update ph bins+    BIO.finalize ph+{-# NOINLINE hash #-}++-- | BLAKE3 hashing with a 'BIO.Key'.+--+-- For incremental hashing, see 'hasherKeyed', 'update' and 'finalize':+--+-- @+-- 'hashKeyed' key = 'finalize' '.' 'update' ('hasherKeyed' key)+-- @+hashKeyed+  :: forall len bin+  .  (KnownNat len, BA.ByteArrayAccess bin)+  => BIO.Key+  -> [bin]           -- ^ Data to hash.+  -> BIO.Digest len  -- ^ Default digest length is 'BIO.DEFAULT_DIGEST_LEN'.+hashKeyed key0 bins = unsafeDupablePerformIO $ do+  fmap fst $ BIO.allocRetHasher $ \ph -> do+    BIO.initKeyed ph key0+    BIO.update ph bins+    BIO.finalize ph+{-# NOINLINE hashKeyed #-}++-- | BLAKE3 key derivation.+derive+  :: forall len ikm+  .  (KnownNat len, BA.ByteArrayAccess ikm)+  => BIO.Context+  -> [ikm]  -- ^ Input key material.+  -> BIO.Digest len  -- ^ Default digest length is 'BIO.DEFAULT_DIGEST_LEN'.+derive ctx ikms = unsafeDupablePerformIO $+  fmap fst $ BIO.allocRetHasher $ \ph -> do+    BIO.initDerive ph ctx+    BIO.update ph ikms+    BIO.finalize ph+{-# NOINLINE derive #-}++-- | Initial 'BIO.Hasher' for incremental hashing.+hasher :: BIO.Hasher -- ^+hasher = unsafeDupablePerformIO $+  fmap snd $ BIO.allocRetHasher BIO.init+{-# NOINLINE hasher #-}++-- | Initial 'BIO.Hasher' for incremental /keyed/ hashing.+hasherKeyed :: BIO.Key -> BIO.Hasher -- ^+hasherKeyed key0 = unsafeDupablePerformIO $+  fmap snd $ BIO.allocRetHasher $ \ph ->+  BIO.initKeyed ph key0+{-# NOINLINE hasherKeyed #-}++-- | Update 'BIO.Hasher' with new data.+update+  :: forall bin+  .  BA.ByteArrayAccess bin+  => BIO.Hasher+  -> [bin]+  -> BIO.Hasher+update h0 bins = unsafeDupablePerformIO $ do+  h1 <- BIO.copyHasher h0+  BIO.withHasherInternal h1 $ \ph1 -> do+    BIO.update ph1 bins+    pure h1+{-# NOINLINE update #-}++-- | Update 'BIO.Hasher' with new data.+finalize+  :: forall len+  .  KnownNat len+  => BIO.Hasher+  -> BIO.Digest len+finalize h0 = unsafeDupablePerformIO $ do+  h1 <- BIO.copyHasher h0+  BIO.withHasherInternal h1 $ \ph1 ->+    BIO.finalize ph1+{-# NOINLINE finalize #-}
+ lib/BLAKE3/IO.hs view
@@ -0,0 +1,261 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}++module BLAKE3.IO+  ( -- * Hashing+    init+  , update+  , finalize+  , Hasher+  , allocRetHasher+  , Digest+  , allocRetDigest+  -- ** Memory+  , Raw.HasherInternal+  , copyHasher+  , withHasherInternal+  -- * Keyed hashing+  , Key+  , key+  , allocRetKey+  , initKeyed+  -- * Key derivation+  , Context+  , context+  , initDerive+  -- * Constants+  , Raw.HASHER_ALIGNMENT+  , Raw.HASHER_SIZE+  , Raw.KEY_LEN+  , Raw.BLOCK_SIZE+  , Raw.DEFAULT_DIGEST_LEN+  )+  where++import Control.Monad (guard)+import qualified Data.ByteArray as BA+import qualified Data.ByteArray.Encoding as BA+import Data.Foldable+import qualified Data.Memory.PtrMethods as BA+import Data.Proxy+import Data.String+import Data.Word+import Foreign.Ptr+import Foreign.Storable+import GHC.TypeLits+import Prelude hiding (init)++import qualified BLAKE3.Raw as Raw++--------------------------------------------------------------------------------++-- | Immutable BLAKE3 hashing state.+--+-- Obtain with 'BLAKE3.hasher' or 'BLAKE3.hasherKeyed'.+newtype Hasher = Hasher BA.ScrubbedBytes+  -- deriving newtype (BA.ByteArrayAccess)++-- | Allocate 'Hasher'.+--+-- The 'Hasher' is wiped and freed as soon as it becomes unused.+allocRetHasher+  :: forall a+  .  (Ptr Raw.HasherInternal -> IO a)  -- ^ Initialize 'Raw.HASHER_SIZE' bytes.+  -> IO (a, Hasher)+allocRetHasher g = do+  let size = fromIntegral (natVal (Proxy @Raw.HASHER_SIZE))+  (a, bs) <- BA.allocRet size g+  pure (a, Hasher bs)++-- | Mutate the given 'Hasher'.+withHasherInternal+  :: Hasher+  -> (Ptr Raw.HasherInternal -> IO a) -- ^ Read or write.+  -> IO a+withHasherInternal (Hasher x) = BA.withByteArray x++-- | Copy an inmutable 'Hasher'.+copyHasher :: Hasher -> IO Hasher -- ^+copyHasher (Hasher x) = fmap Hasher $ BA.copy x (const (pure ()))++--------------------------------------------------------------------------------++-- | Output from BLAKE3 algorithm, of @len@ bytes.+--+-- The default digest length for BLAKE3 is 'Raw.DEFAULT_DIGEST_LEN'.+newtype Digest (len :: Nat) = Digest BA.ScrubbedBytes+  deriving newtype ( Eq -- ^ Constant time.+                   , BA.ByteArrayAccess)++-- | Base 16 (hexadecimal).+instance Show (Digest len) where+  show (Digest x) = showBase16 x++-- | Allocate a 'Digest'.+--+-- The 'Digest' is wiped and freed as soon as it becomes unused.+allocRetDigest+  :: forall len a+  .  KnownNat len+  => (Ptr Word8 -> IO a)  -- ^ Initialize @len@ bytes.+  -> IO (a, Digest len)+allocRetDigest g = do+  let size = fromIntegral (natVal (Proxy @len))+  (a, bs) <- BA.allocRet size g+  pure (a, Digest bs)++--------------------------------------------------------------------------------++-- | Key used for keyed hashing mode.+--+-- Obtain with 'BLAKE3.key'.+--+-- See 'BLAKE3.hashKeyed'.+newtype Key = Key BA.ScrubbedBytes+  deriving newtype ( Eq -- ^ Constant time.+                   , BA.ByteArrayAccess)++-- | Base 16 (hexadecimal).+instance Show Key where+  show (Key x) = showBase16 x++keyLen :: Int+keyLen = fromIntegral (natVal (Proxy @Raw.KEY_LEN))++-- | Obtain a 'Key' for use in BLAKE3 keyed hashing.+--+-- See 'BLAKE3.hashKeyed'.+key+  :: BA.ByteArrayAccess bin+  => bin -- ^ Key bytes. Must have length 'Raw.KEY_LEN'.+  -> Maybe Key -- ^+key bin | BA.length bin == keyLen = Just (Key (BA.convert bin))+        | otherwise = Nothing++-- | Allocate a 'Key'.+--+-- The 'Key' is wiped and freed as soon as it becomes unused.+allocRetKey+  :: forall a+  . (Ptr Word8 -> IO a) -- ^ Initialize 'Raw.KEY_LEN' bytes.+  -> IO (a, Key)+allocRetKey g = do+  (a, bs) <- BA.allocRet keyLen g+  pure (a, Key bs)++--------------------------------------------------------------------------------++-- | Context for BLAKE3 key derivation. Obtain with 'context'.+newtype Context = Context BA.Bytes -- ^ NUL-terminated 'CString'.+  deriving newtype (Eq)++-- | Base 16 (hexadecimal).+instance Show Context where+  show (Context x) = showBase16 (BA.takeView x (BA.length x - 1))++-- | 'fromString' is a /partial/ function that fails if the given 'String'+-- contains 'Char's outside the range @['toEnum' 0 .. 'toEnum' 255]@. See+-- 'context'.+instance IsString Context where+  fromString s = case traverse charToWord8 s of+      Nothing -> error "Not a valid String for Context"+      Just w8s -> Context $! BA.pack (w8s <> [0])+    where+      charToWord8 :: Char -> Maybe Word8+      charToWord8 c = do+        let i = fromEnum c+        guard (i > 0 && i < 256)+        pure (fromIntegral i)++-- | Obtain a 'Context' for BLAKE3 key derivation.+--+-- The context should be hardcoded, globally unique, and+-- application-specific.+--+-- A good format for the context string is:+--+-- @+-- [application] [commit timestamp] [purpose]+-- @+--+-- For example:+--+-- @+-- example.com 2019-12-25 16:18:03 session tokens v1+-- @+context+  :: BA.ByteArrayAccess bin+  => bin -- ^ If @bin@ contains null bytes, this function returns 'Nothing'.+  -> Maybe Context+context src+  | BA.any (0 ==) src = Nothing+  | otherwise = Just $ Context $+      let srcLen = BA.length src+          dstLen = srcLen + 1+      in BA.allocAndFreeze dstLen $ \pdst ->+         BA.withByteArray src $ \psrc -> do+           BA.memCopy pdst psrc srcLen+           pokeByteOff pdst srcLen (0 :: Word8)++--------------------------------------------------------------------------------++-- | Initialize a 'Raw.HasherInternal'.+init+  :: Ptr Raw.HasherInternal -- ^ Will be mutated.+  -> IO ()+init = Raw.init++-- | Initialize a 'Raw.HasherInternal' in keyed mode.+initKeyed+  :: Ptr Raw.HasherInternal -- ^ Will be mutated.+  -> Key+  -> IO () -- ^+initKeyed ph key0 =+  BA.withByteArray key0 $ \pkey ->+  Raw.init_keyed ph pkey++-- | Initialize a 'Raw.HasherInternal' in derivation mode.+--+-- The input key material must be provided afterwards, using 'update'.+initDerive+  :: Ptr Raw.HasherInternal -- ^ Will be mutated.+  -> Context+  -> IO ()+initDerive ph (Context ctx) =+  BA.withByteArray ctx $ \pc ->+  Raw.init_derive_key ph pc++-- | Update 'Raw.HasherInternal' state with new data.+update+  :: forall bin+  .  BA.ByteArrayAccess bin+  => Ptr Raw.HasherInternal -- ^ Will be mutated.+  -> [bin]+  -> IO () -- ^+update ph bins =+  for_ bins $ \bin ->+  BA.withByteArray bin $ \pbin ->+  Raw.update ph pbin (fromIntegral (BA.length bin))++-- | Finalize 'Raw.HasherInternal' state and obtain a digest.+--+-- The 'Raw.HasherInternal' is mutated.+finalize+  :: forall len+  .  KnownNat len+  => Ptr Raw.HasherInternal -- ^ Will be mutated.+  -> IO (Digest len) -- ^+finalize ph =+  fmap snd $ allocRetDigest $ \pd ->+  Raw.finalize ph pd (fromIntegral (natVal (Proxy @len)))++--------------------------------------------------------------------------------++showBase16 :: BA.ByteArrayAccess x => x -> String+showBase16 = fmap (toEnum . fromIntegral)+           . BA.unpack @BA.ScrubbedBytes+           . BA.convertToBase BA.Base16
+ lib/BLAKE3/Raw.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE DataKinds #-}+{-# OPTIONS_HADDOCK hide not_home #-}++module BLAKE3.Raw where++import Data.Word+import Foreign.C.Types+import Foreign.C.String+import Foreign.Ptr+import Prelude hiding (init)++--------------------------------------------------------------------------------++type HASHER_ALIGNMENT = 8+-- | In bytes.+type HASHER_SIZE = 1912+-- | In bytes.+type KEY_LEN = 32+-- | In bytes.+type DEFAULT_DIGEST_LEN = 32+-- | In bytes.+type BLOCK_SIZE = 64++type CHUNK_LEN = 1024+type MAX_DEPTH = 54+type MAX_SIMD_DEGREE = 16++-- | Opaque datatype of size 'HASHER_SIZE' and alignment 'HASHER_ALIGNMENT'.+--+-- Obtain with 'BLAKE3.IO.withHasherInternal'.+data HasherInternal++-- | @void blake3_hasher_init(blake3_hasher *self)@+foreign import ccall unsafe+  "blake3.h blake3_hasher_init"+  init+    :: Ptr HasherInternal+    -> IO () -- ^++-- | @void blake3_hasher_init_keyed(blake3_hasher *self, const uint8_t key['KEY_LEN'])@+foreign import ccall unsafe+  "blake3.h blake3_hasher_init_keyed"+  init_keyed+    :: Ptr HasherInternal+    -> Ptr Word8   -- ^ Key of length 'KEY_LEN'.+    -> IO ()++-- | @void blake3_hasher_init_derive_key(blake3_hasher *self, const char *context)@+foreign import ccall unsafe+  "blake3.h blake3_hasher_init_derive_key"+  init_derive_key+    :: Ptr HasherInternal+    -> CString  -- ^ Context.+    -> IO ()    -- ^++-- | @void blake3_hasher_update(blake3_hasher *self, const void *input, size_t input_len)@+foreign import ccall unsafe+  "blake3.h blake3_hasher_update"+  update+    :: Ptr HasherInternal+    -> Ptr Word8 -- ^ Data.+    -> CSize     -- ^ Data length.+    -> IO () -- ^++-- | @void blake3_hasher_finalize(const blake3_hasher *self, uint8_t *out, size_t out_len)@+foreign import ccall unsafe+  "blake3.h blake3_hasher_finalize"+  finalize+    :: Ptr HasherInternal+    -> Ptr Word8 -- ^ Out.+    -> CSize     -- ^ Out length.+    -> IO () -- ^++-- | @void blake3_hasher_finalize_seek(const blake3_hasher *self, uint64_t seek, uint8_t *out, size_t out_len)@+foreign import ccall unsafe+  "blake3.h blake3_hasher_finalize"+  finalize_seek+    :: Ptr HasherInternal+    -> Word64      -- ^ Seek.+    -> Ptr Word8   -- ^ Out.+    -> CSize       -- ^ Out length.+    -> IO () -- ^
+ test/Main.hs view
@@ -0,0 +1,320 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+module Main (main) where++import qualified Data.ByteArray as BA+import qualified Data.List as List+import Data.Proxy+import Data.Word+import GHC.TypeLits+import qualified Test.Tasty as Tasty+import qualified Test.Tasty.Runners as Tasty+import Test.Tasty (TestTree, testGroup)+import Test.Tasty.HUnit (testCase, (@=?))+import qualified BLAKE3 as B++--------------------------------------------------------------------------------++main :: IO ()+main = Tasty.defaultMainWithIngredients+    [ Tasty.consoleTestReporter+    , Tasty.listingTests+    ] tt_BLAKE3++tt_BLAKE3 :: TestTree+tt_BLAKE3 = testGroup "BLAKE3"+  [ tt_chunksOf+  , tt_testVectors+  , tt_context+  , tt_key+  , tt_hasher+  ]+++{-++"Each test is an input length and three outputs, one for each of the hash,+keyed_hash, and derive_key modes. The input in each case is filled with a+251-byte-long repeating pattern: 0, 1, 2, ..., 249, 250, 0, 1, ... The key used+with keyed_hash is the 32-byte ASCII string given in the 'key' field below. For+derive_key, the test input is used as the input key, and the context string is+'BLAKE3 2019-12-27 16:29:52 test vectors context'. (As good practice for+following the security requirements of derive_key, test runners should make that+context string a hardcoded constant, and we do not provided it in+machine-readable form.) Outputs are encoded as hexadecimal. Each case is an+extended output, and implementations should also check that the first 32 bytes+match their default-length output.",+-}+++--------------------------------------------------------------------------------+-- Helpers++-- | Like 'Data.List.Split.chunksOf'.+chunksOf :: Int -> [x] -> [[x]]+chunksOf _ [] = []+chunksOf n xs+  | n < 1     = error "chunksOf 0 _"+  | otherwise = let (pre, pos) = List.splitAt n xs+                in pre : chunksOf n pos++tt_chunksOf :: TestTree+tt_chunksOf = testGroup "chunksOf"+  [ testCase "empty list" $ do+      [] @=? chunksOf 1 ""+      [] @=? chunksOf 2 ""+  , testCase "non-empty list" $ do+      ["a"] @=? chunksOf 1 "a"+      ["a","b"] @=? chunksOf 1 "ab"+      ["a","b","c"] @=? chunksOf 1 "abc"+      ["a","b","c","d"] @=? chunksOf 1 "abcd"++      ["a"] @=? chunksOf 2 "a"+      ["ab"] @=? chunksOf 2 "ab"+      ["ab","c"] @=? chunksOf 2 "abc"+      ["ab","cd"] @=? chunksOf 2 "abcd"++      ["a"] @=? chunksOf 3 "a"+      ["ab"] @=? chunksOf 3 "ab"+      ["abc"] @=? chunksOf 3 "abc"+      ["abc","d"] @=? chunksOf 3 "abcd"+  ]++--------------------------------------------------------------------------------++tt_hasher :: TestTree+tt_hasher = testGroup "Hasher"+  [ testCase "hasher: no-mutate" $ do+      let h1 = B.hasher+      "af1349b9f5f9a1a6a040" @=? show (B.finalize @10 h1)+      let h2 = B.update @BA.ScrubbedBytes h1 ["upd"]+      "af1349b9f5f9a1a6a040" @=? show (B.finalize @10 h1)+      "3bad57477a5020f06859" @=? show (B.finalize @10 h2)+      let h3 = B.update @BA.ScrubbedBytes h2 ["fin"]+      "af1349b9f5f9a1a6a040" @=? show (B.finalize @10 h1)+      "3bad57477a5020f06859" @=? show (B.finalize @10 h2)+      "48503741232720f6ca03" @=? show (B.finalize @10 h3)++  , testCase "hasherKeyed: no-mutate" $ do+      let h1 = B.hasherKeyed testVector_key+      "92b2b75604ed3c761f9d" @=? show (B.finalize @10 h1)+      let h2 = B.update @BA.ScrubbedBytes h1 ["upd"]+      "92b2b75604ed3c761f9d" @=? show (B.finalize @10 h1)+      "f46255baa87a3280d644" @=? show (B.finalize @10 h2)+      let h3 = B.update @BA.ScrubbedBytes h2 ["fin"]+      "92b2b75604ed3c761f9d" @=? show (B.finalize @10 h1)+      "f46255baa87a3280d644" @=? show (B.finalize @10 h2)+      "b5ee60b5d89ac7e1289b" @=? show (B.finalize @10 h3)+  ]+++testVector_context :: B.Context+testVector_context = "BLAKE3 2019-12-27 16:29:52 test vectors context"++tt_context :: TestTree+tt_context = testGroup "Context"+  [ testCase "empty" $ do+      Just "" @=? fmap show (B.context ("" :: BA.ScrubbedBytes))+  , testCase "non-empty" $ do+      Just "6869" @=? fmap show (B.context ("hi" :: BA.ScrubbedBytes))+  , testCase "has null" $ do+      Nothing @=? B.context ("\NUL" :: BA.ScrubbedBytes)+      Nothing @=? B.context ("hi\NUL" :: BA.ScrubbedBytes)+  ]++tt_testVectors :: TestTree+tt_testVectors = testGroup "TestVectors" (fmap mkTestTree testVectors)++testVector_input :: Int -> [BA.Bytes]+testVector_input n =+  let w8s = take n (cycle [0..250]) :: [Word8]+  in BA.pack <$> chunksOf 100 w8s++testVector_key :: B.Key+Just testVector_key = B.key+  ("whats the Elvish word for friend" :: BA.ScrubbedBytes)++tt_key :: TestTree+tt_key = testGroup "Key"+  [ testCase "empty" $ do+      Nothing @=? B.key (BA.replicate 0 0 :: BA.Bytes)+  , testCase "short" $ do+      Nothing @=? B.key (BA.replicate 2 0 :: BA.Bytes)+  , testCase "ok" $ do+      "77686174732074686520456c7669736820776f726420666f7220667269656e64"+        @=? show testVector_key+  , testCase "long" $ do+      Nothing @=? B.key (BA.replicate 100 0 :: BA.Bytes)+  ]++data TestVector = TestVector+  { tv_inputLen :: Int+  , tv_hash :: String -- ^ Base16 rendering of 'B.Digest'+  , tv_hashKeyed :: String -- ^ Base16 rendering of 'B.Digest'+  , tv_derivedKey :: String -- ^ Base16 rendering of 'B.Digest'+  }++mkTestTree :: TestVector -> TestTree+mkTestTree tv = testGroup (take 16 (tv_hash tv)) $+  let input = testVector_input (tv_inputLen tv)+      digestLen = length (tv_hash tv) `div` 2+  in case someNatVal (fromIntegral digestLen) of+       Nothing -> error "Bad tv_inputLen"+       Just (SomeNat (Proxy :: Proxy len)) ->+         let digest :: B.Digest len = B.hash input+             digestF :: B.Digest len = hashF input+             digestKeyed :: B.Digest len = B.hashKeyed testVector_key input+             digestKeyedF :: B.Digest len = hashKeyedF testVector_key input+             digestDerived :: B.Digest len = B.derive testVector_context input+         in [ testCase "hash" $ tv_hash tv @=? show digest+            , testCase "hashF" $ tv_hash tv @=? show digestF+            , testCase "hashKeyed" $ tv_hashKeyed tv @=? show digestKeyed+            , testCase "hashKeyedF" $ tv_hashKeyed tv @=? show digestKeyedF+            , testCase "derivedKey" $ tv_derivedKey tv @=? show digestDerived+            ]++-- | Same as 'B.hashKeyed'. For testing.+hashKeyedF :: (KnownNat len, BA.ByteArrayAccess bin) => B.Key -> [bin] -> B.Digest len+hashKeyedF k = B.finalize . B.update (B.hasherKeyed k)++-- | Same as 'B.hash'. For testing.+hashF :: (KnownNat len, BA.ByteArrayAccess bin) => [bin] -> B.Digest len+hashF = B.finalize . B.update B.hasher++testVectors :: [TestVector]+testVectors = [+    TestVector {+      tv_inputLen = 0,+      tv_hash = "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262e00f03e7b69af26b7faaf09fcd333050338ddfe085b8cc869ca98b206c08243a26f5487789e8f660afe6c99ef9e0c52b92e7393024a80459cf91f476f9ffdbda7001c22e159b402631f277ca96f2defdf1078282314e763699a31c5363165421cce14d",+      tv_hashKeyed = "92b2b75604ed3c761f9d6f62392c8a9227ad0ea3f09573e783f1498a4ed60d26b18171a2f22a4b94822c701f107153dba24918c4bae4d2945c20ece13387627d3b73cbf97b797d5e59948c7ef788f54372df45e45e4293c7dc18c1d41144a9758be58960856be1eabbe22c2653190de560ca3b2ac4aa692a9210694254c371e851bc8f",+      tv_derivedKey = "2cc39783c223154fea8dfb7c1b1660f2ac2dcbd1c1de8277b0b0dd39b7e50d7d905630c8be290dfcf3e6842f13bddd573c098c3f17361f1f206b8cad9d088aa4a3f746752c6b0ce6a83b0da81d59649257cdf8eb3e9f7d4998e41021fac119deefb896224ac99f860011f73609e6e0e4540f93b273e56547dfd3aa1a035ba6689d89a0"+    },+    TestVector {+      tv_inputLen = 1,+      tv_hash = "2d3adedff11b61f14c886e35afa036736dcd87a74d27b5c1510225d0f592e213c3a6cb8bf623e20cdb535f8d1a5ffb86342d9c0b64aca3bce1d31f60adfa137b358ad4d79f97b47c3d5e79f179df87a3b9776ef8325f8329886ba42f07fb138bb502f4081cbcec3195c5871e6c23e2cc97d3c69a613eba131e5f1351f3f1da786545e5",+      tv_hashKeyed = "6d7878dfff2f485635d39013278ae14f1454b8c0a3a2d34bc1ab38228a80c95b6568c0490609413006fbd428eb3fd14e7756d90f73a4725fad147f7bf70fd61c4e0cf7074885e92b0e3f125978b4154986d4fb202a3f331a3fb6cf349a3a70e49990f98fe4289761c8602c4e6ab1138d31d3b62218078b2f3ba9a88e1d08d0dd4cea11",+      tv_derivedKey = "b3e2e340a117a499c6cf2398a19ee0d29cca2bb7404c73063382693bf66cb06c5827b91bf889b6b97c5477f535361caefca0b5d8c4746441c57617111933158950670f9aa8a05d791daae10ac683cbef8faf897c84e6114a59d2173c3f417023a35d6983f2c7dfa57e7fc559ad751dbfb9ffab39c2ef8c4aafebc9ae973a64f0c76551"+    },+    TestVector {+      tv_inputLen = 1023,+      tv_hash = "10108970eeda3eb932baac1428c7a2163b0e924c9a9e25b35bba72b28f70bd11a182d27a591b05592b15607500e1e8dd56bc6c7fc063715b7a1d737df5bad3339c56778957d870eb9717b57ea3d9fb68d1b55127bba6a906a4a24bbd5acb2d123a37b28f9e9a81bbaae360d58f85e5fc9d75f7c370a0cc09b6522d9c8d822f2f28f485",+      tv_hashKeyed = "c951ecdf03288d0fcc96ee3413563d8a6d3589547f2c2fb36d9786470f1b9d6e890316d2e6d8b8c25b0a5b2180f94fb1a158ef508c3cde45e2966bd796a696d3e13efd86259d756387d9becf5c8bf1ce2192b87025152907b6d8cc33d17826d8b7b9bc97e38c3c85108ef09f013e01c229c20a83d9e8efac5b37470da28575fd755a10",+      tv_derivedKey = "74a16c1c3d44368a86e1ca6df64be6a2f64cce8f09220787450722d85725dea59c413264404661e9e4d955409dfe4ad3aa487871bcd454ed12abfe2c2b1eb7757588cf6cb18d2eccad49e018c0d0fec323bec82bf1644c6325717d13ea712e6840d3e6e730d35553f59eff5377a9c350bcc1556694b924b858f329c44ee64b884ef00d"+    },+    TestVector {+      tv_inputLen = 1024,+      tv_hash = "42214739f095a406f3fc83deb889744ac00df831c10daa55189b5d121c855af71cf8107265ecdaf8505b95d8fcec83a98a6a96ea5109d2c179c47a387ffbb404756f6eeae7883b446b70ebb144527c2075ab8ab204c0086bb22b7c93d465efc57f8d917f0b385c6df265e77003b85102967486ed57db5c5ca170ba441427ed9afa684e",+      tv_hashKeyed = "75c46f6f3d9eb4f55ecaaee480db732e6c2105546f1e675003687c31719c7ba4a78bc838c72852d4f49c864acb7adafe2478e824afe51c8919d06168414c265f298a8094b1ad813a9b8614acabac321f24ce61c5a5346eb519520d38ecc43e89b5000236df0597243e4d2493fd626730e2ba17ac4d8824d09d1a4a8f57b8227778e2de",+      tv_derivedKey = "7356cd7720d5b66b6d0697eb3177d9f8d73a4a5c5e968896eb6a6896843027066c23b601d3ddfb391e90d5c8eccdef4ae2a264bce9e612ba15e2bc9d654af1481b2e75dbabe615974f1070bba84d56853265a34330b4766f8e75edd1f4a1650476c10802f22b64bd3919d246ba20a17558bc51c199efdec67e80a227251808d8ce5bad"+    },+    TestVector {+      tv_inputLen = 1025,+      tv_hash = "d00278ae47eb27b34faecf67b4fe263f82d5412916c1ffd97c8cb7fb814b8444f4c4a22b4b399155358a994e52bf255de60035742ec71bd08ac275a1b51cc6bfe332b0ef84b409108cda080e6269ed4b3e2c3f7d722aa4cdc98d16deb554e5627be8f955c98e1d5f9565a9194cad0c4285f93700062d9595adb992ae68ff12800ab67a",+      tv_hashKeyed = "357dc55de0c7e382c900fd6e320acc04146be01db6a8ce7210b7189bd664ea69362396b77fdc0d2634a552970843722066c3c15902ae5097e00ff53f1e116f1cd5352720113a837ab2452cafbde4d54085d9cf5d21ca613071551b25d52e69d6c81123872b6f19cd3bc1333edf0c52b94de23ba772cf82636cff4542540a7738d5b930",+      tv_derivedKey = "effaa245f065fbf82ac186839a249707c3bddf6d3fdda22d1b95a3c970379bcb5d31013a167509e9066273ab6e2123bc835b408b067d88f96addb550d96b6852dad38e320b9d940f86db74d398c770f462118b35d2724efa13da97194491d96dd37c3c09cbef665953f2ee85ec83d88b88d11547a6f911c8217cca46defa2751e7f3ad"+    },+    TestVector {+      tv_inputLen = 2048,+      tv_hash = "e776b6028c7cd22a4d0ba182a8bf62205d2ef576467e838ed6f2529b85fba24a9a60bf80001410ec9eea6698cd537939fad4749edd484cb541aced55cd9bf54764d063f23f6f1e32e12958ba5cfeb1bf618ad094266d4fc3c968c2088f677454c288c67ba0dba337b9d91c7e1ba586dc9a5bc2d5e90c14f53a8863ac75655461cea8f9",+      tv_hashKeyed = "879cf1fa2ea0e79126cb1063617a05b6ad9d0b696d0d757cf053439f60a99dd10173b961cd574288194b23ece278c330fbb8585485e74967f31352a8183aa782b2b22f26cdcadb61eed1a5bc144b8198fbb0c13abbf8e3192c145d0a5c21633b0ef86054f42809df823389ee40811a5910dcbd1018af31c3b43aa55201ed4edaac74fe",+      tv_derivedKey = "7b2945cb4fef70885cc5d78a87bf6f6207dd901ff239201351ffac04e1088a23e2c11a1ebffcea4d80447867b61badb1383d842d4e79645d48dd82ccba290769caa7af8eaa1bd78a2a5e6e94fbdab78d9c7b74e894879f6a515257ccf6f95056f4e25390f24f6b35ffbb74b766202569b1d797f2d4bd9d17524c720107f985f4ddc583"+    },+    TestVector {+      tv_inputLen = 2049,+      tv_hash = "5f4d72f40d7a5f82b15ca2b2e44b1de3c2ef86c426c95c1af0b687952256303096de31d71d74103403822a2e0bc1eb193e7aecc9643a76b7bbc0c9f9c52e8783aae98764ca468962b5c2ec92f0c74eb5448d519713e09413719431c802f948dd5d90425a4ecdadece9eb178d80f26efccae630734dff63340285adec2aed3b51073ad3",+      tv_hashKeyed = "9f29700902f7c86e514ddc4df1e3049f258b2472b6dd5267f61bf13983b78dd5f9a88abfefdfa1e00b418971f2b39c64ca621e8eb37fceac57fd0c8fc8e117d43b81447be22d5d8186f8f5919ba6bcc6846bd7d50726c06d245672c2ad4f61702c646499ee1173daa061ffe15bf45a631e2946d616a4c345822f1151284712f76b2b0e",+      tv_derivedKey = "2ea477c5515cc3dd606512ee72bb3e0e758cfae7232826f35fb98ca1bcbdf27316d8e9e79081a80b046b60f6a263616f33ca464bd78d79fa18200d06c7fc9bffd808cc4755277a7d5e09da0f29ed150f6537ea9bed946227ff184cc66a72a5f8c1e4bd8b04e81cf40fe6dc4427ad5678311a61f4ffc39d195589bdbc670f63ae70f4b6"+    },+    TestVector {+      tv_inputLen = 3072,+      tv_hash = "b98cb0ff3623be03326b373de6b9095218513e64f1ee2edd2525c7ad1e5cffd29a3f6b0b978d6608335c09dc94ccf682f9951cdfc501bfe47b9c9189a6fc7b404d120258506341a6d802857322fbd20d3e5dae05b95c88793fa83db1cb08e7d8008d1599b6209d78336e24839724c191b2a52a80448306e0daa84a3fdb566661a37e11",+      tv_hashKeyed = "044a0e7b172a312dc02a4c9a818c036ffa2776368d7f528268d2e6b5df19177022f302d0529e4174cc507c463671217975e81dab02b8fdeb0d7ccc7568dd22574c783a76be215441b32e91b9a904be8ea81f7a0afd14bad8ee7c8efc305ace5d3dd61b996febe8da4f56ca0919359a7533216e2999fc87ff7d8f176fbecb3d6f34278b",+      tv_derivedKey = "050df97f8c2ead654d9bb3ab8c9178edcd902a32f8495949feadcc1e0480c46b3604131bbd6e3ba573b6dd682fa0a63e5b165d39fc43a625d00207607a2bfeb65ff1d29292152e26b298868e3b87be95d6458f6f2ce6118437b632415abe6ad522874bcd79e4030a5e7bad2efa90a7a7c67e93f0a18fb28369d0a9329ab5c24134ccb0"+    },+    TestVector {+      tv_inputLen = 3073,+      tv_hash = "7124b49501012f81cc7f11ca069ec9226cecb8a2c850cfe644e327d22d3e1cd39a27ae3b79d68d89da9bf25bc27139ae65a324918a5f9b7828181e52cf373c84f35b639b7fccbb985b6f2fa56aea0c18f531203497b8bbd3a07ceb5926f1cab74d14bd66486d9a91eba99059a98bd1cd25876b2af5a76c3e9eed554ed72ea952b603bf",+      tv_hashKeyed = "68dede9bef00ba89e43f31a6825f4cf433389fedae75c04ee9f0cf16a427c95a96d6da3fe985054d3478865be9a092250839a697bbda74e279e8a9e69f0025e4cfddd6cfb434b1cd9543aaf97c635d1b451a4386041e4bb100f5e45407cbbc24fa53ea2de3536ccb329e4eb9466ec37093a42cf62b82903c696a93a50b702c80f3c3c5",+      tv_derivedKey = "72613c9ec9ff7e40f8f5c173784c532ad852e827dba2bf85b2ab4b76f7079081576288e552647a9d86481c2cae75c2dd4e7c5195fb9ada1ef50e9c5098c249d743929191441301c69e1f48505a4305ec1778450ee48b8e69dc23a25960fe33070ea549119599760a8a2d28aeca06b8c5e9ba58bc19e11fe57b6ee98aa44b2a8e6b14a5"+    },+    TestVector {+      tv_inputLen = 4096,+      tv_hash = "015094013f57a5277b59d8475c0501042c0b642e531b0a1c8f58d2163229e9690289e9409ddb1b99768eafe1623da896faf7e1114bebeadc1be30829b6f8af707d85c298f4f0ff4d9438aef948335612ae921e76d411c3a9111df62d27eaf871959ae0062b5492a0feb98ef3ed4af277f5395172dbe5c311918ea0074ce0036454f620",+      tv_hashKeyed = "befc660aea2f1718884cd8deb9902811d332f4fc4a38cf7c7300d597a081bfc0bbb64a36edb564e01e4b4aaf3b060092a6b838bea44afebd2deb8298fa562b7b597c757b9df4c911c3ca462e2ac89e9a787357aaf74c3b56d5c07bc93ce899568a3eb17d9250c20f6c5f6c1e792ec9a2dcb715398d5a6ec6d5c54f586a00403a1af1de",+      tv_derivedKey = "1e0d7f3db8c414c97c6307cbda6cd27ac3b030949da8e23be1a1a924ad2f25b9d78038f7b198596c6cc4a9ccf93223c08722d684f240ff6569075ed81591fd93f9fff1110b3a75bc67e426012e5588959cc5a4c192173a03c00731cf84544f65a2fb9378989f72e9694a6a394a8a30997c2e67f95a504e631cd2c5f55246024761b245"+    },+    TestVector {+      tv_inputLen = 4097,+      tv_hash = "9b4052b38f1c5fc8b1f9ff7ac7b27cd242487b3d890d15c96a1c25b8aa0fb99505f91b0b5600a11251652eacfa9497b31cd3c409ce2e45cfe6c0a016967316c426bd26f619eab5d70af9a418b845c608840390f361630bd497b1ab44019316357c61dbe091ce72fc16dc340ac3d6e009e050b3adac4b5b2c92e722cffdc46501531956",+      tv_hashKeyed = "00df940cd36bb9fa7cbbc3556744e0dbc8191401afe70520ba292ee3ca80abbc606db4976cfdd266ae0abf667d9481831ff12e0caa268e7d3e57260c0824115a54ce595ccc897786d9dcbf495599cfd90157186a46ec800a6763f1c59e36197e9939e900809f7077c102f888caaf864b253bc41eea812656d46742e4ea42769f89b83f",+      tv_derivedKey = "aca51029626b55fda7117b42a7c211f8c6e9ba4fe5b7a8ca922f34299500ead8a897f66a400fed9198fd61dd2d58d382458e64e100128075fc54b860934e8de2e84170734b06e1d212a117100820dbc48292d148afa50567b8b84b1ec336ae10d40c8c975a624996e12de31abbe135d9d159375739c333798a80c64ae895e51e22f3ad"+    },+    TestVector {+      tv_inputLen = 5120,+      tv_hash = "9cadc15fed8b5d854562b26a9536d9707cadeda9b143978f319ab34230535833acc61c8fdc114a2010ce8038c853e121e1544985133fccdd0a2d507e8e615e611e9a0ba4f47915f49e53d721816a9198e8b30f12d20ec3689989175f1bf7a300eee0d9321fad8da232ece6efb8e9fd81b42ad161f6b9550a069e66b11b40487a5f5059",+      tv_hashKeyed = "2c493e48e9b9bf31e0553a22b23503c0a3388f035cece68eb438d22fa1943e209b4dc9209cd80ce7c1f7c9a744658e7e288465717ae6e56d5463d4f80cdb2ef56495f6a4f5487f69749af0c34c2cdfa857f3056bf8d807336a14d7b89bf62bef2fb54f9af6a546f818dc1e98b9e07f8a5834da50fa28fb5874af91bf06020d1bf0120e",+      tv_derivedKey = "7a7acac8a02adcf3038d74cdd1d34527de8a0fcc0ee3399d1262397ce5817f6055d0cefd84d9d57fe792d65a278fd20384ac6c30fdb340092f1a74a92ace99c482b28f0fc0ef3b923e56ade20c6dba47e49227166251337d80a037e987ad3a7f728b5ab6dfafd6e2ab1bd583a95d9c895ba9c2422c24ea0f62961f0dca45cad47bfa0d"+    },+    TestVector {+      tv_inputLen = 5121,+      tv_hash = "628bd2cb2004694adaab7bbd778a25df25c47b9d4155a55f8fbd79f2fe154cff96adaab0613a6146cdaabe498c3a94e529d3fc1da2bd08edf54ed64d40dcd6777647eac51d8277d70219a9694334a68bc8f0f23e20b0ff70ada6f844542dfa32cd4204ca1846ef76d811cdb296f65e260227f477aa7aa008bac878f72257484f2b6c95",+      tv_hashKeyed = "6ccf1c34753e7a044db80798ecd0782a8f76f33563accaddbfbb2e0ea4b2d0240d07e63f13667a8d1490e5e04f13eb617aea16a8c8a5aaed1ef6fbde1b0515e3c81050b361af6ead126032998290b563e3caddeaebfab592e155f2e161fb7cba939092133f23f9e65245e58ec23457b78a2e8a125588aad6e07d7f11a85b88d375b72d",+      tv_derivedKey = "b07f01e518e702f7ccb44a267e9e112d403a7b3f4883a47ffbed4b48339b3c341a0add0ac032ab5aaea1e4e5b004707ec5681ae0fcbe3796974c0b1cf31a194740c14519273eedaabec832e8a784b6e7cfc2c5952677e6c3f2c3914454082d7eb1ce1766ac7d75a4d3001fc89544dd46b5147382240d689bbbaefc359fb6ae30263165"+    },+    TestVector {+      tv_inputLen = 6144,+      tv_hash = "3e2e5b74e048f3add6d21faab3f83aa44d3b2278afb83b80b3c35164ebeca2054d742022da6fdda444ebc384b04a54c3ac5839b49da7d39f6d8a9db03deab32aade156c1c0311e9b3435cde0ddba0dce7b26a376cad121294b689193508dd63151603c6ddb866ad16c2ee41585d1633a2cea093bea714f4c5d6b903522045b20395c83",+      tv_hashKeyed = "3d6b6d21281d0ade5b2b016ae4034c5dec10ca7e475f90f76eac7138e9bc8f1dc35754060091dc5caf3efabe0603c60f45e415bb3407db67e6beb3d11cf8e4f7907561f05dace0c15807f4b5f389c841eb114d81a82c02a00b57206b1d11fa6e803486b048a5ce87105a686dee041207e095323dfe172df73deb8c9532066d88f9da7e",+      tv_derivedKey = "2a95beae63ddce523762355cf4b9c1d8f131465780a391286a5d01abb5683a1597099e3c6488aab6c48f3c15dbe1942d21dbcdc12115d19a8b8465fb54e9053323a9178e4275647f1a9927f6439e52b7031a0b465c861a3fc531527f7758b2b888cf2f20582e9e2c593709c0a44f9c6e0f8b963994882ea4168827823eef1f64169fef"+    },+    TestVector {+      tv_inputLen = 6145,+      tv_hash = "f1323a8631446cc50536a9f705ee5cb619424d46887f3c376c695b70e0f0507f18a2cfdd73c6e39dd75ce7c1c6e3ef238fd54465f053b25d21044ccb2093beb015015532b108313b5829c3621ce324b8e14229091b7c93f32db2e4e63126a377d2a63a3597997d4f1cba59309cb4af240ba70cebff9a23d5e3ff0cdae2cfd54e070022",+      tv_hashKeyed = "9ac301e9e39e45e3250a7e3b3df701aa0fb6889fbd80eeecf28dbc6300fbc539f3c184ca2f59780e27a576c1d1fb9772e99fd17881d02ac7dfd39675aca918453283ed8c3169085ef4a466b91c1649cc341dfdee60e32231fc34c9c4e0b9a2ba87ca8f372589c744c15fd6f985eec15e98136f25beeb4b13c4e43dc84abcc79cd4646c",+      tv_derivedKey = "379bcc61d0051dd489f686c13de00d5b14c505245103dc040d9e4dd1facab8e5114493d029bdbd295aaa744a59e31f35c7f52dba9c3642f773dd0b4262a9980a2aef811697e1305d37ba9d8b6d850ef07fe41108993180cf779aeece363704c76483458603bbeeb693cffbbe5588d1f3535dcad888893e53d977424bb707201569a8d2"+    },+    TestVector {+      tv_inputLen = 7168,+      tv_hash = "61da957ec2499a95d6b8023e2b0e604ec7f6b50e80a9678b89d2628e99ada77a5707c321c83361793b9af62a40f43b523df1c8633cecb4cd14d00bdc79c78fca5165b863893f6d38b02ff7236c5a9a8ad2dba87d24c547cab046c29fc5bc1ed142e1de4763613bb162a5a538e6ef05ed05199d751f9eb58d332791b8d73fb74e4fce95",+      tv_hashKeyed = "b42835e40e9d4a7f42ad8cc04f85a963a76e18198377ed84adddeaecacc6f3fca2f01d5277d69bb681c70fa8d36094f73ec06e452c80d2ff2257ed82e7ba348400989a65ee8daa7094ae0933e3d2210ac6395c4af24f91c2b590ef87d7788d7066ea3eaebca4c08a4f14b9a27644f99084c3543711b64a070b94f2c9d1d8a90d035d52",+      tv_derivedKey = "11c37a112765370c94a51415d0d651190c288566e295d505defdad895dae223730d5a5175a38841693020669c7638f40b9bc1f9f39cf98bda7a5b54ae24218a800a2116b34665aa95d846d97ea988bfcb53dd9c055d588fa21ba78996776ea6c40bc428b53c62b5f3ccf200f647a5aae8067f0ea1976391fcc72af1945100e2a6dcb88"+    },+    TestVector {+      tv_inputLen = 7169,+      tv_hash = "a003fc7a51754a9b3c7fae0367ab3d782dccf28855a03d435f8cfe74605e781798a8b20534be1ca9eb2ae2df3fae2ea60e48c6fb0b850b1385b5de0fe460dbe9d9f9b0d8db4435da75c601156df9d047f4ede008732eb17adc05d96180f8a73548522840779e6062d643b79478a6e8dbce68927f36ebf676ffa7d72d5f68f050b119c8",+      tv_hashKeyed = "ed9b1a922c046fdb3d423ae34e143b05ca1bf28b710432857bf738bcedbfa5113c9e28d72fcbfc020814ce3f5d4fc867f01c8f5b6caf305b3ea8a8ba2da3ab69fabcb438f19ff11f5378ad4484d75c478de425fb8e6ee809b54eec9bdb184315dc856617c09f5340451bf42fd3270a7b0b6566169f242e533777604c118a6358250f54",+      tv_derivedKey = "554b0a5efea9ef183f2f9b931b7497995d9eb26f5c5c6dad2b97d62fc5ac31d99b20652c016d88ba2a611bbd761668d5eda3e568e940faae24b0d9991c3bd25a65f770b89fdcadabcb3d1a9c1cb63e69721cacf1ae69fefdcef1e3ef41bc5312ccc17222199e47a26552c6adc460cf47a72319cb5039369d0060eaea59d6c65130f1dd"+    },+    TestVector {+      tv_inputLen = 8192,+      tv_hash = "aae792484c8efe4f19e2ca7d371d8c467ffb10748d8a5a1ae579948f718a2a635fe51a27db045a567c1ad51be5aa34c01c6651c4d9b5b5ac5d0fd58cf18dd61a47778566b797a8c67df7b1d60b97b19288d2d877bb2df417ace009dcb0241ca1257d62712b6a4043b4ff33f690d849da91ea3bf711ed583cb7b7a7da2839ba71309bbf",+      tv_hashKeyed = "dc9637c8845a770b4cbf76b8daec0eebf7dc2eac11498517f08d44c8fc00d58a4834464159dcbc12a0ba0c6d6eb41bac0ed6585cabfe0aca36a375e6c5480c22afdc40785c170f5a6b8a1107dbee282318d00d915ac9ed1143ad40765ec120042ee121cd2baa36250c618adaf9e27260fda2f94dea8fb6f08c04f8f10c78292aa46102",+      tv_derivedKey = "ad01d7ae4ad059b0d33baa3c01319dcf8088094d0359e5fd45d6aeaa8b2d0c3d4c9e58958553513b67f84f8eac653aeeb02ae1d5672dcecf91cd9985a0e67f4501910ecba25555395427ccc7241d70dc21c190e2aadee875e5aae6bf1912837e53411dabf7a56cbf8e4fb780432b0d7fe6cec45024a0788cf5874616407757e9e6bef7"+    },+    TestVector {+      tv_inputLen = 8193,+      tv_hash = "bab6c09cb8ce8cf459261398d2e7aef35700bf488116ceb94a36d0f5f1b7bc3bb2282aa69be089359ea1154b9a9286c4a56af4de975a9aa4a5c497654914d279bea60bb6d2cf7225a2fa0ff5ef56bbe4b149f3ed15860f78b4e2ad04e158e375c1e0c0b551cd7dfc82f1b155c11b6b3ed51ec9edb30d133653bb5709d1dbd55f4e1ff6",+      tv_hashKeyed = "954a2a75420c8d6547e3ba5b98d963e6fa6491addc8c023189cc519821b4a1f5f03228648fd983aef045c2fa8290934b0866b615f585149587dda2299039965328835a2b18f1d63b7e300fc76ff260b571839fe44876a4eae66cbac8c67694411ed7e09df51068a22c6e67d6d3dd2cca8ff12e3275384006c80f4db68023f24eebba57",+      tv_derivedKey = "af1e0346e389b17c23200270a64aa4e1ead98c61695d917de7d5b00491c9b0f12f20a01d6d622edf3de026a4db4e4526225debb93c1237934d71c7340bb5916158cbdafe9ac3225476b6ab57a12357db3abbad7a26c6e66290e44034fb08a20a8d0ec264f309994d2810c49cfba6989d7abb095897459f5425adb48aba07c5fb3c83c0"+    },+    TestVector {+      tv_inputLen = 16384,+      tv_hash = "f875d6646de28985646f34ee13be9a576fd515f76b5b0a26bb324735041ddde49d764c270176e53e97bdffa58d549073f2c660be0e81293767ed4e4929f9ad34bbb39a529334c57c4a381ffd2a6d4bfdbf1482651b172aa883cc13408fa67758a3e47503f93f87720a3177325f7823251b85275f64636a8f1d599c2e49722f42e93893",+      tv_hashKeyed = "9e9fc4eb7cf081ea7c47d1807790ed211bfec56aa25bb7037784c13c4b707b0df9e601b101e4cf63a404dfe50f2e1865bb12edc8fca166579ce0c70dba5a5c0fc960ad6f3772183416a00bd29d4c6e651ea7620bb100c9449858bf14e1ddc9ecd35725581ca5b9160de04060045993d972571c3e8f71e9d0496bfa744656861b169d65",+      tv_derivedKey = "160e18b5878cd0df1c3af85eb25a0db5344d43a6fbd7a8ef4ed98d0714c3f7e160dc0b1f09caa35f2f417b9ef309dfe5ebd67f4c9507995a531374d099cf8ae317542e885ec6f589378864d3ea98716b3bbb65ef4ab5e0ab5bb298a501f19a41ec19af84a5e6b428ecd813b1a47ed91c9657c3fba11c406bc316768b58f6802c9e9b57"+    },+    TestVector {+      tv_inputLen = 31744,+      tv_hash = "62b6960e1a44bcc1eb1a611a8d6235b6b4b78f32e7abc4fb4c6cdcce94895c47860cc51f2b0c28a7b77304bd55fe73af663c02d3f52ea053ba43431ca5bab7bfea2f5e9d7121770d88f70ae9649ea713087d1914f7f312147e247f87eb2d4ffef0ac978bf7b6579d57d533355aa20b8b77b13fd09748728a5cc327a8ec470f4013226f",+      tv_hashKeyed = "efa53b389ab67c593dba624d898d0f7353ab99e4ac9d42302ee64cbf9939a4193a7258db2d9cd32a7a3ecfce46144114b15c2fcb68a618a976bd74515d47be08b628be420b5e830fade7c080e351a076fbc38641ad80c736c8a18fe3c66ce12f95c61c2462a9770d60d0f77115bbcd3782b593016a4e728d4c06cee4505cb0c08a42ec",+      tv_derivedKey = "39772aef80e0ebe60596361e45b061e8f417429d529171b6764468c22928e28e9759adeb797a3fbf771b1bcea30150a020e317982bf0d6e7d14dd9f064bc11025c25f31e81bd78a921db0174f03dd481d30e93fd8e90f8b2fee209f849f2d2a52f31719a490fb0ba7aea1e09814ee912eba111a9fde9d5c274185f7bae8ba85d300a2b"+    },+    TestVector {+      tv_inputLen = 102400,+      tv_hash = "bc3e3d41a1146b069abffad3c0d44860cf664390afce4d9661f7902e7943e085e01c59dab908c04c3342b816941a26d69c2605ebee5ec5291cc55e15b76146e6745f0601156c3596cb75065a9c57f35585a52e1ac70f69131c23d611ce11ee4ab1ec2c009012d236648e77be9295dd0426f29b764d65de58eb7d01dd42248204f45f8e",+      tv_hashKeyed = "1c35d1a5811083fd7119f5d5d1ba027b4d01c0c6c49fb6ff2cf75393ea5db4a7f9dbdd3e1d81dcbca3ba241bb18760f207710b751846faaeb9dff8262710999a59b2aa1aca298a032d94eacfadf1aa192418eb54808db23b56e34213266aa08499a16b354f018fc4967d05f8b9d2ad87a7278337be9693fc638a3bfdbe314574ee6fc4",+      tv_derivedKey = "4652cff7a3f385a6103b5c260fc1593e13c778dbe608efb092fe7ee69df6e9c6d83a3e041bc3a48df2879f4a0a3ed40e7c961c73eff740f3117a0504c2dff4786d44fb17f1549eb0ba585e40ec29bf7732f0b7e286ff8acddc4cb1e23b87ff5d824a986458dcc6a04ac83969b80637562953df51ed1a7e90a7926924d2763778be8560"+    }+  ]