projects/linux·blob master · refs

152 lines · 4.0 KiB

1// SPDX-License-Identifier: GPL-2.0
2
3#include <string.h>
4#include <linux/perf_event.h>
5
6#include "util/compress.h"
7#include "util/debug.h"
8
9int 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
17int 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
32ssize_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 /* Output buffer full — cannot fit even the record header */
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
98reset:
99 /* Reset so the context is usable if the caller retries */
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
107size_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}