diff --git a/.hypothesis/unicode_data/15.0.0/codec-utf-8.json.gz b/.hypothesis/unicode_data/15.0.0/codec-utf-8.json.gz index 0a52b9a..5441a87 100644 Binary files a/.hypothesis/unicode_data/15.0.0/codec-utf-8.json.gz and b/.hypothesis/unicode_data/15.0.0/codec-utf-8.json.gz differ diff --git a/__pycache__/frackture (2).cpython-312.pyc b/__pycache__/frackture (2).cpython-312.pyc index 8b782e2..c6738b4 100644 Binary files a/__pycache__/frackture (2).cpython-312.pyc and b/__pycache__/frackture (2).cpython-312.pyc differ diff --git a/benchmarks/analyze_results.py b/benchmarks/analyze_results.py index 5756ac1..2724154 100755 --- a/benchmarks/analyze_results.py +++ b/benchmarks/analyze_results.py @@ -390,10 +390,15 @@ def analyze_latency(results_data): 'aes_gcm': [] } + hash_ratios = [] + for dataset, methods in results_data.items(): for m in methods: if m['name'] == 'Frackture': latency_data['frackture_hash'].append(m.get('hash_time', 0)) + # Compute ratio if sha256_time is present + if m.get('sha256_time') and m['sha256_time'] > 0: + hash_ratios.append(m['hash_time'] / m['sha256_time']) elif m['name'] == 'Frackture Encrypted': latency_data['frackture_encrypt'].append(m.get('hash_time', 0)) elif m['name'] == 'SHA256': @@ -410,6 +415,11 @@ def analyze_latency(results_data): 'max_latency_ms': max(values), 'count': len(values) } + + if hash_ratios: + summary['hash_ratio_avg'] = statistics.mean(hash_ratios) + summary['hash_ratio_min'] = min(hash_ratios) + summary['hash_ratio_max'] = max(hash_ratios) return summary @@ -484,19 +494,22 @@ def detect_weaknesses(results_data, method_comparison, tier_stats, latency_analy }) # Check hash latency ratio vs SHA256 baseline - if latency_analysis and 'sha256' in latency_analysis and 'frackture_hash' in latency_analysis: - sha_lat = latency_analysis['sha256'].get('avg_latency_ms', 0) - frac_lat = latency_analysis['frackture_hash'].get('avg_latency_ms', 0) - if sha_lat > 0: - ratio = frac_lat / sha_lat - if ratio > 2.0: - weaknesses.append({ - 'type': 'hash_latency_regression', - 'hash_latency_ratio': ratio, - 'frackture_hash_ms': frac_lat, - 'sha256_hash_ms': sha_lat, - 'description': f"Frackture hashing is {ratio:.2f}x slower than SHA256 ({frac_lat:.4f}ms vs {sha_lat:.4f}ms)" - }) + if latency_analysis: + ratio = None + if 'hash_ratio_avg' in latency_analysis: + ratio = latency_analysis['hash_ratio_avg'] + elif 'sha256' in latency_analysis and 'frackture_hash' in latency_analysis: + sha_lat = latency_analysis['sha256'].get('avg_latency_ms', 0) + frac_lat = latency_analysis['frackture_hash'].get('avg_latency_ms', 0) + if sha_lat > 0: + ratio = frac_lat / sha_lat + + if ratio and ratio > 2.0: + weaknesses.append({ + 'type': 'hash_latency_regression', + 'hash_latency_ratio': ratio, + 'description': f"Frackture hashing is {ratio:.2f}x slower than SHA256" + }) return weaknesses @@ -717,7 +730,11 @@ def main(): if 'sha256' in latency_analysis and 'frackture_hash' in latency_analysis: sha_lat = latency_analysis['sha256']['avg_latency_ms'] frac_lat = latency_analysis['frackture_hash']['avg_latency_ms'] - if sha_lat > 0: + + if 'hash_ratio_avg' in latency_analysis: + ratio = latency_analysis['hash_ratio_avg'] + f.write(f"**Frackture vs SHA256:** Ratio {ratio:.2f}x (Avg Frackture: {frac_lat:.4f}ms, Avg SHA256: {sha_lat:.4f}ms)\n\n") + elif sha_lat > 0: speedup = sha_lat / frac_lat if frac_lat > 0 else 0 f.write(f"**Frackture vs SHA256:** Frackture is {speedup:.1f}x faster ({frac_lat:.4f}ms vs {sha_lat:.4f}ms)\n\n") @@ -952,11 +969,17 @@ def main(): if 'sha256' in latency_analysis and 'frackture_hash' in latency_analysis: sha_lat = latency_analysis['sha256']['avg_latency_ms'] frac_lat = latency_analysis['frackture_hash']['avg_latency_ms'] + + # Prefer paired ratio if available + hash_ratio = latency_analysis.get('hash_ratio_avg') + if hash_ratio is None and sha_lat > 0: + hash_ratio = frac_lat / sha_lat + if frac_lat > 0: insights['phase2_questions']['q5_latency_hashing_encryption']['comparisons']['frackture_vs_sha256'] = { 'frackture_ms': frac_lat, 'sha256_ms': sha_lat, - 'hash_latency_ratio': (frac_lat / sha_lat) if sha_lat > 0 else None, + 'hash_latency_ratio': hash_ratio, 'speedup_factor': sha_lat / frac_lat if frac_lat > 0 else 0, 'winner': 'Frackture' if frac_lat < sha_lat else 'SHA256' } diff --git a/benchmarks/benchmark_frackture.py b/benchmarks/benchmark_frackture.py index afbdf8d..03cff3a 100755 --- a/benchmarks/benchmark_frackture.py +++ b/benchmarks/benchmark_frackture.py @@ -97,6 +97,7 @@ class BenchmarkResult: hash_time: float peak_memory_mb: float success: bool + sha256_time: Optional[float] = None error: str = "" # Competitor settings (for multi-level sweeps) @@ -524,6 +525,12 @@ def _expect_value_error(label: str, fn: Callable[[], None]) -> None: runs=hash_runs, ) + # Baseline SHA256 timing for comparison + sha256_time = BenchmarkRunner._avg_latency_ms( + lambda: hashlib.sha256(data).hexdigest(), + runs=hash_runs, + ) + # Stop memory tracking peak_memory = mem_tracker.stop() @@ -544,6 +551,7 @@ def _expect_value_error(label: str, fn: Callable[[], None]) -> None: encode_throughput=encode_throughput, decode_throughput=decode_throughput, hash_time=hash_time, + sha256_time=sha256_time, peak_memory_mb=peak_memory, success=True, # New verification metrics diff --git a/frackture (2).py b/frackture (2).py index 477b8fe..0e8e850 100644 --- a/frackture (2).py +++ b/frackture (2).py @@ -6,7 +6,7 @@ import struct from dataclasses import dataclass, asdict from enum import Enum -from typing import Any, Dict, Iterable, Optional, Union +from typing import Any, Dict, Iterable, Optional, Union, Iterator import numpy as np from scipy.fft import fft @@ -380,7 +380,8 @@ def frackture_preprocess_universal_v2_6(data, tier: Optional[CompressionTier] = padded = np.pad(normed, (0, 768 - len(normed) % 768), mode="wrap") return padded[:768].astype(np.float32) - except Exception: + except Exception as e: + print(f"DEBUG: frackture_preprocess_universal_v2_6 failed for input {type(data)}: {e}") return np.zeros(768, dtype=np.float32) @@ -672,52 +673,103 @@ def compress_preset_large(data, optimize=False, return_format="compact"): _HASH_CHUNK_SIZE = 1024 * 1024 # 1MiB -def normalize_to_bytes(data: Any) -> Union[bytes, memoryview]: +try: + # usedforsecurity available in Python 3.9+ + _HASHER_TEMPLATE = hashlib.sha256(usedforsecurity=False) +except TypeError: + _HASHER_TEMPLATE = hashlib.sha256() + + +def _dump_component(data: Any, is_key: bool = False) -> bytes: + """Helper to dump a single component for streaming hash.""" + try: + # keys in json must be strings + if is_key and not isinstance(data, str): + data = str(data) + + return json.dumps( + data, + ensure_ascii=False, + separators=(",", ":"), + default=str, + ).encode("utf-8") + except (TypeError, ValueError): + return str(data).encode("utf-8") + + +def _stream_json_like(data: Any) -> Iterator[bytes]: + """Stream JSON-like representation of data.""" + if isinstance(data, dict): + yield b"{" + items = sorted(data.items()) + for i, (k, v) in enumerate(items): + if i > 0: + yield b"," + yield _dump_component(k, is_key=True) + yield b":" + yield from _stream_json_like(v) + yield b"}" + elif isinstance(data, (list, tuple)): + yield b"[" + for i, v in enumerate(data): + if i > 0: + yield b"," + yield from _stream_json_like(v) + yield b"]" + else: + yield _dump_component(data) + + +def normalize_to_bytes(data: Any) -> Iterator[Union[bytes, memoryview]]: """Normalize arbitrary data into bytes for hashing. Fast-paths bytes-like objects and uses deterministic JSON for dict/list. + Returns an iterator of bytes/memoryview chunks to avoid large copies. """ + if isinstance(data, FrackturePayload): + yield data.to_bytes() + return + if isinstance(data, memoryview): - return data + yield data + return if isinstance(data, (bytes, bytearray)): - return memoryview(data) + yield memoryview(data) + return if isinstance(data, str): - return data.encode("utf-8") + yield data.encode("utf-8") + return if isinstance(data, np.ndarray): - return data.tobytes() + try: + yield memoryview(data) + except (ValueError, TypeError): + # Fallback for non-contiguous arrays + yield data.tobytes() + return if isinstance(data, (dict, list, tuple)): - try: - return json.dumps( - data, - sort_keys=True, - separators=(",", ":"), - ensure_ascii=False, - default=str, - ).encode("utf-8") - except (TypeError, ValueError): - return str(data).encode("utf-8") + yield from _stream_json_like(data) + return - return str(data).encode("utf-8") + yield str(data).encode("utf-8") def frackture_deterministic_hash(data, salt=""): """Generate deterministic hash for collision testing.""" - normalized = normalize_to_bytes(data) - mv = normalized if isinstance(normalized, memoryview) else memoryview(normalized) + hasher = _HASHER_TEMPLATE.copy() - hasher = hashlib.sha256() - - if len(mv) <= _HASH_CHUNK_SIZE: - hasher.update(mv) - else: - for offset in range(0, len(mv), _HASH_CHUNK_SIZE): - hasher.update(mv[offset : offset + _HASH_CHUNK_SIZE]) + for chunk in normalize_to_bytes(data): + mv = chunk if isinstance(chunk, memoryview) else memoryview(chunk) + if len(mv) <= _HASH_CHUNK_SIZE: + hasher.update(mv) + else: + for offset in range(0, len(mv), _HASH_CHUNK_SIZE): + hasher.update(mv[offset : offset + _HASH_CHUNK_SIZE]) if salt: hasher.update(str(salt).encode("utf-8")) diff --git a/tests/__pycache__/conftest.cpython-312-pytest-9.0.2.pyc b/tests/__pycache__/conftest.cpython-312-pytest-9.0.2.pyc index 312862d..45b1d29 100644 Binary files a/tests/__pycache__/conftest.cpython-312-pytest-9.0.2.pyc and b/tests/__pycache__/conftest.cpython-312-pytest-9.0.2.pyc differ diff --git a/tests/__pycache__/test_hashing.cpython-312-pytest-9.0.2.pyc b/tests/__pycache__/test_hashing.cpython-312-pytest-9.0.2.pyc index 5184af0..1c77470 100644 Binary files a/tests/__pycache__/test_hashing.cpython-312-pytest-9.0.2.pyc and b/tests/__pycache__/test_hashing.cpython-312-pytest-9.0.2.pyc differ diff --git a/tests/test_hashing.py b/tests/test_hashing.py index f57294a..ef77daa 100644 --- a/tests/test_hashing.py +++ b/tests/test_hashing.py @@ -101,6 +101,13 @@ def __str__(self) -> str: return f"Custom({self.value})" arr = np.array([1, 2, 3], dtype=np.int32) + FrackturePayload = frackture_module.FrackturePayload + + fp = FrackturePayload( + symbolic=b"x" * 32, + entropy=[0] * 16, + tier_name="default" + ) cases = [ (b"abc", b"abc"), @@ -110,15 +117,19 @@ def __str__(self) -> str: ([1, "a"], json.dumps([1, "a"], sort_keys=True, separators=(",", ":"), ensure_ascii=False, default=str).encode("utf-8")), (arr, arr.tobytes()), (_Custom(123), str(_Custom(123)).encode("utf-8")), + (fp, fp.to_bytes()), # New fast path for FrackturePayload ] salt = "test_salt" salt_bytes = salt.encode("utf-8") for data, expected in cases: - normalized = normalize_to_bytes(data) - assert isinstance(normalized, (bytes, memoryview)) - assert bytes(normalized) == expected + # normalize_to_bytes returns iterator of bytes/memoryview + normalized_iter = normalize_to_bytes(data) + normalized_parts = [bytes(chunk) for chunk in normalized_iter] + normalized_bytes = b"".join(normalized_parts) + + assert normalized_bytes == expected, f"Normalization mismatch for {type(data)}" digest = frackture_deterministic_hash(data, salt) assert isinstance(digest, str)