@tool
class_name AImgIOCRC32
extends Resource
## CRC32 implementation that uses a Resource for better caching.
## Despite this being created for internal reasons, this is public API.


## The reversed polynomial.
@export var reversed_polynomial: int = 0xEDB88320

## The mask (and initialization value).
@export var mask: int = 0xFFFFFFFF

var _crc32_table := []

var _table_init_mutex: Mutex = Mutex.new()
var _table_initialized: bool = false


## Ensures the CRC32's cached part is ready.
## Should be called very infrequently, definitely not in an inner loop.
func ensure_ready():
	_table_init_mutex.lock()
	if not _table_initialized:
		# Calculate CRC32 table.
		var range8 := range(8)
		for i in range(256):
			var crc := i
			for j in range8:
				if (crc & 1) != 0:
					crc = (crc >> 1) ^ reversed_polynomial
				else:
					crc >>= 1
			_crc32_table.push_back(crc & mask)
		_table_initialized = true
	_table_init_mutex.unlock()


## Performs the update step of CRC32 over some bytes.
## Note that this is not the whole story.
## The CRC must be initialized to 0xFFFFFFFF, then updated, then bitwise-inverted.
func update(crc: int, data: PackedByteArray) -> int:
	var i := 0
	var l := len(data)
	while i < l:
		var lb := data[i] ^ (crc & 0xFF)
		crc = _crc32_table[lb] ^ (crc >> 8)
		i += 1
	return crc


## Finishes the CRC by XORing it with the mask.
func end(crc: int) -> int:
	return crc ^ mask
