gh-134938: Add set_pledged_input_size() to ZstdCompressor (GH-135010) · python/cpython@4b44b34 · GitHub
Skip to content

Commit 4b44b34

Browse files
authored
gh-134938: Add set_pledged_input_size() to ZstdCompressor (GH-135010)
1 parent 3d396ab commit 4b44b34

6 files changed

Lines changed: 285 additions & 2 deletions

File tree

Doc/library/compression.zstd.rst

Lines changed: 34 additions & 1 deletion

Lib/test/test_zstd.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,115 @@ def test_compress_empty(self):
395395
c = ZstdCompressor()
396396
self.assertNotEqual(c.compress(b'', c.FLUSH_FRAME), b'')
397397

398+
def test_set_pledged_input_size(self):
399+
DAT = DECOMPRESSED_100_PLUS_32KB
400+
CHUNK_SIZE = len(DAT) // 3
401+
402+
# wrong value
403+
c = ZstdCompressor()
404+
with self.assertRaisesRegex(ValueError,
405+
r'should be a positive int less than \d+'):
406+
c.set_pledged_input_size(-300)
407+
# overflow
408+
with self.assertRaisesRegex(ValueError,
409+
r'should be a positive int less than \d+'):
410+
c.set_pledged_input_size(2**64)
411+
# ZSTD_CONTENTSIZE_ERROR is invalid
412+
with self.assertRaisesRegex(ValueError,
413+
r'should be a positive int less than \d+'):
414+
c.set_pledged_input_size(2**64-2)
415+
# ZSTD_CONTENTSIZE_UNKNOWN should use None
416+
with self.assertRaisesRegex(ValueError,
417+
r'should be a positive int less than \d+'):
418+
c.set_pledged_input_size(2**64-1)
419+
420+
# check valid values are settable
421+
c.set_pledged_input_size(2**63)
422+
c.set_pledged_input_size(2**64-3)
423+
424+
# check that zero means empty frame
425+
c = ZstdCompressor(level=1)
426+
c.set_pledged_input_size(0)
427+
c.compress(b'')
428+
dat = c.flush()
429+
ret = get_frame_info(dat)
430+
self.assertEqual(ret.decompressed_size, 0)
431+
432+
433+
# wrong mode
434+
c = ZstdCompressor(level=1)
435+
c.compress(b'123456')
436+
self.assertEqual(c.last_mode, c.CONTINUE)
437+
with self.assertRaisesRegex(ValueError,
438+
r'last_mode == FLUSH_FRAME'):
439+
c.set_pledged_input_size(300)
440+
441+
# None value
442+
c = ZstdCompressor(level=1)
443+
c.set_pledged_input_size(None)
444+
dat = c.compress(DAT) + c.flush()
445+
446+
ret = get_frame_info(dat)
447+
self.assertEqual(ret.decompressed_size, None)
448+
449+
# correct value
450+
c = ZstdCompressor(level=1)
451+
c.set_pledged_input_size(len(DAT))
452+
453+
chunks = []
454+
posi = 0
455+
while posi < len(DAT):
456+
dat = c.compress(DAT[posi:posi+CHUNK_SIZE])
457+
posi += CHUNK_SIZE
458+
chunks.append(dat)
459+
460+
dat = c.flush()
461+
chunks.append(dat)
462+
chunks = b''.join(chunks)
463+
464+
ret = get_frame_info(chunks)
465+
self.assertEqual(ret.decompressed_size, len(DAT))
466+
self.assertEqual(decompress(chunks), DAT)
467+
468+
c.set_pledged_input_size(len(DAT)) # the second frame
469+
dat = c.compress(DAT) + c.flush()
470+
471+
ret = get_frame_info(dat)
472+
self.assertEqual(ret.decompressed_size, len(DAT))
473+
self.assertEqual(decompress(dat), DAT)
474+
475+
# not enough data
476+
c = ZstdCompressor(level=1)
477+
c.set_pledged_input_size(len(DAT)+1)
478+
479+
for start in range(0, len(DAT), CHUNK_SIZE):
480+
end = min(start+CHUNK_SIZE, len(DAT))
481+
_dat = c.compress(DAT[start:end])
482+
483+
with self.assertRaises(ZstdError):
484+
c.flush()
485+
486+
# too much data
487+
c = ZstdCompressor(level=1)
488+
c.set_pledged_input_size(len(DAT))
489+
490+
for start in range(0, len(DAT), CHUNK_SIZE):
491+
end = min(start+CHUNK_SIZE, len(DAT))
492+
_dat = c.compress(DAT[start:end])
493+
494+
with self.assertRaises(ZstdError):
495+
c.compress(b'extra', ZstdCompressor.FLUSH_FRAME)
496+
497+
# content size not set if content_size_flag == 0
498+
c = ZstdCompressor(options={CompressionParameter.content_size_flag: 0})
499+
c.set_pledged_input_size(10)
500+
dat1 = c.compress(b"hello")
501+
dat2 = c.compress(b"world")
502+
dat3 = c.flush()
503+
frame_data = get_frame_info(dat1 + dat2 + dat3)
504+
self.assertIsNone(frame_data.decompressed_size)
505+
506+
398507
class DecompressorTestCase(unittest.TestCase):
399508

400509
def test_simple_decompress_bad_args(self):

Modules/_zstd/_zstdmodule.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ set_zstd_error(const _zstd_state *state, error_type type, size_t zstd_ret)
7272
case ERR_COMPRESS:
7373
msg = "Unable to compress Zstandard data: %s";
7474
break;
75+
case ERR_SET_PLEDGED_INPUT_SIZE:
76+
msg = "Unable to set pledged uncompressed content size: %s";
77+
break;
7578

7679
case ERR_LOAD_D_DICT:
7780
msg = "Unable to load Zstandard dictionary or prefix for "

Modules/_zstd/_zstdmodule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ typedef struct {
2727
typedef enum {
2828
ERR_DECOMPRESS,
2929
ERR_COMPRESS,
30+
ERR_SET_PLEDGED_INPUT_SIZE,
3031

3132
ERR_LOAD_D_DICT,
3233
ERR_LOAD_C_DICT,

Modules/_zstd/clinic/compressor.c.h

Lines changed: 40 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/_zstd/compressor.c

Lines changed: 98 additions & 0 deletions

0 commit comments

Comments
 (0)