feat: initialize project structure and error types

- go.mod with github.com/dailz/go-kv, Go 1.26.3, testify
- config/config.go with WalConfig, Validate() with checked arithmetic
- errors.go with sentinel errors (ErrCommitUnknown, ErrWriteStopped, etc.)
- wal/constants.go with all WAL format constants and enums
- wal/header.go with WAL File Header encode/decode (CRC32 IEEE)
- wal/record.go with Physical Record codec, block boundary, SplitIntoRecords
- wal/entry.go with WAL Entry codec (varint keys/values, OpType, ValueKind)
- wal/sequence.go with SequenceManager (atomic, CAS, overflow-safe)
- manifest/manifest.go with MANIFEST stub (Load/Save atomic)
- manifest/current.go with CURRENT file (WriteCurrent/ReadCurrent)
- Comprehensive tests for all modules
- .golangci.yml configuration
This commit is contained in:
dailz
2026-06-12 13:23:27 +08:00
parent 59de99ca0b
commit cf913b1d52
52 changed files with 4538 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
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 027 (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 027 (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
}