lz4.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. # -*- coding: utf-8 -*-
  2. """
  3. LZ4-Kompressor.
  4. Implementiert schnelle Komprimierung mit dem LZ4-Algorithmus.
  5. Erfordert das optionale lz4-Paket.
  6. """
  7. from __future__ import annotations
  8. import time
  9. from typing import Any
  10. try:
  11. import lz4.frame
  12. import lz4.block
  13. LZ4_AVAILABLE = True
  14. except ImportError:
  15. LZ4_AVAILABLE = False
  16. from trixy_core.network.compression.compressor import (
  17. Compressor,
  18. CompressionLevel,
  19. CompressionError,
  20. DecompressionError,
  21. )
  22. if not LZ4_AVAILABLE:
  23. raise ImportError(
  24. "LZ4-Kompressor erfordert das lz4-Paket. "
  25. "Installieren mit: pip install lz4"
  26. )
  27. class LZ4Compressor(Compressor):
  28. """
  29. Kompressor basierend auf LZ4.
  30. LZ4 bietet extrem schnelle Komprimierung bei moderater
  31. Komprimierungsrate. Ideal für Echtzeit-Anwendungen.
  32. Beispiel:
  33. compressor = LZ4Compressor(level=CompressionLevel.FAST)
  34. compressed = compressor.compress(data)
  35. original = compressor.decompress(compressed)
  36. """
  37. # Mapping von CompressionLevel zu LZ4-Levels
  38. _LEVEL_MAP = {
  39. CompressionLevel.NONE: 0,
  40. CompressionLevel.FASTEST: 0,
  41. CompressionLevel.FAST: 3,
  42. CompressionLevel.DEFAULT: 6,
  43. CompressionLevel.BEST: 12,
  44. }
  45. def __init__(
  46. self,
  47. level: CompressionLevel = CompressionLevel.DEFAULT,
  48. use_frame: bool = True,
  49. ) -> None:
  50. """
  51. Initialisiert den LZ4-Kompressor.
  52. Args:
  53. level: Komprimierungsstufe
  54. use_frame: Frame-Format verwenden (empfohlen)
  55. False für Block-Format (schneller, aber ohne Streaming)
  56. """
  57. super().__init__(level)
  58. self._use_frame = use_frame
  59. self._lz4_level = self._LEVEL_MAP.get(level, 6)
  60. @property
  61. def name(self) -> str:
  62. return "lz4"
  63. @property
  64. def content_type(self) -> str:
  65. return "application/x-lz4"
  66. @property
  67. def min_size(self) -> int:
  68. """LZ4 hat minimalen Overhead."""
  69. return 16
  70. def compress(self, data: bytes) -> bytes:
  71. """
  72. Komprimiert Daten mit LZ4.
  73. Args:
  74. data: Unkomprimierte Daten
  75. Returns:
  76. Komprimierte Daten
  77. """
  78. if not data:
  79. return b""
  80. start = time.monotonic()
  81. try:
  82. if self._use_frame:
  83. compressed = lz4.frame.compress(
  84. data,
  85. compression_level=self._lz4_level,
  86. )
  87. else:
  88. compressed = lz4.block.compress(
  89. data,
  90. compression=self._lz4_level,
  91. store_size=True,
  92. )
  93. except Exception as e:
  94. self.stats.errors += 1
  95. raise CompressionError(f"LZ4-Komprimierung fehlgeschlagen: {e}") from e
  96. elapsed_ms = (time.monotonic() - start) * 1000
  97. self.stats.update_compression(len(data), len(compressed), elapsed_ms)
  98. return compressed
  99. def decompress(self, data: bytes) -> bytes:
  100. """
  101. Dekomprimiert LZ4-Daten.
  102. Args:
  103. data: Komprimierte Daten
  104. Returns:
  105. Ursprüngliche Daten
  106. """
  107. if not data:
  108. return b""
  109. start = time.monotonic()
  110. try:
  111. if self._use_frame:
  112. decompressed = lz4.frame.decompress(data)
  113. else:
  114. decompressed = lz4.block.decompress(data)
  115. except Exception as e:
  116. self.stats.errors += 1
  117. raise DecompressionError(
  118. f"LZ4-Dekomprimierung fehlgeschlagen: {e}"
  119. ) from e
  120. elapsed_ms = (time.monotonic() - start) * 1000
  121. self.stats.update_decompression(len(data), len(decompressed), elapsed_ms)
  122. return decompressed
  123. class LZ4HCCompressor(Compressor):
  124. """
  125. LZ4 High Compression Kompressor.
  126. Verwendet LZ4 HC-Modus für bessere Komprimierungsrate
  127. bei langsamerer Geschwindigkeit.
  128. """
  129. def __init__(
  130. self,
  131. level: CompressionLevel = CompressionLevel.BEST,
  132. ) -> None:
  133. super().__init__(level)
  134. # HC-Mode verwendet Levels 1-12
  135. self._hc_level = min(12, max(1, int(level) + 3))
  136. @property
  137. def name(self) -> str:
  138. return "lz4hc"
  139. @property
  140. def content_type(self) -> str:
  141. return "application/x-lz4hc"
  142. def compress(self, data: bytes) -> bytes:
  143. """Komprimiert mit LZ4 HC."""
  144. if not data:
  145. return b""
  146. start = time.monotonic()
  147. try:
  148. compressed = lz4.frame.compress(
  149. data,
  150. compression_level=lz4.frame.COMPRESSIONLEVEL_MAX,
  151. )
  152. except Exception as e:
  153. self.stats.errors += 1
  154. raise CompressionError(f"LZ4-HC-Komprimierung fehlgeschlagen: {e}") from e
  155. elapsed_ms = (time.monotonic() - start) * 1000
  156. self.stats.update_compression(len(data), len(compressed), elapsed_ms)
  157. return compressed
  158. def decompress(self, data: bytes) -> bytes:
  159. """Dekomprimiert LZ4-HC-Daten (identisch zu LZ4)."""
  160. if not data:
  161. return b""
  162. start = time.monotonic()
  163. try:
  164. decompressed = lz4.frame.decompress(data)
  165. except Exception as e:
  166. self.stats.errors += 1
  167. raise DecompressionError(
  168. f"LZ4-HC-Dekomprimierung fehlgeschlagen: {e}"
  169. ) from e
  170. elapsed_ms = (time.monotonic() - start) * 1000
  171. self.stats.update_decompression(len(data), len(decompressed), elapsed_ms)
  172. return decompressed