checkpoint.mx raw

   1  // Package checkpoint provides an atomic checksummed checkpoint file.
   2  // Format: 8 bytes uint64 serial (BE) + 4 bytes CRC32-IEEE checksum (BE).
   3  package checkpoint
   4  
   5  import (
   6  	"encoding/binary"
   7  	"fmt"
   8  	"os"
   9  )
  10  
  11  const fileSize = 12
  12  
  13  type Checkpoint struct {
  14  	path string
  15  	ser  uint64
  16  }
  17  
  18  func Open(path string) (*Checkpoint, error) {
  19  	c := &Checkpoint{path: path}
  20  	data, err := os.ReadFile(path)
  21  	if err != nil {
  22  		if os.IsNotExist(err) {
  23  			return c, nil
  24  		}
  25  		return nil, err
  26  	}
  27  	if len(data) != fileSize {
  28  		fmt.Fprintf(os.Stderr, "checkpoint: invalid size %d, treating as zero\n", len(data))
  29  		return c, nil
  30  	}
  31  	ser := binary.BigEndian.Uint64(data[:8])
  32  	stored := binary.BigEndian.Uint32(data[8:12])
  33  	if crc32ieee(data[:8]) != stored {
  34  		fmt.Fprintf(os.Stderr, "checkpoint: CRC mismatch, treating as zero\n")
  35  		return c, nil
  36  	}
  37  	c.ser = ser
  38  	return c, nil
  39  }
  40  
  41  func (c *Checkpoint) Get() uint64 { return c.ser }
  42  
  43  func (c *Checkpoint) Set(ser uint64) error {
  44  	var buf [fileSize]byte
  45  	binary.BigEndian.PutUint64(buf[:8], ser)
  46  	binary.BigEndian.PutUint32(buf[8:], crc32ieee(buf[:8]))
  47  
  48  	tmp := c.path | ".tmp"
  49  	f, err := os.Create(tmp)
  50  	if err != nil {
  51  		return err
  52  	}
  53  	if _, err := f.Write(buf[:]); err != nil {
  54  		f.Close()
  55  		os.Remove(tmp)
  56  		return err
  57  	}
  58  	if err := f.Sync(); err != nil {
  59  		f.Close()
  60  		os.Remove(tmp)
  61  		return err
  62  	}
  63  	f.Close()
  64  	if err := os.Rename(tmp, c.path); err != nil {
  65  		return err
  66  	}
  67  	c.ser = ser
  68  	return nil
  69  }
  70  
  71  // CRC32-IEEE (polynomial 0xEDB88320 reflected).
  72  func crc32ieee(data []byte) uint32 {
  73  	crc := uint32(0xFFFFFFFF)
  74  	for _, b := range data {
  75  		crc ^= uint32(b)
  76  		for i := 0; i < 8; i++ {
  77  			if crc&1 != 0 {
  78  				crc = (crc >> 1) ^ 0xEDB88320
  79  			} else {
  80  				crc >>= 1
  81  			}
  82  		}
  83  	}
  84  	return crc ^ 0xFFFFFFFF
  85  }
  86