| 1 | |
| 2 |
|
| 3 | #include <string.h>
|
| 4 | #include <linux/perf_event.h>
|
| 5 |
|
| 6 | #include "util/compress.h"
|
| 7 | #include "util/debug.h"
|
| 8 |
|
| 9 | int zstd_init(struct zstd_data *data, int level)
|
| 10 | {
|
| 11 | data->comp_level = level;
|
| 12 | data->dstream = NULL;
|
| 13 | data->cstream = NULL;
|
| 14 | return 0;
|
| 15 | }
|
| 16 |
|
| 17 | int zstd_fini(struct zstd_data *data)
|
| 18 | {
|
| 19 | if (data->dstream) {
|
| 20 | ZSTD_freeDStream(data->dstream);
|
| 21 | data->dstream = NULL;
|
| 22 | }
|
| 23 |
|
| 24 | if (data->cstream) {
|
| 25 | ZSTD_freeCStream(data->cstream);
|
| 26 | data->cstream = NULL;
|
| 27 | }
|
| 28 |
|
| 29 | return 0;
|
| 30 | }
|
| 31 |
|
| 32 | ssize_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_t dst_size,
|
| 33 | void *src, size_t src_size, size_t max_record_size,
|
| 34 | ssize_t process_header(void *record, size_t dst_size,
|
| 35 | size_t data_size))
|
| 36 | {
|
| 37 | size_t ret, compressed = 0;
|
| 38 | ssize_t size;
|
| 39 | ZSTD_inBuffer input = { src, src_size, 0 };
|
| 40 | ZSTD_outBuffer output;
|
| 41 | void *record;
|
| 42 |
|
| 43 | if (!data->cstream) {
|
| 44 | data->cstream = ZSTD_createCStream();
|
| 45 | if (data->cstream == NULL) {
|
| 46 | pr_err("Couldn't create compression stream.\n");
|
| 47 | return -1;
|
| 48 | }
|
| 49 |
|
| 50 | ret = ZSTD_initCStream(data->cstream, data->comp_level);
|
| 51 | if (ZSTD_isError(ret)) {
|
| 52 | pr_err("Failed to initialize compression stream: %s\n",
|
| 53 | ZSTD_getErrorName(ret));
|
| 54 | return -1;
|
| 55 | }
|
| 56 | }
|
| 57 |
|
| 58 | while (input.pos < input.size) {
|
| 59 | record = dst;
|
| 60 | size = process_header(record, dst_size, 0);
|
| 61 |
|
| 62 | if (size < 0)
|
| 63 | goto reset;
|
| 64 | compressed += size;
|
| 65 | dst += size;
|
| 66 | dst_size -= size;
|
| 67 | output = (ZSTD_outBuffer){ dst, (dst_size > max_record_size) ?
|
| 68 | max_record_size : dst_size, 0 };
|
| 69 | ret = ZSTD_compressStream(data->cstream, &output, &input);
|
| 70 | ZSTD_flushStream(data->cstream, &output);
|
| 71 | if (ZSTD_isError(ret)) {
|
| 72 | pr_err("failed to compress %ld bytes: %s\n",
|
| 73 | (long)src_size, ZSTD_getErrorName(ret));
|
| 74 | goto reset;
|
| 75 | }
|
| 76 | compressed += output.pos;
|
| 77 | dst += output.pos;
|
| 78 | dst_size -= output.pos;
|
| 79 | |
| 80 | * No progress: ZSTD couldn't emit any bytes into the
|
| 81 | * remaining output buffer. Calling process_header
|
| 82 | * with output.pos=0 would re-trigger header initialization,
|
| 83 | * double-subtracting the header size from dst_size and
|
| 84 | * underflowing the unsigned counter.
|
| 85 |
|
| 86 | if (output.pos == 0)
|
| 87 | goto reset;
|
| 88 | size = process_header(record, dst_size, output.pos);
|
| 89 | if (size < 0)
|
| 90 | goto reset;
|
| 91 | compressed += size;
|
| 92 | dst += size;
|
| 93 | dst_size -= size;
|
| 94 | }
|
| 95 |
|
| 96 | return compressed;
|
| 97 |
|
| 98 | reset:
|
| 99 |
|
| 100 | ret = ZSTD_initCStream(data->cstream, data->comp_level);
|
| 101 | if (ZSTD_isError(ret))
|
| 102 | pr_err("failed to reset compression context: %s\n",
|
| 103 | ZSTD_getErrorName(ret));
|
| 104 | return -1;
|
| 105 | }
|
| 106 |
|
| 107 | size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size,
|
| 108 | void *dst, size_t dst_size)
|
| 109 | {
|
| 110 | size_t ret;
|
| 111 | ZSTD_inBuffer input = { src, src_size, 0 };
|
| 112 | ZSTD_outBuffer output = { dst, dst_size, 0 };
|
| 113 |
|
| 114 | if (!data->dstream) {
|
| 115 | data->dstream = ZSTD_createDStream();
|
| 116 | if (data->dstream == NULL) {
|
| 117 | pr_err("Couldn't create decompression stream.\n");
|
| 118 | return 0;
|
| 119 | }
|
| 120 |
|
| 121 | ret = ZSTD_initDStream(data->dstream);
|
| 122 | if (ZSTD_isError(ret)) {
|
| 123 | pr_err("Failed to initialize decompression stream: %s\n",
|
| 124 | ZSTD_getErrorName(ret));
|
| 125 | return 0;
|
| 126 | }
|
| 127 | }
|
| 128 | while (input.pos < input.size) {
|
| 129 | size_t prev_in = input.pos;
|
| 130 | size_t prev_out = output.pos;
|
| 131 |
|
| 132 | ret = ZSTD_decompressStream(data->dstream, &output, &input);
|
| 133 | if (ZSTD_isError(ret)) {
|
| 134 | pr_err("failed to decompress (B): %zd -> %zd, dst_size %zd : %s\n",
|
| 135 | src_size, output.pos, dst_size, ZSTD_getErrorName(ret));
|
| 136 | return 0;
|
| 137 | }
|
| 138 | |
| 139 | * Neither stream advanced — decompression is stuck.
|
| 140 | * Return 0 (error) rather than partial output: perf
|
| 141 | * uses ZSTD_flushStream (not ZSTD_endStream), so the
|
| 142 | * stream is continuous across compressed events.
|
| 143 | * Discarding unconsumed input would desynchronize the
|
| 144 | * decompressor, causing the next call to produce
|
| 145 | * garbage that could be misinterpreted as valid events.
|
| 146 |
|
| 147 | if (input.pos == prev_in && output.pos == prev_out)
|
| 148 | return 0;
|
| 149 | }
|
| 150 |
|
| 151 | return output.pos;
|
| 152 | }
|