Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include "alloc-util.h"
4 : #include "compress.h"
5 : #include "env-util.h"
6 : #include "macro.h"
7 : #include "memory-util.h"
8 : #include "nulstr-util.h"
9 : #include "parse-util.h"
10 : #include "process-util.h"
11 : #include "random-util.h"
12 : #include "string-util.h"
13 : #include "tests.h"
14 :
15 : typedef int (compress_t)(const void *src, uint64_t src_size, void *dst,
16 : size_t dst_alloc_size, size_t *dst_size);
17 : typedef int (decompress_t)(const void *src, uint64_t src_size,
18 : void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max);
19 :
20 : #if HAVE_XZ || HAVE_LZ4
21 :
22 : static usec_t arg_duration;
23 : static size_t arg_start;
24 :
25 : #define MAX_SIZE (1024*1024LU)
26 : #define PRIME 1048571 /* A prime close enough to one megabyte that mod 4 == 3 */
27 :
28 538 : static size_t _permute(size_t x) {
29 : size_t residue;
30 :
31 538 : if (x >= PRIME)
32 0 : return x;
33 :
34 538 : residue = x*x % PRIME;
35 538 : if (x <= PRIME / 2)
36 269 : return residue;
37 : else
38 269 : return PRIME - residue;
39 : }
40 :
41 269 : static size_t permute(size_t x) {
42 269 : return _permute((_permute(x) + arg_start) % MAX_SIZE ^ 0xFF345);
43 : }
44 :
45 6 : static char* make_buf(size_t count, const char *type) {
46 : char *buf;
47 : size_t i;
48 :
49 6 : buf = malloc(count);
50 6 : assert_se(buf);
51 :
52 6 : if (streq(type, "zeros"))
53 2 : memzero(buf, count);
54 4 : else if (streq(type, "simple"))
55 2097154 : for (i = 0; i < count; i++)
56 2097152 : buf[i] = 'a' + i % ('z' - 'a' + 1);
57 2 : else if (streq(type, "random")) {
58 2 : size_t step = count / 10;
59 :
60 2 : random_bytes(buf, step);
61 2 : memzero(buf + 1*step, step);
62 2 : random_bytes(buf + 2*step, step);
63 2 : memzero(buf + 3*step, step);
64 2 : random_bytes(buf + 4*step, step);
65 2 : memzero(buf + 5*step, step);
66 2 : random_bytes(buf + 6*step, step);
67 2 : memzero(buf + 7*step, step);
68 2 : random_bytes(buf + 8*step, step);
69 2 : memzero(buf + 9*step, step);
70 : } else
71 0 : assert_not_reached("here");
72 :
73 6 : return buf;
74 : }
75 :
76 6 : static void test_compress_decompress(const char* label, const char* type,
77 : compress_t compress, decompress_t decompress) {
78 6 : usec_t n, n2 = 0;
79 : float dt;
80 :
81 6 : _cleanup_free_ char *text, *buf;
82 12 : _cleanup_free_ void *buf2 = NULL;
83 6 : size_t buf2_allocated = 0;
84 6 : size_t skipped = 0, compressed = 0, total = 0;
85 :
86 6 : text = make_buf(MAX_SIZE, type);
87 6 : buf = calloc(MAX_SIZE + 1, 1);
88 6 : assert_se(text && buf);
89 :
90 6 : n = now(CLOCK_MONOTONIC);
91 :
92 269 : for (size_t i = 0; i <= MAX_SIZE; i++) {
93 269 : size_t j = 0, k = 0, size;
94 : int r;
95 :
96 269 : size = permute(i);
97 269 : if (size == 0)
98 9 : continue;
99 :
100 269 : log_debug("%s %zu %zu", type, i, size);
101 :
102 269 : memzero(buf, MIN(size + 1000, MAX_SIZE));
103 :
104 269 : r = compress(text, size, buf, size, &j);
105 : /* assume compression must be successful except for small or random inputs */
106 269 : assert_se(r == 0 || (size < 2048 && r == -ENOBUFS) || streq(type, "random"));
107 :
108 : /* check for overwrites */
109 269 : assert_se(buf[size] == 0);
110 269 : if (r != 0) {
111 9 : skipped += size;
112 9 : continue;
113 : }
114 :
115 260 : assert_se(j > 0);
116 260 : if (j >= size)
117 0 : log_error("%s \"compressed\" %zu -> %zu", label, size, j);
118 :
119 260 : r = decompress(buf, j, &buf2, &buf2_allocated, &k, 0);
120 260 : assert_se(r == 0);
121 260 : assert_se(buf2_allocated >= k);
122 260 : assert_se(k == size);
123 :
124 260 : assert_se(memcmp(text, buf2, size) == 0);
125 :
126 260 : total += size;
127 260 : compressed += j;
128 :
129 260 : n2 = now(CLOCK_MONOTONIC);
130 260 : if (n2 - n > arg_duration)
131 6 : break;
132 : }
133 :
134 6 : dt = (n2-n) / 1e6;
135 :
136 6 : log_info("%s/%s: compressed & decompressed %zu bytes in %.2fs (%.2fMiB/s), "
137 : "mean compression %.2f%%, skipped %zu bytes",
138 : label, type, total, dt,
139 : total / 1024. / 1024 / dt,
140 : 100 - compressed * 100. / total,
141 : skipped);
142 6 : }
143 : #endif
144 :
145 1 : int main(int argc, char *argv[]) {
146 : #if HAVE_XZ || HAVE_LZ4
147 1 : test_setup_logging(LOG_INFO);
148 :
149 1 : if (argc >= 2) {
150 : unsigned x;
151 :
152 0 : assert_se(safe_atou(argv[1], &x) >= 0);
153 0 : arg_duration = x * USEC_PER_SEC;
154 : } else
155 2 : arg_duration = slow_tests_enabled() ?
156 1 : 2 * USEC_PER_SEC : USEC_PER_SEC / 50;
157 :
158 1 : if (argc == 3)
159 0 : (void) safe_atozu(argv[2], &arg_start);
160 : else
161 1 : arg_start = getpid_cached();
162 :
163 : const char *i;
164 4 : NULSTR_FOREACH(i, "zeros\0simple\0random\0") {
165 : #if HAVE_XZ
166 3 : test_compress_decompress("XZ", i, compress_blob_xz, decompress_blob_xz);
167 : #endif
168 : #if HAVE_LZ4
169 3 : test_compress_decompress("LZ4", i, compress_blob_lz4, decompress_blob_lz4);
170 : #endif
171 : }
172 1 : return 0;
173 : #else
174 : return log_tests_skipped("No compression feature is enabled");
175 : #endif
176 : }
|