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