package wal import ( "encoding/binary" "hash/crc32" "io" "git.crumpington.com/public/jldb/lib/errs" ) const recordHeaderSize = 28 type Record struct { SeqNum int64 TimestampMS int64 DataSize int64 Reader io.Reader } func (rec Record) writeHeaderTo(w io.Writer) (int, error) { buf := make([]byte, recordHeaderSize) binary.LittleEndian.PutUint64(buf[0:], uint64(rec.SeqNum)) binary.LittleEndian.PutUint64(buf[8:], uint64(rec.TimestampMS)) binary.LittleEndian.PutUint64(buf[16:], uint64(rec.DataSize)) crc := crc32.ChecksumIEEE(buf[:recordHeaderSize-4]) binary.LittleEndian.PutUint32(buf[24:], crc) n, err := w.Write(buf) if err != nil { err = errs.IO.WithErr(err) } return n, err } func (rec *Record) readHeaderFrom(r io.Reader) error { buf := make([]byte, recordHeaderSize) if _, err := io.ReadFull(r, buf); err != nil { return errs.IO.WithErr(err) } crc := crc32.ChecksumIEEE(buf[:recordHeaderSize-4]) stored := binary.LittleEndian.Uint32(buf[recordHeaderSize-4:]) if crc != stored { return errs.Corrupt.WithMsg("checksum mismatch") } rec.SeqNum = int64(binary.LittleEndian.Uint64(buf[0:])) rec.TimestampMS = int64(binary.LittleEndian.Uint64(buf[8:])) rec.DataSize = int64(binary.LittleEndian.Uint64(buf[16:])) return nil } func (rec Record) serializedSize() int64 { return recordHeaderSize + rec.DataSize + 4 // 4 for data CRC32. } func (rec Record) writeTo(w io.Writer) (int64, error) { nn, err := rec.writeHeaderTo(w) if err != nil { return int64(nn), err } n := int64(nn) // Write the data. crcW := newCRCWriter(w) n2, err := io.CopyN(crcW, rec.Reader, rec.DataSize) n += n2 if err != nil { return n, errs.IO.WithErr(err) } // Write the data crc value. err = binary.Write(w, binary.LittleEndian, crcW.CRC()) if err != nil { return n, errs.IO.WithErr(err) } n += 4 return n, nil } func (rec *Record) readFrom(r io.Reader) error { if err := rec.readHeaderFrom(r); err != nil { return err } rec.Reader = newDataReader(r, rec.DataSize) return nil }