cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

rlecompress.c (6141B)


      1// This is free and unencumbered software released into the public domain.
      2// For more information, please refer to <https://unlicense.org>
      3// bbbbbr 2020
      4
      5#include <stdio.h>
      6//#include <string.h>
      7#include <stdlib.h>
      8#include <stdbool.h>
      9#include <stdint.h>
     10#include "rlecompress.h"
     11
     12
     13static uint8_t * FinBuf      = NULL;
     14static uint32_t  Fsize_in    = 0;
     15static uint32_t  FinIndex    = 0;
     16
     17static uint8_t ** pp_FoutBuf = NULL;
     18static uint8_t * FoutBuf     = NULL;
     19static uint32_t  Fsize_out   = 0;
     20static uint32_t  FoutIndex   = 0;
     21
     22
     23#define RLE_MASK_LEN    0x7F
     24#define RLE_MASK_TYPE   0x80
     25#define RLE_TYPE_RAND   0x00
     26#define RLE_TYPE_REPEAT 0x80
     27#define RLE_MAX_LEN     127
     28#define RLE_CTRL_END    0x00
     29#define RLE_MAX_BLOCK   127
     30#define RLE_CHANGE_COST 2
     31
     32
     33static uint8_t rle_queued[128];
     34static int rle_queue_idx = 0;
     35static int run_len = 0;
     36
     37
     38// Initialize the buffer vars
     39static void initbufs(uint8_t * inBuf, uint32_t size_in, uint8_t ** pp_outBuf, uint32_t size_out) {
     40
     41    FinBuf     = inBuf;
     42    Fsize_in   = size_in;
     43    FinIndex   = 0;
     44
     45    pp_FoutBuf = pp_outBuf;
     46    FoutBuf    = *pp_outBuf;
     47    Fsize_out  = size_out;
     48    FoutIndex = 0;
     49}
     50
     51
     52static void check_write_size(int len) {
     53
     54    // Grow output buffer if needed
     55    if ((FoutIndex + len) >= Fsize_out) {
     56        uint8_t * p_tmp = *pp_FoutBuf;
     57
     58        // Reallocate to twice as large
     59        Fsize_out = Fsize_out * 2;
     60        *pp_FoutBuf = (void *)realloc(*pp_FoutBuf, Fsize_out);
     61
     62        // If realloc failed, free original buffer before quitting
     63        if (!(*pp_FoutBuf)) {
     64            printf("Error: Failed to grow memory for output buffer!\n");
     65            if (p_tmp) free(p_tmp);
     66            p_tmp = NULL;
     67            exit(EXIT_FAILURE);
     68        } else
     69            FoutBuf = *pp_FoutBuf; // Update working pointer
     70    }
     71}
     72
     73
     74
     75static void write_end_of_data(void) {
     76
     77    check_write_size(1);                 // writing 1 control byte
     78    FoutBuf[FoutIndex++] = RLE_CTRL_END; // Write sequence control byte
     79}
     80
     81
     82static void write_run_repeat(int len, uint8_t value) {
     83
     84    if (len) {
     85        check_write_size(2); // writing 1 repeated value bytes + 1 control byte
     86        // Write sequence control byte (length + type)
     87        // Convert length to twos complement to identify it as repeat run
     88        FoutBuf[FoutIndex++] = ((len & RLE_MASK_LEN) ^ 0xFFu) + 1u;
     89        FoutBuf[FoutIndex++] = value;
     90    }
     91}
     92
     93
     94static void rle_commit() {
     95
     96    int idx = 0;
     97
     98    if (rle_queue_idx > 0) {
     99        check_write_size(rle_queue_idx + 1); // writing len bytes + 1 control byte
    100        // Write sequence control byte (length + type)
    101        FoutBuf[FoutIndex++] = (rle_queue_idx & RLE_MASK_LEN) | RLE_TYPE_RAND;
    102        while (rle_queue_idx > 0) {
    103            FoutBuf[FoutIndex++] = rle_queued[idx++];
    104            rle_queue_idx--;
    105        }
    106    }
    107    rle_queue_idx = 0; // redundant
    108}
    109
    110
    111
    112// Convert buffer inBuf to rlecompress rle encoding and write out to outBuf
    113// Returns converted length
    114uint32_t rlecompress_buf(uint8_t * inBuf, uint32_t size_in, uint8_t ** pp_outBuf, uint32_t size_out) {
    115
    116    uint8_t  last, current;
    117    initbufs(inBuf, size_in, pp_outBuf, size_out);
    118
    119    last = 0;
    120    run_len = 0;
    121    rle_queue_idx = 0;
    122
    123    while (FinIndex < Fsize_in) {
    124
    125        current = FinBuf[FinIndex++];
    126
    127        if (current != last) {
    128            // The run stopped, if switching to repeat is worthwhile then
    129            // flush preceding random data
    130            if (run_len > RLE_CHANGE_COST) {
    131                rle_commit();
    132                write_run_repeat(run_len, last);
    133            } else {
    134                // Otherwise treat repeat data as random and queue it
    135                // with flushing as needed
    136                while(run_len--) {
    137                    if (rle_queue_idx >= RLE_MAX_BLOCK) {
    138                        rle_commit();
    139                    }
    140                    rle_queued[rle_queue_idx++] = last;
    141                }
    142            }
    143          run_len = 1;
    144          last = current;
    145        }
    146        else {
    147            // Flush pending repeat if max length is encountered
    148            if (run_len >= RLE_MAX_BLOCK) {
    149                rle_commit();
    150                write_run_repeat(run_len, last);
    151                run_len = 0;
    152            }
    153            run_len++;
    154        }
    155
    156    }
    157
    158    // If switching to repeat is worthwhile then flush preceding random data
    159    if (run_len > RLE_CHANGE_COST) {
    160        rle_commit();
    161        write_run_repeat(run_len, last);
    162    } else {
    163        // Otherwise treat repeat data as random and queue it
    164        // with flushing as needed
    165        while(run_len--) {
    166            if (rle_queue_idx >= RLE_MAX_BLOCK) {
    167                rle_commit();
    168            }
    169            rle_queued[rle_queue_idx++] = last;
    170        }
    171    }
    172
    173    // Flush any trailing data
    174    rle_commit();
    175    write_end_of_data();
    176
    177    return FoutIndex;
    178
    179}
    180
    181
    182
    183static void write_single_byte(uint8_t data) {
    184
    185    check_write_size(1);
    186
    187    FoutBuf[FoutIndex++] = data;
    188}
    189
    190
    191static uint8_t read_single_byte(void) {
    192
    193    if (FinIndex >= Fsize_in) {
    194        printf("Error: Read past end of input buffer!\n");
    195        exit(EXIT_FAILURE);
    196    }
    197
    198    return (FinBuf[FinIndex++]);
    199}
    200
    201
    202
    203// Decompress buffer inBuf from gbcompress rle encoding and write to outBuf
    204// Returns converted length
    205uint32_t rledecompress_buf(uint8_t * inBuf, uint32_t size_in, uint8_t ** pp_outBuf, uint32_t size_out) {
    206
    207    initbufs(inBuf, size_in, pp_outBuf, size_out);
    208
    209    uint8_t token, rle_len, value;
    210
    211    while (FinIndex < size_in) {
    212
    213        token = read_single_byte();
    214
    215        // Check for EOF token, exit if encountered
    216        if (token == RLE_CTRL_END)
    217            break;
    218        else if ((token & RLE_MASK_TYPE) == RLE_TYPE_RAND) {
    219            rle_len = token & RLE_MASK_LEN;
    220            while (rle_len--)
    221                write_single_byte(read_single_byte());
    222        }
    223        else { // if ((token & RLE_MASK_TYPE) == RLE_TYPE_REPEAT) {
    224            rle_len = ((token ^ 0xFF) + 1) & RLE_MASK_LEN; // length is two's complement
    225            value = read_single_byte();
    226            while (rle_len--)
    227                write_single_byte(value);
    228        }
    229    }
    230
    231    return FoutIndex;
    232}