Files
my-os-project2/kernel/FastLZ/6unpack_mem.c

116 lines
3.0 KiB
C

#include <stdint.h>
#include <stddef.h>
#include "hal/hal.h"
#include "fastlz.h"
#define SIXPACK_OK 0
#define SIXPACK_ERR_MAGIC -1
#define SIXPACK_ERR_CHECKSUM -2
#define SIXPACK_ERR_DECOMPRESS -3
#define SIXPACK_ERR_BAD_FORMAT -4
#define SIXPACK_ERR_NO_SPACE -5
#define ADLER32_BASE 65521
#define BLOCK_SIZE 65536
static const uint8_t sixpack_magic[8] = {137, '6', 'P', 'K', 13, 10, 26, 10};
static uint32_t update_adler32(uint32_t checksum, const void *buf, int len) {
const uint8_t *ptr = (const uint8_t*)buf;
uint32_t s1 = checksum & 0xffff;
uint32_t s2 = (checksum >> 16) & 0xffff;
while (len > 0) {
unsigned k = len < 5552 ? len : 5552;
len -= k;
while (k--) {
s1 += *ptr++;
if (s1 >= ADLER32_BASE) s1 -= ADLER32_BASE;
s2 += s1;
if (s2 >= ADLER32_BASE) s2 -= ADLER32_BASE;
}
}
return (s2 << 16) + s1;
}
static uint32_t readU16(const uint8_t *p) {
return (uint32_t)p[0] | ((uint32_t)p[1] << 8);
}
static uint32_t readU32(const uint8_t *p) {
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}
static void read_chunk_header(const uint8_t *p, int *id, int *opts,
uint32_t *size, uint32_t *checksum, uint32_t *extra) {
*id = (int)readU16(p);
*opts = (int)readU16(p + 2);
*size = readU32(p + 4);
*checksum = readU32(p + 8);
*extra = readU32(p + 12);
}
int sixpack_decompress_mem(const uint8_t *input, size_t in_size,
uint8_t *output, size_t out_cap)
{
if (!input || in_size < 8)
return SIXPACK_ERR_BAD_FORMAT;
/* Check magic */
for (int i = 0; i < 8; i++)
if (input[i] != sixpack_magic[i])
return SIXPACK_ERR_MAGIC;
size_t pos = 8; /* skip magic */
size_t written = 0;
while (pos + 16 <= in_size) {
int chunk_id, opts;
uint32_t size, checksum, extra;
read_chunk_header(input + pos, &chunk_id, &opts, &size, &checksum, &extra);
pos += 16;
if (pos + size > in_size)
return SIXPACK_ERR_BAD_FORMAT;
const uint8_t *chunk_data = input + pos;
pos += size;
/* File header chunk */
if (chunk_id == 1) {
uint32_t decomp_size = readU32(chunk_data);
(void)decomp_size; /* not strictly needed here */
continue;
}
/* Data chunk */
if (chunk_id == 17) {
uint32_t cksum = update_adler32(1L, chunk_data, size);
if (cksum != checksum)
return SIXPACK_ERR_CHECKSUM;
if (opts == 0) {
/* Stored (uncompressed) */
if (written + size > out_cap)
return SIXPACK_ERR_NO_SPACE;
hal_memcpy(output + written, chunk_data, size);
written += size;
} else if (opts == 1) {
/* FastLZ compressed */
if (written + extra > out_cap)
return SIXPACK_ERR_NO_SPACE;
int dec = fastlz_decompress(chunk_data, size, output + written, extra);
if (dec != (int)extra)
return SIXPACK_ERR_DECOMPRESS;
written += extra;
} else {
return SIXPACK_ERR_BAD_FORMAT;
}
}
}
return (int)written;
}