/*
 * SPDX-License-Identifier: LicenseRef-CSSL-1.0
 */

/*!
 * \brief generate 3GPP LTE CRCs. Byte-oriented implementation of CRC's
 *  Original UMTS version by P. Humblet
 */

#if !defined(__SSE4_1__) && !defined(__aarch64__)
#define USE_INTEL_CRC 0
#else
#define USE_INTEL_CRC 1
#include "crc.h"
#endif

#include "coding_defs.h"
#include "assertions.h"
/*ref 36-212 v8.6.0 , pp 8-9 */
/* the highest degree is set by default */


static const uint32_t poly24a =
    0x864cfb00; // 1000 0110 0100 1100 1111 1011
                // D^24 + D^23 + D^18 + D^17 + D^14 + D^11 + D^10 + D^7 + D^6 + D^5 + D^4 + D^3 + D + 1
static const uint32_t poly24b = 0x80006300; // 1000 0000 0000 0000 0110 0011
                                                // D^24 + D^23 + D^6 + D^5 + D + 1
static const uint32_t poly24c = 0xb2b11700; // 1011 0010 1011 0001 0001 0111
                                                // D^24+D^23+D^21+D^20+D^17+D^15+D^13+D^12+D^8+D^4+D^2+D+1

static const uint32_t poly16 = 0x10210000; // 0001 0000 0010 0001            D^16 + D^12 + D^5 + 1
static const uint32_t poly12 = 0x80F00000; // 1000 0000 1111                 D^12 + D^11 + D^3 + D^2 + D + 1
static const uint32_t poly8 = 0x9B000000; // 1001 1011                      D^8  + D^7  + D^4 + D^3 + D + 1
static const uint32_t poly6 = 0x84000000; // 10000100000... -> D^6+D^5+1
static const uint32_t poly11 = 0xc4200000; // 11000100001000... -> D^11+D^10+D^9+D^5+1

/*********************************************************

For initialization && verification purposes,
   bit by bit implementation with any polynomial

The first bit is in the MSB of each byte

*********************************************************/
uint32_t crcbit(unsigned char* inputptr, int octetlen, uint32_t poly)
{
  uint32_t i, crc = 0, c;

  while (octetlen-- > 0) {
    c = ((uint32_t)(*inputptr++)) << 24;

    for (i = 8; i != 0; i--) {
      if ((1U << 31) & (c ^ crc))
        crc = (crc << 1) ^ poly;
      else
        crc <<= 1;

      c <<= 1;
    }
  }

  return crc;
}

/*********************************************************

crc table initialization

*********************************************************/
static uint32_t crc24aTable[256];
static uint32_t crc24bTable[256];
static uint32_t crc24cTable[256];
static uint32_t crc16Table[256];
static uint32_t crc12Table[256];
static uint32_t crc11Table[256];
static uint32_t crc8Table[256];
static uint32_t crc6Table[256];

#if USE_INTEL_CRC
static const struct crc_pclmulqdq_ctx lte_crc24a_pclmulqdq __attribute__((aligned(16))) = {
        0x64e4d700,     /**< k1 */
        0x2c8c9d00,     /**< k2 */
        0xd9fe8c00,     /**< k3 */
        0xf845fe24,     /**< q */
        0x864cfb00,     /**< p */
        0ULL            /**< res */
};
simde__m128i crc_xmm_be_le_swap128;

const uint8_t crc_xmm_shift_tab[48]
    __attribute__((aligned(16))) = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
                                    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

#endif

void crcTableInit (void)
{
  unsigned char c = 0;

  do {
    crc24aTable[c] = crcbit (&c, 1, poly24a);
    crc24bTable[c] = crcbit (&c, 1, poly24b);
    crc24cTable[c] = crcbit (&c, 1, poly24c);
    crc16Table[c] = crcbit(&c, 1, poly16) >> 16;
    crc12Table[c] = crcbit(&c, 1, poly12) >> 16;
    crc11Table[c] = crcbit(&c, 1, poly11) >> 16;
    crc8Table[c] = crcbit(&c, 1, poly8) >> 24;
    crc6Table[c] = crcbit(&c, 1, poly6) >> 24;
  } while (++c);
#if defined(__SSE4_1__) || defined(__aarch64__) 
    crc_xmm_be_le_swap128 = simde_mm_setr_epi32(0x0c0d0e0f, 0x08090a0b,
				    	        0x04050607, 0x00010203);

#endif
}

/*********************************************************

Byte by byte LUT implementations,
assuming initial byte is 0 padded (in MSB) if necessary
can use SIMD optimized Intel CRC for LTE/NR 24a/24b variants
*********************************************************/

uint32_t crc24a(unsigned char* inptr, int bitlen)
{
  int octetlen = bitlen / 8;  /* Change in octets */

  if ( bitlen % 8 || !USE_INTEL_CRC ) {
    uint32_t crc = 0;
    int resbit = (bitlen % 8);

    while (octetlen-- > 0) {
      //   printf("crc24a: in %x => crc %x\n",crc,*inptr);
      crc = (crc << 8) ^ crc24aTable[(*inptr++) ^ (crc >> 24)];
    }

  if (resbit > 0)
    crc = (crc << resbit) ^ crc24aTable[((*inptr) >> (8 - resbit)) ^ (crc >> (32 - resbit))];
  return crc;
  }
  #if defined(__SSE4_1__) || defined(__aarch64__) 
  else {
  return crc32_calc_pclmulqdq(inptr, octetlen, 0,
                              &lte_crc24a_pclmulqdq);
  }
  #endif

}

#if USE_INTEL_CRC
static const struct crc_pclmulqdq_ctx lte_crc24b_pclmulqdq __attribute__((aligned(16))) = {
    0x80140500, /**< k1 */
    0x42000100, /**< k2 */
    0x90042100, /**< k3 */
    0xffff83ff, /**< q */
    0x80006300, /**< p */
    0ULL /**< res */
};
#endif
uint32_t crc24b(unsigned char* inptr, int bitlen)
{
  int octetlen = bitlen / 8;  /* Change in octets */

  if ( bitlen % 8 || !USE_INTEL_CRC ) {
    uint32_t crc = 0;
    int resbit = (bitlen % 8);

    while (octetlen-- > 0) {
      //    printf("crc24b: in %x => crc %x (%x)\n",crc,*inptr,crc24bTable[(*inptr) ^ (crc >> 24)]);
      crc = (crc << 8) ^ crc24bTable[(*inptr++) ^ (crc >> 24)];
    }

  if (resbit > 0)
    crc = (crc << resbit) ^ crc24bTable[((*inptr) >> (8 - resbit)) ^ (crc >> (32 - resbit))];

  return crc;
  }
#if USE_INTEL_CRC
  else {
  return crc32_calc_pclmulqdq(inptr, octetlen, 0,
                              &lte_crc24b_pclmulqdq);
  }
#endif
}

uint32_t crc24c(unsigned char* inptr, int bitlen)
{
  int octetlen, resbit;
  uint32_t crc = 0;
  octetlen = bitlen / 8;        /* Change in octets */
  resbit = (bitlen % 8);

  while (octetlen-- > 0) {
    crc = (crc << 8) ^ crc24cTable[(*inptr++) ^ (crc >> 24)];
  }

  if (resbit > 0) {
    crc = (crc << resbit) ^ crc24cTable[((*inptr) >> (8 - resbit)) ^ (crc >> (32 - resbit))];
  }

  return crc;
}

uint32_t crc16(unsigned char* inptr, int bitlen)
{
  int             octetlen, resbit;
  uint32_t crc = 0;
  octetlen = bitlen / 8;        /* Change in octets */
  resbit = (bitlen % 8);

  while (octetlen-- > 0) {

    crc = (crc << 8) ^ (((uint32_t)crc16Table[(*inptr++) ^ (crc >> 24)]) << 16);
  }

  if (resbit > 0)
    crc = (crc << resbit) ^ (((uint32_t)crc16Table[(*inptr) >> (8 - resbit) ^ (crc >> (32 - resbit))]) << 16);

  return crc;
}

uint32_t crc12(unsigned char* inptr, int bitlen)
{
  int             octetlen, resbit;
  uint32_t crc = 0;
  octetlen = bitlen / 8;        /* Change in octets */
  resbit = (bitlen % 8);

  while (octetlen-- > 0) {
    crc = (crc << 8) ^ (crc12Table[(*inptr++) ^ (crc >> 24)] << 16);
  }

  if (resbit > 0)
    crc = (crc << resbit) ^ (crc12Table[((*inptr) >> (8 - resbit)) ^ (crc >> (32 - resbit))] << 16);

  return crc;
}

uint32_t crc11(unsigned char* inptr, int bitlen)
{
  int             octetlen, resbit;
  uint32_t crc = 0;
  octetlen = bitlen / 8;        /* Change in octets */
  resbit = (bitlen % 8);

  while (octetlen-- > 0) {
    crc = (crc << 8) ^ (crc11Table[(*inptr++) ^ (crc >> 24)] << 16);
  }

  if (resbit > 0)
    crc = (crc << resbit) ^ (crc11Table[((*inptr) >> (8 - resbit)) ^ (crc >> (32 - resbit))] << 16);

  return crc;
}

uint32_t crc8(unsigned char* inptr, int bitlen)
{
  int             octetlen, resbit;
  uint32_t crc = 0;
  octetlen = bitlen / 8;        /* Change in octets */
  resbit = (bitlen % 8);

  while (octetlen-- > 0) {
    crc = ((uint32_t)crc8Table[(*inptr++) ^ (crc >> 24)]) << 24;
  }

  if (resbit > 0)
    crc = (crc << resbit) ^ ((uint32_t)(crc8Table[((*inptr) >> (8 - resbit)) ^ (crc >> (32 - resbit))]) << 24);

  return crc;
}

uint32_t crc6(unsigned char* inptr, int bitlen)
{
  int             octetlen, resbit;
  uint32_t crc = 0;
  octetlen = bitlen / 8;        /* Change in octets */
  resbit = (bitlen % 8);

  while (octetlen-- > 0) {
    crc = crc6Table[(*inptr++) ^ (crc >> 24)] << 24;
  }

  if (resbit > 0)
    crc = (crc << resbit) ^ (crc6Table[((*inptr) >> (8 - resbit)) ^ (crc >> (32 - resbit))] << 24);

  return crc;
}

int check_crc(uint8_t* decoded_bytes, uint32_t n, uint8_t crc_type)
{
  uint32_t crc=0,oldcrc=0;
  uint8_t crc_len=0;

  switch (crc_type) {
  case CRC24_A:
  case CRC24_B:
    crc_len=3;
    break;

  case CRC16:
    crc_len=2;
    break;

  case CRC8:
    crc_len=1;
    break;

  default:
    AssertFatal(1,"Invalid crc_type \n");
  }

  for (int i=0; i<crc_len; i++)
    oldcrc |= (decoded_bytes[(n>>3)-crc_len+i])<<((crc_len-1-i)<<3);

  switch (crc_type) {
    
  case CRC24_A:
    oldcrc&=0x00ffffff;
    crc = crc24a(decoded_bytes,
		 n-24)>>8;
    
    break;
    
  case CRC24_B:
      oldcrc&=0x00ffffff;
      crc = crc24b(decoded_bytes,
                   n-24)>>8;
      
      break;

    case CRC16:
      oldcrc&=0x0000ffff;
      crc = crc16(decoded_bytes,
                  n-16)>>16;
      
      break;

    case CRC8:
      oldcrc&=0x000000ff;
      crc = crc8(decoded_bytes,
                 n-8)>>24;
      break;

    default:
      AssertFatal(1,"Invalid crc_type \n");
    }


    if (crc == oldcrc)
      return(1);
    else
      return(0);

}


#ifdef DEBUG_CRC
/*******************************************************************/
/**
   Test code
********************************************************************/

#include <stdio.h>

main()
{
  unsigned char test[] = "Thebigredfox";
  crcTableInit();
  printf("%x\n", crcbit(test, sizeof(test) - 1, poly24a));
  printf("%x\n", crc24(test, (sizeof(test) - 1)*8));
  printf("%x\n", crcbit(test, sizeof(test) - 1, poly8));
  printf("%x\n", crc8(test, (sizeof(test) - 1)*8));
}
#endif


