Add educational decoder to /contrib

This commit is contained in:
Sean Purcell 2017-01-30 11:42:45 -08:00
parent b5fd15ccb2
commit 9700f92583
4 changed files with 2213 additions and 0 deletions

View File

@ -0,0 +1,18 @@
Educational Decoder
===================
`zstd_decompress.c` is a self-contained implementation of a decoder according
to the Zstandard format specification written in C99.
While it does not implement as many features as the reference decoder,
such as the streaming API or content checksums, it is written to be easy to
follow and understand, to help understand how the Zstandard format works.
It's laid out to match the [format specification],
so it can be used to understand how confusing segments could be implemented.
It also contains implementations of Huffman and FSE table decoding.
[format specification]: https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md
`harness.c` provides a simple test harness around the decoder:
harness <input-file> <output-file> [dictionary]

View File

@ -0,0 +1,93 @@
#include <stdio.h>
#include <stdlib.h>
#include "zstd_decompress.h"
typedef unsigned char u8;
// There's no good way to determine output size without decompressing
// For this example assume we'll never decompress at a ratio larger than 16
#define MAX_COMPRESSION_RATIO (16)
u8 *input;
u8 *output;
u8 *dict;
size_t read_file(const char *path, u8 **ptr) {
FILE *f = fopen(path, "rb");
if (!f) {
fprintf(stderr, "failed to open file %s\n", path);
exit(1);
}
fseek(f, 0L, SEEK_END);
size_t size = ftell(f);
rewind(f);
*ptr = malloc(size);
if (!ptr) {
fprintf(stderr, "failed to allocate memory to hold %s\n", path);
exit(1);
}
size_t pos = 0;
while (!feof(f)) {
size_t read = fread(&(*ptr)[pos], 1, size, f);
if (ferror(f)) {
fprintf(stderr, "error while reading file %s\n", path);
exit(1);
}
pos += read;
}
fclose(f);
return pos;
}
void write_file(const char *path, const u8 *ptr, size_t size) {
FILE *f = fopen(path, "wb");
size_t written = 0;
while (written < size) {
written += fwrite(&ptr[written], 1, size, f);
if (ferror(f)) {
fprintf(stderr, "error while writing file %s\n", path);
exit(1);
}
}
fclose(f);
}
int main(int argc, char **argv) {
if (argc < 3) {
fprintf(stderr, "usage: %s <file.zst> <out_path> [dictionary]\n", argv[0]);
return 1;
}
size_t input_size = read_file(argv[1], &input);
size_t dict_size = 0;
if (argc >= 4) {
dict_size = read_file(argv[3], &dict);
}
output = malloc(MAX_COMPRESSION_RATIO * input_size);
if (!output) {
fprintf(stderr, "failed to allocate memory\n");
return 1;
}
size_t decompressed =
ZSTD_decompress_with_dict(output, input_size * MAX_COMPRESSION_RATIO,
input, input_size, dict, dict_size);
write_file(argv[2], output, decompressed);
free(input);
free(output);
free(dict);
input = output = dict = NULL;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
size_t ZSTD_decompress(void *dst, size_t dst_len, const void *src,
size_t src_len);
size_t ZSTD_decompress_with_dict(void *dst, size_t dst_len, const void *src,
size_t src_len, const void *dict,
size_t dict_len);