You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
6.1 KiB
211 lines
6.1 KiB
4 months ago
|
/*
|
||
|
* Copyright (c) 7-2020, Facebook, Inc.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* This source code is licensed under both the BSD-style license (found in the
|
||
|
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
||
|
* in the COPYING file in the root directory of this source tree).
|
||
|
* You may select, at your option, one of the above-listed licenses.
|
||
|
*/
|
||
|
#include <stddef.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <linux/zstd.h>
|
||
|
|
||
|
#define CONTROL(x) \
|
||
|
do { \
|
||
|
if (!(x)) { \
|
||
|
fprintf(stderr, "%s:%u: %s failed!\n", __FUNCTION__, __LINE__, #x); \
|
||
|
abort(); \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
typedef struct {
|
||
|
char *data;
|
||
|
char *data2;
|
||
|
size_t dataSize;
|
||
|
char *comp;
|
||
|
size_t compSize;
|
||
|
} test_data_t;
|
||
|
|
||
|
test_data_t create_test_data(void) {
|
||
|
test_data_t data;
|
||
|
data.dataSize = 128 * 1024;
|
||
|
data.data = malloc(data.dataSize);
|
||
|
CONTROL(data.data != NULL);
|
||
|
data.data2 = malloc(data.dataSize);
|
||
|
CONTROL(data.data2 != NULL);
|
||
|
data.compSize = zstd_compress_bound(data.dataSize);
|
||
|
data.comp = malloc(data.compSize);
|
||
|
CONTROL(data.comp != NULL);
|
||
|
memset(data.data, 0, data.dataSize);
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
static void free_test_data(test_data_t const *data) {
|
||
|
free(data->data);
|
||
|
free(data->data2);
|
||
|
free(data->comp);
|
||
|
}
|
||
|
|
||
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||
|
|
||
|
static void test_btrfs(test_data_t const *data) {
|
||
|
fprintf(stderr, "testing btrfs use cases... ");
|
||
|
size_t const size = MIN(data->dataSize, 128 * 1024);
|
||
|
for (int level = -1; level < 16; ++level) {
|
||
|
struct zstd_parameters params = zstd_get_params(level, size);
|
||
|
CONTROL(params.cparams.window_log <= 17);
|
||
|
size_t const workspaceSize =
|
||
|
MAX(zstd_cstream_workspace_bound(¶ms.cparams),
|
||
|
zstd_dstream_workspace_bound(size));
|
||
|
void *workspace = malloc(workspaceSize);
|
||
|
CONTROL(workspace != NULL);
|
||
|
|
||
|
char const *ip = data->data;
|
||
|
char const *iend = ip + size;
|
||
|
char *op = data->comp;
|
||
|
char *oend = op + data->compSize;
|
||
|
{
|
||
|
zstd_cstream *cctx = zstd_init_cstream(¶ms, size, workspace, workspaceSize);
|
||
|
CONTROL(cctx != NULL);
|
||
|
struct zstd_out_buffer out = {NULL, 0, 0};
|
||
|
struct zstd_in_buffer in = {NULL, 0, 0};
|
||
|
for (;;) {
|
||
|
if (in.pos == in.size) {
|
||
|
in.src = ip;
|
||
|
in.size = MIN(4096, iend - ip);
|
||
|
in.pos = 0;
|
||
|
ip += in.size;
|
||
|
}
|
||
|
|
||
|
if (out.pos == out.size) {
|
||
|
out.dst = op;
|
||
|
out.size = MIN(4096, oend - op);
|
||
|
out.pos = 0;
|
||
|
op += out.size;
|
||
|
}
|
||
|
|
||
|
if (ip != iend || in.pos < in.size) {
|
||
|
CONTROL(!zstd_is_error(zstd_compress_stream(cctx, &out, &in)));
|
||
|
} else {
|
||
|
size_t const ret = zstd_end_stream(cctx, &out);
|
||
|
CONTROL(!zstd_is_error(ret));
|
||
|
if (ret == 0) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
op += out.pos;
|
||
|
}
|
||
|
|
||
|
ip = data->comp;
|
||
|
iend = op;
|
||
|
op = data->data2;
|
||
|
oend = op + size;
|
||
|
{
|
||
|
zstd_dstream *dctx = zstd_init_dstream(1ULL << params.cparams.window_log, workspace, workspaceSize);
|
||
|
CONTROL(dctx != NULL);
|
||
|
struct zstd_out_buffer out = {NULL, 0, 0};
|
||
|
struct zstd_in_buffer in = {NULL, 0, 0};
|
||
|
for (;;) {
|
||
|
if (in.pos == in.size) {
|
||
|
in.src = ip;
|
||
|
in.size = MIN(4096, iend - ip);
|
||
|
in.pos = 0;
|
||
|
ip += in.size;
|
||
|
}
|
||
|
|
||
|
if (out.pos == out.size) {
|
||
|
out.dst = op;
|
||
|
out.size = MIN(4096, oend - op);
|
||
|
out.pos = 0;
|
||
|
op += out.size;
|
||
|
}
|
||
|
|
||
|
size_t const ret = zstd_decompress_stream(dctx, &out, &in);
|
||
|
CONTROL(!zstd_is_error(ret));
|
||
|
if (ret == 0) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
CONTROL(op - data->data2 == data->dataSize);
|
||
|
CONTROL(!memcmp(data->data, data->data2, data->dataSize));
|
||
|
free(workspace);
|
||
|
}
|
||
|
fprintf(stderr, "Ok\n");
|
||
|
}
|
||
|
|
||
|
static void test_decompress_unzstd(test_data_t const *data) {
|
||
|
fprintf(stderr, "Testing decompress unzstd... ");
|
||
|
size_t cSize;
|
||
|
{
|
||
|
struct zstd_parameters params = zstd_get_params(19, 0);
|
||
|
size_t const wkspSize = zstd_cctx_workspace_bound(¶ms.cparams);
|
||
|
void* wksp = malloc(wkspSize);
|
||
|
CONTROL(wksp != NULL);
|
||
|
zstd_cctx* cctx = zstd_init_cctx(wksp, wkspSize);
|
||
|
CONTROL(cctx != NULL);
|
||
|
cSize = zstd_compress_cctx(cctx, data->comp, data->compSize, data->data, data->dataSize, ¶ms);
|
||
|
CONTROL(!zstd_is_error(cSize));
|
||
|
free(wksp);
|
||
|
}
|
||
|
{
|
||
|
size_t const wkspSize = zstd_dctx_workspace_bound();
|
||
|
void* wksp = malloc(wkspSize);
|
||
|
CONTROL(wksp != NULL);
|
||
|
zstd_dctx* dctx = zstd_init_dctx(wksp, wkspSize);
|
||
|
CONTROL(dctx != NULL);
|
||
|
size_t const dSize = zstd_decompress_dctx(dctx, data->data2, data->dataSize, data->comp, cSize);
|
||
|
CONTROL(!zstd_is_error(dSize));
|
||
|
CONTROL(dSize == data->dataSize);
|
||
|
CONTROL(!memcmp(data->data, data->data2, data->dataSize));
|
||
|
free(wksp);
|
||
|
}
|
||
|
fprintf(stderr, "Ok\n");
|
||
|
}
|
||
|
|
||
|
static char *g_stack = NULL;
|
||
|
|
||
|
static void __attribute__((noinline)) use(void *x) {
|
||
|
asm volatile("" : "+r"(x));
|
||
|
}
|
||
|
|
||
|
static void __attribute__((noinline)) set_stack() {
|
||
|
|
||
|
char stack[8192];
|
||
|
g_stack = stack;
|
||
|
memset(g_stack, 0x33, 8192);
|
||
|
use(g_stack);
|
||
|
}
|
||
|
|
||
|
static void __attribute__((noinline)) check_stack() {
|
||
|
size_t cleanStack = 0;
|
||
|
while (cleanStack < 8192 && g_stack[cleanStack] == 0x33) {
|
||
|
++cleanStack;
|
||
|
}
|
||
|
size_t const stackSize = 8192 - cleanStack;
|
||
|
fprintf(stderr, "Maximum stack size: %zu\n", stackSize);
|
||
|
CONTROL(stackSize <= 2048 + 512);
|
||
|
}
|
||
|
|
||
|
static void test_stack_usage(test_data_t const *data) {
|
||
|
set_stack();
|
||
|
test_btrfs(data);
|
||
|
test_decompress_unzstd(data);
|
||
|
check_stack();
|
||
|
}
|
||
|
|
||
|
int main(void) {
|
||
|
test_data_t data = create_test_data();
|
||
|
test_btrfs(&data);
|
||
|
test_decompress_unzstd(&data);
|
||
|
test_stack_usage(&data);
|
||
|
free_test_data(&data);
|
||
|
return 0;
|
||
|
}
|