From 65199a227482f677a2bc3abb67afdc9bdc650dc3 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Thu, 20 Jan 2022 09:50:25 +0100 Subject: [PATCH] op,internal/ops: support CallOps without macros A recorded macro is prefixed with an internal macro op that stores the end of the macro. The end is used to efficiently skip the macro when not calling it. The call a macro, the CallOp stores the start position of the macro. To support seamless wrapping of Ops lists, this change removes the dependency on the macro op prefix from CallOp. Internal code can now call an Ops like this: var ops op.Ops var wrapper op.Ops ops.AddCall(&wrapper.Internal, &ops.Internal, ops.PC{}, ops.PCFor(&ops.Internal)) References: https://todo.sr.ht/~eliasnaur/gio/318 Signed-off-by: Elias Naur --- internal/ops/ops.go | 6 ++++-- internal/ops/reader.go | 40 +++++++++++++++++++++------------------- op/op.go | 11 +++++++---- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/internal/ops/ops.go b/internal/ops/ops.go index b3201009..37f3b193 100644 --- a/internal/ops/ops.go +++ b/internal/ops/ops.go @@ -119,7 +119,7 @@ const ( const ( TypeMacroLen = 1 + 4 + 4 - TypeCallLen = 1 + 4 + 4 + TypeCallLen = 1 + 4 + 4 + 4 + 4 TypeDeferLen = 1 TypePushTransformLen = 1 + 4*6 TypeTransformLen = 1 + 1 + 4*6 @@ -240,12 +240,14 @@ func FillMacro(o *Ops, startPC PC) { bo.PutUint32(data[5:], uint32(pc.refs)) } -func AddCall(o *Ops, callOps *Ops, pc PC) { +func AddCall(o *Ops, callOps *Ops, pc PC, end PC) { data := Write1(o, TypeCallLen, callOps) data[0] = byte(TypeCall) bo := binary.LittleEndian bo.PutUint32(data[1:], uint32(pc.data)) bo.PutUint32(data[5:], uint32(pc.refs)) + bo.PutUint32(data[9:], uint32(end.data)) + bo.PutUint32(data[13:], uint32(end.refs)) } func PushOp(o *Ops, kind StackKind) (StackID, int) { diff --git a/internal/ops/reader.go b/internal/ops/reader.go index 99b8cb64..63ec9cc3 100644 --- a/internal/ops/reader.go +++ b/internal/ops/reader.go @@ -32,8 +32,9 @@ type Key struct { // Shadow of op.MacroOp. type macroOp struct { - ops *Ops - pc PC + ops *Ops + start PC + end PC } // PC is an instruction counter for an operation list. @@ -52,6 +53,13 @@ type opMacroDef struct { endpc PC } +func (pc PC) Add(op OpType) PC { + return PC{ + data: pc.data + op.Size(), + refs: pc.refs + op.NumRefs(), + } +} + // Reset start reading from the beginning of ops. func (r *Reader) Reset(ops *Ops) { r.ResetAt(ops, PC{}) @@ -128,24 +136,16 @@ func (r *Reader) Decode() (EncodedOp, bool) { } var op macroOp op.decode(data, refs) - macroData := op.ops.data[op.pc.data:] - if OpType(macroData[0]) != TypeMacro { - panic("invalid macro reference") - } - var opDef opMacroDef - opDef.decode(macroData[:TypeMacro.Size()]) retPC := r.pc retPC.data += n retPC.refs += nrefs r.stack = append(r.stack, macro{ ops: r.ops, retPC: retPC, - endPC: opDef.endpc, + endPC: op.end, }) r.ops = op.ops - r.pc = op.pc - r.pc.data += TypeMacro.Size() - r.pc.refs += TypeMacro.NumRefs() + r.pc = op.start continue case TypeMacro: var op opMacroDef @@ -164,7 +164,7 @@ func (op *opMacroDef) decode(data []byte) { panic("invalid op") } bo := binary.LittleEndian - data = data[:9] + data = data[:TypeMacroLen] dataIdx := int(int32(bo.Uint32(data[1:]))) refsIdx := int(int32(bo.Uint32(data[5:]))) *op = opMacroDef{ @@ -179,15 +179,17 @@ func (m *macroOp) decode(data []byte, refs []interface{}) { if OpType(data[0]) != TypeCall { panic("invalid op") } - data = data[:9] + data = data[:TypeCallLen] bo := binary.LittleEndian - dataIdx := int(int32(bo.Uint32(data[1:]))) - refsIdx := int(int32(bo.Uint32(data[5:]))) *m = macroOp{ ops: refs[0].(*Ops), - pc: PC{ - data: dataIdx, - refs: refsIdx, + start: PC{ + data: int(int32(bo.Uint32(data[1:]))), + refs: int(int32(bo.Uint32(data[5:]))), + }, + end: PC{ + data: int(int32(bo.Uint32(data[9:]))), + refs: int(int32(bo.Uint32(data[13:]))), }, } } diff --git a/op/op.go b/op/op.go index 5d595a76..8251123c 100644 --- a/op/op.go +++ b/op/op.go @@ -92,8 +92,9 @@ type MacroOp struct { // CallOp invokes the operations recorded by Record. type CallOp struct { // Ops is the list of operations to invoke. - ops *ops.Ops - pc ops.PC + ops *ops.Ops + start ops.PC + end ops.PC } // InvalidateOp requests a redraw at the given time. Use @@ -165,7 +166,9 @@ func (m MacroOp) Stop() CallOp { ops.FillMacro(m.ops, m.pc) return CallOp{ ops: m.ops, - pc: m.pc, + // Skip macro header. + start: m.pc.Add(ops.TypeMacro), + end: ops.PCFor(m.ops), } } @@ -176,7 +179,7 @@ func (c CallOp) Add(o *Ops) { if c.ops == nil { return } - ops.AddCall(&o.Internal, c.ops, c.pc) + ops.AddCall(&o.Internal, c.ops, c.start, c.end) } func (r InvalidateOp) Add(o *Ops) {