aboutsummaryrefslogtreecommitdiffstats
path: root/encoding.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--encoding.go194
1 files changed, 194 insertions, 0 deletions
diff --git a/encoding.go b/encoding.go
new file mode 100644
index 0000000..ff07d2d
--- /dev/null
+++ b/encoding.go
@@ -0,0 +1,194 @@
+package cache
+
+import (
+ "bufio"
+ "encoding/binary"
+ "io"
+ "time"
+)
+
+type Encoder struct {
+ *bufio.Writer
+ buf []byte
+}
+
+func NewEncoder(w io.Writer) *Encoder {
+ return &Encoder{
+ Writer: bufio.NewWriter(w),
+ buf: make([]byte, 8),
+ }
+}
+
+func (e *Encoder) EncodeUint64(val uint64) error {
+ binary.LittleEndian.PutUint64(e.buf, val)
+ _, err := e.Write(e.buf)
+ return err
+}
+
+func (e *Encoder) EncodeTime(val time.Time) error {
+ return e.EncodeUint64(uint64(val.Unix()))
+}
+
+func (e *Encoder) EncodeBytes(val []byte) error {
+ if err := e.EncodeUint64(uint64(len(val))); err != nil {
+ return err
+ }
+
+ _, err := e.Write(val)
+ return err
+}
+
+func (e *Encoder) EncodeNode(n *Node) error {
+ if err := e.EncodeUint64(n.Hash); err != nil {
+ return err
+ }
+
+ if err := e.EncodeTime(n.Expiration); err != nil {
+ return err
+ }
+
+ if err := e.EncodeUint64(n.Access); err != nil {
+ return err
+ }
+
+ if err := e.EncodeBytes(n.Key); err != nil {
+ return err
+ }
+
+ if err := e.EncodeBytes(n.Value); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+type Decoder struct {
+ *bufio.Reader
+ buf []byte
+}
+
+func NewDecoder(r io.Reader) *Decoder {
+ return &Decoder{
+ Reader: bufio.NewReader(r),
+ buf: make([]byte, 8),
+ }
+}
+
+func (d *Decoder) DecodeUint64() (uint64, error) {
+ _, err := io.ReadFull(d, d.buf)
+ if err != nil {
+ return 0, err
+ }
+ return binary.LittleEndian.Uint64(d.buf), nil
+}
+
+func (d *Decoder) DecodeTime() (time.Time, error) {
+ ts, err := d.DecodeUint64()
+ if err != nil {
+ return zero[time.Time](), err
+ }
+ return time.Unix(int64(ts), 0), nil
+}
+
+func (d *Decoder) DecodeBytes() ([]byte, error) {
+ lenVal, err := d.DecodeUint64()
+ if err != nil {
+ return nil, err
+ }
+ data := make([]byte, lenVal)
+ _, err = io.ReadFull(d, data)
+ return data, err
+}
+
+func (d *Decoder) DecodeNodes() (*Node, error) {
+ n := &Node{}
+
+ hash, err := d.DecodeUint64()
+ if err != nil {
+ return nil, err
+ }
+ n.Hash = hash
+
+ expiration, err := d.DecodeTime()
+ if err != nil {
+ return nil, err
+ }
+ n.Expiration = expiration
+
+ access, err := d.DecodeUint64()
+ if err != nil {
+ return nil, err
+ }
+ n.Access = access
+
+ n.Key, err = d.DecodeBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ n.Value, err = d.DecodeBytes()
+ if err != nil {
+ return nil, err
+ }
+ return n, err
+}
+
+func (s *Store) Snapshot(w io.WriteSeeker) error {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ w.Seek(0, io.SeekStart)
+ wr := NewEncoder(w)
+
+ wr.EncodeUint64(s.lenght)
+
+ for v := s.evict.EvictNext; v != &s.evict; v = v.EvictNext {
+ if err := wr.EncodeNode(v); err != nil {
+ return err
+ }
+ }
+ wr.Flush()
+ return nil
+}
+
+func (s *Store) LoadSnapshot(r io.ReadSeeker) error {
+ r.Seek(0, io.SeekStart)
+ rr := NewDecoder(r)
+
+ lenght, err := rr.DecodeUint64()
+ if err != nil {
+ return err
+ }
+ s.lenght = lenght
+
+ k := 128
+ for k < int(s.lenght) {
+ k = k << 1
+ }
+
+ s.bucket = make([]Node, k)
+ for range s.lenght {
+ v, err := rr.DecodeNodes()
+ if err != nil {
+ return err
+ }
+
+ idx := v.Hash % uint64(len(s.bucket))
+
+ bucket := &s.bucket[idx]
+ lazyInitBucket(bucket)
+
+ v.HashPrev = bucket
+ v.HashNext = v.HashPrev.HashNext
+ v.HashNext.HashPrev = v
+ v.HashPrev.HashNext = v
+
+ v.EvictPrev = &s.evict
+ v.EvictNext = v.EvictPrev.EvictNext
+ v.EvictNext.EvictPrev = v
+ v.EvictPrev.EvictNext = v
+
+ s.cost = s.cost + uint64(len(v.Key)) + uint64(len(v.Value))
+ }
+ return nil
+}