fastcrypter 2.3.0__py3-none-any.whl
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.
- fastcrypter/__init__.py +285 -0
- fastcrypter/advanced_encryptor.py +368 -0
- fastcrypter/algorithms/__init__.py +119 -0
- fastcrypter/algorithms/compression/__init__.py +17 -0
- fastcrypter/algorithms/encryption/__init__.py +17 -0
- fastcrypter/core/__init__.py +16 -0
- fastcrypter/core/compressor.py +409 -0
- fastcrypter/core/custom_encoder.py +309 -0
- fastcrypter/core/encryptor.py +703 -0
- fastcrypter/core/enhanced_compressor.py +542 -0
- fastcrypter/core/key_manager.py +315 -0
- fastcrypter/exceptions.py +219 -0
- fastcrypter/file_encryptor.py +135 -0
- fastcrypter/secure_compressor.py +568 -0
- fastcrypter/utils/__init__.py +17 -0
- fastcrypter-2.3.0.dist-info/METADATA +381 -0
- fastcrypter-2.3.0.dist-info/RECORD +21 -0
- fastcrypter-2.3.0.dist-info/WHEEL +5 -0
- fastcrypter-2.3.0.dist-info/entry_points.txt +2 -0
- fastcrypter-2.3.0.dist-info/licenses/LICENSE +21 -0
- fastcrypter-2.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Algorithm implementations for the Encrypter package.
|
|
3
|
+
|
|
4
|
+
This package contains various compression and encryption algorithms
|
|
5
|
+
that can be used with the core components.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CompressionAlgorithm(ABC):
|
|
13
|
+
"""
|
|
14
|
+
Abstract base class for compression algorithms.
|
|
15
|
+
|
|
16
|
+
All compression algorithms must inherit from this class
|
|
17
|
+
and implement the required methods.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def compress(self, data: bytes) -> bytes:
|
|
22
|
+
"""
|
|
23
|
+
Compress the given data.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
data (bytes): Data to compress.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
bytes: Compressed data.
|
|
30
|
+
"""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def decompress(self, data: bytes) -> bytes:
|
|
35
|
+
"""
|
|
36
|
+
Decompress the given data.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
data (bytes): Compressed data to decompress.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
bytes: Decompressed data.
|
|
43
|
+
"""
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def name(self) -> str:
|
|
49
|
+
"""Get the algorithm name."""
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def settings(self) -> Dict[str, Any]:
|
|
55
|
+
"""Get the algorithm settings."""
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class EncryptionAlgorithm(ABC):
|
|
60
|
+
"""
|
|
61
|
+
Abstract base class for encryption algorithms.
|
|
62
|
+
|
|
63
|
+
All encryption algorithms must inherit from this class
|
|
64
|
+
and implement the required methods.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def encrypt(self, data: bytes, key: bytes, **kwargs) -> bytes:
|
|
69
|
+
"""
|
|
70
|
+
Encrypt the given data.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
data (bytes): Data to encrypt.
|
|
74
|
+
key (bytes): Encryption key.
|
|
75
|
+
**kwargs: Additional algorithm-specific parameters.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
bytes: Encrypted data.
|
|
79
|
+
"""
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
def decrypt(self, data: bytes, key: bytes, **kwargs) -> bytes:
|
|
84
|
+
"""
|
|
85
|
+
Decrypt the given data.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
data (bytes): Encrypted data to decrypt.
|
|
89
|
+
key (bytes): Decryption key.
|
|
90
|
+
**kwargs: Additional algorithm-specific parameters.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
bytes: Decrypted data.
|
|
94
|
+
"""
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def name(self) -> str:
|
|
100
|
+
"""Get the algorithm name."""
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
@abstractmethod
|
|
105
|
+
def key_size(self) -> int:
|
|
106
|
+
"""Get the required key size in bytes."""
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
@abstractmethod
|
|
111
|
+
def settings(self) -> Dict[str, Any]:
|
|
112
|
+
"""Get the algorithm settings."""
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
__all__ = [
|
|
117
|
+
"CompressionAlgorithm",
|
|
118
|
+
"EncryptionAlgorithm",
|
|
119
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Compression algorithm implementations.
|
|
3
|
+
|
|
4
|
+
This module contains various compression algorithms including
|
|
5
|
+
ZLIB, LZMA, and Brotli implementations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Import will be added when algorithm files are created
|
|
9
|
+
# from .zlib_compressor import ZlibCompressor
|
|
10
|
+
# from .lzma_compressor import LzmaCompressor
|
|
11
|
+
# from .brotli_compressor import BrotliCompressor
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
# "ZlibCompressor",
|
|
15
|
+
# "LzmaCompressor",
|
|
16
|
+
# "BrotliCompressor",
|
|
17
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Encryption algorithm implementations.
|
|
3
|
+
|
|
4
|
+
This module contains various encryption algorithms including
|
|
5
|
+
AES, ChaCha20, and RSA implementations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Import will be added when algorithm files are created
|
|
9
|
+
# from .aes_encryptor import AESEncryptor
|
|
10
|
+
# from .chacha20_encryptor import ChaCha20Encryptor
|
|
11
|
+
# from .rsa_encryptor import RSAEncryptor
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
# "AESEncryptor",
|
|
15
|
+
# "ChaCha20Encryptor",
|
|
16
|
+
# "RSAEncryptor",
|
|
17
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core modules for the Encrypter package.
|
|
3
|
+
|
|
4
|
+
This package contains the fundamental components for compression,
|
|
5
|
+
encryption, and key management.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .compressor import Compressor
|
|
9
|
+
from .encryptor import Encryptor
|
|
10
|
+
from .key_manager import KeyManager
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"Compressor",
|
|
14
|
+
"Encryptor",
|
|
15
|
+
"KeyManager",
|
|
16
|
+
]
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data Compression System for the Encrypter package.
|
|
3
|
+
|
|
4
|
+
This module provides high-performance data compression functionality
|
|
5
|
+
with support for multiple compression algorithms and optimization levels.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import zlib
|
|
9
|
+
import lzma
|
|
10
|
+
import brotli
|
|
11
|
+
import io
|
|
12
|
+
from typing import Union, Dict, Any, Optional, Tuple
|
|
13
|
+
from enum import Enum
|
|
14
|
+
|
|
15
|
+
from ..exceptions import CompressionError, ValidationError, ErrorCodes
|
|
16
|
+
from ..algorithms import CompressionAlgorithm
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CompressionLevel(Enum):
|
|
20
|
+
"""Compression level enumeration."""
|
|
21
|
+
FASTEST = 1
|
|
22
|
+
FAST = 3
|
|
23
|
+
BALANCED = 6
|
|
24
|
+
BEST = 9
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CompressionAlgorithmType(Enum):
|
|
28
|
+
"""Supported compression algorithms."""
|
|
29
|
+
ZLIB = "zlib"
|
|
30
|
+
LZMA = "lzma"
|
|
31
|
+
BROTLI = "brotli"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Compressor:
|
|
35
|
+
"""
|
|
36
|
+
High-performance data compression system.
|
|
37
|
+
|
|
38
|
+
Supports multiple compression algorithms with configurable compression
|
|
39
|
+
levels and automatic algorithm selection based on data characteristics.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
# Algorithm-specific settings
|
|
43
|
+
ALGORITHM_SETTINGS = {
|
|
44
|
+
CompressionAlgorithmType.ZLIB: {
|
|
45
|
+
'min_level': 1,
|
|
46
|
+
'max_level': 9,
|
|
47
|
+
'default_level': 6,
|
|
48
|
+
'chunk_size': 64 * 1024, # 64KB
|
|
49
|
+
},
|
|
50
|
+
CompressionAlgorithmType.LZMA: {
|
|
51
|
+
'min_level': 0,
|
|
52
|
+
'max_level': 9,
|
|
53
|
+
'default_level': 6,
|
|
54
|
+
'chunk_size': 128 * 1024, # 128KB
|
|
55
|
+
},
|
|
56
|
+
CompressionAlgorithmType.BROTLI: {
|
|
57
|
+
'min_level': 0,
|
|
58
|
+
'max_level': 11,
|
|
59
|
+
'default_level': 6,
|
|
60
|
+
'chunk_size': 64 * 1024, # 64KB
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
def __init__(self,
|
|
65
|
+
algorithm: Union[CompressionAlgorithmType, str] = CompressionAlgorithmType.ZLIB,
|
|
66
|
+
level: Union[CompressionLevel, int] = CompressionLevel.BALANCED,
|
|
67
|
+
auto_select: bool = False):
|
|
68
|
+
"""
|
|
69
|
+
Initialize the Compressor.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
algorithm: Compression algorithm to use.
|
|
73
|
+
level: Compression level (1-9 for most algorithms).
|
|
74
|
+
auto_select: Whether to automatically select best algorithm.
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
ValidationError: If parameters are invalid.
|
|
78
|
+
"""
|
|
79
|
+
# Handle string algorithm input
|
|
80
|
+
if isinstance(algorithm, str):
|
|
81
|
+
try:
|
|
82
|
+
algorithm = CompressionAlgorithmType(algorithm.lower())
|
|
83
|
+
except ValueError:
|
|
84
|
+
raise ValidationError(
|
|
85
|
+
f"Unsupported compression algorithm: {algorithm}",
|
|
86
|
+
ErrorCodes.UNSUPPORTED_COMPRESSION
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Handle integer level input
|
|
90
|
+
if isinstance(level, int):
|
|
91
|
+
level = min(max(level, 1), 9) # Clamp to valid range
|
|
92
|
+
elif isinstance(level, CompressionLevel):
|
|
93
|
+
level = level.value
|
|
94
|
+
|
|
95
|
+
self.algorithm = algorithm
|
|
96
|
+
self.level = level
|
|
97
|
+
self.auto_select = auto_select
|
|
98
|
+
|
|
99
|
+
# Validate level for selected algorithm
|
|
100
|
+
settings = self.ALGORITHM_SETTINGS[algorithm]
|
|
101
|
+
if not (settings['min_level'] <= level <= settings['max_level']):
|
|
102
|
+
raise ValidationError(
|
|
103
|
+
f"Invalid compression level {level} for {algorithm.value}. "
|
|
104
|
+
f"Must be between {settings['min_level']} and {settings['max_level']}",
|
|
105
|
+
ErrorCodes.INVALID_CONFIGURATION
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
self.chunk_size = settings['chunk_size']
|
|
109
|
+
|
|
110
|
+
def compress(self, data: Union[str, bytes]) -> bytes:
|
|
111
|
+
"""
|
|
112
|
+
Compress data using the configured algorithm.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
data: Data to compress (string or bytes).
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
bytes: Compressed data with metadata header.
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
CompressionError: If compression fails.
|
|
122
|
+
ValidationError: If input data is invalid.
|
|
123
|
+
"""
|
|
124
|
+
# Convert string to bytes if necessary
|
|
125
|
+
if isinstance(data, str):
|
|
126
|
+
data = data.encode('utf-8')
|
|
127
|
+
|
|
128
|
+
if not isinstance(data, bytes):
|
|
129
|
+
raise ValidationError(
|
|
130
|
+
"Input data must be string or bytes",
|
|
131
|
+
ErrorCodes.INVALID_INPUT_FORMAT
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if len(data) == 0:
|
|
135
|
+
raise ValidationError(
|
|
136
|
+
"Cannot compress empty data",
|
|
137
|
+
ErrorCodes.INVALID_INPUT_SIZE
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Auto-select algorithm if enabled
|
|
141
|
+
if self.auto_select:
|
|
142
|
+
algorithm = self._select_best_algorithm(data)
|
|
143
|
+
else:
|
|
144
|
+
algorithm = self.algorithm
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
# Compress data
|
|
148
|
+
if algorithm == CompressionAlgorithmType.ZLIB:
|
|
149
|
+
compressed = self._compress_zlib(data)
|
|
150
|
+
elif algorithm == CompressionAlgorithmType.LZMA:
|
|
151
|
+
compressed = self._compress_lzma(data)
|
|
152
|
+
elif algorithm == CompressionAlgorithmType.BROTLI:
|
|
153
|
+
compressed = self._compress_brotli(data)
|
|
154
|
+
else:
|
|
155
|
+
raise CompressionError(
|
|
156
|
+
f"Unsupported algorithm: {algorithm}",
|
|
157
|
+
ErrorCodes.UNSUPPORTED_COMPRESSION
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Add metadata header
|
|
161
|
+
header = self._create_header(algorithm, len(data))
|
|
162
|
+
result = header + compressed
|
|
163
|
+
|
|
164
|
+
# Validate compression ratio
|
|
165
|
+
ratio = len(result) / len(data)
|
|
166
|
+
if ratio > 1.1: # If compressed size is more than 110% of original
|
|
167
|
+
# Return original data with special header indicating no compression
|
|
168
|
+
no_comp_header = self._create_header(None, len(data))
|
|
169
|
+
result = no_comp_header + data
|
|
170
|
+
|
|
171
|
+
return result
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
raise CompressionError(
|
|
175
|
+
f"Compression failed: {str(e)}",
|
|
176
|
+
ErrorCodes.COMPRESSION_FAILED,
|
|
177
|
+
details={"algorithm": algorithm.value if algorithm else None, "error": str(e)}
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def decompress(self, data: bytes) -> bytes:
|
|
181
|
+
"""
|
|
182
|
+
Decompress data.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
data: Compressed data with metadata header.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
bytes: Decompressed data.
|
|
189
|
+
|
|
190
|
+
Raises:
|
|
191
|
+
CompressionError: If decompression fails.
|
|
192
|
+
ValidationError: If input data is invalid.
|
|
193
|
+
"""
|
|
194
|
+
if not isinstance(data, bytes):
|
|
195
|
+
raise ValidationError(
|
|
196
|
+
"Input data must be bytes",
|
|
197
|
+
ErrorCodes.INVALID_INPUT_FORMAT
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if len(data) < 8: # Minimum header size
|
|
201
|
+
raise ValidationError(
|
|
202
|
+
"Data too small to contain valid header",
|
|
203
|
+
ErrorCodes.INVALID_INPUT_SIZE
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
# Parse header
|
|
208
|
+
algorithm, original_size = self._parse_header(data)
|
|
209
|
+
compressed_data = data[8:] # Skip 8-byte header
|
|
210
|
+
|
|
211
|
+
# Handle uncompressed data
|
|
212
|
+
if algorithm is None:
|
|
213
|
+
return compressed_data
|
|
214
|
+
|
|
215
|
+
# Decompress data
|
|
216
|
+
if algorithm == CompressionAlgorithmType.ZLIB:
|
|
217
|
+
result = self._decompress_zlib(compressed_data)
|
|
218
|
+
elif algorithm == CompressionAlgorithmType.LZMA:
|
|
219
|
+
result = self._decompress_lzma(compressed_data)
|
|
220
|
+
elif algorithm == CompressionAlgorithmType.BROTLI:
|
|
221
|
+
result = self._decompress_brotli(compressed_data)
|
|
222
|
+
else:
|
|
223
|
+
raise CompressionError(
|
|
224
|
+
f"Unsupported algorithm in header: {algorithm}",
|
|
225
|
+
ErrorCodes.UNSUPPORTED_COMPRESSION
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Validate decompressed size
|
|
229
|
+
if len(result) != original_size:
|
|
230
|
+
raise CompressionError(
|
|
231
|
+
f"Decompressed size mismatch: expected {original_size}, got {len(result)}",
|
|
232
|
+
ErrorCodes.DECOMPRESSION_FAILED
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
return result
|
|
236
|
+
|
|
237
|
+
except CompressionError:
|
|
238
|
+
raise
|
|
239
|
+
except Exception as e:
|
|
240
|
+
raise CompressionError(
|
|
241
|
+
f"Decompression failed: {str(e)}",
|
|
242
|
+
ErrorCodes.DECOMPRESSION_FAILED,
|
|
243
|
+
details={"error": str(e)}
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
def _compress_zlib(self, data: bytes) -> bytes:
|
|
247
|
+
"""Compress data using zlib."""
|
|
248
|
+
return zlib.compress(data, level=self.level)
|
|
249
|
+
|
|
250
|
+
def _decompress_zlib(self, data: bytes) -> bytes:
|
|
251
|
+
"""Decompress zlib data."""
|
|
252
|
+
return zlib.decompress(data)
|
|
253
|
+
|
|
254
|
+
def _compress_lzma(self, data: bytes) -> bytes:
|
|
255
|
+
"""Compress data using LZMA."""
|
|
256
|
+
return lzma.compress(
|
|
257
|
+
data,
|
|
258
|
+
format=lzma.FORMAT_ALONE,
|
|
259
|
+
preset=self.level
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
def _decompress_lzma(self, data: bytes) -> bytes:
|
|
263
|
+
"""Decompress LZMA data."""
|
|
264
|
+
return lzma.decompress(data, format=lzma.FORMAT_ALONE)
|
|
265
|
+
|
|
266
|
+
def _compress_brotli(self, data: bytes) -> bytes:
|
|
267
|
+
"""Compress data using Brotli."""
|
|
268
|
+
return brotli.compress(data, quality=self.level)
|
|
269
|
+
|
|
270
|
+
def _decompress_brotli(self, data: bytes) -> bytes:
|
|
271
|
+
"""Decompress Brotli data."""
|
|
272
|
+
return brotli.decompress(data)
|
|
273
|
+
|
|
274
|
+
def _select_best_algorithm(self, data: bytes) -> CompressionAlgorithmType:
|
|
275
|
+
"""
|
|
276
|
+
Automatically select the best compression algorithm for the data.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
data: Data to analyze.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
CompressionAlgorithmType: Best algorithm for the data.
|
|
283
|
+
"""
|
|
284
|
+
# For small data, use zlib (fastest)
|
|
285
|
+
if len(data) < 1024:
|
|
286
|
+
return CompressionAlgorithmType.ZLIB
|
|
287
|
+
|
|
288
|
+
# Analyze data characteristics
|
|
289
|
+
entropy = self._calculate_entropy(data)
|
|
290
|
+
|
|
291
|
+
# High entropy data (already compressed/encrypted) - use fastest
|
|
292
|
+
if entropy > 7.5:
|
|
293
|
+
return CompressionAlgorithmType.ZLIB
|
|
294
|
+
|
|
295
|
+
# Low entropy data - use best compression
|
|
296
|
+
if entropy < 4.0:
|
|
297
|
+
return CompressionAlgorithmType.LZMA
|
|
298
|
+
|
|
299
|
+
# Medium entropy - use balanced approach
|
|
300
|
+
return CompressionAlgorithmType.BROTLI
|
|
301
|
+
|
|
302
|
+
def _calculate_entropy(self, data: bytes) -> float:
|
|
303
|
+
"""Calculate Shannon entropy of data."""
|
|
304
|
+
if not data:
|
|
305
|
+
return 0.0
|
|
306
|
+
|
|
307
|
+
# Count byte frequencies
|
|
308
|
+
frequencies = {}
|
|
309
|
+
for byte in data:
|
|
310
|
+
frequencies[byte] = frequencies.get(byte, 0) + 1
|
|
311
|
+
|
|
312
|
+
# Calculate entropy
|
|
313
|
+
entropy = 0.0
|
|
314
|
+
length = len(data)
|
|
315
|
+
for count in frequencies.values():
|
|
316
|
+
probability = count / length
|
|
317
|
+
if probability > 0:
|
|
318
|
+
import math
|
|
319
|
+
entropy -= probability * math.log2(probability)
|
|
320
|
+
|
|
321
|
+
return entropy
|
|
322
|
+
|
|
323
|
+
def _create_header(self, algorithm: Optional[CompressionAlgorithmType], original_size: int) -> bytes:
|
|
324
|
+
"""
|
|
325
|
+
Create metadata header for compressed data.
|
|
326
|
+
|
|
327
|
+
Header format (8 bytes):
|
|
328
|
+
- 1 byte: Algorithm ID (0=none, 1=zlib, 2=lzma, 3=brotli)
|
|
329
|
+
- 1 byte: Compression level
|
|
330
|
+
- 2 bytes: Reserved
|
|
331
|
+
- 4 bytes: Original size (little-endian)
|
|
332
|
+
"""
|
|
333
|
+
if algorithm is None:
|
|
334
|
+
algo_id = 0
|
|
335
|
+
else:
|
|
336
|
+
algo_map = {
|
|
337
|
+
CompressionAlgorithmType.ZLIB: 1,
|
|
338
|
+
CompressionAlgorithmType.LZMA: 2,
|
|
339
|
+
CompressionAlgorithmType.BROTLI: 3,
|
|
340
|
+
}
|
|
341
|
+
algo_id = algo_map[algorithm]
|
|
342
|
+
|
|
343
|
+
header = bytearray(8)
|
|
344
|
+
header[0] = algo_id
|
|
345
|
+
header[1] = self.level
|
|
346
|
+
header[2:4] = b'\x00\x00' # Reserved
|
|
347
|
+
header[4:8] = original_size.to_bytes(4, 'little')
|
|
348
|
+
|
|
349
|
+
return bytes(header)
|
|
350
|
+
|
|
351
|
+
def _parse_header(self, data: bytes) -> Tuple[Optional[CompressionAlgorithmType], int]:
|
|
352
|
+
"""
|
|
353
|
+
Parse metadata header from compressed data.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
Tuple[Optional[CompressionAlgorithmType], int]: (algorithm, original_size)
|
|
357
|
+
"""
|
|
358
|
+
header = data[:8]
|
|
359
|
+
|
|
360
|
+
algo_id = header[0]
|
|
361
|
+
if algo_id == 0:
|
|
362
|
+
algorithm = None
|
|
363
|
+
else:
|
|
364
|
+
algo_map = {
|
|
365
|
+
1: CompressionAlgorithmType.ZLIB,
|
|
366
|
+
2: CompressionAlgorithmType.LZMA,
|
|
367
|
+
3: CompressionAlgorithmType.BROTLI,
|
|
368
|
+
}
|
|
369
|
+
algorithm = algo_map.get(algo_id)
|
|
370
|
+
if algorithm is None:
|
|
371
|
+
raise CompressionError(
|
|
372
|
+
f"Unknown algorithm ID in header: {algo_id}",
|
|
373
|
+
ErrorCodes.INVALID_INPUT_FORMAT
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
original_size = int.from_bytes(header[4:8], 'little')
|
|
377
|
+
|
|
378
|
+
return algorithm, original_size
|
|
379
|
+
|
|
380
|
+
def get_compression_ratio(self, original_data: bytes, compressed_data: bytes) -> float:
|
|
381
|
+
"""
|
|
382
|
+
Calculate compression ratio.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
original_data: Original uncompressed data.
|
|
386
|
+
compressed_data: Compressed data.
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
float: Compression ratio (compressed_size / original_size).
|
|
390
|
+
"""
|
|
391
|
+
if len(original_data) == 0:
|
|
392
|
+
return 0.0
|
|
393
|
+
|
|
394
|
+
return len(compressed_data) / len(original_data)
|
|
395
|
+
|
|
396
|
+
def get_info(self) -> Dict[str, Any]:
|
|
397
|
+
"""
|
|
398
|
+
Get information about the compressor configuration.
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
Dict[str, Any]: Configuration information.
|
|
402
|
+
"""
|
|
403
|
+
return {
|
|
404
|
+
"algorithm": self.algorithm.value,
|
|
405
|
+
"level": self.level,
|
|
406
|
+
"auto_select": self.auto_select,
|
|
407
|
+
"chunk_size": self.chunk_size,
|
|
408
|
+
"supported_algorithms": [algo.value for algo in CompressionAlgorithmType],
|
|
409
|
+
}
|