mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 09:03:46 +08:00
a82935f326
Integrate onroad views, configurable widgets, local settings, steering-profile contracts, Bluetooth and Android Auto with their device and runtime services. Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> Co-authored-by: Daniel Koepping <elkoled@gmail.com> Co-authored-by: Danny <dannyirwin31@yahoo.com> Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com> Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com> Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com> Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu> Co-authored-by: Shane Smiskol <shane@smiskol.com> Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com> Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Co-authored-by: stef <19478336+stefpi@users.noreply.github.com> Co-authored-by: whoisdomi <dcantu83@gmail.com> Co-authored-by: Intelli <contact@intelli.software> Co-authored-by: pharmacomaniac <blittle65@gmail.com> Co-authored-by: prabhaavp <prabhaav@msu.edu>
104 lines
3.8 KiB
Python
104 lines
3.8 KiB
Python
"""Bounded, all-or-nothing decode of an already admitted local log segment."""
|
|
|
|
import bz2
|
|
import io
|
|
from collections.abc import Callable
|
|
from typing import Any
|
|
|
|
import zstandard as zstd
|
|
|
|
from openpilot.cereal import log
|
|
|
|
|
|
MAX_COMPRESSED_BYTES = 32 * 1024 * 1024
|
|
MAX_EXPANDED_BYTES = 64 * 1024 * 1024
|
|
MAX_MESSAGES = 250_000
|
|
EVENT_TRAVERSAL_WORDS = 1_000_000
|
|
CHUNK_BYTES = 64 * 1024
|
|
|
|
|
|
class LogDecodeError(ValueError):
|
|
"""The whole segment is unavailable; callers must discard every event."""
|
|
|
|
|
|
def _not_cancelled(cancelled: Callable[[], bool]) -> None:
|
|
try:
|
|
if cancelled():
|
|
raise LogDecodeError("segment decode cancelled")
|
|
except LogDecodeError:
|
|
raise
|
|
except Exception as error:
|
|
raise LogDecodeError("segment cancellation check unavailable") from error
|
|
|
|
|
|
def _bounded_read(reader: Any, cancelled: Callable[[], bool]) -> bytes:
|
|
chunks: list[bytes] = []
|
|
total = 0
|
|
while True:
|
|
_not_cancelled(cancelled)
|
|
chunk = reader.read(min(CHUNK_BYTES, MAX_EXPANDED_BYTES + 1 - total))
|
|
if not chunk:
|
|
break
|
|
total += len(chunk)
|
|
if total > MAX_EXPANDED_BYTES:
|
|
raise LogDecodeError("expanded segment exceeds 64 MiB")
|
|
chunks.append(chunk)
|
|
return b"".join(chunks)
|
|
|
|
|
|
def _expand(data: bytes, codec: str, cancelled: Callable[[], bool]) -> bytes:
|
|
try:
|
|
if codec == "zst":
|
|
with zstd.ZstdDecompressor(max_window_size=MAX_EXPANDED_BYTES).stream_reader(io.BytesIO(data)) as reader:
|
|
expanded = _bounded_read(reader, cancelled)
|
|
# stream_reader in pinned zstandard 0.25 returns short data for a
|
|
# truncated frame. Validate the complete frame after proving its output
|
|
# fits our bound, and reject a concatenated/trailing second frame.
|
|
decoder = zstd.ZstdDecompressor(max_window_size=MAX_EXPANDED_BYTES).decompressobj()
|
|
verified = decoder.decompress(data)
|
|
if not decoder.eof or decoder.unused_data or verified != expanded:
|
|
raise LogDecodeError("incomplete or trailing zstd frame")
|
|
elif codec == "bz2":
|
|
with bz2.BZ2File(io.BytesIO(data), "rb") as reader:
|
|
expanded = _bounded_read(reader, cancelled)
|
|
decoder = bz2.BZ2Decompressor()
|
|
verified = decoder.decompress(data)
|
|
if not decoder.eof or decoder.unused_data or verified != expanded:
|
|
raise LogDecodeError("incomplete or trailing bzip2 stream")
|
|
else:
|
|
raise LogDecodeError("unsupported segment compression")
|
|
_not_cancelled(cancelled)
|
|
return expanded
|
|
except LogDecodeError:
|
|
raise
|
|
except (OSError, EOFError, ValueError, zstd.ZstdError) as error:
|
|
raise LogDecodeError("corrupt or incomplete compressed segment") from error
|
|
|
|
|
|
def decode_segment(data: bytes, codec: str, *, cancelled: Callable[[], bool] = lambda: False,
|
|
retain: frozenset[str] | None = None) -> tuple[Any, ...]:
|
|
"""Return all complete Event readers, or reject the entire segment."""
|
|
if type(data) is not bytes or len(data) > MAX_COMPRESSED_BYTES or type(codec) is not str or \
|
|
codec not in ("zst", "bz2"):
|
|
raise LogDecodeError("unsupported or oversized compressed segment")
|
|
_not_cancelled(cancelled)
|
|
expanded = _expand(data, codec, cancelled)
|
|
events: list[Any] = []
|
|
try:
|
|
for count, event in enumerate(log.Event.read_multiple_bytes(expanded, traversal_limit_in_words=EVENT_TRAVERSAL_WORDS)):
|
|
_not_cancelled(cancelled)
|
|
if count >= MAX_MESSAGES:
|
|
raise LogDecodeError("segment message count exceeded")
|
|
# Force envelope and union decoding before a reader can escape; a later
|
|
# malformed record invalidates the entire segment, never a prefix.
|
|
int(event.logMonoTime)
|
|
kind = event.which()
|
|
if retain is None or kind in retain:
|
|
events.append(event)
|
|
except LogDecodeError:
|
|
raise
|
|
except Exception as error:
|
|
raise LogDecodeError("corrupt Cereal Event segment") from error
|
|
_not_cancelled(cancelled)
|
|
return tuple(events)
|