Skip to content

Instantly share code, notes, and snippets.

@robskillington
Created May 22, 2017 18:39
Show Gist options
  • Save robskillington/7d4af5e6b52987637c8fbc6ee0ace1eb to your computer and use it in GitHub Desktop.
Save robskillington/7d4af5e6b52987637c8fbc6ee0ace1eb to your computer and use it in GitHub Desktop.
A value pack and unpacker
package valuepack
import (
"container/list"
"fmt"
)
type value struct {
metadata ValueMetadata
elem interface{}
lru *list.Element
}
type ValueMetadata struct {
ID int64
Version int64
}
type Packer struct {
values []*value
lru *list.List
maxValues int64
}
type ReusedType int8
const (
NotReused ReusedType = iota
Reused
)
func NewPacker(maxValues int) *Packer {
return &Packer{maxValues: int64(maxValues), lru: list.New()}
}
func (p *Packer) Pack(metadata ValueMetadata) (ValueMetadata, ReusedType) {
if metadata.ID == 0 && metadata.Version == 0 {
// Always an insert, no cached metadata about this value
return p.insert()
}
if metadata.Version != p.values[metadata.ID].metadata.Version {
// This was cached but was reused
return p.insert()
}
// Cached and not reused
v := p.values[metadata.ID]
p.lru.MoveToBack(v.lru)
return metadata, NotReused
}
func (p *Packer) insert() (ValueMetadata, ReusedType) {
currLen := int64(len(p.values))
if currLen < p.maxValues {
// Not reused
metadata := ValueMetadata{ID: currLen, Version: 1}
v := &value{metadata: metadata}
p.values = append(p.values, v)
v.lru = p.lru.PushBack(v)
return metadata, NotReused
}
// Reused
v := p.lru.Front().Value.(*value)
p.lru.MoveToBack(v.lru)
id := v.metadata.ID
p.values[id].metadata.Version++
return p.values[id].metadata, Reused
}
type Unpacker struct {
values []*value
}
func NewUnpacker() *Unpacker {
return &Unpacker{}
}
func (p *Unpacker) Seen(metadata ValueMetadata) bool {
if metadata.ID >= int64(len(p.values)) {
// Would cause an insertion
return false
}
return metadata.Version == p.values[metadata.ID].metadata.Version
}
func (p *Unpacker) UnpackSeen(metadata ValueMetadata) (interface{}, error) {
if metadata.ID >= int64(len(p.values)) {
return nil, fmt.Errorf("not seen %d", metadata.ID)
}
v := p.values[metadata.ID]
actualMetadata := v.metadata
if metadata.Version != actualMetadata.Version {
return nil, fmt.Errorf("not seen %d at version %d, actual version %d", metadata.ID, metadata.Version, actualMetadata.Version)
}
return v.elem, nil
}
func (p *Unpacker) StoreUnseen(metadata ValueMetadata, elem interface{}) error {
if metadata.ID >= int64(len(p.values)) {
// Always an insert
for int64(len(p.values))-1 != metadata.ID {
// Insert missing
p.values = append(p.values, nil)
}
p.values = append(p.values, &value{metadata: metadata, elem: elem})
return nil
}
v := p.values[metadata.ID]
if v.metadata.Version != metadata.Version-1 {
return fmt.Errorf("expected to eject version %d with %d, instead tried to eject with %d", v.metadata.Version, v.metadata.Version+1, metadata.Version)
}
v.elem = elem
v.metadata.Version = metadata.Version
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment