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