3 #include <stdlib.h> // malloc
8 #include "../zstd_seekable.h"
11 /* ZSTD_seekable_customFile implementation that reads/seeks a buffer while keeping track of total bytes read */
17 } buffWrapperWithTotal_t;
19 static int readBuffWithTotal(void* opaque, void* buffer, size_t n)
21 buffWrapperWithTotal_t* const buff = (buffWrapperWithTotal_t*)opaque;
23 if (buff->pos + n > buff->size) return -1;
24 memcpy(buffer, (const char*)buff->ptr + buff->pos, n);
30 static int seekBuffWithTotal(void* opaque, long long offset, int origin)
32 buffWrapperWithTotal_t* const buff = (buffWrapperWithTotal_t*) opaque;
33 unsigned long long newOffset;
38 newOffset = (unsigned long long)offset;
41 newOffset = (unsigned long long)((long long)buff->pos + offset);
44 newOffset = (unsigned long long)((long long)buff->size + offset);
47 assert(0); /* not possible */
49 if (newOffset > buff->size) {
52 buff->pos = newOffset;
56 /* Basic unit tests for zstd seekable format */
57 int main(int argc, const char** argv)
60 (void)argc; (void)argv;
61 printf("Beginning zstd seekable format tests...\n");
63 printf("Test %u - simple round trip: ", testNb++);
64 { size_t const inSize = 4000;
65 void* const inBuffer = malloc(inSize);
66 assert(inBuffer != NULL);
68 size_t const seekCapacity = 5000;
69 void* const seekBuffer = malloc(seekCapacity);
70 assert(seekBuffer != NULL);
73 size_t const outCapacity = inSize;
74 void* const outBuffer = malloc(outCapacity);
75 assert(outBuffer != NULL);
77 ZSTD_seekable_CStream* const zscs = ZSTD_seekable_createCStream();
80 { size_t const initStatus = ZSTD_seekable_initCStream(zscs, 9, 0 /* checksumFlag */, (unsigned)inSize /* maxFrameSize */);
81 assert(!ZSTD_isError(initStatus));
84 { ZSTD_outBuffer outb = { .dst=seekBuffer, .pos=0, .size=seekCapacity };
85 ZSTD_inBuffer inb = { .src=inBuffer, .pos=0, .size=inSize };
87 size_t const cStatus = ZSTD_seekable_compressStream(zscs, &outb, &inb);
88 assert(!ZSTD_isError(cStatus));
89 assert(inb.pos == inb.size);
91 size_t const endStatus = ZSTD_seekable_endStream(zscs, &outb);
92 assert(!ZSTD_isError(endStatus));
96 ZSTD_seekable* const stream = ZSTD_seekable_create();
97 assert(stream != NULL);
98 { size_t const initStatus = ZSTD_seekable_initBuff(stream, seekBuffer, seekSize);
99 assert(!ZSTD_isError(initStatus)); }
101 { size_t const decStatus = ZSTD_seekable_decompress(stream, outBuffer, outCapacity, 0);
102 assert(decStatus == inSize); }
104 /* unit test ZSTD_seekTable functions */
105 ZSTD_seekTable* const zst = ZSTD_seekTable_create_fromSeekable(stream);
108 unsigned const nbFrames = ZSTD_seekTable_getNumFrames(zst);
109 assert(nbFrames > 0);
111 unsigned long long const frame0Offset = ZSTD_seekTable_getFrameCompressedOffset(zst, 0);
112 assert(frame0Offset == 0);
114 unsigned long long const content0Offset = ZSTD_seekTable_getFrameDecompressedOffset(zst, 0);
115 assert(content0Offset == 0);
117 size_t const cSize = ZSTD_seekTable_getFrameCompressedSize(zst, 0);
118 assert(!ZSTD_isError(cSize));
119 assert(cSize <= seekCapacity);
121 size_t const origSize = ZSTD_seekTable_getFrameDecompressedSize(zst, 0);
122 assert(origSize == inSize);
124 unsigned const fo1idx = ZSTD_seekTable_offsetToFrameIndex(zst, 1);
130 ZSTD_seekable_freeCStream(zscs);
131 ZSTD_seekTable_free(zst);
132 ZSTD_seekable_free(stream);
134 printf("Success!\n");
137 printf("Test %u - check that seekable decompress does not hang: ", testNb++);
138 { /* Github issue #2335 */
139 const size_t compressed_size = 17;
140 const uint8_t compressed_data[17] = {
159 const size_t uncompressed_size = 32;
160 uint8_t uncompressed_data[32];
162 ZSTD_seekable* const stream = ZSTD_seekable_create();
163 assert(stream != NULL);
164 { size_t const status = ZSTD_seekable_initBuff(stream, compressed_data, compressed_size);
165 if (ZSTD_isError(status)) {
166 ZSTD_seekable_free(stream);
170 /* Should return an error, but not hang */
171 { const size_t offset = 2;
172 size_t const status = ZSTD_seekable_decompress(stream, uncompressed_data, uncompressed_size, offset);
173 if (!ZSTD_isError(status)) {
174 ZSTD_seekable_free(stream);
178 ZSTD_seekable_free(stream);
180 printf("Success!\n");
182 printf("Test %u - check #2 that seekable decompress does not hang: ", testNb++);
183 { /* Github issue #FIXME */
184 const size_t compressed_size = 27;
185 const uint8_t compressed_data[27] = {
214 const size_t uncompressed_size = 400;
215 uint8_t uncompressed_data[400];
217 ZSTD_seekable* stream = ZSTD_seekable_create();
218 size_t status = ZSTD_seekable_initBuff(stream, compressed_data, compressed_size);
219 if (ZSTD_isError(status)) {
220 ZSTD_seekable_free(stream);
224 const size_t offset = 2;
225 /* Should return an error, but not hang */
226 status = ZSTD_seekable_decompress(stream, uncompressed_data, uncompressed_size, offset);
227 if (!ZSTD_isError(status)) {
228 ZSTD_seekable_free(stream);
232 ZSTD_seekable_free(stream);
234 printf("Success!\n");
237 printf("Test %u - check ZSTD magic in compressing empty string: ", testNb++);
238 { // compressing empty string should return a zstd header
239 size_t const capacity = 255;
240 char* inBuffer = malloc(capacity);
241 assert(inBuffer != NULL);
243 void* const outBuffer = malloc(capacity);
244 assert(outBuffer != NULL);
246 ZSTD_seekable_CStream *s = ZSTD_seekable_createCStream();
247 ZSTD_seekable_initCStream(s, 1, 1, 255);
249 ZSTD_inBuffer input = { .src=inBuffer, .pos=0, .size=0 };
250 ZSTD_outBuffer output = { .dst=outBuffer, .pos=0, .size=capacity };
252 ZSTD_seekable_compressStream(s, &output, &input);
253 ZSTD_seekable_endStream(s, &output);
255 if((((char*)output.dst)[0] != '\x28') | (((char*)output.dst)[1] != '\xb5') | (((char*)output.dst)[2] != '\x2f') | (((char*)output.dst)[3] != '\xfd')) {
256 printf("%#02x %#02x %#02x %#02x\n", ((char*)output.dst)[0], ((char*)output.dst)[1] , ((char*)output.dst)[2] , ((char*)output.dst)[3] );
260 ZSTD_seekable_freeCStream(s);
266 ZSTD_seekable_freeCStream(s);
268 printf("Success!\n");
271 printf("Test %u - multiple decompress calls: ", testNb++);
272 { char const inBuffer[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt";
273 size_t const inSize = sizeof(inBuffer);
275 size_t const seekCapacity = 5000;
276 void* const seekBuffer = malloc(seekCapacity);
277 assert(seekBuffer != NULL);
280 size_t const outCapacity = inSize;
281 char* const outBuffer = malloc(outCapacity);
282 assert(outBuffer != NULL);
284 ZSTD_seekable_CStream* const zscs = ZSTD_seekable_createCStream();
285 assert(zscs != NULL);
287 /* compress test data with a small frame size to ensure multiple frames in the output */
288 unsigned const maxFrameSize = 40;
289 { size_t const initStatus = ZSTD_seekable_initCStream(zscs, 9, 0 /* checksumFlag */, maxFrameSize);
290 assert(!ZSTD_isError(initStatus));
293 { ZSTD_outBuffer outb = { .dst=seekBuffer, .pos=0, .size=seekCapacity };
294 ZSTD_inBuffer inb = { .src=inBuffer, .pos=0, .size=inSize };
296 while (inb.pos < inb.size) {
297 size_t const cStatus = ZSTD_seekable_compressStream(zscs, &outb, &inb);
298 assert(!ZSTD_isError(cStatus));
301 size_t const endStatus = ZSTD_seekable_endStream(zscs, &outb);
302 assert(!ZSTD_isError(endStatus));
306 ZSTD_seekable* const stream = ZSTD_seekable_create();
307 assert(stream != NULL);
308 buffWrapperWithTotal_t buffWrapper = {seekBuffer, seekSize, 0, 0};
309 { ZSTD_seekable_customFile srcFile = {&buffWrapper, &readBuffWithTotal, &seekBuffWithTotal};
310 size_t const initStatus = ZSTD_seekable_initAdvanced(stream, srcFile);
311 assert(!ZSTD_isError(initStatus)); }
313 /* Perform a series of small reads and seeks (repeatedly read 1 byte and skip 1 byte)
314 and check that we didn't reread input data unnecessarily */
316 for (pos = 0; pos < inSize; pos += 2) {
317 size_t const decStatus = ZSTD_seekable_decompress(stream, outBuffer, 1, pos);
318 if (decStatus != 1 || outBuffer[0] != inBuffer[pos]) {
322 if (buffWrapper.totalRead > seekSize) {
323 /* We read more than the compressed size, meaning there were some rereads.
324 This is unneeded because we only seeked forward. */
325 printf("Too much data read: %zu read, with compressed size %zu\n", buffWrapper.totalRead, seekSize);
329 /* Perform some reads and seeks to ensure correctness */
333 } const tests[] = { /* Assume the frame size is 40 */
334 {20, 40}, /* read partial data from two frames */
335 {60, 10}, /* continue reading from the same offset */
336 {50, 20}, /* seek backward within the same frame */
337 {10, 10}, /* seek backward to a different frame */
338 {25, 10}, /* seek forward within the same frame */
339 {60, 10}, /* seek forward to a different frame */
342 for (idx = 0; idx < sizeof(tests) / sizeof(tests[0]); idx++) {
343 size_t const decStatus = ZSTD_seekable_decompress(stream, outBuffer, tests[idx].size, tests[idx].offset);
344 if (decStatus != tests[idx].size || memcmp(outBuffer, inBuffer + tests[idx].offset, tests[idx].size) != 0) {
351 ZSTD_seekable_freeCStream(zscs);
352 ZSTD_seekable_free(stream);
354 printf("Success!\n");
356 /* TODO: Add more tests */
357 printf("Finished tests\n");
361 printf("test failed! Exiting..\n");