Created
May 22, 2017 18:39
-
-
Save robskillington/7d4af5e6b52987637c8fbc6ee0ace1eb to your computer and use it in GitHub Desktop.
A value pack and unpacker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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