tamp 2.2.2__tar.gz → 2.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tamp-2.2.2 → tamp-2.2.4}/PKG-INFO +1 -1
- {tamp-2.2.2 → tamp-2.2.4}/pyproject.toml +3 -3
- {tamp-2.2.2 → tamp-2.2.4}/tamp/__init__.py +1 -1
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/compressor.c +19 -1
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/compressor.h +10 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/compressor.py +28 -17
- {tamp-2.2.2 → tamp-2.2.4}/LICENSE +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/README.md +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/build.py +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/__init__.pyi +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/__main__.py +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_build_dictionary.pyx +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_common.pxd +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_common.pyx +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_compressor.pyx +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_decompressor.pyx +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/common.c +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/common.h +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/compressor_find_match_desktop.c +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/decompressor.c +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/decompressor.h +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/cli/__init__.py +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/cli/build_dictionary.py +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/cli/main.py +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/compressor_viper.py +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/ctamp.pxd +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/decompressor.py +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/decompressor_viper.py +0 -0
- {tamp-2.2.2 → tamp-2.2.4}/tamp/py.typed +0 -0
|
@@ -9,7 +9,7 @@ style = "semver"
|
|
|
9
9
|
|
|
10
10
|
[tool.poetry]
|
|
11
11
|
name = "tamp"
|
|
12
|
-
version = "2.2.
|
|
12
|
+
version = "2.2.4" # Do not change, let poetry-dynamic-versioning handle it.
|
|
13
13
|
homepage = "https://github.com/BrianPugh/tamp"
|
|
14
14
|
repository = "https://github.com/BrianPugh/tamp"
|
|
15
15
|
license = "Apache-2.0"
|
|
@@ -44,7 +44,7 @@ cli = ["cyclopts", "rich"]
|
|
|
44
44
|
[tool.poetry.group.docs.dependencies]
|
|
45
45
|
sphinx = ">=4.5.0"
|
|
46
46
|
sphinx_rtd_theme = ">=3.0.0"
|
|
47
|
-
gitpython = ">=3.1.
|
|
47
|
+
gitpython = ">=3.1.50"
|
|
48
48
|
sphinx-copybutton = ">=0.5.2"
|
|
49
49
|
myst-parser = {extras = ["linkify"], version = "^3.0.1"}
|
|
50
50
|
|
|
@@ -61,7 +61,7 @@ matplotlib = ">=3.7.4"
|
|
|
61
61
|
ipdb = ">=0.13.13"
|
|
62
62
|
packaging = ">=26.1"
|
|
63
63
|
mpremote = ">=1.20.0"
|
|
64
|
-
cython = ">=
|
|
64
|
+
cython = ">=3.2.5"
|
|
65
65
|
pytest-subtests = ">=0.10.0"
|
|
66
66
|
numpy = ">=1.24"
|
|
67
67
|
pyelftools = "^0.32"
|
|
@@ -208,6 +208,9 @@ TAMP_OPTIMIZE_SIZE tamp_res tamp_compressor_init(TampCompressor* compressor, con
|
|
|
208
208
|
// previous stream's trailing FLUSH, this triggers a dictionary reset.
|
|
209
209
|
write_to_bit_buffer(compressor, FLUSH_CODE, 9);
|
|
210
210
|
compressor->bit_buffer_pos = 16;
|
|
211
|
+
// This FLUSH is the most recently emitted token; mark it so an immediate
|
|
212
|
+
// redundant flush() doesn't append a second FLUSH and over-signal a reset.
|
|
213
|
+
compressor->last_was_flush = 1;
|
|
211
214
|
} else {
|
|
212
215
|
// Header byte 1: [window:3][literal:2][use_custom_dictionary:1][extended:1][more_headers:1]
|
|
213
216
|
uint8_t header = ((conf->window - 8) << 5) | ((conf->literal - 5) << 3) | (conf->use_custom_dictionary << 2) |
|
|
@@ -491,6 +494,10 @@ TAMP_NOINLINE tamp_res tamp_compressor_poll(TampCompressor* compressor, unsigned
|
|
|
491
494
|
|
|
492
495
|
if (TAMP_UNLIKELY(compressor->input_size == 0)) return TAMP_OK;
|
|
493
496
|
|
|
497
|
+
// Real data is being processed: any pending RLE/extended state drained by a
|
|
498
|
+
// later flush() was also created here, so the next FLUSH won't be "consecutive".
|
|
499
|
+
compressor->last_was_flush = 0;
|
|
500
|
+
|
|
494
501
|
// Make sure there's enough room in the bit buffer.
|
|
495
502
|
res = partial_flush(compressor, &output, &output_size, output_written_size);
|
|
496
503
|
if (TAMP_UNLIKELY(res != TAMP_OK)) return res;
|
|
@@ -711,7 +718,14 @@ flush_check:
|
|
|
711
718
|
flush_done:
|
|
712
719
|
// At this point, up to 7 bits may remain in the compressor->bit_buffer
|
|
713
720
|
// The output buffer may have 0 bytes remaining.
|
|
714
|
-
|
|
721
|
+
// Suppress a redundant second consecutive FLUSH. Two FLUSH tokens with no data
|
|
722
|
+
// between them are the double-FLUSH dictionary-reset signal: a decompressor on a
|
|
723
|
+
// dictionary_reset stream would re-initialize its window, but THIS compressor's
|
|
724
|
+
// window is not reset here, desyncing the two and corrupting all later output.
|
|
725
|
+
// The sanctioned reset path, tamp_compressor_reset_dictionary(), clears
|
|
726
|
+
// last_was_flush before each flush so it can still emit the real double-FLUSH.
|
|
727
|
+
if (write_token && !compressor->last_was_flush &&
|
|
728
|
+
(compressor->bit_buffer_pos || compressor->conf.dictionary_reset)) {
|
|
715
729
|
// We don't want to write the FLUSH token to the bit_buffer unless
|
|
716
730
|
// we are confident that it'll wind up in the output buffer
|
|
717
731
|
// in THIS function call.
|
|
@@ -719,6 +733,7 @@ flush_done:
|
|
|
719
733
|
// end up accidentally writing multiple FLUSH tokens.
|
|
720
734
|
if (TAMP_UNLIKELY(output_size < 2)) return TAMP_OUTPUT_FULL;
|
|
721
735
|
write_to_bit_buffer(compressor, FLUSH_CODE, 9);
|
|
736
|
+
compressor->last_was_flush = 1;
|
|
722
737
|
}
|
|
723
738
|
|
|
724
739
|
// At this point, up to 16 bits may remain in the compressor->bit_buffer
|
|
@@ -787,6 +802,9 @@ TAMP_OPTIMIZE_SIZE tamp_res tamp_compressor_reset_dictionary(TampCompressor* com
|
|
|
787
802
|
// FLUSH token even when bit_buffer_pos is 0.
|
|
788
803
|
for (uint8_t i = 0; i < 2; i++) {
|
|
789
804
|
size_t flush_written_size;
|
|
805
|
+
// Bypass the double-FLUSH suppression in flush(): here we *intend* to emit
|
|
806
|
+
// two consecutive FLUSH tokens, paired with the dictionary re-init below.
|
|
807
|
+
compressor->last_was_flush = 0;
|
|
790
808
|
res = tamp_compressor_flush(compressor, output, output_size, &flush_written_size, true);
|
|
791
809
|
*output_written_size += flush_written_size;
|
|
792
810
|
if (TAMP_UNLIKELY(res != TAMP_OK)) return res;
|
|
@@ -23,6 +23,8 @@ typedef struct TampCompressor {
|
|
|
23
23
|
|
|
24
24
|
/* WARM: read frequently, often cached in locals */
|
|
25
25
|
uint8_t min_pattern_size; // Minimum pattern size (2 bits used; 2 or 3)
|
|
26
|
+
uint8_t last_was_flush; // 1 if the most recently emitted token was a FLUSH (no data since).
|
|
27
|
+
// Suppresses an accidental double-FLUSH (a dictionary-reset signal).
|
|
26
28
|
TampConf conf;
|
|
27
29
|
#else // Use bitfields for reduced memory-usage
|
|
28
30
|
/* HOT: accessed every iteration of the compression loop */
|
|
@@ -53,6 +55,14 @@ typedef struct TampCompressor {
|
|
|
53
55
|
uint8_t rle_count; // Current RLE run length (max 241)
|
|
54
56
|
uint8_t extended_match_count; // Current extended match size (max ~134)
|
|
55
57
|
#endif
|
|
58
|
+
#if !TAMP_ESP32
|
|
59
|
+
// 1 if the most recently emitted token was a FLUSH (no data since); suppresses an
|
|
60
|
+
// accidental double-FLUSH (a dictionary-reset signal). Placed at the struct tail (in
|
|
61
|
+
// existing padding) so it does NOT shift the trailing fields above to higher offsets,
|
|
62
|
+
// which would push them out of Cortex-M0+ small load/store immediate range and bloat
|
|
63
|
+
// unrelated functions. The ESP32 variant keeps it inline (free in WARM-section padding).
|
|
64
|
+
uint8_t last_was_flush;
|
|
65
|
+
#endif
|
|
56
66
|
} TampCompressor;
|
|
57
67
|
|
|
58
68
|
/**
|
|
@@ -237,6 +237,9 @@ class Compressor:
|
|
|
237
237
|
# trailing FLUSH, this triggers a dictionary reset in the decompressor.
|
|
238
238
|
self._bit_writer.write(_FLUSH_CODE, 9, flush=False)
|
|
239
239
|
self._bit_writer.bit_pos = 16 # Pad to 2 bytes (bits are already zero)
|
|
240
|
+
# This FLUSH is the most recently emitted token; mark it so an immediate
|
|
241
|
+
# redundant flush() doesn't append a second FLUSH and over-signal a reset.
|
|
242
|
+
self._last_was_flush = True
|
|
240
243
|
else:
|
|
241
244
|
# Write header
|
|
242
245
|
self._bit_writer.write(window - 8, 3, flush=False)
|
|
@@ -265,6 +268,9 @@ class Compressor:
|
|
|
265
268
|
self._extended_match_position = 0
|
|
266
269
|
self._cached_match_index = -1
|
|
267
270
|
self._cached_match_size = 0
|
|
271
|
+
# True if the most recently emitted token was a FLUSH with no data since.
|
|
272
|
+
# Suppresses an accidental double-FLUSH (a dictionary-reset signal).
|
|
273
|
+
self._last_was_flush = False
|
|
268
274
|
|
|
269
275
|
def _validate_no_match_overlap(self, write_pos, match_index, match_size):
|
|
270
276
|
"""Check if writing a single byte will overlap with a future match section."""
|
|
@@ -276,6 +282,10 @@ class Compressor:
|
|
|
276
282
|
if not self._input_buffer:
|
|
277
283
|
return bytes_written
|
|
278
284
|
|
|
285
|
+
# Real data is being processed: any pending RLE/extended state drained by a
|
|
286
|
+
# later flush() was also created here, so the next FLUSH won't be "consecutive".
|
|
287
|
+
self._last_was_flush = False
|
|
288
|
+
|
|
279
289
|
if self._extended_match_count:
|
|
280
290
|
while self._input_buffer:
|
|
281
291
|
if (self._extended_match_position + self._extended_match_count) >= self._window_buffer.size:
|
|
@@ -567,8 +577,15 @@ class Compressor:
|
|
|
567
577
|
self._cached_match_index = -1
|
|
568
578
|
self._cached_match_size = 0
|
|
569
579
|
|
|
570
|
-
|
|
571
|
-
|
|
580
|
+
# Suppress a redundant second consecutive FLUSH. Two FLUSH tokens with no
|
|
581
|
+
# data between them are the double-FLUSH dictionary-reset signal: a
|
|
582
|
+
# decompressor on a dictionary_reset stream would re-initialize its window,
|
|
583
|
+
# but this compressor's window is not reset here, desyncing the two. The
|
|
584
|
+
# sanctioned reset path, reset_dictionary(), emits its FLUSH tokens directly.
|
|
585
|
+
emit_token = write_token and not self._last_was_flush
|
|
586
|
+
bytes_written += self._bit_writer.flush(write_token=emit_token, force_token=self.dictionary_reset)
|
|
587
|
+
if self._bit_writer._flush_token_written:
|
|
588
|
+
self._last_was_flush = True
|
|
572
589
|
return bytes_written
|
|
573
590
|
|
|
574
591
|
def reset_dictionary(self) -> int:
|
|
@@ -593,21 +610,15 @@ class Compressor:
|
|
|
593
610
|
|
|
594
611
|
bytes_written = 0
|
|
595
612
|
|
|
596
|
-
#
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
#
|
|
600
|
-
#
|
|
601
|
-
|
|
602
|
-
for _ in range(
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if self._bit_writer.bit_pos > 0:
|
|
606
|
-
byte = (self._bit_writer.buffer >> 24) & 0xFF
|
|
607
|
-
self._bit_writer.f.write(byte.to_bytes(1, "big"))
|
|
608
|
-
self._bit_writer.bit_pos = 0
|
|
609
|
-
self._bit_writer.buffer = 0
|
|
610
|
-
bytes_written += 1
|
|
613
|
+
# Write 2 FLUSH tokens for the double-FLUSH reset signal. The first drains
|
|
614
|
+
# any pending data. dictionary_reset being set guarantees flush() writes a
|
|
615
|
+
# FLUSH token even when the bit buffer is empty. Clearing _last_was_flush
|
|
616
|
+
# before each flush bypasses the double-FLUSH suppression in flush(): here
|
|
617
|
+
# we *intend* to emit two consecutive FLUSH tokens, paired with the
|
|
618
|
+
# dictionary re-init below. Mirrors tamp_compressor_reset_dictionary in C.
|
|
619
|
+
for _ in range(2):
|
|
620
|
+
self._last_was_flush = False
|
|
621
|
+
bytes_written += self.flush(write_token=True)
|
|
611
622
|
|
|
612
623
|
# Re-initialize dictionary with default and reset all mutable state,
|
|
613
624
|
# matching what the decompressor does on a double-FLUSH reset
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|