package wal import ( "encoding/binary" "errors" "hash/crc32" ) const ( walFileHeaderSize = 32 walMagic = 0x57414C4B // "WALK" in ASCII walFormatVersion = 1 ) // WalFileHeader is the 32-byte header written at the start of every WAL segment file. type WalFileHeader struct { Magic uint32 FormatVersion uint16 HeaderSize uint16 BlockSize uint32 SegmentID uint64 StartSequence uint64 HeaderCRC uint32 } // EncodeWalHeader serializes h into a fixed-size 32-byte array using little-endian byte order. // The HeaderCRC field is computed over bytes 0–27 (everything except the CRC itself). func EncodeWalHeader(h *WalFileHeader) [walFileHeaderSize]byte { h.HeaderSize = walFileHeaderSize h.Magic = walMagic h.FormatVersion = walFormatVersion var buf [walFileHeaderSize]byte le := binary.LittleEndian le.PutUint32(buf[0:4], h.Magic) le.PutUint16(buf[4:6], h.FormatVersion) le.PutUint16(buf[6:8], h.HeaderSize) le.PutUint32(buf[8:12], h.BlockSize) le.PutUint64(buf[12:20], h.SegmentID) le.PutUint64(buf[20:28], h.StartSequence) // CRC32 IEEE over bytes 0–27 (excludes the CRC field itself) h.HeaderCRC = crc32.ChecksumIEEE(buf[0:28]) le.PutUint32(buf[28:32], h.HeaderCRC) return buf } var ( errBadMagic = errors.New("wal: bad magic number") errBadVersion = errors.New("wal: unsupported format version") errBadHeaderSize = errors.New("wal: bad header size") errCRCMismatch = errors.New("wal: header CRC mismatch") errHeaderTooShort = errors.New("wal: header data too short") ) // DecodeWalHeader parses a 32-byte little-endian header and validates magic, // format version, header size, and CRC. func DecodeWalHeader(data []byte) (*WalFileHeader, error) { if len(data) < walFileHeaderSize { return nil, errHeaderTooShort } le := binary.LittleEndian magic := le.Uint32(data[0:4]) if magic != walMagic { return nil, errBadMagic } version := le.Uint16(data[4:6]) if version != walFormatVersion { return nil, errBadVersion } hdrSize := le.Uint16(data[6:8]) if hdrSize != walFileHeaderSize { return nil, errBadHeaderSize } // Verify CRC before trusting any other fields gotCRC := crc32.ChecksumIEEE(data[0:28]) storedCRC := le.Uint32(data[28:32]) if gotCRC != storedCRC { return nil, errCRCMismatch } return &WalFileHeader{ Magic: magic, FormatVersion: version, HeaderSize: hdrSize, BlockSize: le.Uint32(data[8:12]), SegmentID: le.Uint64(data[12:20]), StartSequence: le.Uint64(data[20:28]), HeaderCRC: storedCRC, }, nil }