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 11*3117ece4Schristos #include "method.h" 12*3117ece4Schristos 13*3117ece4Schristos #include <stdio.h> 14*3117ece4Schristos #include <stdlib.h> 15*3117ece4Schristos 16*3117ece4Schristos #define ZSTD_STATIC_LINKING_ONLY 17*3117ece4Schristos #include <zstd.h> 18*3117ece4Schristos 19*3117ece4Schristos #define MIN(x, y) ((x) < (y) ? (x) : (y)) 20*3117ece4Schristos 21*3117ece4Schristos static char const* g_zstdcli = NULL; 22*3117ece4Schristos 23*3117ece4Schristos void method_set_zstdcli(char const* zstdcli) { 24*3117ece4Schristos g_zstdcli = zstdcli; 25*3117ece4Schristos } 26*3117ece4Schristos 27*3117ece4Schristos /** 28*3117ece4Schristos * Macro to get a pointer of type, given ptr, which is a member variable with 29*3117ece4Schristos * the given name, member. 30*3117ece4Schristos * 31*3117ece4Schristos * method_state_t* base = ...; 32*3117ece4Schristos * buffer_state_t* state = container_of(base, buffer_state_t, base); 33*3117ece4Schristos */ 34*3117ece4Schristos #define container_of(ptr, type, member) \ 35*3117ece4Schristos ((type*)(ptr == NULL ? NULL : (char*)(ptr)-offsetof(type, member))) 36*3117ece4Schristos 37*3117ece4Schristos /** State to reuse the same buffers between compression calls. */ 38*3117ece4Schristos typedef struct { 39*3117ece4Schristos method_state_t base; 40*3117ece4Schristos data_buffers_t inputs; /**< The input buffer for each file. */ 41*3117ece4Schristos data_buffer_t dictionary; /**< The dictionary. */ 42*3117ece4Schristos data_buffer_t compressed; /**< The compressed data buffer. */ 43*3117ece4Schristos data_buffer_t decompressed; /**< The decompressed data buffer. */ 44*3117ece4Schristos } buffer_state_t; 45*3117ece4Schristos 46*3117ece4Schristos static size_t buffers_max_size(data_buffers_t buffers) { 47*3117ece4Schristos size_t max = 0; 48*3117ece4Schristos for (size_t i = 0; i < buffers.size; ++i) { 49*3117ece4Schristos if (buffers.buffers[i].size > max) 50*3117ece4Schristos max = buffers.buffers[i].size; 51*3117ece4Schristos } 52*3117ece4Schristos return max; 53*3117ece4Schristos } 54*3117ece4Schristos 55*3117ece4Schristos static method_state_t* buffer_state_create(data_t const* data) { 56*3117ece4Schristos buffer_state_t* state = (buffer_state_t*)calloc(1, sizeof(buffer_state_t)); 57*3117ece4Schristos if (state == NULL) 58*3117ece4Schristos return NULL; 59*3117ece4Schristos state->base.data = data; 60*3117ece4Schristos state->inputs = data_buffers_get(data); 61*3117ece4Schristos state->dictionary = data_buffer_get_dict(data); 62*3117ece4Schristos size_t const max_size = buffers_max_size(state->inputs); 63*3117ece4Schristos state->compressed = data_buffer_create(ZSTD_compressBound(max_size)); 64*3117ece4Schristos state->decompressed = data_buffer_create(max_size); 65*3117ece4Schristos return &state->base; 66*3117ece4Schristos } 67*3117ece4Schristos 68*3117ece4Schristos static void buffer_state_destroy(method_state_t* base) { 69*3117ece4Schristos if (base == NULL) 70*3117ece4Schristos return; 71*3117ece4Schristos buffer_state_t* state = container_of(base, buffer_state_t, base); 72*3117ece4Schristos free(state); 73*3117ece4Schristos } 74*3117ece4Schristos 75*3117ece4Schristos static int buffer_state_bad( 76*3117ece4Schristos buffer_state_t const* state, 77*3117ece4Schristos config_t const* config) { 78*3117ece4Schristos if (state == NULL) { 79*3117ece4Schristos fprintf(stderr, "buffer_state_t is NULL\n"); 80*3117ece4Schristos return 1; 81*3117ece4Schristos } 82*3117ece4Schristos if (state->inputs.size == 0 || state->compressed.data == NULL || 83*3117ece4Schristos state->decompressed.data == NULL) { 84*3117ece4Schristos fprintf(stderr, "buffer state allocation failure\n"); 85*3117ece4Schristos return 1; 86*3117ece4Schristos } 87*3117ece4Schristos if (config->use_dictionary && state->dictionary.data == NULL) { 88*3117ece4Schristos fprintf(stderr, "dictionary loading failed\n"); 89*3117ece4Schristos return 1; 90*3117ece4Schristos } 91*3117ece4Schristos return 0; 92*3117ece4Schristos } 93*3117ece4Schristos 94*3117ece4Schristos static result_t simple_compress(method_state_t* base, config_t const* config) { 95*3117ece4Schristos buffer_state_t* state = container_of(base, buffer_state_t, base); 96*3117ece4Schristos 97*3117ece4Schristos if (buffer_state_bad(state, config)) 98*3117ece4Schristos return result_error(result_error_system_error); 99*3117ece4Schristos 100*3117ece4Schristos /* Keep the tests short by skipping directories, since behavior shouldn't 101*3117ece4Schristos * change. 102*3117ece4Schristos */ 103*3117ece4Schristos if (base->data->type != data_type_file) 104*3117ece4Schristos return result_error(result_error_skip); 105*3117ece4Schristos 106*3117ece4Schristos if (config->advanced_api_only) 107*3117ece4Schristos return result_error(result_error_skip); 108*3117ece4Schristos 109*3117ece4Schristos if (config->use_dictionary || config->no_pledged_src_size) 110*3117ece4Schristos return result_error(result_error_skip); 111*3117ece4Schristos 112*3117ece4Schristos /* If the config doesn't specify a level, skip. */ 113*3117ece4Schristos int const level = config_get_level(config); 114*3117ece4Schristos if (level == CONFIG_NO_LEVEL) 115*3117ece4Schristos return result_error(result_error_skip); 116*3117ece4Schristos 117*3117ece4Schristos data_buffer_t const input = state->inputs.buffers[0]; 118*3117ece4Schristos 119*3117ece4Schristos /* Compress, decompress, and check the result. */ 120*3117ece4Schristos state->compressed.size = ZSTD_compress( 121*3117ece4Schristos state->compressed.data, 122*3117ece4Schristos state->compressed.capacity, 123*3117ece4Schristos input.data, 124*3117ece4Schristos input.size, 125*3117ece4Schristos level); 126*3117ece4Schristos if (ZSTD_isError(state->compressed.size)) 127*3117ece4Schristos return result_error(result_error_compression_error); 128*3117ece4Schristos 129*3117ece4Schristos state->decompressed.size = ZSTD_decompress( 130*3117ece4Schristos state->decompressed.data, 131*3117ece4Schristos state->decompressed.capacity, 132*3117ece4Schristos state->compressed.data, 133*3117ece4Schristos state->compressed.size); 134*3117ece4Schristos if (ZSTD_isError(state->decompressed.size)) 135*3117ece4Schristos return result_error(result_error_decompression_error); 136*3117ece4Schristos if (data_buffer_compare(input, state->decompressed)) 137*3117ece4Schristos return result_error(result_error_round_trip_error); 138*3117ece4Schristos 139*3117ece4Schristos result_data_t data; 140*3117ece4Schristos data.total_size = state->compressed.size; 141*3117ece4Schristos return result_data(data); 142*3117ece4Schristos } 143*3117ece4Schristos 144*3117ece4Schristos static result_t compress_cctx_compress( 145*3117ece4Schristos method_state_t* base, 146*3117ece4Schristos config_t const* config) { 147*3117ece4Schristos buffer_state_t* state = container_of(base, buffer_state_t, base); 148*3117ece4Schristos 149*3117ece4Schristos if (buffer_state_bad(state, config)) 150*3117ece4Schristos return result_error(result_error_system_error); 151*3117ece4Schristos 152*3117ece4Schristos if (config->no_pledged_src_size) 153*3117ece4Schristos return result_error(result_error_skip); 154*3117ece4Schristos 155*3117ece4Schristos if (base->data->type != data_type_dir) 156*3117ece4Schristos return result_error(result_error_skip); 157*3117ece4Schristos 158*3117ece4Schristos if (config->advanced_api_only) 159*3117ece4Schristos return result_error(result_error_skip); 160*3117ece4Schristos 161*3117ece4Schristos int const level = config_get_level(config); 162*3117ece4Schristos 163*3117ece4Schristos ZSTD_CCtx* cctx = ZSTD_createCCtx(); 164*3117ece4Schristos ZSTD_DCtx* dctx = ZSTD_createDCtx(); 165*3117ece4Schristos if (cctx == NULL || dctx == NULL) { 166*3117ece4Schristos fprintf(stderr, "context creation failed\n"); 167*3117ece4Schristos return result_error(result_error_system_error); 168*3117ece4Schristos } 169*3117ece4Schristos 170*3117ece4Schristos result_t result; 171*3117ece4Schristos result_data_t data = {.total_size = 0}; 172*3117ece4Schristos for (size_t i = 0; i < state->inputs.size; ++i) { 173*3117ece4Schristos data_buffer_t const input = state->inputs.buffers[i]; 174*3117ece4Schristos ZSTD_parameters const params = 175*3117ece4Schristos config_get_zstd_params(config, input.size, state->dictionary.size); 176*3117ece4Schristos 177*3117ece4Schristos if (level == CONFIG_NO_LEVEL) 178*3117ece4Schristos state->compressed.size = ZSTD_compress_advanced( 179*3117ece4Schristos cctx, 180*3117ece4Schristos state->compressed.data, 181*3117ece4Schristos state->compressed.capacity, 182*3117ece4Schristos input.data, 183*3117ece4Schristos input.size, 184*3117ece4Schristos config->use_dictionary ? state->dictionary.data : NULL, 185*3117ece4Schristos config->use_dictionary ? state->dictionary.size : 0, 186*3117ece4Schristos params); 187*3117ece4Schristos else if (config->use_dictionary) 188*3117ece4Schristos state->compressed.size = ZSTD_compress_usingDict( 189*3117ece4Schristos cctx, 190*3117ece4Schristos state->compressed.data, 191*3117ece4Schristos state->compressed.capacity, 192*3117ece4Schristos input.data, 193*3117ece4Schristos input.size, 194*3117ece4Schristos state->dictionary.data, 195*3117ece4Schristos state->dictionary.size, 196*3117ece4Schristos level); 197*3117ece4Schristos else 198*3117ece4Schristos state->compressed.size = ZSTD_compressCCtx( 199*3117ece4Schristos cctx, 200*3117ece4Schristos state->compressed.data, 201*3117ece4Schristos state->compressed.capacity, 202*3117ece4Schristos input.data, 203*3117ece4Schristos input.size, 204*3117ece4Schristos level); 205*3117ece4Schristos 206*3117ece4Schristos if (ZSTD_isError(state->compressed.size)) { 207*3117ece4Schristos result = result_error(result_error_compression_error); 208*3117ece4Schristos goto out; 209*3117ece4Schristos } 210*3117ece4Schristos 211*3117ece4Schristos if (config->use_dictionary) 212*3117ece4Schristos state->decompressed.size = ZSTD_decompress_usingDict( 213*3117ece4Schristos dctx, 214*3117ece4Schristos state->decompressed.data, 215*3117ece4Schristos state->decompressed.capacity, 216*3117ece4Schristos state->compressed.data, 217*3117ece4Schristos state->compressed.size, 218*3117ece4Schristos state->dictionary.data, 219*3117ece4Schristos state->dictionary.size); 220*3117ece4Schristos else 221*3117ece4Schristos state->decompressed.size = ZSTD_decompressDCtx( 222*3117ece4Schristos dctx, 223*3117ece4Schristos state->decompressed.data, 224*3117ece4Schristos state->decompressed.capacity, 225*3117ece4Schristos state->compressed.data, 226*3117ece4Schristos state->compressed.size); 227*3117ece4Schristos if (ZSTD_isError(state->decompressed.size)) { 228*3117ece4Schristos result = result_error(result_error_decompression_error); 229*3117ece4Schristos goto out; 230*3117ece4Schristos } 231*3117ece4Schristos if (data_buffer_compare(input, state->decompressed)) { 232*3117ece4Schristos result = result_error(result_error_round_trip_error); 233*3117ece4Schristos goto out; 234*3117ece4Schristos } 235*3117ece4Schristos 236*3117ece4Schristos data.total_size += state->compressed.size; 237*3117ece4Schristos } 238*3117ece4Schristos 239*3117ece4Schristos result = result_data(data); 240*3117ece4Schristos out: 241*3117ece4Schristos ZSTD_freeCCtx(cctx); 242*3117ece4Schristos ZSTD_freeDCtx(dctx); 243*3117ece4Schristos return result; 244*3117ece4Schristos } 245*3117ece4Schristos 246*3117ece4Schristos /** Generic state creation function. */ 247*3117ece4Schristos static method_state_t* method_state_create(data_t const* data) { 248*3117ece4Schristos method_state_t* state = (method_state_t*)malloc(sizeof(method_state_t)); 249*3117ece4Schristos if (state == NULL) 250*3117ece4Schristos return NULL; 251*3117ece4Schristos state->data = data; 252*3117ece4Schristos return state; 253*3117ece4Schristos } 254*3117ece4Schristos 255*3117ece4Schristos static void method_state_destroy(method_state_t* state) { 256*3117ece4Schristos free(state); 257*3117ece4Schristos } 258*3117ece4Schristos 259*3117ece4Schristos static result_t cli_compress(method_state_t* state, config_t const* config) { 260*3117ece4Schristos if (config->cli_args == NULL) 261*3117ece4Schristos return result_error(result_error_skip); 262*3117ece4Schristos 263*3117ece4Schristos if (config->advanced_api_only) 264*3117ece4Schristos return result_error(result_error_skip); 265*3117ece4Schristos 266*3117ece4Schristos /* We don't support no pledged source size with directories. Too slow. */ 267*3117ece4Schristos if (state->data->type == data_type_dir && config->no_pledged_src_size) 268*3117ece4Schristos return result_error(result_error_skip); 269*3117ece4Schristos 270*3117ece4Schristos if (g_zstdcli == NULL) 271*3117ece4Schristos return result_error(result_error_system_error); 272*3117ece4Schristos 273*3117ece4Schristos /* '<zstd>' -cqr <args> [-D '<dict>'] '<file/dir>' */ 274*3117ece4Schristos char cmd[1024]; 275*3117ece4Schristos size_t const cmd_size = snprintf( 276*3117ece4Schristos cmd, 277*3117ece4Schristos sizeof(cmd), 278*3117ece4Schristos "'%s' -cqr %s %s%s%s %s '%s'", 279*3117ece4Schristos g_zstdcli, 280*3117ece4Schristos config->cli_args, 281*3117ece4Schristos config->use_dictionary ? "-D '" : "", 282*3117ece4Schristos config->use_dictionary ? state->data->dict.path : "", 283*3117ece4Schristos config->use_dictionary ? "'" : "", 284*3117ece4Schristos config->no_pledged_src_size ? "<" : "", 285*3117ece4Schristos state->data->data.path); 286*3117ece4Schristos if (cmd_size >= sizeof(cmd)) { 287*3117ece4Schristos fprintf(stderr, "command too large: %s\n", cmd); 288*3117ece4Schristos return result_error(result_error_system_error); 289*3117ece4Schristos } 290*3117ece4Schristos FILE* zstd = popen(cmd, "r"); 291*3117ece4Schristos if (zstd == NULL) { 292*3117ece4Schristos fprintf(stderr, "failed to popen command: %s\n", cmd); 293*3117ece4Schristos return result_error(result_error_system_error); 294*3117ece4Schristos } 295*3117ece4Schristos 296*3117ece4Schristos char out[4096]; 297*3117ece4Schristos size_t total_size = 0; 298*3117ece4Schristos while (1) { 299*3117ece4Schristos size_t const size = fread(out, 1, sizeof(out), zstd); 300*3117ece4Schristos total_size += size; 301*3117ece4Schristos if (size != sizeof(out)) 302*3117ece4Schristos break; 303*3117ece4Schristos } 304*3117ece4Schristos if (ferror(zstd) || pclose(zstd) != 0) { 305*3117ece4Schristos fprintf(stderr, "zstd failed with command: %s\n", cmd); 306*3117ece4Schristos return result_error(result_error_compression_error); 307*3117ece4Schristos } 308*3117ece4Schristos 309*3117ece4Schristos result_data_t const data = {.total_size = total_size}; 310*3117ece4Schristos return result_data(data); 311*3117ece4Schristos } 312*3117ece4Schristos 313*3117ece4Schristos static int advanced_config( 314*3117ece4Schristos ZSTD_CCtx* cctx, 315*3117ece4Schristos buffer_state_t* state, 316*3117ece4Schristos config_t const* config) { 317*3117ece4Schristos ZSTD_CCtx_reset(cctx, ZSTD_reset_session_and_parameters); 318*3117ece4Schristos for (size_t p = 0; p < config->param_values.size; ++p) { 319*3117ece4Schristos param_value_t const pv = config->param_values.data[p]; 320*3117ece4Schristos if (ZSTD_isError(ZSTD_CCtx_setParameter(cctx, pv.param, pv.value))) { 321*3117ece4Schristos return 1; 322*3117ece4Schristos } 323*3117ece4Schristos } 324*3117ece4Schristos if (config->use_dictionary) { 325*3117ece4Schristos if (ZSTD_isError(ZSTD_CCtx_loadDictionary( 326*3117ece4Schristos cctx, state->dictionary.data, state->dictionary.size))) { 327*3117ece4Schristos return 1; 328*3117ece4Schristos } 329*3117ece4Schristos } 330*3117ece4Schristos return 0; 331*3117ece4Schristos } 332*3117ece4Schristos 333*3117ece4Schristos static result_t advanced_one_pass_compress_output_adjustment( 334*3117ece4Schristos method_state_t* base, 335*3117ece4Schristos config_t const* config, 336*3117ece4Schristos size_t const subtract) { 337*3117ece4Schristos buffer_state_t* state = container_of(base, buffer_state_t, base); 338*3117ece4Schristos 339*3117ece4Schristos if (buffer_state_bad(state, config)) 340*3117ece4Schristos return result_error(result_error_system_error); 341*3117ece4Schristos 342*3117ece4Schristos ZSTD_CCtx* cctx = ZSTD_createCCtx(); 343*3117ece4Schristos result_t result; 344*3117ece4Schristos 345*3117ece4Schristos if (!cctx || advanced_config(cctx, state, config)) { 346*3117ece4Schristos result = result_error(result_error_compression_error); 347*3117ece4Schristos goto out; 348*3117ece4Schristos } 349*3117ece4Schristos 350*3117ece4Schristos result_data_t data = {.total_size = 0}; 351*3117ece4Schristos for (size_t i = 0; i < state->inputs.size; ++i) { 352*3117ece4Schristos data_buffer_t const input = state->inputs.buffers[i]; 353*3117ece4Schristos 354*3117ece4Schristos if (!config->no_pledged_src_size) { 355*3117ece4Schristos if (ZSTD_isError(ZSTD_CCtx_setPledgedSrcSize(cctx, input.size))) { 356*3117ece4Schristos result = result_error(result_error_compression_error); 357*3117ece4Schristos goto out; 358*3117ece4Schristos } 359*3117ece4Schristos } 360*3117ece4Schristos size_t const size = ZSTD_compress2( 361*3117ece4Schristos cctx, 362*3117ece4Schristos state->compressed.data, 363*3117ece4Schristos ZSTD_compressBound(input.size) - subtract, 364*3117ece4Schristos input.data, 365*3117ece4Schristos input.size); 366*3117ece4Schristos if (ZSTD_isError(size)) { 367*3117ece4Schristos result = result_error(result_error_compression_error); 368*3117ece4Schristos goto out; 369*3117ece4Schristos } 370*3117ece4Schristos data.total_size += size; 371*3117ece4Schristos } 372*3117ece4Schristos 373*3117ece4Schristos result = result_data(data); 374*3117ece4Schristos out: 375*3117ece4Schristos ZSTD_freeCCtx(cctx); 376*3117ece4Schristos return result; 377*3117ece4Schristos } 378*3117ece4Schristos 379*3117ece4Schristos static result_t advanced_one_pass_compress( 380*3117ece4Schristos method_state_t* base, 381*3117ece4Schristos config_t const* config) { 382*3117ece4Schristos return advanced_one_pass_compress_output_adjustment(base, config, 0); 383*3117ece4Schristos } 384*3117ece4Schristos 385*3117ece4Schristos static result_t advanced_one_pass_compress_small_output( 386*3117ece4Schristos method_state_t* base, 387*3117ece4Schristos config_t const* config) { 388*3117ece4Schristos return advanced_one_pass_compress_output_adjustment(base, config, 1); 389*3117ece4Schristos } 390*3117ece4Schristos 391*3117ece4Schristos static result_t advanced_streaming_compress( 392*3117ece4Schristos method_state_t* base, 393*3117ece4Schristos config_t const* config) { 394*3117ece4Schristos buffer_state_t* state = container_of(base, buffer_state_t, base); 395*3117ece4Schristos 396*3117ece4Schristos if (buffer_state_bad(state, config)) 397*3117ece4Schristos return result_error(result_error_system_error); 398*3117ece4Schristos 399*3117ece4Schristos ZSTD_CCtx* cctx = ZSTD_createCCtx(); 400*3117ece4Schristos result_t result; 401*3117ece4Schristos 402*3117ece4Schristos if (!cctx || advanced_config(cctx, state, config)) { 403*3117ece4Schristos result = result_error(result_error_compression_error); 404*3117ece4Schristos goto out; 405*3117ece4Schristos } 406*3117ece4Schristos 407*3117ece4Schristos result_data_t data = {.total_size = 0}; 408*3117ece4Schristos for (size_t i = 0; i < state->inputs.size; ++i) { 409*3117ece4Schristos data_buffer_t input = state->inputs.buffers[i]; 410*3117ece4Schristos 411*3117ece4Schristos if (!config->no_pledged_src_size) { 412*3117ece4Schristos if (ZSTD_isError(ZSTD_CCtx_setPledgedSrcSize(cctx, input.size))) { 413*3117ece4Schristos result = result_error(result_error_compression_error); 414*3117ece4Schristos goto out; 415*3117ece4Schristos } 416*3117ece4Schristos } 417*3117ece4Schristos 418*3117ece4Schristos while (input.size > 0) { 419*3117ece4Schristos ZSTD_inBuffer in = {input.data, MIN(input.size, 4096)}; 420*3117ece4Schristos input.data += in.size; 421*3117ece4Schristos input.size -= in.size; 422*3117ece4Schristos ZSTD_EndDirective const op = 423*3117ece4Schristos input.size > 0 ? ZSTD_e_continue : ZSTD_e_end; 424*3117ece4Schristos size_t ret = 0; 425*3117ece4Schristos while (in.pos < in.size || (op == ZSTD_e_end && ret != 0)) { 426*3117ece4Schristos ZSTD_outBuffer out = {state->compressed.data, 427*3117ece4Schristos MIN(state->compressed.capacity, 1024)}; 428*3117ece4Schristos ret = ZSTD_compressStream2(cctx, &out, &in, op); 429*3117ece4Schristos if (ZSTD_isError(ret)) { 430*3117ece4Schristos result = result_error(result_error_compression_error); 431*3117ece4Schristos goto out; 432*3117ece4Schristos } 433*3117ece4Schristos data.total_size += out.pos; 434*3117ece4Schristos } 435*3117ece4Schristos } 436*3117ece4Schristos } 437*3117ece4Schristos 438*3117ece4Schristos result = result_data(data); 439*3117ece4Schristos out: 440*3117ece4Schristos ZSTD_freeCCtx(cctx); 441*3117ece4Schristos return result; 442*3117ece4Schristos } 443*3117ece4Schristos 444*3117ece4Schristos static int init_cstream( 445*3117ece4Schristos buffer_state_t* state, 446*3117ece4Schristos ZSTD_CStream* zcs, 447*3117ece4Schristos config_t const* config, 448*3117ece4Schristos int const advanced, 449*3117ece4Schristos ZSTD_CDict** cdict) 450*3117ece4Schristos { 451*3117ece4Schristos size_t zret; 452*3117ece4Schristos if (advanced) { 453*3117ece4Schristos ZSTD_parameters const params = config_get_zstd_params(config, 0, 0); 454*3117ece4Schristos ZSTD_CDict* dict = NULL; 455*3117ece4Schristos if (cdict) { 456*3117ece4Schristos if (!config->use_dictionary) 457*3117ece4Schristos return 1; 458*3117ece4Schristos *cdict = ZSTD_createCDict_advanced( 459*3117ece4Schristos state->dictionary.data, 460*3117ece4Schristos state->dictionary.size, 461*3117ece4Schristos ZSTD_dlm_byRef, 462*3117ece4Schristos ZSTD_dct_auto, 463*3117ece4Schristos params.cParams, 464*3117ece4Schristos ZSTD_defaultCMem); 465*3117ece4Schristos if (!*cdict) { 466*3117ece4Schristos return 1; 467*3117ece4Schristos } 468*3117ece4Schristos zret = ZSTD_initCStream_usingCDict_advanced( 469*3117ece4Schristos zcs, *cdict, params.fParams, ZSTD_CONTENTSIZE_UNKNOWN); 470*3117ece4Schristos } else { 471*3117ece4Schristos zret = ZSTD_initCStream_advanced( 472*3117ece4Schristos zcs, 473*3117ece4Schristos config->use_dictionary ? state->dictionary.data : NULL, 474*3117ece4Schristos config->use_dictionary ? state->dictionary.size : 0, 475*3117ece4Schristos params, 476*3117ece4Schristos ZSTD_CONTENTSIZE_UNKNOWN); 477*3117ece4Schristos } 478*3117ece4Schristos } else { 479*3117ece4Schristos int const level = config_get_level(config); 480*3117ece4Schristos if (level == CONFIG_NO_LEVEL) 481*3117ece4Schristos return 1; 482*3117ece4Schristos if (cdict) { 483*3117ece4Schristos if (!config->use_dictionary) 484*3117ece4Schristos return 1; 485*3117ece4Schristos *cdict = ZSTD_createCDict( 486*3117ece4Schristos state->dictionary.data, 487*3117ece4Schristos state->dictionary.size, 488*3117ece4Schristos level); 489*3117ece4Schristos if (!*cdict) { 490*3117ece4Schristos return 1; 491*3117ece4Schristos } 492*3117ece4Schristos zret = ZSTD_initCStream_usingCDict(zcs, *cdict); 493*3117ece4Schristos } else if (config->use_dictionary) { 494*3117ece4Schristos zret = ZSTD_initCStream_usingDict( 495*3117ece4Schristos zcs, 496*3117ece4Schristos state->dictionary.data, 497*3117ece4Schristos state->dictionary.size, 498*3117ece4Schristos level); 499*3117ece4Schristos } else { 500*3117ece4Schristos zret = ZSTD_initCStream(zcs, level); 501*3117ece4Schristos } 502*3117ece4Schristos } 503*3117ece4Schristos if (ZSTD_isError(zret)) { 504*3117ece4Schristos return 1; 505*3117ece4Schristos } 506*3117ece4Schristos return 0; 507*3117ece4Schristos } 508*3117ece4Schristos 509*3117ece4Schristos static result_t old_streaming_compress_internal( 510*3117ece4Schristos method_state_t* base, 511*3117ece4Schristos config_t const* config, 512*3117ece4Schristos int const advanced, 513*3117ece4Schristos int const cdict) { 514*3117ece4Schristos buffer_state_t* state = container_of(base, buffer_state_t, base); 515*3117ece4Schristos 516*3117ece4Schristos if (buffer_state_bad(state, config)) 517*3117ece4Schristos return result_error(result_error_system_error); 518*3117ece4Schristos 519*3117ece4Schristos 520*3117ece4Schristos ZSTD_CStream* zcs = ZSTD_createCStream(); 521*3117ece4Schristos ZSTD_CDict* cd = NULL; 522*3117ece4Schristos result_t result; 523*3117ece4Schristos if (zcs == NULL) { 524*3117ece4Schristos result = result_error(result_error_compression_error); 525*3117ece4Schristos goto out; 526*3117ece4Schristos } 527*3117ece4Schristos if (!advanced && config_get_level(config) == CONFIG_NO_LEVEL) { 528*3117ece4Schristos result = result_error(result_error_skip); 529*3117ece4Schristos goto out; 530*3117ece4Schristos } 531*3117ece4Schristos if (cdict && !config->use_dictionary) { 532*3117ece4Schristos result = result_error(result_error_skip); 533*3117ece4Schristos goto out; 534*3117ece4Schristos } 535*3117ece4Schristos if (config->advanced_api_only) { 536*3117ece4Schristos result = result_error(result_error_skip); 537*3117ece4Schristos goto out; 538*3117ece4Schristos } 539*3117ece4Schristos if (init_cstream(state, zcs, config, advanced, cdict ? &cd : NULL)) { 540*3117ece4Schristos result = result_error(result_error_compression_error); 541*3117ece4Schristos goto out; 542*3117ece4Schristos } 543*3117ece4Schristos 544*3117ece4Schristos result_data_t data = {.total_size = 0}; 545*3117ece4Schristos for (size_t i = 0; i < state->inputs.size; ++i) { 546*3117ece4Schristos data_buffer_t input = state->inputs.buffers[i]; 547*3117ece4Schristos size_t zret = ZSTD_resetCStream( 548*3117ece4Schristos zcs, 549*3117ece4Schristos config->no_pledged_src_size ? ZSTD_CONTENTSIZE_UNKNOWN : input.size); 550*3117ece4Schristos if (ZSTD_isError(zret)) { 551*3117ece4Schristos result = result_error(result_error_compression_error); 552*3117ece4Schristos goto out; 553*3117ece4Schristos } 554*3117ece4Schristos 555*3117ece4Schristos while (input.size > 0) { 556*3117ece4Schristos ZSTD_inBuffer in = {input.data, MIN(input.size, 4096)}; 557*3117ece4Schristos input.data += in.size; 558*3117ece4Schristos input.size -= in.size; 559*3117ece4Schristos ZSTD_EndDirective const op = 560*3117ece4Schristos input.size > 0 ? ZSTD_e_continue : ZSTD_e_end; 561*3117ece4Schristos zret = 0; 562*3117ece4Schristos while (in.pos < in.size || (op == ZSTD_e_end && zret != 0)) { 563*3117ece4Schristos ZSTD_outBuffer out = {state->compressed.data, 564*3117ece4Schristos MIN(state->compressed.capacity, 1024)}; 565*3117ece4Schristos if (op == ZSTD_e_continue || in.pos < in.size) 566*3117ece4Schristos zret = ZSTD_compressStream(zcs, &out, &in); 567*3117ece4Schristos else 568*3117ece4Schristos zret = ZSTD_endStream(zcs, &out); 569*3117ece4Schristos if (ZSTD_isError(zret)) { 570*3117ece4Schristos result = result_error(result_error_compression_error); 571*3117ece4Schristos goto out; 572*3117ece4Schristos } 573*3117ece4Schristos data.total_size += out.pos; 574*3117ece4Schristos } 575*3117ece4Schristos } 576*3117ece4Schristos } 577*3117ece4Schristos 578*3117ece4Schristos result = result_data(data); 579*3117ece4Schristos out: 580*3117ece4Schristos ZSTD_freeCStream(zcs); 581*3117ece4Schristos ZSTD_freeCDict(cd); 582*3117ece4Schristos return result; 583*3117ece4Schristos } 584*3117ece4Schristos 585*3117ece4Schristos static result_t old_streaming_compress( 586*3117ece4Schristos method_state_t* base, 587*3117ece4Schristos config_t const* config) 588*3117ece4Schristos { 589*3117ece4Schristos return old_streaming_compress_internal( 590*3117ece4Schristos base, config, /* advanced */ 0, /* cdict */ 0); 591*3117ece4Schristos } 592*3117ece4Schristos 593*3117ece4Schristos static result_t old_streaming_compress_advanced( 594*3117ece4Schristos method_state_t* base, 595*3117ece4Schristos config_t const* config) 596*3117ece4Schristos { 597*3117ece4Schristos return old_streaming_compress_internal( 598*3117ece4Schristos base, config, /* advanced */ 1, /* cdict */ 0); 599*3117ece4Schristos } 600*3117ece4Schristos 601*3117ece4Schristos static result_t old_streaming_compress_cdict( 602*3117ece4Schristos method_state_t* base, 603*3117ece4Schristos config_t const* config) 604*3117ece4Schristos { 605*3117ece4Schristos return old_streaming_compress_internal( 606*3117ece4Schristos base, config, /* advanced */ 0, /* cdict */ 1); 607*3117ece4Schristos } 608*3117ece4Schristos 609*3117ece4Schristos static result_t old_streaming_compress_cdict_advanced( 610*3117ece4Schristos method_state_t* base, 611*3117ece4Schristos config_t const* config) 612*3117ece4Schristos { 613*3117ece4Schristos return old_streaming_compress_internal( 614*3117ece4Schristos base, config, /* advanced */ 1, /* cdict */ 1); 615*3117ece4Schristos } 616*3117ece4Schristos 617*3117ece4Schristos method_t const simple = { 618*3117ece4Schristos .name = "compress simple", 619*3117ece4Schristos .create = buffer_state_create, 620*3117ece4Schristos .compress = simple_compress, 621*3117ece4Schristos .destroy = buffer_state_destroy, 622*3117ece4Schristos }; 623*3117ece4Schristos 624*3117ece4Schristos method_t const compress_cctx = { 625*3117ece4Schristos .name = "compress cctx", 626*3117ece4Schristos .create = buffer_state_create, 627*3117ece4Schristos .compress = compress_cctx_compress, 628*3117ece4Schristos .destroy = buffer_state_destroy, 629*3117ece4Schristos }; 630*3117ece4Schristos 631*3117ece4Schristos method_t const advanced_one_pass = { 632*3117ece4Schristos .name = "advanced one pass", 633*3117ece4Schristos .create = buffer_state_create, 634*3117ece4Schristos .compress = advanced_one_pass_compress, 635*3117ece4Schristos .destroy = buffer_state_destroy, 636*3117ece4Schristos }; 637*3117ece4Schristos 638*3117ece4Schristos method_t const advanced_one_pass_small_out = { 639*3117ece4Schristos .name = "advanced one pass small out", 640*3117ece4Schristos .create = buffer_state_create, 641*3117ece4Schristos .compress = advanced_one_pass_compress, 642*3117ece4Schristos .destroy = buffer_state_destroy, 643*3117ece4Schristos }; 644*3117ece4Schristos 645*3117ece4Schristos method_t const advanced_streaming = { 646*3117ece4Schristos .name = "advanced streaming", 647*3117ece4Schristos .create = buffer_state_create, 648*3117ece4Schristos .compress = advanced_streaming_compress, 649*3117ece4Schristos .destroy = buffer_state_destroy, 650*3117ece4Schristos }; 651*3117ece4Schristos 652*3117ece4Schristos method_t const old_streaming = { 653*3117ece4Schristos .name = "old streaming", 654*3117ece4Schristos .create = buffer_state_create, 655*3117ece4Schristos .compress = old_streaming_compress, 656*3117ece4Schristos .destroy = buffer_state_destroy, 657*3117ece4Schristos }; 658*3117ece4Schristos 659*3117ece4Schristos method_t const old_streaming_advanced = { 660*3117ece4Schristos .name = "old streaming advanced", 661*3117ece4Schristos .create = buffer_state_create, 662*3117ece4Schristos .compress = old_streaming_compress_advanced, 663*3117ece4Schristos .destroy = buffer_state_destroy, 664*3117ece4Schristos }; 665*3117ece4Schristos 666*3117ece4Schristos method_t const old_streaming_cdict = { 667*3117ece4Schristos .name = "old streaming cdict", 668*3117ece4Schristos .create = buffer_state_create, 669*3117ece4Schristos .compress = old_streaming_compress_cdict, 670*3117ece4Schristos .destroy = buffer_state_destroy, 671*3117ece4Schristos }; 672*3117ece4Schristos 673*3117ece4Schristos method_t const old_streaming_advanced_cdict = { 674*3117ece4Schristos .name = "old streaming advanced cdict", 675*3117ece4Schristos .create = buffer_state_create, 676*3117ece4Schristos .compress = old_streaming_compress_cdict_advanced, 677*3117ece4Schristos .destroy = buffer_state_destroy, 678*3117ece4Schristos }; 679*3117ece4Schristos 680*3117ece4Schristos method_t const cli = { 681*3117ece4Schristos .name = "zstdcli", 682*3117ece4Schristos .create = method_state_create, 683*3117ece4Schristos .compress = cli_compress, 684*3117ece4Schristos .destroy = method_state_destroy, 685*3117ece4Schristos }; 686*3117ece4Schristos 687*3117ece4Schristos static method_t const* g_methods[] = { 688*3117ece4Schristos &simple, 689*3117ece4Schristos &compress_cctx, 690*3117ece4Schristos &cli, 691*3117ece4Schristos &advanced_one_pass, 692*3117ece4Schristos &advanced_one_pass_small_out, 693*3117ece4Schristos &advanced_streaming, 694*3117ece4Schristos &old_streaming, 695*3117ece4Schristos &old_streaming_advanced, 696*3117ece4Schristos &old_streaming_cdict, 697*3117ece4Schristos &old_streaming_advanced_cdict, 698*3117ece4Schristos NULL, 699*3117ece4Schristos }; 700*3117ece4Schristos 701*3117ece4Schristos method_t const* const* methods = g_methods; 702