/*
 * Copyright (c) 2009-2017 Satlab Aps <satlab@satlab.com>
 * All rights reserved. Do not distribute without permission.
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define CSP_CRC32_POLY 0x82F63B78

static uint32_t crc32c_tab[256];
static bool crc32c_initialized = false;

static void btp_crc32c_gentab(void)
{
	uint32_t crc;
	int i, j;

	for (i = 0; i < 256; i++) {
		crc = i;
		for (j = 0; j < 8; j++)
			crc = (crc >> 1) ^ (CSP_CRC32_POLY & -(crc & 1));
		crc32c_tab[i] = crc;
	}
}

uint32_t btp_crc32c_push(uint8_t *data, size_t length, uint32_t crc)
{
	if (!crc32c_initialized) {
		btp_crc32c_gentab();
		crc32c_initialized = true;
	}

	while (length--)
		crc = crc32c_tab[(crc ^ *data++) & 0xFFL] ^ (crc >> 8);

	return crc;
}

uint32_t btp_crc32c_memory(uint8_t *data, uint32_t length)
{
	return btp_crc32c_push(data, length, 0);
}

uint32_t btp_crc32c_fd(int fd, off_t offset)
{
	int i;
	uint8_t buf[256];
	uint32_t crc, size, remain, bytes;

	size = lseek(fd, 0, SEEK_END) - offset;
	if (lseek(fd, offset, SEEK_SET) < 0) {
		printf("Failed to lseek before CRC32\r\n");
		return 0;
	}

	crc = 0;
	for (i = 0; size && i < (size + sizeof(buf) - 1) / sizeof(buf); i++) {
		remain = size - i * sizeof(buf);
		bytes = remain >= sizeof(buf) ? sizeof(buf) : remain;

		if (read(fd, buf, bytes) < bytes) {
			printf("Failed to read file for CRC32\r\n");
			return 0;
		}

		crc = btp_crc32c_push(buf, bytes, crc);
	}

	if (lseek(fd, 0, SEEK_SET) < 0) {
		printf("Failed to lseek after CRC32\r\n");
		return 0;
	}

	return crc;
}
