ops.mx raw

   1  // SPDX-License-Identifier: Unlicense OR MIT
   2  
   3  // Binary op encoding, reader, and decoder.
   4  // Ported from gioui.org/internal/ops.
   5  
   6  package gio
   7  
   8  import (
   9  	"encoding/binary"
  10  	"image"
  11  	"math"
  12  )
  13  
  14  type Ops struct {
  15  	// version is incremented at each ResetOps.
  16  	version uint32
  17  	// data contains the serialized operations.
  18  	data []byte
  19  	// refs hold external references for operations.
  20  	refs []any
  21  	// stringRefs provides space for string references, pointers to which will
  22  	// be stored in refs. Storing a string directly in refs would cause a heap
  23  	// allocation, to store the string header in an interface value. The backing
  24  	// array of stringRefs, on the other hand, gets reused between calls to
  25  	// reset, making string references free on average.
  26  	stringRefs []string
  27  	// nextStateID is the id allocated for the next StateOp.
  28  	nextStateID uint32
  29  	// multipOp indicates a multi-op such as clip.Path is being added.
  30  	multipOp bool
  31  
  32  	macroStack opsStack
  33  	stacks     [_StackKind]opsStack
  34  }
  35  
  36  type OpType byte
  37  
  38  type Shape byte
  39  
  40  // Start at a high number for easier debugging.
  41  const firstOpIndex = 200
  42  
  43  const (
  44  	TypeMacro OpType = iota + firstOpIndex
  45  	TypeCall
  46  	TypeDefer
  47  	TypeTransform
  48  	TypePopTransform
  49  	TypePushOpacity
  50  	TypePopOpacity
  51  	TypeImage
  52  	TypePaint
  53  	TypeColor
  54  	TypeLinearGradient
  55  	TypePass
  56  	TypePopPass
  57  	TypeInput
  58  	TypeKeyInputHint
  59  	TypeSave
  60  	TypeLoad
  61  	TypeAux
  62  	TypeClip
  63  	TypePopClip
  64  	TypeCursor
  65  	TypePath
  66  	TypeStroke
  67  	TypeSemanticLabel
  68  	TypeSemanticDesc
  69  	TypeSemanticClass
  70  	TypeSemanticSelected
  71  	TypeSemanticEnabled
  72  	TypeActionInput
  73  )
  74  
  75  type StackID struct {
  76  	id   uint32
  77  	prev uint32
  78  }
  79  
  80  // StateOp represents a saved operation snapshot to be restored later.
  81  type StateOp struct {
  82  	id      uint32
  83  	macroID uint32
  84  	ops     *Ops
  85  }
  86  
  87  // opsStack tracks the integer identities of stack operations to ensure correct
  88  // pairing of their push and pop methods.
  89  type opsStack struct {
  90  	currentID uint32
  91  	nextID    uint32
  92  }
  93  
  94  type StackKind uint8
  95  
  96  // DecodedClipOp is the decode-side shadow of the public ClipOp.
  97  type DecodedClipOp struct {
  98  	Bounds  image.Rectangle
  99  	Outline bool
 100  	Shape   Shape
 101  }
 102  
 103  const (
 104  	ClipStackKind StackKind = iota
 105  	TransStack
 106  	PassStackKind
 107  	OpacityStackKind
 108  	_StackKind
 109  )
 110  
 111  const (
 112  	ShapePath Shape = iota
 113  	ShapeEllipse
 114  	ShapeRect
 115  )
 116  
 117  const (
 118  	TypeMacroLen            = 1 + 4 + 4
 119  	TypeCallLen             = 1 + 4 + 4 + 4 + 4
 120  	TypeDeferLen            = 1
 121  	TypeTransformLen        = 1 + 1 + 4*6
 122  	TypePopTransformLen     = 1
 123  	TypePushOpacityLen      = 1 + 4
 124  	TypePopOpacityLen       = 1
 125  	TypeRedrawLen           = 1 + 8
 126  	TypeImageLen            = 1 + 1
 127  	TypePaintLen            = 1
 128  	TypeColorLen            = 1 + 4
 129  	TypeLinearGradientLen   = 1 + 8*2 + 4*2
 130  	TypePassLen             = 1
 131  	TypePopPassLen          = 1
 132  	TypeInputLen            = 1
 133  	TypeKeyInputHintLen     = 1 + 1
 134  	TypeSaveLen             = 1 + 4
 135  	TypeLoadLen             = 1 + 4
 136  	TypeAuxLen              = 1
 137  	TypeClipLen             = 1 + 4*4 + 1 + 1
 138  	TypePopClipLen          = 1
 139  	TypeCursorLen           = 2
 140  	TypePathLen             = 8 + 1
 141  	TypeStrokeLen           = 1 + 4
 142  	TypeSemanticLabelLen    = 1
 143  	TypeSemanticDescLen     = 1
 144  	TypeSemanticClassLen    = 2
 145  	TypeSemanticSelectedLen = 2
 146  	TypeSemanticEnabledLen  = 2
 147  	TypeActionInputLen      = 1 + 1
 148  )
 149  
 150  func (op *DecodedClipOp) Decode(data []byte) {
 151  	if len(data) < TypeClipLen || OpType(data[0]) != TypeClip {
 152  		panic("invalid op")
 153  	}
 154  	data = data[:TypeClipLen]
 155  	bo := binary.LittleEndian()
 156  	op.Bounds.Min.X = int(int32(bo.Uint32(data[1:])))
 157  	op.Bounds.Min.Y = int(int32(bo.Uint32(data[5:])))
 158  	op.Bounds.Max.X = int(int32(bo.Uint32(data[9:])))
 159  	op.Bounds.Max.Y = int(int32(bo.Uint32(data[13:])))
 160  	op.Outline = data[17] == 1
 161  	op.Shape = Shape(data[18])
 162  }
 163  
 164  func ResetOps(o *Ops) {
 165  	o.macroStack = opsStack{}
 166  	o.stacks = [_StackKind]opsStack{}
 167  	// Leave references to the GC.
 168  	for i := range o.refs {
 169  		o.refs[i] = nil
 170  	}
 171  	for i := range o.stringRefs {
 172  		o.stringRefs[i] = ""
 173  	}
 174  	o.data = o.data[:0]
 175  	o.refs = o.refs[:0]
 176  	o.stringRefs = o.stringRefs[:0]
 177  	o.nextStateID = 0
 178  	o.version++
 179  }
 180  
 181  func WriteOps(o *Ops, n int) []byte {
 182  	if o.multipOp {
 183  		panic("cannot mix multi ops with single ones")
 184  	}
 185  	o.data = append(o.data, []byte{:n}...)
 186  	return o.data[len(o.data)-n:]
 187  }
 188  
 189  func BeginMulti(o *Ops) {
 190  	if o.multipOp {
 191  		panic("cannot interleave multi ops")
 192  	}
 193  	o.multipOp = true
 194  }
 195  
 196  func EndMulti(o *Ops) {
 197  	if !o.multipOp {
 198  		panic("cannot end non multi ops")
 199  	}
 200  	o.multipOp = false
 201  }
 202  
 203  func WriteMulti(o *Ops, n int) []byte {
 204  	if !o.multipOp {
 205  		panic("cannot use multi ops in single ops")
 206  	}
 207  	o.data = append(o.data, []byte{:n}...)
 208  	return o.data[len(o.data)-n:]
 209  }
 210  
 211  func PushMacro(o *Ops) StackID {
 212  	return o.macroStack.push()
 213  }
 214  
 215  func PopMacro(o *Ops, id StackID) {
 216  	o.macroStack.pop(id)
 217  }
 218  
 219  func FillMacro(o *Ops, startPC PC) {
 220  	pc := PCFor(o)
 221  	// Fill out the macro definition reserved in Record.
 222  	data := o.data[startPC.data:]
 223  	data = data[:TypeMacroLen]
 224  	data[0] = byte(TypeMacro)
 225  	bo := binary.LittleEndian()
 226  	bo.PutUint32(data[1:], pc.data)
 227  	bo.PutUint32(data[5:], pc.refs)
 228  }
 229  
 230  func AddCall(o *Ops, callOps *Ops, pc PC, end PC) {
 231  	data := WriteOps1(o, TypeCallLen, callOps)
 232  	data[0] = byte(TypeCall)
 233  	bo := binary.LittleEndian()
 234  	bo.PutUint32(data[1:], pc.data)
 235  	bo.PutUint32(data[5:], pc.refs)
 236  	bo.PutUint32(data[9:], end.data)
 237  	bo.PutUint32(data[13:], end.refs)
 238  }
 239  
 240  func PushOp(o *Ops, kind StackKind) (StackID, uint32) {
 241  	return o.stacks[kind].push(), o.macroStack.currentID
 242  }
 243  
 244  func PopOp(o *Ops, kind StackKind, sid StackID, macroID uint32) {
 245  	if o.macroStack.currentID != macroID {
 246  		panic("stack push and pop must not cross macro boundary")
 247  	}
 248  	o.stacks[kind].pop(sid)
 249  }
 250  
 251  func WriteOps1(o *Ops, n int, ref1 any) []byte {
 252  	o.data = append(o.data, []byte{:n}...)
 253  	o.refs = append(o.refs, ref1)
 254  	return o.data[len(o.data)-n:]
 255  }
 256  
 257  func WriteOps1String(o *Ops, n int, ref1 string) []byte {
 258  	o.data = append(o.data, []byte{:n}...)
 259  	o.stringRefs = append(o.stringRefs, ref1)
 260  	o.refs = append(o.refs, &o.stringRefs[len(o.stringRefs)-1])
 261  	return o.data[len(o.data)-n:]
 262  }
 263  
 264  func WriteOps2(o *Ops, n int, ref1, ref2 any) []byte {
 265  	o.data = append(o.data, []byte{:n}...)
 266  	o.refs = append(o.refs, ref1, ref2)
 267  	return o.data[len(o.data)-n:]
 268  }
 269  
 270  func WriteOps2String(o *Ops, n int, ref1 any, ref2 string) []byte {
 271  	o.data = append(o.data, []byte{:n}...)
 272  	o.stringRefs = append(o.stringRefs, ref2)
 273  	o.refs = append(o.refs, ref1, &o.stringRefs[len(o.stringRefs)-1])
 274  	return o.data[len(o.data)-n:]
 275  }
 276  
 277  func WriteOps3(o *Ops, n int, ref1, ref2, ref3 any) []byte {
 278  	o.data = append(o.data, []byte{:n}...)
 279  	o.refs = append(o.refs, ref1, ref2, ref3)
 280  	return o.data[len(o.data)-n:]
 281  }
 282  
 283  func PCFor(o *Ops) PC {
 284  	return PC{data: uint32(len(o.data)), refs: uint32(len(o.refs))}
 285  }
 286  
 287  func (s *opsStack) push() StackID {
 288  	s.nextID++
 289  	sid := StackID{
 290  		id:   s.nextID,
 291  		prev: s.currentID,
 292  	}
 293  	s.currentID = s.nextID
 294  	return sid
 295  }
 296  
 297  func (s *opsStack) check(sid StackID) {
 298  	if s.currentID != sid.id {
 299  		panic("unbalanced operation")
 300  	}
 301  }
 302  
 303  func (s *opsStack) pop(sid StackID) {
 304  	s.check(sid)
 305  	s.currentID = sid.prev
 306  }
 307  
 308  // SaveOps saves the effective transformation.
 309  func SaveOps(o *Ops) StateOp {
 310  	o.nextStateID++
 311  	s := StateOp{
 312  		ops:     o,
 313  		id:      o.nextStateID,
 314  		macroID: o.macroStack.currentID,
 315  	}
 316  	bo := binary.LittleEndian()
 317  	data := WriteOps(o, TypeSaveLen)
 318  	data[0] = byte(TypeSave)
 319  	bo.PutUint32(data[1:], s.id)
 320  	return s
 321  }
 322  
 323  // Load a previously saved operations state given its ID.
 324  func (s StateOp) Load() {
 325  	bo := binary.LittleEndian()
 326  	data := WriteOps(s.ops, TypeLoadLen)
 327  	data[0] = byte(TypeLoad)
 328  	bo.PutUint32(data[1:], s.id)
 329  }
 330  
 331  // DecodeCommand decodes a Command from a byte slice.
 332  // Uses Uint32sToBytes for the same host-endian reinterpret as the original.
 333  func DecodeCommand(d []byte) Command {
 334  	var cmd Command
 335  	copy(Uint32sToBytes(cmd[:]), d)
 336  	return cmd
 337  }
 338  
 339  // EncodeCommand encodes a Command into a byte slice.
 340  func EncodeCommand(out []byte, cmd Command) {
 341  	copy(out, Uint32sToBytes(cmd[:]))
 342  }
 343  
 344  func DecodeTransform(data []byte) (t Affine2D, push bool) {
 345  	if OpType(data[0]) != TypeTransform {
 346  		panic("invalid op")
 347  	}
 348  	push = data[1] != 0
 349  	data = data[2:]
 350  	data = data[:4*6]
 351  
 352  	bo := binary.LittleEndian()
 353  	a := math.Float32frombits(bo.Uint32(data))
 354  	b := math.Float32frombits(bo.Uint32(data[4*1:]))
 355  	c := math.Float32frombits(bo.Uint32(data[4*2:]))
 356  	d := math.Float32frombits(bo.Uint32(data[4*3:]))
 357  	e := math.Float32frombits(bo.Uint32(data[4*4:]))
 358  	f := math.Float32frombits(bo.Uint32(data[4*5:]))
 359  	return NewAffine2D(a, b, c, d, e, f), push
 360  }
 361  
 362  func DecodeOpacity(data []byte) float32 {
 363  	if OpType(data[0]) != TypePushOpacity {
 364  		panic("invalid op")
 365  	}
 366  	bo := binary.LittleEndian()
 367  	return math.Float32frombits(bo.Uint32(data[1:]))
 368  }
 369  
 370  // DecodeSave decodes the state id of a save op.
 371  func DecodeSave(data []byte) int {
 372  	if OpType(data[0]) != TypeSave {
 373  		panic("invalid op")
 374  	}
 375  	bo := binary.LittleEndian()
 376  	return int(bo.Uint32(data[1:]))
 377  }
 378  
 379  // DecodeLoad decodes the state id of a load op.
 380  func DecodeLoad(data []byte) int {
 381  	if OpType(data[0]) != TypeLoad {
 382  		panic("invalid op")
 383  	}
 384  	bo := binary.LittleEndian()
 385  	return int(bo.Uint32(data[1:]))
 386  }
 387  
 388  type opProp struct {
 389  	Size    byte
 390  	NumRefs byte
 391  }
 392  
 393  func opProps() [0x100]opProp {
 394  	return [0x100]opProp{
 395  	TypeMacro:            {Size: TypeMacroLen, NumRefs: 0},
 396  	TypeCall:             {Size: TypeCallLen, NumRefs: 1},
 397  	TypeDefer:            {Size: TypeDeferLen, NumRefs: 0},
 398  	TypeTransform:        {Size: TypeTransformLen, NumRefs: 0},
 399  	TypePopTransform:     {Size: TypePopTransformLen, NumRefs: 0},
 400  	TypePushOpacity:      {Size: TypePushOpacityLen, NumRefs: 0},
 401  	TypePopOpacity:       {Size: TypePopOpacityLen, NumRefs: 0},
 402  	TypeImage:            {Size: TypeImageLen, NumRefs: 2},
 403  	TypePaint:            {Size: TypePaintLen, NumRefs: 0},
 404  	TypeColor:            {Size: TypeColorLen, NumRefs: 0},
 405  	TypeLinearGradient:   {Size: TypeLinearGradientLen, NumRefs: 0},
 406  	TypePass:             {Size: TypePassLen, NumRefs: 0},
 407  	TypePopPass:          {Size: TypePopPassLen, NumRefs: 0},
 408  	TypeInput:            {Size: TypeInputLen, NumRefs: 1},
 409  	TypeKeyInputHint:     {Size: TypeKeyInputHintLen, NumRefs: 1},
 410  	TypeSave:             {Size: TypeSaveLen, NumRefs: 0},
 411  	TypeLoad:             {Size: TypeLoadLen, NumRefs: 0},
 412  	TypeAux:              {Size: TypeAuxLen, NumRefs: 0},
 413  	TypeClip:             {Size: TypeClipLen, NumRefs: 0},
 414  	TypePopClip:          {Size: TypePopClipLen, NumRefs: 0},
 415  	TypeCursor:           {Size: TypeCursorLen, NumRefs: 0},
 416  	TypePath:             {Size: TypePathLen, NumRefs: 0},
 417  	TypeStroke:           {Size: TypeStrokeLen, NumRefs: 0},
 418  	TypeSemanticLabel:    {Size: TypeSemanticLabelLen, NumRefs: 1},
 419  	TypeSemanticDesc:     {Size: TypeSemanticDescLen, NumRefs: 1},
 420  	TypeSemanticClass:    {Size: TypeSemanticClassLen, NumRefs: 0},
 421  	TypeSemanticSelected: {Size: TypeSemanticSelectedLen, NumRefs: 0},
 422  	TypeSemanticEnabled:  {Size: TypeSemanticEnabledLen, NumRefs: 0},
 423  	TypeActionInput:      {Size: TypeActionInputLen, NumRefs: 0},
 424  	}
 425  }
 426  
 427  func (t OpType) props() (size, numRefs uint32) {
 428  	v := opProps()[t]
 429  	return uint32(v.Size), uint32(v.NumRefs)
 430  }
 431  
 432  func (t OpType) Size() uint32 {
 433  	return uint32(opProps()[t].Size)
 434  }
 435  
 436  func (t OpType) NumRefs() uint32 {
 437  	return uint32(opProps()[t].NumRefs)
 438  }
 439  
 440  func (t OpType) String() string {
 441  	switch t {
 442  	case TypeMacro:
 443  		return "Macro"
 444  	case TypeCall:
 445  		return "Call"
 446  	case TypeDefer:
 447  		return "Defer"
 448  	case TypeTransform:
 449  		return "Transform"
 450  	case TypePopTransform:
 451  		return "PopTransform"
 452  	case TypePushOpacity:
 453  		return "PushOpacity"
 454  	case TypePopOpacity:
 455  		return "PopOpacity"
 456  	case TypeImage:
 457  		return "Image"
 458  	case TypePaint:
 459  		return "Paint"
 460  	case TypeColor:
 461  		return "Color"
 462  	case TypeLinearGradient:
 463  		return "LinearGradient"
 464  	case TypePass:
 465  		return "Pass"
 466  	case TypePopPass:
 467  		return "PopPass"
 468  	case TypeInput:
 469  		return "Input"
 470  	case TypeKeyInputHint:
 471  		return "KeyInputHint"
 472  	case TypeSave:
 473  		return "Save"
 474  	case TypeLoad:
 475  		return "Load"
 476  	case TypeAux:
 477  		return "Aux"
 478  	case TypeClip:
 479  		return "Clip"
 480  	case TypePopClip:
 481  		return "PopClip"
 482  	case TypeCursor:
 483  		return "Cursor"
 484  	case TypePath:
 485  		return "Path"
 486  	case TypeStroke:
 487  		return "Stroke"
 488  	case TypeSemanticLabel:
 489  		return "SemanticDescription"
 490  	default:
 491  		panic("unknown OpType")
 492  	}
 493  }
 494  
 495  // --- Reader (from reader.go) ---
 496  
 497  // Reader parses an ops list.
 498  type Reader struct {
 499  	pc        PC
 500  	stack     []readerMacro
 501  	ops       *Ops
 502  	deferOps  Ops
 503  	deferDone bool
 504  }
 505  
 506  // EncodedOp represents an encoded op returned by Reader.
 507  type EncodedOp struct {
 508  	Key  Key
 509  	Data []byte
 510  	Refs []any
 511  }
 512  
 513  // Key is a unique key for a given op.
 514  type Key struct {
 515  	ops     *Ops
 516  	pc      uint32
 517  	version uint32
 518  }
 519  
 520  // Shadow of op.MacroOp.
 521  type macroOp struct {
 522  	ops   *Ops
 523  	start PC
 524  	end   PC
 525  }
 526  
 527  // PC is an instruction counter for an operation list.
 528  type PC struct {
 529  	data uint32
 530  	refs uint32
 531  }
 532  
 533  type readerMacro struct {
 534  	ops   *Ops
 535  	retPC PC
 536  	endPC PC
 537  }
 538  
 539  type opMacroDef struct {
 540  	endpc PC
 541  }
 542  
 543  func (pc PC) Add(op OpType) PC {
 544  	size, numRefs := op.props()
 545  	return PC{
 546  		data: pc.data + size,
 547  		refs: pc.refs + numRefs,
 548  	}
 549  }
 550  
 551  // ResetReader starts reading from the beginning of ops.
 552  func (r *Reader) ResetReader(ops *Ops) {
 553  	r.ResetAt(ops, PC{})
 554  }
 555  
 556  // ResetAt is like ResetReader, except it starts reading from pc.
 557  func (r *Reader) ResetAt(ops *Ops, pc PC) {
 558  	r.stack = r.stack[:0]
 559  	ResetOps(&r.deferOps)
 560  	r.deferDone = false
 561  	r.pc = pc
 562  	r.ops = ops
 563  }
 564  
 565  func (r *Reader) Decode() (EncodedOp, bool) {
 566  	if r.ops == nil {
 567  		return EncodedOp{}, false
 568  	}
 569  	deferring := false
 570  	for {
 571  		if len(r.stack) > 0 {
 572  			b := r.stack[len(r.stack)-1]
 573  			if r.pc == b.endPC {
 574  				r.ops = b.ops
 575  				r.pc = b.retPC
 576  				r.stack = r.stack[:len(r.stack)-1]
 577  				continue
 578  			}
 579  		}
 580  		data := r.ops.data
 581  		data = data[r.pc.data:]
 582  		refs := r.ops.refs
 583  		if len(data) == 0 {
 584  			if r.deferDone {
 585  				return EncodedOp{}, false
 586  			}
 587  			r.deferDone = true
 588  			// Execute deferred macros.
 589  			r.ops = &r.deferOps
 590  			r.pc = PC{}
 591  			continue
 592  		}
 593  		key := Key{ops: r.ops, pc: r.pc.data, version: r.ops.version}
 594  		t := OpType(data[0])
 595  		n, nrefs := t.props()
 596  		data = data[:n]
 597  		refs = refs[r.pc.refs:]
 598  		refs = refs[:nrefs]
 599  		switch t {
 600  		case TypeDefer:
 601  			deferring = true
 602  			r.pc.data += n
 603  			r.pc.refs += nrefs
 604  			continue
 605  		case TypeAux:
 606  			// An Aux operation is always wrapped in a macro, and
 607  			// its length is the remaining space.
 608  			block := r.stack[len(r.stack)-1]
 609  			n += block.endPC.data - r.pc.data - TypeAuxLen
 610  			data = data[:n]
 611  		case TypeCall:
 612  			if deferring {
 613  				deferring = false
 614  				// Copy macro for deferred execution.
 615  				if nrefs != 1 {
 616  					panic("internal error: unexpected number of macro refs")
 617  				}
 618  				deferData := WriteOps1(&r.deferOps, int(n), refs[0])
 619  				copy(deferData, data)
 620  				r.pc.data += n
 621  				r.pc.refs += nrefs
 622  				continue
 623  			}
 624  			var op macroOp
 625  			op.decode(data, refs)
 626  			retPC := r.pc
 627  			retPC.data += n
 628  			retPC.refs += nrefs
 629  			r.stack = append(r.stack, readerMacro{
 630  				ops:   r.ops,
 631  				retPC: retPC,
 632  				endPC: op.end,
 633  			})
 634  			r.ops = op.ops
 635  			r.pc = op.start
 636  			continue
 637  		case TypeMacro:
 638  			var op opMacroDef
 639  			op.decode(data)
 640  			if op.endpc != (PC{}) {
 641  				r.pc = op.endpc
 642  			} else {
 643  				// Treat an incomplete macro as containing all remaining ops.
 644  				r.pc.data = uint32(len(r.ops.data))
 645  				r.pc.refs = uint32(len(r.ops.refs))
 646  			}
 647  			continue
 648  		}
 649  		r.pc.data += n
 650  		r.pc.refs += nrefs
 651  		return EncodedOp{Key: key, Data: data, Refs: refs}, true
 652  	}
 653  }
 654  
 655  func (op *opMacroDef) decode(data []byte) {
 656  	if len(data) < TypeMacroLen || OpType(data[0]) != TypeMacro {
 657  		panic("invalid op")
 658  	}
 659  	bo := binary.LittleEndian()
 660  	data = data[:TypeMacroLen]
 661  	op.endpc.data = bo.Uint32(data[1:])
 662  	op.endpc.refs = bo.Uint32(data[5:])
 663  }
 664  
 665  func (m *macroOp) decode(data []byte, refs []any) {
 666  	if len(data) < TypeCallLen || len(refs) < 1 || OpType(data[0]) != TypeCall {
 667  		panic("invalid op")
 668  	}
 669  	bo := binary.LittleEndian()
 670  	data = data[:TypeCallLen]
 671  
 672  	m.ops = refs[0].(*Ops)
 673  	m.start.data = bo.Uint32(data[1:])
 674  	m.start.refs = bo.Uint32(data[5:])
 675  	m.end.data = bo.Uint32(data[9:])
 676  	m.end.refs = bo.Uint32(data[13:])
 677  }
 678