package mem
type Mem interface {
Allocate(kind CellKind) (Ptr, error)
AllocateAt(ptr Ptr, kind CellKind) error
Set(ptr Ptr, v CellData) error
Get(ptr Ptr) (CellData, error)
Is(ptr Ptr, kind CellKind) bool
Kind(ptr Ptr) CellKind
Retain(ptr Ptr) error
Release(ptr Ptr) error
RefCount(ptr Ptr) int
}
type memImpl struct {
cells []cell
free []Ptr
}
func New() Mem {
cells := make([]cell, 1)
// Reserve NullPtr
cells[NullPtr].kind = CellKindForbidden
return &memImpl{
cells: cells,
free: make([]Ptr, 0),
}
}
func (m *memImpl) Allocate(kind CellKind) (Ptr, error) {
if kind == CellKindForbidden || kind == CellKindEmpty {
return NullPtr, ErrInvalidCellKind{kind}
}
if len(m.free) > 0 {
idx := m.free[len(m.free)-1]
m.free = m.free[:len(m.free)-1]
if m.cells[idx].kind != CellKindEmpty {
// This should never happen.
panic("cell marked as free was not empty")
}
m.cells[idx] = cell{kind: kind, refs: 1}
return Ptr(idx), nil
} else {
if len(m.cells) > 10000 {
return NullPtr, ErrMemOverflow
}
idx := len(m.cells)
m.cells = append(m.cells, cell{kind: kind, refs: 1})
return Ptr(idx), nil
}
}
func (m *memImpl) AllocateAt(ptr Ptr, kind CellKind) error {
if kind == CellKindForbidden || kind == CellKindEmpty {
return ErrInvalidCellKind{kind}
}
if ptr < Ptr(len(m.cells)) && m.cells[ptr].kind != CellKindEmpty {
return ErrInvalidMemAccess{ptr}
}
if ptr > 10000 {
return ErrMemOverflow
}
if ptr >= Ptr(len(m.cells)) {
m.cells = append(m.cells, make([]cell, ptr-Ptr(len(m.cells))+1)...)
}
m.cells[ptr] = cell{kind: kind, refs: 1}
// Remove cell from free list, if it was there.
for i, f := range m.free {
if f == ptr {
m.free = append(m.free[:i], m.free[i+1:]...)
break
}
}
return nil
}
func (m *memImpl) Set(ptr Ptr, v CellData) error {
if err := m.validPtr(ptr); err != nil {
return err
}
if m.cells[ptr].kind != v.MatchingCellKind() {
return ErrDifferingCellKind{Ptr: ptr, Expected: m.cells[ptr].kind, Got: v.MatchingCellKind()}
}
m.cells[ptr].data = v
return nil
}
func (m *memImpl) Get(ptr Ptr) (CellData, error) {
if err := m.validPtr(ptr); err != nil {
return nil, err
}
return m.cells[ptr].data, nil
}
func (m *memImpl) Is(ptr Ptr, kind CellKind) bool {
if ptr >= Ptr(len(m.cells)) {
return kind == CellKindEmpty
}
return m.cells[ptr].kind == kind
}
func (m *memImpl) Kind(ptr Ptr) CellKind {
if ptr >= Ptr(len(m.cells)) {
return CellKindEmpty
}
return m.cells[ptr].kind
}
func (m *memImpl) Retain(ptr Ptr) error {
if err := m.validPtr(ptr); err != nil {
return err
}
m.cells[ptr].refs++
return nil
}
func (m *memImpl) Release(ptr Ptr) error {
if err := m.validPtr(ptr); err != nil {
return err
}
m.cells[ptr].refs--
if m.cells[ptr].refs == 0 {
c := m.cells[ptr].data
if c != nil {
if err := c.DropCell(m); err != nil {
return err
}
}
m.cells[ptr] = cell{}
m.free = append(m.free, ptr)
}
return nil
}
func (m *memImpl) RefCount(ptr Ptr) int {
if err := m.validPtr(ptr); err != nil {
return 0
}
return m.cells[ptr].refs
}
func (m *memImpl) validPtr(ptr Ptr) error {
if ptr >= Ptr(len(m.cells)) {
return ErrInvalidMemAccess{ptr}
}
kind := m.cells[ptr].kind
if kind == CellKindForbidden || kind == CellKindEmpty {
return ErrInvalidMemAccess{ptr}
}
return nil
}