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) (cp *Checkpoint, derr 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() (n uint64) { return c.ser }
  42  
  43  func (c *Checkpoint) Set(ser uint64) (err 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  		os.Remove(tmp)
  66  		return err
  67  	}
  68  	c.ser = ser
  69  	return nil
  70  }
  71  
  72  // CRC32-IEEE (polynomial 0xEDB88320 reflected).
  73  func crc32ieee(data []byte) (n uint32) {
  74  	crc := uint32(0xFFFFFFFF)
  75  	for _, b := range data {
  76  		crc ^= uint32(b)
  77  		for i := 0; i < 8; i++ {
  78  			if crc&1 != 0 {
  79  				crc = (crc >> 1) ^ 0xEDB88320
  80  			} else {
  81  				crc >>= 1
  82  			}
  83  		}
  84  	}
  85  	return crc ^ 0xFFFFFFFF
  86  }
  87