journal

package
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 25, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package journal provides the block-based binary journal format (.lpj files) for recording and replaying NMEA 2000 CAN frames.

Index

Constants

View Source
const DeviceEntryMaxSize = 1 + 8 + 4 + 2 + 4 + 128 // = 147

DeviceEntryMaxSize is the worst-case size of a single device table entry: Source(1) + NAME(8) + ActiveFrom(4) + ProductCode(2) + 4 length-prefixed strings at max lengths (32+40+24+32 = 128 bytes + 4 length bytes).

Variables

View Source
var (
	Magic           = [3]byte{'L', 'P', 'J'}
	Version         = byte(0x01)
	Version2        = byte(0x02)
	CRC32cTable     = crc32.MakeTable(crc32.Castagnoli)
	FileHeaderSize  = 16
	BlockTrailerLen = 10 // DeviceTableSize(2) + FrameCount(4) + Checksum(4)
	BlockIndexMagic = [4]byte{'L', 'P', 'J', 'I'}

	// v1 compressed block headers
	BlockHeaderLen     = 12 // BaseTime(8) + CompressedLen(4)
	BlockHeaderLenDict = 16 // BaseTime(8) + DictLen(4) + CompressedLen(4)

	// Block data offsets within uncompressed blocks
	BlockDataOffsetV1 = 8  // frame data starts after BaseTime
	BlockDataOffsetV2 = 16 // frame data starts after BaseTime + BaseSeq
)

Functions

func PutLenPrefixedString

func PutLenPrefixedString(buf []byte, s string) int

PutLenPrefixedString writes a length-prefixed string (1-byte length + data) into buf at offset 0. Returns the number of bytes written. Truncates to 255 bytes if needed.

func ReadLenPrefixedString

func ReadLenPrefixedString(block []byte, off int) (string, int, error)

ReadLenPrefixedString reads a length-prefixed string from block at offset off. Returns the string, number of bytes consumed, and any error.

func UvarintSize

func UvarintSize(v uint64) int

UvarintSize returns the number of bytes needed to encode v as a uvarint.

Types

type BlockInfo

type BlockInfo struct {
	Index         int
	Offset        int64 // file offset
	BaseTime      time.Time
	BaseSeq       uint64 // v2 only; 0 for v1
	FrameCount    int
	DeviceCount   int
	CompressedLen int // 0 for uncompressed blocks
	DictLen       int // 0 unless CompressionZstdDict
}

BlockInfo holds metadata about a single block for inspection.

type CompressionType

type CompressionType uint8

CompressionType identifies the block compression algorithm.

const (
	CompressionNone     CompressionType = 0
	CompressionZstd     CompressionType = 1
	CompressionZstdDict CompressionType = 2
)

type CorruptError added in v0.4.0

type CorruptError struct {
	Path     string // file path, if known
	BlockIdx int    // block index, -1 if header-level
	Offset   int64  // byte offset in file, -1 if unknown
	Reason   string // short description (e.g. "bad_magic", "checksum_mismatch")
	Detail   string // human-readable detail
	Err      error  // underlying error, if any
}

CorruptError indicates that a journal file has invalid or corrupted data. It carries diagnostic context about where the corruption was detected.

func (*CorruptError) Error added in v0.4.0

func (e *CorruptError) Error() string

func (*CorruptError) Unwrap added in v0.4.0

func (e *CorruptError) Unwrap() error

type Device

type Device struct {
	Source          uint8
	NAME            uint64
	ProductCode     uint16
	ModelID         string
	SoftwareVersion string
	ModelVersion    string
	ModelSerial     string
}

Device is a NAME-to-Source binding from the journal device table, optionally carrying PGN 126996 product info.

type Entry

type Entry struct {
	Timestamp time.Time
	Header    canbus.CANHeader
	Data      []byte
}

Entry is a single decoded frame from the journal.

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader reads frames from a block-based journal file.

func NewReader

func NewReader(r io.ReadSeeker) (*Reader, error)

NewReader opens a journal for reading. Validates the file header.

func (*Reader) BlockCount

func (jr *Reader) BlockCount() int

BlockCount returns the number of complete blocks in the file.

func (*Reader) BlockDevices

func (jr *Reader) BlockDevices() []Device

BlockDevices returns all device table entries for the current block.

func (*Reader) BlockDevicesAt

func (jr *Reader) BlockDevicesAt(frameIdx int) []Device

BlockDevicesAt returns the resolved device table at the given frame index. For each source, the entry with the largest ActiveFrom <= frameIdx wins.

func (*Reader) BlockSize

func (jr *Reader) BlockSize() int

BlockSize returns the uncompressed block size.

func (*Reader) Close added in v0.4.0

func (jr *Reader) Close()

Close releases resources held by the reader, including any in-flight prefetch goroutine and zstd decoders.

func (*Reader) Compression

func (jr *Reader) Compression() CompressionType

Compression returns the compression type used by this journal file.

func (*Reader) CurrentBlock

func (jr *Reader) CurrentBlock() int

CurrentBlock returns the current block index, or -1 if before the first block.

func (*Reader) EnablePrefetch added in v0.4.0

func (jr *Reader) EnablePrefetch()

EnablePrefetch enables read-ahead for sequential iteration. After loading each block, the reader eagerly reads the next block's data from disk and (for compressed files) decompresses it in a background goroutine, so the work overlaps with frame processing in the caller.

func (*Reader) Err

func (jr *Reader) Err() error

Err returns the first error encountered during iteration.

func (*Reader) Frame

func (jr *Reader) Frame() Entry

Frame returns the current frame. Only valid after Next returns true.

func (*Reader) FrameSeq

func (jr *Reader) FrameSeq() uint64

FrameSeq returns the sequence number of the current frame. Only valid after Next returns true. Returns 0 for v1 files (no seq info).

func (*Reader) HasBlockIndex

func (jr *Reader) HasBlockIndex() bool

HasBlockIndex reports whether the file had a valid block index footer. Only meaningful for compressed files.

func (*Reader) InspectBlock

func (jr *Reader) InspectBlock(n int) (BlockInfo, error)

InspectBlock loads block n and returns its metadata without iterating frames.

func (*Reader) Next

func (jr *Reader) Next() bool

Next advances to the next frame. Returns false when done or on error. If a frame within a block is corrupt, the remaining frames in that block are skipped and iteration continues with the next block.

func (*Reader) SeekBlock

func (jr *Reader) SeekBlock(n int) error

SeekBlock positions the reader at the start of block n (0-indexed).

func (*Reader) SeekToSeq

func (jr *Reader) SeekToSeq(seq uint64) error

SeekToSeq positions the reader at the block containing the given sequence number via binary search on BaseSeq. Only works for v2 files. Returns an error for v1 files or if the seq is not found.

func (*Reader) SeekToTime

func (jr *Reader) SeekToTime(t time.Time) error

SeekToTime finds the block containing the given timestamp via binary search and positions the reader at the start of that block.

func (*Reader) Version

func (jr *Reader) Version() byte

Version returns the journal format version (0x01 or 0x02).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL