Skip to content

Instantly share code, notes, and snippets.

@D1360-64RC14
Last active April 16, 2022 16:50
Show Gist options
  • Save D1360-64RC14/33a12b3990474043a1bf915f41c5c77d to your computer and use it in GitHub Desktop.
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.
#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