forked from wwt/guac
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinstruction.go
113 lines (91 loc) · 2.49 KB
/
instruction.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package guac
import (
"errors"
"fmt"
"strconv"
)
// Instruction represents a Guacamole instruction
type Instruction struct {
Opcode string
Args []string
cache string
}
// NewInstruction creates an instruction
func NewInstruction(opcode string, args ...string) *Instruction {
return &Instruction{
Opcode: opcode,
Args: args,
}
}
// String returns the on-wire representation of the instruction
func (i *Instruction) String() string {
if len(i.cache) > 0 {
return i.cache
}
i.cache = fmt.Sprintf("%d.%s", len(i.Opcode), i.Opcode)
for _, value := range i.Args {
i.cache += fmt.Sprintf(",%d.%s", len(value), value)
}
i.cache += ";"
return i.cache
}
func (i *Instruction) Byte() []byte {
return []byte(i.String())
}
func Parse(buf []byte) (*Instruction, error) {
data := []rune(string(buf))
elementStart := 0
// Build list of elements
elements := make([]string, 0, 1)
for elementStart < len(data) {
// Find end of length
lengthEnd := -1
for i := elementStart; i < len(data); i++ {
if data[i] == '.' {
lengthEnd = i
break
}
}
// read() is required to return a complete instruction. If it does
// not, this is a severe internal error.
if lengthEnd == -1 {
return nil, errors.New("guac.Parse: incomplete instruction")
}
// Parse length
length, e := strconv.Atoi(string(data[elementStart:lengthEnd]))
if e != nil {
return nil, errors.New("guac.Parse: wrong pattern instruction")
}
// Parse element from just after period
elementStart = lengthEnd + 1
elementEnd := elementStart + length
if elementEnd >= len(data) {
return nil, errors.New("guac.Parse: invalid length (corrupted instruction?)")
}
element := string(data[elementStart:elementEnd])
// Append element to list of elements
elements = append(elements, element)
// ReadSome terminator after element
elementStart += length
if elementStart >= len(data) {
return nil, errors.New("guac.Parse: invalid length (corrupted instruction?)")
}
terminator := data[elementStart]
// Continue reading instructions after terminator
elementStart++
// If we've reached the end of the instruction
if terminator == ';' {
break
}
}
return NewInstruction(elements[0], elements[1:]...), nil
}
// ReadOne takes an instruction from the stream and parses it into an Instruction
func ReadOne(stream *Stream) (instruction *Instruction, err error) {
var instructionBuffer []byte
instructionBuffer, err = stream.ReadSome()
if err != nil {
return
}
return Parse(instructionBuffer)
}