1*3117ece4Schristos /* 2*3117ece4Schristos * Copyright (c) Meta Platforms, Inc. and affiliates. 3*3117ece4Schristos * All rights reserved. 4*3117ece4Schristos * 5*3117ece4Schristos * This source code is licensed under both the BSD-style license (found in the 6*3117ece4Schristos * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7*3117ece4Schristos * in the COPYING file in the root directory of this source tree). 8*3117ece4Schristos * You may select, at your option, one of the above-listed licenses. 9*3117ece4Schristos */ 10*3117ece4Schristos #include <stddef.h> 11*3117ece4Schristos #include <stdio.h> 12*3117ece4Schristos #include <stdlib.h> 13*3117ece4Schristos #include <string.h> 14*3117ece4Schristos 15*3117ece4Schristos #include <linux/zstd.h> 16*3117ece4Schristos 17*3117ece4Schristos #define CONTROL(x) \ 18*3117ece4Schristos do { \ 19*3117ece4Schristos if (!(x)) { \ 20*3117ece4Schristos fprintf(stderr, "%s:%u: %s failed!\n", __FUNCTION__, __LINE__, #x); \ 21*3117ece4Schristos abort(); \ 22*3117ece4Schristos } \ 23*3117ece4Schristos } while (0) 24*3117ece4Schristos 25*3117ece4Schristos typedef struct { 26*3117ece4Schristos char *data; 27*3117ece4Schristos char *data2; 28*3117ece4Schristos size_t dataSize; 29*3117ece4Schristos char *comp; 30*3117ece4Schristos size_t compSize; 31*3117ece4Schristos } test_data_t; 32*3117ece4Schristos 33*3117ece4Schristos static test_data_t create_test_data(void) { 34*3117ece4Schristos test_data_t data; 35*3117ece4Schristos data.dataSize = 128 * 1024; 36*3117ece4Schristos data.data = (char*)malloc(data.dataSize); 37*3117ece4Schristos CONTROL(data.data != NULL); 38*3117ece4Schristos data.data2 = (char*)malloc(data.dataSize); 39*3117ece4Schristos CONTROL(data.data2 != NULL); 40*3117ece4Schristos data.compSize = zstd_compress_bound(data.dataSize); 41*3117ece4Schristos data.comp = (char*)malloc(data.compSize); 42*3117ece4Schristos CONTROL(data.comp != NULL); 43*3117ece4Schristos memset(data.data, 0, data.dataSize); 44*3117ece4Schristos return data; 45*3117ece4Schristos } 46*3117ece4Schristos 47*3117ece4Schristos static void free_test_data(test_data_t const *data) { 48*3117ece4Schristos free(data->data); 49*3117ece4Schristos free(data->data2); 50*3117ece4Schristos free(data->comp); 51*3117ece4Schristos } 52*3117ece4Schristos 53*3117ece4Schristos #define MIN(a, b) ((a) < (b) ? (a) : (b)) 54*3117ece4Schristos #define MAX(a, b) ((a) > (b) ? (a) : (b)) 55*3117ece4Schristos 56*3117ece4Schristos static void test_btrfs(test_data_t const *data) { 57*3117ece4Schristos size_t const size = MIN(data->dataSize, 128 * 1024); 58*3117ece4Schristos fprintf(stderr, "testing btrfs use cases... "); 59*3117ece4Schristos for (int level = -1; level < 16; ++level) { 60*3117ece4Schristos zstd_parameters params = zstd_get_params(level, size); 61*3117ece4Schristos size_t const workspaceSize = 62*3117ece4Schristos MAX(zstd_cstream_workspace_bound(¶ms.cParams), 63*3117ece4Schristos zstd_dstream_workspace_bound(size)); 64*3117ece4Schristos void *workspace = malloc(workspaceSize); 65*3117ece4Schristos 66*3117ece4Schristos char const *ip = data->data; 67*3117ece4Schristos char const *iend = ip + size; 68*3117ece4Schristos char *op = data->comp; 69*3117ece4Schristos char *oend = op + data->compSize; 70*3117ece4Schristos 71*3117ece4Schristos CONTROL(params.cParams.windowLog <= 17); 72*3117ece4Schristos CONTROL(workspace != NULL); 73*3117ece4Schristos { 74*3117ece4Schristos zstd_cstream *cctx = zstd_init_cstream(¶ms, size, workspace, workspaceSize); 75*3117ece4Schristos zstd_out_buffer out = {NULL, 0, 0}; 76*3117ece4Schristos zstd_in_buffer in = {NULL, 0, 0}; 77*3117ece4Schristos CONTROL(cctx != NULL); 78*3117ece4Schristos for (;;) { 79*3117ece4Schristos if (in.pos == in.size) { 80*3117ece4Schristos in.src = ip; 81*3117ece4Schristos in.size = MIN(4096, iend - ip); 82*3117ece4Schristos in.pos = 0; 83*3117ece4Schristos ip += in.size; 84*3117ece4Schristos } 85*3117ece4Schristos 86*3117ece4Schristos if (out.pos == out.size) { 87*3117ece4Schristos out.dst = op; 88*3117ece4Schristos out.size = MIN(4096, oend - op); 89*3117ece4Schristos out.pos = 0; 90*3117ece4Schristos op += out.size; 91*3117ece4Schristos } 92*3117ece4Schristos 93*3117ece4Schristos if (ip != iend || in.pos < in.size) { 94*3117ece4Schristos CONTROL(!zstd_is_error(zstd_compress_stream(cctx, &out, &in))); 95*3117ece4Schristos } else { 96*3117ece4Schristos size_t const ret = zstd_end_stream(cctx, &out); 97*3117ece4Schristos CONTROL(!zstd_is_error(ret)); 98*3117ece4Schristos if (ret == 0) { 99*3117ece4Schristos break; 100*3117ece4Schristos } 101*3117ece4Schristos } 102*3117ece4Schristos } 103*3117ece4Schristos op += out.pos; 104*3117ece4Schristos } 105*3117ece4Schristos 106*3117ece4Schristos ip = data->comp; 107*3117ece4Schristos iend = op; 108*3117ece4Schristos op = data->data2; 109*3117ece4Schristos oend = op + size; 110*3117ece4Schristos { 111*3117ece4Schristos zstd_dstream *dctx = zstd_init_dstream(1ULL << params.cParams.windowLog, workspace, workspaceSize); 112*3117ece4Schristos zstd_out_buffer out = {NULL, 0, 0}; 113*3117ece4Schristos zstd_in_buffer in = {NULL, 0, 0}; 114*3117ece4Schristos CONTROL(dctx != NULL); 115*3117ece4Schristos for (;;) { 116*3117ece4Schristos if (in.pos == in.size) { 117*3117ece4Schristos in.src = ip; 118*3117ece4Schristos in.size = MIN(4096, iend - ip); 119*3117ece4Schristos in.pos = 0; 120*3117ece4Schristos ip += in.size; 121*3117ece4Schristos } 122*3117ece4Schristos 123*3117ece4Schristos if (out.pos == out.size) { 124*3117ece4Schristos out.dst = op; 125*3117ece4Schristos out.size = MIN(4096, oend - op); 126*3117ece4Schristos out.pos = 0; 127*3117ece4Schristos op += out.size; 128*3117ece4Schristos } 129*3117ece4Schristos { 130*3117ece4Schristos size_t const ret = zstd_decompress_stream(dctx, &out, &in); 131*3117ece4Schristos CONTROL(!zstd_is_error(ret)); 132*3117ece4Schristos if (ret == 0) { 133*3117ece4Schristos break; 134*3117ece4Schristos } 135*3117ece4Schristos } 136*3117ece4Schristos } 137*3117ece4Schristos } 138*3117ece4Schristos CONTROL((size_t)(op - data->data2) == data->dataSize); 139*3117ece4Schristos CONTROL(!memcmp(data->data, data->data2, data->dataSize)); 140*3117ece4Schristos free(workspace); 141*3117ece4Schristos } 142*3117ece4Schristos fprintf(stderr, "Ok\n"); 143*3117ece4Schristos } 144*3117ece4Schristos 145*3117ece4Schristos static void test_decompress_unzstd(test_data_t const *data) { 146*3117ece4Schristos size_t cSize; 147*3117ece4Schristos fprintf(stderr, "Testing decompress unzstd... "); 148*3117ece4Schristos { 149*3117ece4Schristos zstd_parameters params = zstd_get_params(19, 0); 150*3117ece4Schristos size_t const wkspSize = zstd_cctx_workspace_bound(¶ms.cParams); 151*3117ece4Schristos void* wksp = malloc(wkspSize); 152*3117ece4Schristos zstd_cctx* cctx = zstd_init_cctx(wksp, wkspSize); 153*3117ece4Schristos CONTROL(wksp != NULL); 154*3117ece4Schristos CONTROL(cctx != NULL); 155*3117ece4Schristos cSize = zstd_compress_cctx(cctx, data->comp, data->compSize, data->data, data->dataSize, ¶ms); 156*3117ece4Schristos CONTROL(!zstd_is_error(cSize)); 157*3117ece4Schristos free(wksp); 158*3117ece4Schristos } 159*3117ece4Schristos { 160*3117ece4Schristos size_t const wkspSize = zstd_dctx_workspace_bound(); 161*3117ece4Schristos void* wksp = malloc(wkspSize); 162*3117ece4Schristos zstd_dctx* dctx = zstd_init_dctx(wksp, wkspSize); 163*3117ece4Schristos CONTROL(wksp != NULL); 164*3117ece4Schristos CONTROL(dctx != NULL); 165*3117ece4Schristos { 166*3117ece4Schristos size_t const dSize = zstd_decompress_dctx(dctx, data->data2, data->dataSize, data->comp, cSize); 167*3117ece4Schristos CONTROL(!zstd_is_error(dSize)); 168*3117ece4Schristos CONTROL(dSize == data->dataSize); 169*3117ece4Schristos } 170*3117ece4Schristos CONTROL(!memcmp(data->data, data->data2, data->dataSize)); 171*3117ece4Schristos free(wksp); 172*3117ece4Schristos } 173*3117ece4Schristos fprintf(stderr, "Ok\n"); 174*3117ece4Schristos } 175*3117ece4Schristos 176*3117ece4Schristos static void test_f2fs(void) { 177*3117ece4Schristos fprintf(stderr, "testing f2fs uses... "); 178*3117ece4Schristos CONTROL(zstd_min_clevel() < 0); 179*3117ece4Schristos CONTROL(zstd_max_clevel() == 22); 180*3117ece4Schristos fprintf(stderr, "Ok\n"); 181*3117ece4Schristos } 182*3117ece4Schristos 183*3117ece4Schristos static char *g_stack = NULL; 184*3117ece4Schristos 185*3117ece4Schristos static void __attribute__((noinline)) use(void *x) { 186*3117ece4Schristos asm volatile("" : "+r"(x)); 187*3117ece4Schristos } 188*3117ece4Schristos 189*3117ece4Schristos static void __attribute__((noinline)) fill_stack(void) { 190*3117ece4Schristos memset(g_stack, 0x33, 8192); 191*3117ece4Schristos } 192*3117ece4Schristos 193*3117ece4Schristos static void __attribute__((noinline)) set_stack(void) { 194*3117ece4Schristos 195*3117ece4Schristos char stack[8192]; 196*3117ece4Schristos g_stack = stack; 197*3117ece4Schristos use(g_stack); 198*3117ece4Schristos } 199*3117ece4Schristos 200*3117ece4Schristos static void __attribute__((noinline)) check_stack(void) { 201*3117ece4Schristos size_t cleanStack = 0; 202*3117ece4Schristos while (cleanStack < 8192 && g_stack[cleanStack] == 0x33) { 203*3117ece4Schristos ++cleanStack; 204*3117ece4Schristos } 205*3117ece4Schristos { 206*3117ece4Schristos size_t const stackSize = 8192 - cleanStack; 207*3117ece4Schristos fprintf(stderr, "Maximum stack size: %zu\n", stackSize); 208*3117ece4Schristos CONTROL(stackSize <= 2048 + 512); 209*3117ece4Schristos } 210*3117ece4Schristos } 211*3117ece4Schristos 212*3117ece4Schristos static void test_stack_usage(test_data_t const *data) { 213*3117ece4Schristos set_stack(); 214*3117ece4Schristos fill_stack(); 215*3117ece4Schristos test_f2fs(); 216*3117ece4Schristos test_btrfs(data); 217*3117ece4Schristos test_decompress_unzstd(data); 218*3117ece4Schristos check_stack(); 219*3117ece4Schristos } 220*3117ece4Schristos 221*3117ece4Schristos int main(void) { 222*3117ece4Schristos test_data_t data = create_test_data(); 223*3117ece4Schristos test_f2fs(); 224*3117ece4Schristos test_btrfs(&data); 225*3117ece4Schristos test_decompress_unzstd(&data); 226*3117ece4Schristos test_stack_usage(&data); 227*3117ece4Schristos free_test_data(&data); 228*3117ece4Schristos return 0; 229*3117ece4Schristos } 230