Skip to content

Commit

Permalink
Move stack/storage to where they're used
Browse files Browse the repository at this point in the history
  • Loading branch information
chriso committed Sep 25, 2023
1 parent a8648b4 commit 0d4850a
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 127 deletions.
122 changes: 121 additions & 1 deletion coroutine_durable.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

package coroutine

import "github.com/stealthrocket/coroutine/internal/serde"
import (
"slices"
"strconv"

"github.com/stealthrocket/coroutine/internal/serde"
)

// New creates a new coroutine which executes f as entry point.
func New[R, S any](f func()) Coroutine[R, S] {
Expand Down Expand Up @@ -120,3 +125,118 @@ func (c *Context[R, S]) Unmarshal(b []byte) (int, error) {
sn := start - len(b)
return sn, nil
}

// Stack is the call stack for a coroutine.
type Stack struct {
// FP is the frame pointer. Functions always use the Frame
// located at Frames[FP].
FP int

// Frames is the set of stack frames.
Frames []Frame
}

// Push prepares the stack for an impending function call.
//
// The stack's frame pointer is incremented, and the stack is resized
// to make room for a new frame if the caller is on the topmost frame.
//
// If the caller is not on the topmost frame it means that a coroutine
// is being resumed and the next frame is already present on the stack.
//
// The Frame is returned by value rather than by reference, since the
// stack's underlying frame backing array might change. Callers
// intending to serialize the stack should call Store(fp, frame) for each
// frame during stack unwinding.
func (s *Stack) Push() (frame Frame, fp int) {
if s.isTop() {
s.Frames = append(s.Frames, Frame{})
}
s.FP++
return s.Frames[s.FP], s.FP
}

// Pop pops the topmost stack frame after a function call.
func (s *Stack) Pop() {
if !s.isTop() {
panic("pop when caller is not on topmost frame")
}
s.Frames = s.Frames[:len(s.Frames)-1]
s.FP--
}

// Store stores a frame at the specified index.
func (s *Stack) Store(i int, f Frame) {
if i < 0 || i >= len(s.Frames) {
panic("invalid frame index")
}
s.Frames[i] = f
}

func (s *Stack) isTop() bool {
return s.FP == len(s.Frames)-1
}

// Frame is a stack frame.
//
// A frame is created when a function is called and torn down after it
// returns. A Frame holds the position of execution within that function,
// and any Serializable objects that it uses or returns.
type Frame struct {
// IP is the instruction pointer.
IP int

// Storage holds the Serializable objects on the frame.
Storage
}

// Storage is a sparse collection of Serializable objects.
type Storage struct {
// This is private so that the data structure is allowed to switch
// the in-memory representation dynamically (e.g. a map[int]Serializable
// may be more efficient for very sparse maps).
objects []any
}

// NewStorage creates a Storage.
func NewStorage(objects []any) Storage {
return Storage{objects: objects}
}

// Has is true if an object is defined for a specific index.
func (v *Storage) Has(i int) bool {
return i >= 0 && i < len(v.objects)
}

// Get gets the object for a specific index.
func (v *Storage) Get(i int) any {
if !v.Has(i) {
panic("missing object " + strconv.Itoa(i))
}
return v.objects[i]
}

// Delete gets the object for a specific index.
func (v *Storage) Delete(i int) {
if !v.Has(i) {
panic("missing object " + strconv.Itoa(i))
}
v.objects[i] = nil
}

// Set sets the object for a specific index.
func (v *Storage) Set(i int, value any) {
if n := i + 1; n > len(v.objects) {
v.objects = slices.Grow(v.objects, n-len(v.objects))
v.objects = v.objects[:n]
}
v.objects[i] = value
}

func (v *Storage) shrink() {
i := len(v.objects) - 1
for i >= 0 && v.objects[i] == nil {
i--
}
v.objects = v.objects[:i+1]
}
67 changes: 0 additions & 67 deletions stack_durable.go

This file was deleted.

59 changes: 0 additions & 59 deletions storage_durable.go

This file was deleted.

0 comments on commit 0d4850a

Please sign in to comment.