/*
* SHA-1 using CPU instructions in ARMv8
*
* Contributed by Jeffrey Walton. Based on public domain code by
* Johannes Schneiders, Skip Hovsmith and Barry O'Rourke.
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/internal/sha1.h>

#include <botan/internal/isa_extn.h>
#include <arm_neon.h>

namespace Botan {

/*
* SHA-1 using CPU instructions in ARMv8
*/
//static
void BOTAN_FN_ISA_SHA2 SHA_1::sha1_armv8_compress_n(digest_type& digest,
                                                    std::span<const uint8_t> input8,
                                                    size_t blocks) {
   // Load magic constants
   const uint32x4_t C0 = vdupq_n_u32(0x5A827999);
   const uint32x4_t C1 = vdupq_n_u32(0x6ED9EBA1);
   const uint32x4_t C2 = vdupq_n_u32(0x8F1BBCDC);
   const uint32x4_t C3 = vdupq_n_u32(0xCA62C1D6);

   uint32x4_t ABCD = vld1q_u32(&digest[0]);  // NOLINT(*-container-data-pointer)
   uint32_t E0 = digest[4];

   const uint8_t* input = input8.data();

   while(blocks > 0) {
      // Save current hash
      const uint32x4_t ABCD_SAVED = ABCD;
      const uint32_t E0_SAVED = E0;

      uint32x4_t MSG0 = vreinterpretq_u32_u8(vld1q_u8(input + 0));
      uint32x4_t MSG1 = vreinterpretq_u32_u8(vld1q_u8(input + 16));
      uint32x4_t MSG2 = vreinterpretq_u32_u8(vld1q_u8(input + 32));
      uint32x4_t MSG3 = vreinterpretq_u32_u8(vld1q_u8(input + 48));

      MSG0 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG0)));
      MSG1 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG1)));
      MSG2 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG2)));
      MSG3 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(MSG3)));

      uint32x4_t TMP0 = vaddq_u32(MSG0, C0);
      uint32x4_t TMP1 = vaddq_u32(MSG1, C0);

      // Rounds 0-3
      uint32_t E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1cq_u32(ABCD, E0, TMP0);
      TMP0 = vaddq_u32(MSG2, C0);
      MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);

      // Rounds 4-7
      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1cq_u32(ABCD, E1, TMP1);
      TMP1 = vaddq_u32(MSG3, C0);
      MSG0 = vsha1su1q_u32(MSG0, MSG3);
      MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);

      // Rounds 8-11
      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1cq_u32(ABCD, E0, TMP0);
      TMP0 = vaddq_u32(MSG0, C0);
      MSG1 = vsha1su1q_u32(MSG1, MSG0);
      MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);

      // Rounds 12-15
      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1cq_u32(ABCD, E1, TMP1);
      TMP1 = vaddq_u32(MSG1, C1);
      MSG2 = vsha1su1q_u32(MSG2, MSG1);
      MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);

      // Rounds 16-19
      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1cq_u32(ABCD, E0, TMP0);
      TMP0 = vaddq_u32(MSG2, C1);
      MSG3 = vsha1su1q_u32(MSG3, MSG2);
      MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);

      // Rounds 20-23
      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1pq_u32(ABCD, E1, TMP1);
      TMP1 = vaddq_u32(MSG3, C1);
      MSG0 = vsha1su1q_u32(MSG0, MSG3);
      MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);

      // Rounds 24-27
      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1pq_u32(ABCD, E0, TMP0);
      TMP0 = vaddq_u32(MSG0, C1);
      MSG1 = vsha1su1q_u32(MSG1, MSG0);
      MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);

      // Rounds 28-31
      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1pq_u32(ABCD, E1, TMP1);
      TMP1 = vaddq_u32(MSG1, C1);
      MSG2 = vsha1su1q_u32(MSG2, MSG1);
      MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);

      // Rounds 32-35
      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1pq_u32(ABCD, E0, TMP0);
      TMP0 = vaddq_u32(MSG2, C2);
      MSG3 = vsha1su1q_u32(MSG3, MSG2);
      MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);

      // Rounds 36-39
      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1pq_u32(ABCD, E1, TMP1);
      TMP1 = vaddq_u32(MSG3, C2);
      MSG0 = vsha1su1q_u32(MSG0, MSG3);
      MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);

      // Rounds 40-43
      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1mq_u32(ABCD, E0, TMP0);
      TMP0 = vaddq_u32(MSG0, C2);
      MSG1 = vsha1su1q_u32(MSG1, MSG0);
      MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);

      // Rounds 44-47
      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1mq_u32(ABCD, E1, TMP1);
      TMP1 = vaddq_u32(MSG1, C2);
      MSG2 = vsha1su1q_u32(MSG2, MSG1);
      MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);

      // Rounds 48-51
      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1mq_u32(ABCD, E0, TMP0);
      TMP0 = vaddq_u32(MSG2, C2);
      MSG3 = vsha1su1q_u32(MSG3, MSG2);
      MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);

      // Rounds 52-55
      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1mq_u32(ABCD, E1, TMP1);
      TMP1 = vaddq_u32(MSG3, C3);
      MSG0 = vsha1su1q_u32(MSG0, MSG3);
      MSG1 = vsha1su0q_u32(MSG1, MSG2, MSG3);

      // Rounds 56-59
      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1mq_u32(ABCD, E0, TMP0);
      TMP0 = vaddq_u32(MSG0, C3);
      MSG1 = vsha1su1q_u32(MSG1, MSG0);
      MSG2 = vsha1su0q_u32(MSG2, MSG3, MSG0);

      // Rounds 60-63
      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1pq_u32(ABCD, E1, TMP1);
      TMP1 = vaddq_u32(MSG1, C3);
      MSG2 = vsha1su1q_u32(MSG2, MSG1);
      MSG3 = vsha1su0q_u32(MSG3, MSG0, MSG1);

      // Rounds 64-67
      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1pq_u32(ABCD, E0, TMP0);
      TMP0 = vaddq_u32(MSG2, C3);
      MSG3 = vsha1su1q_u32(MSG3, MSG2);
      MSG0 = vsha1su0q_u32(MSG0, MSG1, MSG2);

      // Rounds 68-71
      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1pq_u32(ABCD, E1, TMP1);
      TMP1 = vaddq_u32(MSG3, C3);

      // Rounds 72-75
      E1 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1pq_u32(ABCD, E0, TMP0);

      // Rounds 76-79
      E0 = vsha1h_u32(vgetq_lane_u32(ABCD, 0));
      ABCD = vsha1pq_u32(ABCD, E1, TMP1);

      // Add state back
      E0 += E0_SAVED;
      ABCD = vaddq_u32(ABCD_SAVED, ABCD);

      input += 64;
      blocks--;
   }

   // Save digest
   vst1q_u32(&digest[0], ABCD);  // NOLINT(*-container-data-pointer)
   digest[4] = E0;
}

}  // namespace Botan
