Last active
April 16, 2022 16:50
-
-
Save D1360-64RC14/33a12b3990474043a1bf915f41c5c77d to your computer and use it in GitHub Desktop.
Uma implementação de n bits Number Packing em C. Encoder/decoder de number packing baseado em RGB to Integer, porém com a possibilidade de n bits e n grupos (contanto que tudo isso caiba em um uint64). Desenvolvido em C11.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <errno.h> | |
/** | |
* Recebe uma lista contendo uint8_t e retorna a soma desses. | |
* Ex: | |
* | |
* uint8_t numbers[] = { 1, 2, 3, 4, 5 }; | |
* | |
* auto result = sum_uint8(numbers, 5); | |
* // result -> 15 | |
* | |
* @param data Lista de uint8_t contendo valores que serão somados. | |
* @param size Tamanho total da lista 'data'. | |
* @return Soma de todos os números presentes na lista 'data'. | |
*/ | |
uint64_t sum_uint8(const uint8_t*const data, size_t size) { | |
uint64_t accumulator = 0; | |
while (size--) { | |
accumulator += data[size]; | |
} | |
return accumulator; | |
} | |
/** | |
* Preenche um uint64_t com a quantidade 'count' de bits. | |
* Ex: | |
* | |
* auto result = bit_fill(8); | |
* // result -> 255 (0b11111) | |
* | |
* auto result = bit_fill(3); | |
* // result -> 7 (0b111) | |
* | |
* @param count Quantidade de bits que serão preenchidos. | |
* @return Número contendo todos os primeiros 'count' bits preenchidos. | |
*/ | |
uint64_t bit_fill(uint8_t count) { | |
uint64_t result = 1; | |
count--; | |
while (count--) { | |
result = result << 1; | |
result += 1; | |
} | |
return result; | |
} | |
/** | |
* Verifica se a soma dos valores presentes na lista 'bit_mask' ultrapassa a | |
* quantidade de bits limite de um uint64_t. | |
* | |
* @param data_size Tamanho da lista de dados. | |
* @param bit_mask Máscara de bits. | |
*/ | |
void check_bitmask_overflow(size_t data_size, const uint8_t*const bit_mask) { | |
uint8_t bit_count = sum_uint8(bit_mask, data_size); | |
if (bit_count > sizeof(uint64_t) * 8) { | |
errno = EOVERFLOW; | |
perror("'bit_mast' must be less than 32 bits"); | |
exit(1); | |
} | |
} | |
/** | |
* Verifica se as quantidades presentes em 'data' cabem dentro de suas | |
* respectivas máscaras em 'bit_mask'. | |
* | |
* @param data_size Tamanho da lista 'data'. | |
* @param data Lista contendo os valores a serem encodados. | |
* @param bit_mask Máscara de bits. | |
*/ | |
void check_data_bitmask_overflow( | |
size_t data_size, | |
const uint64_t*const data, | |
const uint8_t *const bit_mask | |
) { | |
for (size_t i = 0; i < data_size; i++) { | |
if (data[i] > bit_fill(bit_mask[i])) { | |
errno = EOVERFLOW; | |
perror("Values from 'data' must be less than their masks"); | |
exit(1); | |
} | |
} | |
} | |
/** | |
* Codifica em inteiro os dados de 'data' de acordo com a 'bit_mask'. | |
* Ex: | |
* | |
* // RGB to Integer | |
* uint8_t mask_888[] = { 8, 8, 8 }; | |
* uint64_t rgb[] = { 255, 0, 255 }; | |
* | |
* auto integer_rgb = encode_global_number_packing(3, rgb, mask_888); | |
* // integer_rgb -> 16711935 (11111111 00000000 11111111) | |
* | |
* // RGB565 to Integer | |
* uint8_t mask_565[] = { 5, 6, 5 }; | |
* uint64_t rgb[] = { 31, 0, 31 }; | |
* | |
* auto integer_rgb = encode_global_number_packing(3, rgb, mask_565); | |
* // integer_rgb -> 63519 (11111 000000 11111) | |
* | |
* @param data_size Tamanho da lista 'data'. | |
* @param data Lista contendo os valores a serem encodados. | |
* @param bit_mask Máscara de bits. | |
* @return Número inteiro com os valores encodados. | |
*/ | |
uint64_t encode_global_number_packing( | |
size_t data_size, | |
const uint64_t*const data, | |
const uint8_t * bit_mask | |
) { | |
check_bitmask_overflow(data_size, bit_mask); | |
check_data_bitmask_overflow(data_size, data, bit_mask); | |
uint64_t result = 0; | |
for (size_t i = 0; i < data_size; i++) { | |
size_t arr_size = data_size - 1 - i; | |
/** | |
* A cada ciclo do for, bit_mask avança 1 byte na memória, Pois | |
* sizeof(uint8_t) == 1 byte. | |
* | |
* Quando o for rodar pela última vez, o ponteiro de bit_mask terá | |
* excedido o tamanho da array, porém como arr_size será 0, sum_uint8 | |
* não fará nada, retornando 0. | |
*/ | |
bit_mask += sizeof(uint8_t); | |
uint64_t bits2shift = sum_uint8(bit_mask, arr_size); | |
result |= data[i] << bits2shift; | |
} | |
return result; | |
} | |
/** | |
* Decodifica de inteiro o dado contido em 'encoded' de acordo com a 'bit_mask' | |
* e retorna esse em 'dst'. | |
* Ex: | |
* | |
* // Integer to RGB | |
* uint8_t mask_888[] = { 8, 8, 8 }; | |
* uint64_t encoded = 16711935; | |
* | |
* uint64_t* result; | |
* decode_global_number_packing(encoded, 3, mask_888, result); | |
* // result -> { 255, 0, 255 } | |
* | |
* // Integer to RGB565 | |
* uint8_t mask_565[] = { 5, 6, 5 }; | |
* uint64_t encoded = 63519; | |
* | |
* uint64_t* result; | |
* encode_global_number_packing(encoded, 3, mask_565, result); | |
* // result -> { 31, 0, 31 } | |
* | |
* @param encoded Número inteiro encodado. | |
* @param data_size Tamanho da 'bit_mask'. | |
* @param bit_mask Máscara de bits. | |
* @param dst Destino da lista de valores (deve ter o mesmo 'data_size'). | |
*/ | |
void decode_global_number_packing( | |
uint64_t encoded, | |
size_t data_size, | |
const uint8_t *const bit_mask, | |
uint64_t*const dst | |
) { | |
check_bitmask_overflow(data_size, bit_mask); | |
/** | |
* bit_mask_ptr andará 1 byte a cada ciclo do for, enquanto bit_mask | |
* permanecerá estático. | |
*/ | |
const uint8_t* bit_mask_ptr = bit_mask; | |
for (size_t i = 0; i < data_size; i++) { | |
bit_mask_ptr += sizeof(uint8_t); | |
size_t arr_size = data_size - 1 - i; | |
uint64_t sum_of_bits = sum_uint8(bit_mask_ptr, arr_size); | |
dst[i] = (encoded >> sum_of_bits) & bit_fill(bit_mask[i]); | |
} | |
} | |
void test(size_t size,const uint64_t* values,const uint8_t* mask); | |
int main() { | |
{ | |
uint8_t mask_RGB_888[] = { 8, 8, 8 }; | |
uint8_t mask_RGB_565[] = { 5, 6, 5 }; | |
size_t size = 3; | |
{ | |
uint64_t values[] = { 24, 42, 13 }; | |
test(size, values, mask_RGB_888); | |
test(size, values, mask_RGB_565); | |
} | |
{ | |
uint64_t values[] = { 0b11100001, 0b10011001, 0b10000111 }; | |
test(size, values, mask_RGB_888); | |
} | |
{ | |
uint64_t values[] = { 0b10000000, 0b10011000, 0b10000001 }; | |
test(size, values, mask_RGB_888); | |
} | |
{ | |
uint64_t values[] = { 0b11001, 0b101101, 0b10011 }; | |
test(size, values, mask_RGB_565); | |
} | |
{ | |
uint64_t values[] = { 0b10000, 0b100100, 0b10001 }; | |
test(size, values, mask_RGB_565); | |
} | |
} | |
{ | |
uint8_t mask_RGBA_8888[] = { 8, 8, 8, 8 }; | |
uint8_t mask_RGBA_5655[] = { 5, 6, 5, 5 }; | |
uint8_t mask_RGBA_16161616[] = { 16, 16, 16, 16 }; | |
uint8_t mask_RGBA_1616168[] = { 16, 16, 16, 8 }; | |
size_t size = 4; | |
{ | |
uint64_t values[] = { 24, 42, 13, 7 }; | |
test(size, values, mask_RGBA_8888); | |
test(size, values, mask_RGBA_5655); | |
test(size, values, mask_RGBA_16161616); | |
test(size, values, mask_RGBA_1616168); | |
} | |
} | |
} | |
uint16_t test_n = 0; | |
/** | |
* Função feita para automatizar os testes. | |
*/ | |
void test( | |
size_t size, | |
const uint64_t*const values, | |
const uint8_t *const mask | |
) { | |
test_n++; | |
uint64_t encoded = encode_global_number_packing(size, values, mask); | |
uint64_t decoded[size]; | |
decode_global_number_packing(encoded, size, mask, decoded); | |
printf("%d. Input:", test_n); | |
for (size_t i = 0; i < size; i++) { | |
if (i != size) printf(" "); | |
printf("%lu", values[i]); | |
} | |
printf("; Output:"); | |
for (size_t i = 0; i < size; i++) { | |
if (i != size) printf(" "); | |
printf("%lu", decoded[i]); | |
} | |
printf("; Integer: %lu | ", encoded); | |
int32_t diff = 0; | |
for (size_t i = 0; i < size; i++) { | |
diff += (int32_t) values[i] - (int32_t) decoded[i]; | |
} | |
if (diff == 0) { | |
printf("PASS"); | |
} else { | |
printf("FAIL"); | |
} | |
printf("\n"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment