text: // -
  // Copyright 2009 Colin Percival
  // All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions
  // are met:
  // 1. Redistributions of source code must retain the above copyright
  //    notice, this list of conditions and the following disclaimer.
  // 2. Redistributions in binary form must reproduce the above copyright
  //    notice, this list of conditions and the following disclaimer in the
  //    documentation and/or other materials provided with the distribution.
  //
  // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  // SUCH DAMAGE.
  //
  // The code in this file is taken from a file which was originally written by Colin Percival as part of the Tarsnap
  // online backup system.
  // -
  //
  // File contains modifications by: The Gulden developers
  // All modifications:
  // Copyright (c) 2019 The Gulden developers
  // Authored by: Malcolm MacLeod (mmacleod@gmx.com)
  // Distributed under the GULDEN software license, see the accompanying
  // file COPYING in the root of this repository
  
  #if defined(USE_SSE2)
  
  #include <emmintrin.h>
  #include <stdint.h>
  
  #include "sysendian.h"
  
  #include "crypto_scrypt_smix_sse2.h"
  
  static void blkcpy(void*, const void*, size_t);
  static void blkxor(void*, const void*, size_t);
  static void salsa20_8(__m128i*);
  static void blockmix_salsa8(const __m128i*, __m128i*, __m128i*, size_t);
  static uint64_t integerify(const void*, size_t);
  
  static void blkcpy(void* dest, const void* src, size_t len)
  {
      __m128i* D = dest;
      const __m128i* S = src;
      size_t L = len / 16;
      size_t i;
  
      for (i = 0; i < L; i++)
      {
          D[i] = S[i];
      }
  }
  
  static void blkxor(void* dest, const void* src, size_t len)
  {
      __m128i* D = dest;
      const __m128i* S = src;
      size_t L = len / 16;
      size_t i;
  
      for (i = 0; i < L; i++)
      {
          D[i] = _mm_xor_si128(D[i], S[i]);
      }
  }
  
  /* salsa20_8(B):
   * Apply the salsa20/8 core to the provided block.
   */
  static void salsa20_8(__m128i B[4])
  {
      __m128i X0, X1, X2, X3;
      __m128i T;
      size_t i;
  
      X0 = B[0];
      X1 = B[1];
      X2 = B[2];
      X3 = B[3];
  
      for (i = 0; i < 8; i += 2)
      {
          // Operate on "columns".
          T = _mm_add_epi32(X0, X3);
          X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 7));
          X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 25));
          T = _mm_add_epi32(X1, X0);
          X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9));
          X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23));
          T = _mm_add_epi32(X2, X1);
          X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 13));
          X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 19));
          T = _mm_add_epi32(X3, X2);
          X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18));
          X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14));
  
          // Rearrange data.
          X1 = _mm_shuffle_epi32(X1, 0x93);
          X2 = _mm_shuffle_epi32(X2, 0x4E);
          X3 = _mm_shuffle_epi32(X3, 0x39);
  
          // Operate on "rows".
          T = _mm_add_epi32(X0, X1);
          X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 7));
          X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 25));
          T = _mm_add_epi32(X3, X0);
          X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9));
          X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23));
          T = _mm_add_epi32(X2, X3);
          X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 13));
          X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 19));
          T = _mm_add_epi32(X1, X2);
          X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18));
          X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14));
  
          // Rearrange data.
          X1 = _mm_shuffle_epi32(X1, 0x39);
          X2 = _mm_shuffle_epi32(X2, 0x4E);
          X3 = _mm_shuffle_epi32(X3, 0x93);
      }
  
      B[0] = _mm_add_epi32(B[0], X0);
      B[1] = _mm_add_epi32(B[1], X1);
      B[2] = _mm_add_epi32(B[2], X2);
      B[3] = _mm_add_epi32(B[3], X3);
  }
  
  /* blockmix_salsa8(Bin, Bout, X, r):
   * Compute Bout = BlockMix_{salsa20/8, r}(Bin).  The input Bin must be 128r
   * bytes in length; the output Bout must also be the same size.  The
   * temporary space X must be 64 bytes.
   */
  static void blockmix_salsa8(const __m128i* Bin, __m128i* Bout, __m128i* X, size_t r)
  {
      size_t i;
  
      // 1: X <-- B_{2r - 1}
      blkcpy(X, &Bin[8 * r - 4], 64);
  
      // 2: for i = 0 to 2r - 1 do
      for (i = 0; i < r; i++) {
          // 3: X <-- H(X \xor B_i)
          blkxor(X, &Bin[i * 8], 64);
          salsa20_8(X);
  
          // 4: Y_i <-- X
          // 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1})
          blkcpy(&Bout[i * 4], X, 64);
  
          /* 3: X <-- H(X \xor B_i) */
          blkxor(X, &Bin[i * 8 + 4], 64);
          salsa20_8(X);
  
          // 4: Y_i <-- X
          // 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1})
          blkcpy(&Bout[(r + i) * 4], X, 64);
      }
  }
  
  /* integerify(B, r):
   * Return the result of parsing B_{2r-1} as a little-endian integer.
   * Note that B's layout is permuted compared to the generic implementation.
   */
  static uint64_t integerify(const void* B, size_t r)
  {
      const uint32_t* X = (const void*)((uintptr_t)(B) + (2 * r - 1) * 64);
  
      return (((uint64_t)(X[13]) << 32) + X[0]);
  }
  
  void crypto_scrypt_smix_sse2(uint8_t * B, size_t r, uint64_t N, void * V, void * XY)
  {
      __m128i* X = XY;
      __m128i* Y = (void*)((uintptr_t)(XY) + 128 * r);
      __m128i* Z = (void*)((uintptr_t)(XY) + 256 * r);
      uint32_t* X32 = (void*)X;
      uint64_t i, j;
      size_t k;
  
      // 1: X <-- B
      for (k = 0; k < 2 * r; k++)
      {
          for (i = 0; i < 16; i++)
          {
              X32[k * 16 + i] = le32dec(&B[(k * 16 + (i * 5 % 16)) * 4]);
          }
      }
  
      // 2: for i = 0 to N - 1 do
      for (i = 0; i < N; i += 2)
      {
          // 3: V_i <-- X
          blkcpy((void*)((uintptr_t)(V) + i * 128 * r), X, 128 * r);
  
          // 4: X <-- H(X)
          blockmix_salsa8(X, Y, Z, r);
  
          // 3: V_i <-- X
          blkcpy((void*)((uintptr_t)(V) + (i + 1) * 128 * r), Y, 128 * r);
  
          // 4: X <-- H(X)
          blockmix_salsa8(Y, X, Z, r);
      }
  
      // 6: for i = 0 to N - 1 do
      for (i = 0; i < N; i += 2)
      {
          // 7: j <-- Integerify(X) mod N
          j = integerify(X, r) & (N - 1);
  
          // 8: X <-- H(X \xor V_j)
          blkxor(X, (void*)((uintptr_t)(V) + j * 128 * r), 128 * r);
          blockmix_salsa8(X, Y, Z, r);
  
          // 7: j <-- Integerify(X) mod N
          j = integerify(Y, r) & (N - 1);
  
          // 8: X <-- H(X \xor V_j)
          blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
          blockmix_salsa8(Y, X, Z, r);
      }
  
      // 10: B' <-- X
      for (k = 0; k < 2 * r; k++)
      {
          for (i = 0; i < 16; i++)
          {
              le32enc(&B[(k * 16 + (i * 5 % 16)) * 4], X32[k * 16 + i]);
          }
      }
  }
  
  #endif /* CPUSUPPORT_X86_SSE2 */
  