@@ -3,8 +3,9 @@ package guacd
33import (
44 "errors"
55 "fmt"
6- "strconv "
6+ "io "
77 "strings"
8+ "unicode/utf8"
89)
910
1011type Instruction struct {
@@ -24,9 +25,9 @@ func (opt *Instruction) String() string {
2425 if len (opt .ProtocolForm ) > 0 {
2526 return opt .ProtocolForm
2627 }
27- opt .ProtocolForm = fmt .Sprintf ("%d.%s" , len (opt .Opcode ), opt .Opcode )
28+ opt .ProtocolForm = fmt .Sprintf ("%d.%s" , utf8 . RuneCountInString (opt .Opcode ), opt .Opcode )
2829 for _ , value := range opt .Args {
29- opt .ProtocolForm += fmt .Sprintf (",%d.%s" , len ([] rune ( value ) ), value )
30+ opt .ProtocolForm += fmt .Sprintf (",%d.%s" , utf8 . RuneCountInString ( value ), value )
3031 }
3132 opt .ProtocolForm += semicolonDelimiter
3233 return opt .ProtocolForm
@@ -43,58 +44,37 @@ const (
4344)
4445
4546var (
46- ErrInstructionMissSemicolon = errors .New ("instruction without semicolon" )
47- ErrInstructionMissDot = errors .New ("instruction without dot" )
48- ErrInstructionBadDigit = errors .New ("instruction with bad digit" )
49- ErrInstructionBadContent = errors .New ("instruction with bad Content" )
47+ ErrInstructionMissSemicolon = errors .New ("instruction without semicolon" )
48+ ErrInstructionMissDot = errors .New ("instruction without dot" )
49+ ErrInstructionBadDigit = errors .New ("instruction with bad digit" )
50+ ErrInstructionBadContent = errors .New ("instruction with bad Content" )
51+ ErrInstructionBadTerminator = errors .New ("instruction with bad element terminator" )
52+ ErrInstructionTooLong = errors .New ("instruction exceeds size limit" )
53+ ErrInstructionTooManyElements = errors .New ("instruction has too many elements" )
54+ ErrInstructionInvalidUTF8 = errors .New ("instruction contains invalid UTF-8" )
55+ ErrInstructionTrailingData = errors .New ("data follows complete instruction" )
5056)
5157
5258// raw 是以 `;` 为结束符的原生字符串
5359func ParseInstructionString (raw string ) (ret Instruction , err error ) {
5460 if ! strings .HasSuffix (raw , semicolonDelimiter ) {
5561 return Instruction {}, fmt .Errorf ("%w: %s" , ErrInstructionMissSemicolon , raw )
5662 }
57- raw = trimSuffixSemicolonDelimiter (raw )
58- rawRune := []rune (raw )
59- args := make ([]string , 0 , 1024 )
60- var i = 0
61- for len (rawRune ) > 0 {
62- switch rawRune [i ] {
63- case ByteCommaDelimiter , ByteSemicolonDelimiter :
64- // 重置数据
65- rawRune = rawRune [i + 1 :]
66- i = 0
67- continue
68- case ByteDotDelimiter :
69- // 解析 LENGTH.VALUE 格式的数据
70- dotIndex := rawRune [:i ]
71- argContentLen , err := strconv .Atoi (string (dotIndex ))
72- if err != nil {
73- return Instruction {}, fmt .Errorf ("%w: %s" ,
74- ErrInstructionBadDigit , err .Error ())
75- }
76- if argContentLen > len (rawRune [i + 1 :]) {
77- return Instruction {}, fmt .Errorf ("%w: %s" ,
78- ErrInstructionBadContent , raw )
79- }
80- argContent := string (rawRune [i + 1 : argContentLen + i + 1 ])
81- args = append (args , argContent )
82- rawRune = rawRune [argContentLen + i + 1 :]
83- i = 0
84- continue
85- default :
86- i ++
87- }
88- if i >= len (rawRune ) {
89- return Instruction {}, fmt .Errorf ("%w: %s" , ErrInstructionBadContent , raw )
90- }
63+
64+ decoder := NewInstructionDecoder (strings .NewReader (raw ))
65+ ret , err = decoder .ReadInstruction ()
66+ if err != nil {
67+ return Instruction {}, err
9168 }
92- if len (args ) < 1 {
93- return Instruction {}, fmt .Errorf ("%w: no content" , ErrInstructionBadContent )
69+
70+ // ParseInstructionString represents exactly one instruction. Accepting a
71+ // second instruction here would silently reinterpret its opcode as an
72+ // argument of the first instruction.
73+ if _ , nextErr := decoder .ReadInstruction (); nextErr == nil {
74+ return Instruction {}, fmt .Errorf ("%w: multiple instructions" , ErrInstructionTrailingData )
75+ } else if ! errors .Is (nextErr , io .EOF ) {
76+ return Instruction {}, fmt .Errorf ("%w: %v" , ErrInstructionTrailingData , nextErr )
9477 }
95- return NewInstruction (args [0 ], args [1 :]... ), nil
96- }
9778
98- func trimSuffixSemicolonDelimiter (content string ) string {
99- return strings .TrimSuffix (content , semicolonDelimiter )
79+ return ret , nil
10080}
0 commit comments