| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- # -*- coding: utf-8 -*-
- """
- Basis-Kompressor-Interface.
- Definiert die abstrakte Schnittstelle für alle
- Komprimierungsimplementierungen.
- """
- from __future__ import annotations
- from abc import ABC, abstractmethod
- from dataclasses import dataclass, field
- from datetime import datetime
- from enum import IntEnum
- from typing import Any
- class CompressionLevel(IntEnum):
- """Komprimierungsstufen."""
- NONE = 0 # Keine Komprimierung
- FASTEST = 1 # Schnellste Komprimierung
- FAST = 3 # Schnell
- DEFAULT = 6 # Standard
- BEST = 9 # Beste Komprimierung (langsamste)
- @dataclass
- class CompressionStats:
- """Statistiken über Komprimierungsoperationen."""
- total_compressed: int = 0 # Anzahl Komprimierungen
- total_decompressed: int = 0 # Anzahl Dekomprimierungen
- bytes_in: int = 0 # Eingabe-Bytes insgesamt
- bytes_out: int = 0 # Ausgabe-Bytes insgesamt
- compression_ratio: float = 0.0 # Durchschnittliches Verhältnis
- total_time_ms: float = 0.0 # Gesamtzeit in ms
- errors: int = 0 # Fehler
- last_operation: datetime | None = None
- def update_compression(
- self,
- input_size: int,
- output_size: int,
- time_ms: float,
- ) -> None:
- """Aktualisiert Statistik nach Komprimierung."""
- self.total_compressed += 1
- self.bytes_in += input_size
- self.bytes_out += output_size
- self.total_time_ms += time_ms
- self.last_operation = datetime.now()
- if self.bytes_in > 0:
- self.compression_ratio = self.bytes_out / self.bytes_in
- def update_decompression(
- self,
- input_size: int,
- output_size: int,
- time_ms: float,
- ) -> None:
- """Aktualisiert Statistik nach Dekomprimierung."""
- self.total_decompressed += 1
- self.total_time_ms += time_ms
- self.last_operation = datetime.now()
- def to_dict(self) -> dict[str, Any]:
- """Konvertiert zu Dictionary."""
- return {
- "total_compressed": self.total_compressed,
- "total_decompressed": self.total_decompressed,
- "bytes_in": self.bytes_in,
- "bytes_out": self.bytes_out,
- "compression_ratio": round(self.compression_ratio, 4),
- "total_time_ms": round(self.total_time_ms, 2),
- "errors": self.errors,
- "space_saved_percent": round(
- (1 - self.compression_ratio) * 100, 2
- ) if self.compression_ratio > 0 else 0,
- }
- class Compressor(ABC):
- """
- Abstrakte Basis-Klasse für Kompressoren.
- Definiert die Schnittstelle die alle konkreten
- Komprimierungsimplementierungen erfüllen müssen.
- Beispiel:
- compressor = ZlibCompressor(level=CompressionLevel.BEST)
- compressed = compressor.compress(data)
- original = compressor.decompress(compressed)
- """
- def __init__(
- self,
- level: CompressionLevel = CompressionLevel.DEFAULT,
- ) -> None:
- """
- Initialisiert den Kompressor.
- Args:
- level: Komprimierungsstufe
- """
- self.level = level
- self.stats = CompressionStats()
- @property
- @abstractmethod
- def name(self) -> str:
- """Name des Komprimierungsalgorithmus."""
- pass
- @property
- @abstractmethod
- def content_type(self) -> str:
- """Content-Type für komprimierte Daten."""
- pass
- @property
- def min_size(self) -> int:
- """
- Minimale Größe für Komprimierung.
- Daten unter dieser Größe werden nicht komprimiert,
- da der Overhead größer als der Nutzen sein kann.
- """
- return 100 # 100 Bytes Standard
- def should_compress(self, data: bytes) -> bool:
- """
- Prüft ob Daten komprimiert werden sollten.
- Args:
- data: Zu prüfende Daten
- Returns:
- True wenn Komprimierung sinnvoll
- """
- return len(data) >= self.min_size
- @abstractmethod
- def compress(self, data: bytes) -> bytes:
- """
- Komprimiert Daten.
- Args:
- data: Unkomprimierte Daten
- Returns:
- Komprimierte Daten
- """
- pass
- @abstractmethod
- def decompress(self, data: bytes) -> bytes:
- """
- Dekomprimiert Daten.
- Args:
- data: Komprimierte Daten
- Returns:
- Ursprüngliche Daten
- """
- pass
- def compress_if_beneficial(self, data: bytes) -> tuple[bytes, bool]:
- """
- Komprimiert nur wenn es sich lohnt.
- Args:
- data: Zu komprimierende Daten
- Returns:
- Tuple (Daten, ob_komprimiert)
- """
- if not self.should_compress(data):
- return data, False
- compressed = self.compress(data)
- # Nur verwenden wenn kleiner
- if len(compressed) < len(data):
- return compressed, True
- return data, False
- def get_stats(self) -> CompressionStats:
- """Gibt Komprimierungsstatistiken zurück."""
- return self.stats
- def reset_stats(self) -> None:
- """Setzt Statistiken zurück."""
- self.stats = CompressionStats()
- def __repr__(self) -> str:
- return f"{self.__class__.__name__}(level={self.level.name})"
- class CompressorError(Exception):
- """Fehler bei Komprimierung/Dekomprimierung."""
- pass
- class CompressionError(CompressorError):
- """Fehler bei Komprimierung."""
- pass
- class DecompressionError(CompressorError):
- """Fehler bei Dekomprimierung."""
- pass
|