op: move Ops internal methods and state to internal package ops

Merge package opconsts into ops as well; it only existed to break
import cycles.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-10-04 15:44:48 +02:00
parent 936c266b03
commit 391725b9d0
18 changed files with 558 additions and 561 deletions
-172
View File
@@ -1,172 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
package opconst
type OpType byte
// Start at a high number for easier debugging.
const firstOpIndex = 200
const (
TypeMacro OpType = iota + firstOpIndex
TypeCall
TypeDefer
TypeTransform
TypePopTransform
TypeInvalidate
TypeImage
TypePaint
TypeColor
TypeLinearGradient
TypeArea
TypePopArea
TypePointerInput
TypeClipboardRead
TypeClipboardWrite
TypeKeyInput
TypeKeyFocus
TypeKeySoftKeyboard
TypeSave
TypeLoad
TypeAux
TypeClip
TypePopClip
TypeProfile
TypeCursor
TypePath
TypeStroke
)
const (
TypeMacroLen = 1 + 4 + 4
TypeCallLen = 1 + 4 + 4
TypeDeferLen = 1
TypeTransformLen = 1 + 1 + 4*6
TypePopTransformLen = 1
TypeRedrawLen = 1 + 8
TypeImageLen = 1
TypePaintLen = 1
TypeColorLen = 1 + 4
TypeLinearGradientLen = 1 + 8*2 + 4*2
TypeAreaLen = 1 + 1 + 1 + 4*4
TypePopAreaLen = 1
TypePointerInputLen = 1 + 1 + 1 + 2*4 + 2*4
TypeClipboardReadLen = 1
TypeClipboardWriteLen = 1
TypeKeyInputLen = 1 + 1
TypeKeyFocusLen = 1 + 1
TypeKeySoftKeyboardLen = 1 + 1
TypeSaveLen = 1 + 4
TypeLoadLen = 1 + 4
TypeAuxLen = 1
TypeClipLen = 1 + 4*4 + 1 + 1
TypePopClipLen = 1
TypeProfileLen = 1
TypeCursorLen = 1 + 1
TypePathLen = 8 + 1
TypeStrokeLen = 1 + 4
)
func (t OpType) Size() int {
return [...]int{
TypeMacroLen,
TypeCallLen,
TypeDeferLen,
TypeTransformLen,
TypePopTransformLen,
TypeRedrawLen,
TypeImageLen,
TypePaintLen,
TypeColorLen,
TypeLinearGradientLen,
TypeAreaLen,
TypePopAreaLen,
TypePointerInputLen,
TypeClipboardReadLen,
TypeClipboardWriteLen,
TypeKeyInputLen,
TypeKeyFocusLen,
TypeKeySoftKeyboardLen,
TypeSaveLen,
TypeLoadLen,
TypeAuxLen,
TypeClipLen,
TypePopClipLen,
TypeProfileLen,
TypeCursorLen,
TypePathLen,
TypeStrokeLen,
}[t-firstOpIndex]
}
func (t OpType) NumRefs() int {
switch t {
case TypeKeyInput, TypeKeyFocus, TypePointerInput, TypeProfile, TypeCall, TypeClipboardRead, TypeClipboardWrite, TypeCursor:
return 1
case TypeImage:
return 2
default:
return 0
}
}
func (t OpType) String() string {
switch t {
case TypeMacro:
return "Macro"
case TypeCall:
return "Call"
case TypeDefer:
return "Defer"
case TypeTransform:
return "Transform"
case TypePopTransform:
return "PopTransform"
case TypeInvalidate:
return "Invalidate"
case TypeImage:
return "Image"
case TypePaint:
return "Paint"
case TypeColor:
return "Color"
case TypeLinearGradient:
return "LinearGradient"
case TypeArea:
return "Area"
case TypePopArea:
return "PopArea"
case TypePointerInput:
return "PointerInput"
case TypeClipboardRead:
return "ClipboardRead"
case TypeClipboardWrite:
return "ClipboardWrite"
case TypeKeyInput:
return "KeyInput"
case TypeKeyFocus:
return "KeyFocus"
case TypeKeySoftKeyboard:
return "KeySoftKeyboard"
case TypeSave:
return "Save"
case TypeLoad:
return "Load"
case TypeAux:
return "Aux"
case TypeClip:
return "Clip"
case TypePopClip:
return "PopClip"
case TypeProfile:
return "Profile"
case TypeCursor:
return "Cursor"
case TypePath:
return "Path"
case TypeStroke:
return "Stroke"
default:
panic("unnkown OpType")
}
}
+351 -4
View File
@@ -8,10 +8,250 @@ import (
"gioui.org/f32"
"gioui.org/internal/byteslice"
"gioui.org/internal/opconst"
"gioui.org/internal/scene"
)
type Ops struct {
// version is incremented at each Reset.
version int
// data contains the serialized operations.
data []byte
// refs hold external references for operations.
refs []interface{}
// nextStateID is the id allocated for the next
// StateOp.
nextStateID int
macroStack stack
stacks [3]stack
}
type OpType byte
// Start at a high number for easier debugging.
const firstOpIndex = 200
const (
TypeMacro OpType = iota + firstOpIndex
TypeCall
TypeDefer
TypePushTransform
TypeTransform
TypePopTransform
TypeInvalidate
TypeImage
TypePaint
TypeColor
TypeLinearGradient
TypeArea
TypePopArea
TypePointerInput
TypeClipboardRead
TypeClipboardWrite
TypeKeyInput
TypeKeyFocus
TypeKeySoftKeyboard
TypeSave
TypeLoad
TypeAux
TypeClip
TypePopClip
TypeProfile
TypeCursor
TypePath
TypeStroke
)
type StackID struct {
id int
prev int
}
// StateOp represents a saved operation snapshop to be restored
// later.
type StateOP struct {
id int
macroID int
ops *Ops
}
// stack tracks the integer identities of stack operations to ensure correct
// pairing of their push and pop methods.
type stack struct {
currentID int
nextID int
}
type StackKind uint8
const (
ClipStack StackKind = iota
AreaStack
TransStack
)
const (
TypeMacroLen = 1 + 4 + 4
TypeCallLen = 1 + 4 + 4
TypeDeferLen = 1
TypePushTransformLen = 1 + 4*6
TypeTransformLen = 1 + 1 + 4*6
TypePopTransformLen = 1
TypeRedrawLen = 1 + 8
TypeImageLen = 1
TypePaintLen = 1
TypeColorLen = 1 + 4
TypeLinearGradientLen = 1 + 8*2 + 4*2
TypeAreaLen = 1 + 1 + 1 + 4*4
TypePopAreaLen = 1
TypePointerInputLen = 1 + 1 + 1 + 2*4 + 2*4
TypeClipboardReadLen = 1
TypeClipboardWriteLen = 1
TypeKeyInputLen = 1 + 1
TypeKeyFocusLen = 1 + 1
TypeKeySoftKeyboardLen = 1 + 1
TypeSaveLen = 1 + 4
TypeLoadLen = 1 + 4
TypeAuxLen = 1
TypeClipLen = 1 + 4*4 + 1 + 1
TypePopClipLen = 1
TypeProfileLen = 1
TypeCursorLen = 1 + 1
TypePathLen = 8 + 1
TypeStrokeLen = 1 + 4
)
func (o *Ops) Reset() {
o.macroStack = stack{}
for i := range o.stacks {
o.stacks[i] = stack{}
}
// Leave references to the GC.
for i := range o.refs {
o.refs[i] = nil
}
o.data = o.data[:0]
o.refs = o.refs[:0]
o.nextStateID = 0
o.version++
}
func (o *Ops) Data() []byte {
return o.data
}
func (o *Ops) Refs() []interface{} {
return o.refs
}
func (o *Ops) Version() int {
return o.version
}
func (o *Ops) Write(n int) []byte {
o.data = append(o.data, make([]byte, n)...)
return o.data[len(o.data)-n:]
}
func (o *Ops) PushMacro() StackID {
return o.macroStack.push()
}
func (o *Ops) PopMacro(id StackID) {
o.macroStack.pop(id)
}
func (o *Ops) FillMacro(startPC PC) {
pc := o.PC()
// Fill out the macro definition reserved in Record.
data := o.data[startPC.data:]
data = data[:TypeMacroLen]
data[0] = byte(TypeMacro)
bo := binary.LittleEndian
bo.PutUint32(data[1:], uint32(pc.data))
bo.PutUint32(data[5:], uint32(pc.refs))
}
func (o *Ops) AddCall(callOps *Ops, pc PC) {
data := o.Write1(TypeCallLen, callOps)
data[0] = byte(TypeCall)
bo := binary.LittleEndian
bo.PutUint32(data[1:], uint32(pc.data))
bo.PutUint32(data[5:], uint32(pc.refs))
}
func (o *Ops) PushOp(kind StackKind) (StackID, int) {
return o.stacks[kind].push(), o.macroStack.currentID
}
func (o *Ops) PopOp(kind StackKind, sid StackID, macroID int) {
if o.macroStack.currentID != macroID {
panic("stack push and pop must not cross macro boundary")
}
o.stacks[kind].pop(sid)
}
func (o *Ops) Write1(n int, ref1 interface{}) []byte {
o.data = append(o.data, make([]byte, n)...)
o.refs = append(o.refs, ref1)
return o.data[len(o.data)-n:]
}
func (o *Ops) Write2(n int, ref1, ref2 interface{}) []byte {
o.data = append(o.data, make([]byte, n)...)
o.refs = append(o.refs, ref1, ref2)
return o.data[len(o.data)-n:]
}
func (o *Ops) PC() PC {
return PC{data: len(o.data), refs: len(o.refs)}
}
func (s *stack) push() StackID {
s.nextID++
sid := StackID{
id: s.nextID,
prev: s.currentID,
}
s.currentID = s.nextID
return sid
}
func (s *stack) check(sid StackID) {
if s.currentID != sid.id {
panic("unbalanced operation")
}
}
func (s *stack) pop(sid StackID) {
s.check(sid)
s.currentID = sid.prev
}
// Save the effective transformation.
func (o *Ops) Save() StateOP {
o.nextStateID++
s := StateOP{
ops: o,
id: o.nextStateID,
macroID: o.macroStack.currentID,
}
bo := binary.LittleEndian
data := o.Write(TypeSaveLen)
data[0] = byte(TypeSave)
bo.PutUint32(data[1:], uint32(s.id))
return s
}
// load a previously saved operations state given
// its ID.
func (s StateOP) Load() {
bo := binary.LittleEndian
data := s.ops.Write(TypeLoadLen)
data[0] = byte(TypeLoad)
bo.PutUint32(data[1:], uint32(s.id))
}
func DecodeCommand(d []byte) scene.Command {
var cmd scene.Command
copy(byteslice.Uint32(cmd[:]), d)
@@ -23,7 +263,7 @@ func EncodeCommand(out []byte, cmd scene.Command) {
}
func DecodeTransform(data []byte) (t f32.Affine2D, push bool) {
if opconst.OpType(data[0]) != opconst.TypeTransform {
if OpType(data[0]) != TypeTransform {
panic("invalid op")
}
push = data[1] != 0
@@ -42,7 +282,7 @@ func DecodeTransform(data []byte) (t f32.Affine2D, push bool) {
// DecodeSave decodes the state id of a save op.
func DecodeSave(data []byte) int {
if opconst.OpType(data[0]) != opconst.TypeSave {
if OpType(data[0]) != TypeSave {
panic("invalid op")
}
bo := binary.LittleEndian
@@ -51,9 +291,116 @@ func DecodeSave(data []byte) int {
// DecodeLoad decodes the state id of a load op.
func DecodeLoad(data []byte) int {
if opconst.OpType(data[0]) != opconst.TypeLoad {
if OpType(data[0]) != TypeLoad {
panic("invalid op")
}
bo := binary.LittleEndian
return int(bo.Uint32(data[1:]))
}
func (t OpType) Size() int {
return [...]int{
TypeMacroLen,
TypeCallLen,
TypeDeferLen,
TypePushTransformLen,
TypeTransformLen,
TypePopTransformLen,
TypeRedrawLen,
TypeImageLen,
TypePaintLen,
TypeColorLen,
TypeLinearGradientLen,
TypeAreaLen,
TypePopAreaLen,
TypePointerInputLen,
TypeClipboardReadLen,
TypeClipboardWriteLen,
TypeKeyInputLen,
TypeKeyFocusLen,
TypeKeySoftKeyboardLen,
TypeSaveLen,
TypeLoadLen,
TypeAuxLen,
TypeClipLen,
TypePopClipLen,
TypeProfileLen,
TypeCursorLen,
TypePathLen,
TypeStrokeLen,
}[t-firstOpIndex]
}
func (t OpType) NumRefs() int {
switch t {
case TypeKeyInput, TypeKeyFocus, TypePointerInput, TypeProfile, TypeCall, TypeClipboardRead, TypeClipboardWrite, TypeCursor:
return 1
case TypeImage:
return 2
default:
return 0
}
}
func (t OpType) String() string {
switch t {
case TypeMacro:
return "Macro"
case TypeCall:
return "Call"
case TypeDefer:
return "Defer"
case TypePushTransform:
return "PushTransform"
case TypeTransform:
return "Transform"
case TypePopTransform:
return "PopTransform"
case TypeInvalidate:
return "Invalidate"
case TypeImage:
return "Image"
case TypePaint:
return "Paint"
case TypeColor:
return "Color"
case TypeLinearGradient:
return "LinearGradient"
case TypeArea:
return "Area"
case TypePopArea:
return "PopArea"
case TypePointerInput:
return "PointerInput"
case TypeClipboardRead:
return "ClipboardRead"
case TypeClipboardWrite:
return "ClipboardWrite"
case TypeKeyInput:
return "KeyInput"
case TypeKeyFocus:
return "KeyFocus"
case TypeKeySoftKeyboard:
return "KeySoftKeyboard"
case TypeSave:
return "Save"
case TypeLoad:
return "Load"
case TypeAux:
return "Aux"
case TypeClip:
return "Clip"
case TypePopClip:
return "PopClip"
case TypeProfile:
return "Profile"
case TypeCursor:
return "Cursor"
case TypePath:
return "Path"
case TypeStroke:
return "Stroke"
default:
panic("unnkown OpType")
}
}
+20 -32
View File
@@ -4,17 +4,14 @@ package ops
import (
"encoding/binary"
"gioui.org/internal/opconst"
"gioui.org/op"
)
// Reader parses an ops list.
type Reader struct {
pc PC
stack []macro
ops *op.Ops
deferOps op.Ops
ops *Ops
deferOps Ops
deferDone bool
}
@@ -28,14 +25,14 @@ type EncodedOp struct {
// Key is a unique key for a given op.
type Key struct {
ops *op.Ops
ops *Ops
pc int
version int
}
// Shadow of op.MacroOp.
type macroOp struct {
ops *op.Ops
ops *Ops
pc PC
}
@@ -46,7 +43,7 @@ type PC struct {
}
type macro struct {
ops *op.Ops
ops *Ops
retPC PC
endPC PC
}
@@ -56,12 +53,12 @@ type opMacroDef struct {
}
// Reset start reading from the beginning of ops.
func (r *Reader) Reset(ops *op.Ops) {
func (r *Reader) Reset(ops *Ops) {
r.ResetAt(ops, PC{})
}
// ResetAt is like Reset, except it starts reading from pc.
func (r *Reader) ResetAt(ops *op.Ops, pc PC) {
func (r *Reader) ResetAt(ops *Ops, pc PC) {
r.stack = r.stack[:0]
r.deferOps.Reset()
r.deferDone = false
@@ -69,15 +66,6 @@ func (r *Reader) ResetAt(ops *op.Ops, pc PC) {
r.ops = ops
}
// NewPC returns a PC representing the current instruction counter of
// ops.
func NewPC(ops *op.Ops) PC {
return PC{
data: len(ops.Data()),
refs: len(ops.Refs()),
}
}
func (r *Reader) Decode() (EncodedOp, bool) {
if r.ops == nil {
return EncodedOp{}, false
@@ -107,25 +95,25 @@ func (r *Reader) Decode() (EncodedOp, bool) {
continue
}
key := Key{ops: r.ops, pc: r.pc.data, version: r.ops.Version()}
t := opconst.OpType(data[0])
t := OpType(data[0])
n := t.Size()
nrefs := t.NumRefs()
data = data[:n]
refs = refs[r.pc.refs:]
refs = refs[:nrefs]
switch t {
case opconst.TypeDefer:
case TypeDefer:
deferring = true
r.pc.data += n
r.pc.refs += nrefs
continue
case opconst.TypeAux:
case TypeAux:
// An Aux operations is always wrapped in a macro, and
// its length is the remaining space.
block := r.stack[len(r.stack)-1]
n += block.endPC.data - r.pc.data - opconst.TypeAuxLen
n += block.endPC.data - r.pc.data - TypeAuxLen
data = data[:n]
case opconst.TypeCall:
case TypeCall:
if deferring {
deferring = false
// Copy macro for deferred execution.
@@ -141,11 +129,11 @@ func (r *Reader) Decode() (EncodedOp, bool) {
var op macroOp
op.decode(data, refs)
macroData := op.ops.Data()[op.pc.data:]
if opconst.OpType(macroData[0]) != opconst.TypeMacro {
if OpType(macroData[0]) != TypeMacro {
panic("invalid macro reference")
}
var opDef opMacroDef
opDef.decode(macroData[:opconst.TypeMacro.Size()])
opDef.decode(macroData[:TypeMacro.Size()])
retPC := r.pc
retPC.data += n
retPC.refs += nrefs
@@ -156,10 +144,10 @@ func (r *Reader) Decode() (EncodedOp, bool) {
})
r.ops = op.ops
r.pc = op.pc
r.pc.data += opconst.TypeMacro.Size()
r.pc.refs += opconst.TypeMacro.NumRefs()
r.pc.data += TypeMacro.Size()
r.pc.refs += TypeMacro.NumRefs()
continue
case opconst.TypeMacro:
case TypeMacro:
var op opMacroDef
op.decode(data)
r.pc = op.endpc
@@ -172,7 +160,7 @@ func (r *Reader) Decode() (EncodedOp, bool) {
}
func (op *opMacroDef) decode(data []byte) {
if opconst.OpType(data[0]) != opconst.TypeMacro {
if OpType(data[0]) != TypeMacro {
panic("invalid op")
}
bo := binary.LittleEndian
@@ -188,7 +176,7 @@ func (op *opMacroDef) decode(data []byte) {
}
func (m *macroOp) decode(data []byte, refs []interface{}) {
if opconst.OpType(data[0]) != opconst.TypeCall {
if OpType(data[0]) != TypeCall {
panic("invalid op")
}
data = data[:9]
@@ -196,7 +184,7 @@ func (m *macroOp) decode(data []byte, refs []interface{}) {
dataIdx := int(int32(bo.Uint32(data[1:])))
refsIdx := int(int32(bo.Uint32(data[5:])))
*m = macroOp{
ops: refs[0].(*op.Ops),
ops: refs[0].(*Ops),
pc: PC{
data: dataIdx,
refs: refsIdx,