summary refs log tree commit diff
path: root/modules/foundation/default.nix
blob: 10ec503b8562fc61401f96eda3cbc56adbca005b (plain)
1
2
3
4
5
{ ... }:

{
  imports = [ ./services.nix ];
}
/a>
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
		c.DropCell(m)

		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
}