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.
Files changed (29) hide show
  1. {tamp-2.2.2 → tamp-2.2.4}/PKG-INFO +1 -1
  2. {tamp-2.2.2 → tamp-2.2.4}/pyproject.toml +3 -3
  3. {tamp-2.2.2 → tamp-2.2.4}/tamp/__init__.py +1 -1
  4. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/compressor.c +19 -1
  5. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/compressor.h +10 -0
  6. {tamp-2.2.2 → tamp-2.2.4}/tamp/compressor.py +28 -17
  7. {tamp-2.2.2 → tamp-2.2.4}/LICENSE +0 -0
  8. {tamp-2.2.2 → tamp-2.2.4}/README.md +0 -0
  9. {tamp-2.2.2 → tamp-2.2.4}/build.py +0 -0
  10. {tamp-2.2.2 → tamp-2.2.4}/tamp/__init__.pyi +0 -0
  11. {tamp-2.2.2 → tamp-2.2.4}/tamp/__main__.py +0 -0
  12. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_build_dictionary.pyx +0 -0
  13. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_common.pxd +0 -0
  14. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_common.pyx +0 -0
  15. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_compressor.pyx +0 -0
  16. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_decompressor.pyx +0 -0
  17. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/common.c +0 -0
  18. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/common.h +0 -0
  19. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/compressor_find_match_desktop.c +0 -0
  20. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/decompressor.c +0 -0
  21. {tamp-2.2.2 → tamp-2.2.4}/tamp/_c_src/tamp/decompressor.h +0 -0
  22. {tamp-2.2.2 → tamp-2.2.4}/tamp/cli/__init__.py +0 -0
  23. {tamp-2.2.2 → tamp-2.2.4}/tamp/cli/build_dictionary.py +0 -0
  24. {tamp-2.2.2 → tamp-2.2.4}/tamp/cli/main.py +0 -0
  25. {tamp-2.2.2 → tamp-2.2.4}/tamp/compressor_viper.py +0 -0
  26. {tamp-2.2.2 → tamp-2.2.4}/tamp/ctamp.pxd +0 -0
  27. {tamp-2.2.2 → tamp-2.2.4}/tamp/decompressor.py +0 -0
  28. {tamp-2.2.2 → tamp-2.2.4}/tamp/decompressor_viper.py +0 -0
  29. {tamp-2.2.2 → tamp-2.2.4}/tamp/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tamp
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Summary:
5
5
  Home-page: https://github.com/BrianPugh/tamp
6
6
  License: Apache-2.0
@@ -9,7 +9,7 @@ style = "semver"
9
9
 
10
10
  [tool.poetry]
11
11
  name = "tamp"
12
- version = "2.2.2" # Do not change, let poetry-dynamic-versioning handle it.
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.49"
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 = ">=0.29.34"
64
+ cython = ">=3.2.5"
65
65
  pytest-subtests = ">=0.10.0"
66
66
  numpy = ">=1.24"
67
67
  pyelftools = "^0.32"
@@ -1,4 +1,4 @@
1
- __version__ = "2.2.2"
1
+ __version__ = "2.2.4"
2
2
 
3
3
 
4
4
  class ExcessBitsError(Exception):
@@ -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
- if (write_token && (compressor->bit_buffer_pos || compressor->conf.dictionary_reset)) {
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
- bytes_written_flush = self._bit_writer.flush(write_token=write_token, force_token=self.dictionary_reset)
571
- bytes_written += bytes_written_flush
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
- # Flush all pending data with write_token=True.
597
- bytes_written += self.flush(write_token=True)
598
-
599
- # We need exactly 2 consecutive FLUSHes in the stream.
600
- # If flush already wrote one, we need 1 more; otherwise 2.
601
- flushes_needed = 1 if self._bit_writer._flush_token_written else 2
602
- for _ in range(flushes_needed):
603
- bytes_written += self._bit_writer.write(_FLUSH_CODE, 9)
604
- # Pad to byte boundary
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