~eliasnaur/gio

06217c532056c0bb3c5a808b403bdca108f84e74 — Elias Naur 4 years ago 9114dbe
op: introduce CallOp

We'd like to improve the API of Flex, Stack and similar layouts
that use MacroOps internall. Unfortunately, the

  func (m MacroOp) Add(o *Ops)

method causes the MacroOp to be allocated on the heap, ruining the
nice garbage-free property of layouts.

Fortunately, layouts don't need the feature that caused the heap
allocation: invoking operation lists different than the current.

CallOp separates the invoke-different-list semantic from MacroOp,
in preparation for removing the feature from MacroOp.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
3 files changed, 58 insertions(+), 1 deletions(-)

M internal/opconst/ops.go
M internal/ops/reader.go
M op/op.go
M internal/opconst/ops.go => internal/opconst/ops.go +4 -1
@@ 26,6 26,7 @@ const (
	TypeAux
	TypeClip
	TypeProfile
	TypeCall
)

const (


@@ 47,6 48,7 @@ const (
	TypeAuxLen          = 1
	TypeClipLen         = 1 + 4*4
	TypeProfileLen      = 1
	TypeCallLen         = 1
)

func (t OpType) Size() int {


@@ 69,12 71,13 @@ func (t OpType) Size() int {
		TypeAuxLen,
		TypeClipLen,
		TypeProfileLen,
		TypeCallLen,
	}[t-firstOpIndex]
}

func (t OpType) NumRefs() int {
	switch t {
	case TypeMacro, TypeKeyInput, TypePointerInput, TypeProfile:
	case TypeMacro, TypeKeyInput, TypePointerInput, TypeProfile, TypeCall:
		return 1
	case TypeImage:
		return 2

M internal/ops/reader.go => internal/ops/reader.go +32 -0
@@ 38,6 38,11 @@ type macroOp struct {
	pc      pc
}

// Shadow of op.CallOp.
type callOp struct {
	ops *op.Ops
}

type pc struct {
	data int
	refs int


@@ 94,6 99,24 @@ func (r *Reader) Decode() (EncodedOp, bool) {
			block := r.stack[len(r.stack)-1]
			n += block.endPC.data - r.pc.data - opconst.TypeAuxLen
			data = data[:n]
		case opconst.TypeCall:
			var op callOp
			op.decode(data, refs)
			endPC := pc{
				data: len(op.ops.Data()),
				refs: len(op.ops.Refs()),
			}
			retPC := r.pc
			retPC.data += n
			retPC.refs += nrefs
			r.stack = append(r.stack, macro{
				ops:   r.ops,
				retPC: retPC,
				endPC: endPC,
			})
			r.pc = pc{}
			r.ops = op.ops
			continue
		case opconst.TypeMacro:
			var op macroOp
			op.decode(data, refs)


@@ 148,6 171,15 @@ func (op *opMacroDef) decode(data []byte) {
	}
}

func (m *callOp) decode(data []byte, refs []interface{}) {
	if opconst.OpType(data[0]) != opconst.TypeCall {
		panic("invalid op")
	}
	*m = callOp{
		ops: refs[0].(*op.Ops),
	}
}

func (m *macroOp) decode(data []byte, refs []interface{}) {
	if opconst.OpType(data[0]) != opconst.TypeMacro {
		panic("invalid op")

M op/op.go => op/op.go +22 -0
@@ 45,6 45,12 @@ The StackOp saves the current state to the state stack and restores it later:
	// Restore the previous transform.
	stack.Pop()

The CallOp invokes another operation list:

	ops := new(op.Ops)
	ops2 := new(op.Ops)
	op.CallOp{Ops: ops2}.Add(ops)

The MacroOp records a list of operations to be executed later:

	ops := new(op.Ops)


@@ 105,6 111,13 @@ type MacroOp struct {
	pc        pc
}

// CallOp invokes all the operations from a separate
// operations list.
type CallOp struct {
	// Ops is the list of operations to invoke.
	Ops *Ops
}

// InvalidateOp requests a redraw at the given time. Use
// the zero value to request an immediate redraw.
type InvalidateOp struct {


@@ 134,6 147,15 @@ type pc struct {
	refs int
}

// Add the call to the operation list.
func (c CallOp) Add(o *Ops) {
	if c.Ops == nil {
		return
	}
	data := o.Write(opconst.TypeCallLen, c.Ops)
	data[0] = byte(opconst.TypeCall)
}

// Push (save) the current operations state.
func (s *StackOp) Push(o *Ops) {
	if s.active {