Asked Copilot to comment the code.

This commit is contained in:
2025-08-23 22:54:33 +01:00
parent b3474d88f6
commit beb8b405db

View File

@@ -1,5 +1,5 @@
/* /*
* This file is part of the Aaru Data Preservation Suite. * This file is part of the Aaru Data Preservation Suite.
* Copyright (c) 2019-2025 Natalia Portillo. * Copyright (c) 2019-2025 Natalia Portillo.
* *
* This library is free software; you can redistribute it and/or modify * This library is free software; you can redistribute it and/or modify
@@ -19,73 +19,83 @@
// Lempel-Ziv-Davis compression implementation based on the public domain code from // Lempel-Ziv-Davis compression implementation based on the public domain code from
// Rahul Dhesi from zoo // Rahul Dhesi from zoo
#include "lzd.h"
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h>
#include "lzd.h"
#include "../library.h" #include "../library.h"
// Reset the dictionary to its initial state
static void init_dict(LZDContext *ctx) static void init_dict(LZDContext *ctx)
{ {
ctx->nbits = 9; ctx->nbits = 9; // start with 9bit codes
ctx->max_code = 1u << 9; ctx->max_code = 1u << 9; // maximum code value for current nbits
ctx->free_code = FIRST_FREE; ctx->free_code = FIRST_FREE; // next free dictionary slot
ctx->have_old = 0; ctx->have_old = 0; // no "previous code" yet
} }
// Follow the head[] chain until you get the first literal byte of a code
static int firstch(LZDContext *ctx, int code) static int firstch(LZDContext *ctx, int code)
{ {
int steps = 0; int steps = 0;
while(code > 255) while(code > 255)
{ { // follow links until you hit a literal (0255)
if((unsigned)code > MAXMAX) return -1; if((unsigned)code > MAXMAX) return -1; // invalid code range
if(++steps > (int)MAXMAX) return -1; if(++steps > (int)MAXMAX) return -1; // prevent infinite loop
code = ctx->head[code]; code = ctx->head[code];
} }
return code; return code;
} }
// Ensure there are at least nbits available in the bit buffer
static int fill_bits(LZDContext *ctx) static int fill_bits(LZDContext *ctx)
{ {
while(ctx->bitcount < (int)ctx->nbits) while(ctx->bitcount < (int)ctx->nbits)
{ {
if(ctx->in_pos >= ctx->in_len) return -1; if(ctx->in_pos >= ctx->in_len) return -1; // no more input available
// pull a byte from the input stream into bitbuf at current position
ctx->bitbuf |= (uint64_t)ctx->in_ptr[ctx->in_pos++] << ctx->bitcount; ctx->bitbuf |= (uint64_t)ctx->in_ptr[ctx->in_pos++] << ctx->bitcount;
ctx->bitcount += 8; ctx->bitcount += 8;
} }
return 0; return 0;
} }
// Read the next code of nbits from the bit buffer
static int read_code(LZDContext *ctx) static int read_code(LZDContext *ctx)
{ {
if(fill_bits(ctx) < 0) return -1; if(fill_bits(ctx) < 0) return -1; // top up bits if needed
int code = (int)(ctx->bitbuf & masks[ctx->nbits]); int code = (int)(ctx->bitbuf & masks[ctx->nbits]); // mask off the low nbits
ctx->bitbuf >>= ctx->nbits; ctx->bitbuf >>= ctx->nbits; // consume bits
ctx->bitcount -= ctx->nbits; ctx->bitcount -= ctx->nbits;
return code; return code;
} }
// Initialise a decompression context: allocate tables, set up initial dictionary
LZDStatus LZD_Init(LZDContext *ctx) LZDStatus LZD_Init(LZDContext *ctx)
{ {
memset(ctx, 0, sizeof *ctx); memset(ctx, 0, sizeof *ctx);
// allocate head, tail and stack arrays to hold dictionary links and output stack
ctx->head = malloc((MAXMAX + 1) * sizeof *ctx->head); ctx->head = malloc((MAXMAX + 1) * sizeof *ctx->head);
ctx->tail = malloc((MAXMAX + 1) * sizeof *ctx->tail); ctx->tail = malloc((MAXMAX + 1) * sizeof *ctx->tail);
ctx->stack = malloc((MAXMAX + 1) * sizeof *ctx->stack); ctx->stack = malloc((MAXMAX + 1) * sizeof *ctx->stack);
if(!ctx->head || !ctx->tail || !ctx->stack) return LZD_NEED_INPUT; if(!ctx->head || !ctx->tail || !ctx->stack) return LZD_NEED_INPUT;
// initialise first 256 dictionary entries to literal bytes
for(int i = 0; i < 256; i++) for(int i = 0; i < 256; i++)
{ {
ctx->head[i] = -1; ctx->head[i] = -1;
ctx->tail[i] = (uint8_t)i; ctx->tail[i] = (uint8_t)i;
} }
// set stack pointers to empty
ctx->stack_lim = ctx->stack + (MAXMAX + 1); ctx->stack_lim = ctx->stack + (MAXMAX + 1);
ctx->stack_ptr = ctx->stack_lim; ctx->stack_ptr = ctx->stack_lim;
init_dict(ctx); init_dict(ctx); // reset code size/free_code
ctx->bitbuf = 0; ctx->bitbuf = 0;
ctx->bitcount = 0; ctx->bitcount = 0;
return LZD_OK; return LZD_OK;
} }
// Point the context at a new input buffer
LZDStatus LZD_Feed(LZDContext *ctx, const unsigned char *in, size_t in_len) LZDStatus LZD_Feed(LZDContext *ctx, const unsigned char *in, size_t in_len)
{ {
ctx->in_ptr = in; ctx->in_ptr = in;
@@ -94,26 +104,31 @@ LZDStatus LZD_Feed(LZDContext *ctx, const unsigned char *in, size_t in_len)
return LZD_OK; return LZD_OK;
} }
// Pull decompressed bytes into `out` up to out_len or until input is exhausted
LZDStatus LZD_Drain(LZDContext *ctx, unsigned char *out, size_t out_len, size_t *out_produced) LZDStatus LZD_Drain(LZDContext *ctx, unsigned char *out, size_t out_len, size_t *out_produced)
{ {
size_t outpos = 0; size_t outpos = 0;
while(outpos < out_len) while(outpos < out_len)
{ {
// If there are bytes on the stack (from expanding a code), emit them first
if(ctx->stack_ptr < ctx->stack_lim) if(ctx->stack_ptr < ctx->stack_lim)
{ {
out[outpos++] = (uint8_t)*ctx->stack_ptr++; out[outpos++] = (uint8_t)*ctx->stack_ptr++;
continue; continue;
} }
// Otherwise, read the next code from the bitstream
int raw = read_code(ctx); int raw = read_code(ctx);
if(raw < 0) if(raw < 0)
{ {
*out_produced = outpos; *out_produced = outpos;
// If we emitted something, signal OK; otherwise tell caller we need more input
return outpos > 0 ? LZD_OK : LZD_NEED_INPUT; return outpos > 0 ? LZD_OK : LZD_NEED_INPUT;
} }
unsigned code = (unsigned)raw; unsigned code = (unsigned)raw;
// Special code: CLEAR reset the dictionary and read a fresh literal
if(code == CLEAR) if(code == CLEAR)
{ {
init_dict(ctx); init_dict(ctx);
@@ -128,6 +143,7 @@ LZDStatus LZD_Drain(LZDContext *ctx, unsigned char *out, size_t out_len, size_t
out[outpos++] = (uint8_t)lit; out[outpos++] = (uint8_t)lit;
continue; continue;
} }
// Special code: Z_EOF end of compressed stream
if(code == Z_EOF) if(code == Z_EOF)
{ {
*out_produced = outpos; *out_produced = outpos;
@@ -135,15 +151,17 @@ LZDStatus LZD_Drain(LZDContext *ctx, unsigned char *out, size_t out_len, size_t
} }
unsigned in_code = code; unsigned in_code = code;
// Handle KwKwK case: code not yet in the dictionary
if(code >= ctx->free_code) if(code >= ctx->free_code)
{ {
if(!ctx->have_old) return LZD_DONE; if(!ctx->have_old) return LZD_DONE;
int fc = firstch(ctx, ctx->old_code); int fc = firstch(ctx, ctx->old_code); // get first character of previous code
if(fc < 0) return LZD_DONE; if(fc < 0) return LZD_DONE;
*--ctx->stack_ptr = (char)fc; *--ctx->stack_ptr = (char)fc;
code = ctx->old_code; code = ctx->old_code;
} }
// Walk backwards through dictionary, pushing bytes onto the stack
while(code > 255) while(code > 255)
{ {
*--ctx->stack_ptr = (char)ctx->tail[code]; *--ctx->stack_ptr = (char)ctx->tail[code];
@@ -152,11 +170,13 @@ LZDStatus LZD_Drain(LZDContext *ctx, unsigned char *out, size_t out_len, size_t
uint8_t first_byte = (uint8_t)code; uint8_t first_byte = (uint8_t)code;
*--ctx->stack_ptr = (char)first_byte; *--ctx->stack_ptr = (char)first_byte;
// Add new sequence to dictionary if we have a valid previous code
if(ctx->have_old && ctx->free_code <= MAXMAX) if(ctx->have_old && ctx->free_code <= MAXMAX)
{ {
ctx->tail[ctx->free_code] = first_byte; ctx->tail[ctx->free_code] = first_byte;
ctx->head[ctx->free_code] = (int)ctx->old_code; ctx->head[ctx->free_code] = (int)ctx->old_code;
ctx->free_code++; ctx->free_code++;
// Increase code width when table fills up to current max_code
if(ctx->free_code >= ctx->max_code && ctx->nbits < MAXBITS) if(ctx->free_code >= ctx->max_code && ctx->nbits < MAXBITS)
{ {
ctx->nbits++; ctx->nbits++;
@@ -171,6 +191,7 @@ LZDStatus LZD_Drain(LZDContext *ctx, unsigned char *out, size_t out_len, size_t
return LZD_OK; return LZD_OK;
} }
// Free dynamic allocations inside an LZDContext
void LZD_Destroy(LZDContext *ctx) void LZD_Destroy(LZDContext *ctx)
{ {
if(!ctx) return; if(!ctx) return;
@@ -179,12 +200,14 @@ void LZD_Destroy(LZDContext *ctx)
free(ctx->stack); free(ctx->stack);
} }
// Public API: allocate+initialise a new context
AARU_EXPORT void AARU_CALL *CreateLZDContext(void) AARU_EXPORT void AARU_CALL *CreateLZDContext(void)
{ {
LZDContext *c = malloc(sizeof *c); LZDContext *c = malloc(sizeof *c);
return c && LZD_Init(c) == LZD_OK ? c : (free(c), NULL); return c && LZD_Init(c) == LZD_OK ? c : (free(c), NULL);
} }
// Public API: destroy and free a context
AARU_EXPORT void AARU_CALL DestroyLZDContext(void *ctx) AARU_EXPORT void AARU_CALL DestroyLZDContext(void *ctx)
{ {
if(ctx) if(ctx)
@@ -194,11 +217,13 @@ AARU_EXPORT void AARU_CALL DestroyLZDContext(void *ctx)
} }
} }
// Public API wrapper to feed new compressed data
AARU_EXPORT int AARU_CALL LZD_FeedNative(void *ctx, const unsigned char *data, size_t length) AARU_EXPORT int AARU_CALL LZD_FeedNative(void *ctx, const unsigned char *data, size_t length)
{ {
return (int)LZD_Feed(ctx, data, length); return (int)LZD_Feed(ctx, data, length);
} }
// Public API wrapper to drain decompressed data
AARU_EXPORT int AARU_CALL LZD_DrainNative(void *ctx, unsigned char *outBuf, size_t outBufLen, size_t *produced) AARU_EXPORT int AARU_CALL LZD_DrainNative(void *ctx, unsigned char *outBuf, size_t outBufLen, size_t *produced)
{ {
return (int)LZD_Drain(ctx, outBuf, outBufLen, produced); return (int)LZD_Drain(ctx, outBuf, outBufLen, produced);