-
Notifications
You must be signed in to change notification settings - Fork 0
/
parts.go
148 lines (117 loc) · 2.69 KB
/
parts.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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package syslogsidecar
import (
"fmt"
)
//
// Subset of https://github.com/linkdotnet/golang-stringbuilder/blob/main/stringbuilder.go
//
type parts struct {
data []rune
position int
}
// Creates a new instance of the parts with preallocated array
func newparts(initialCapacity int) *parts {
return &parts{data: make([]rune, initialCapacity)}
}
// Initiates parts
func (p *parts) set(initialCapacity int) {
if len(p.data) == 0 {
p.data = make([]rune, initialCapacity)
}
p.rewind()
}
// Appends a text to the parts instance
func (p *parts) appendText(text string) int {
if len(text) == 0 {
return 0
}
p.resize(text)
textRunes := []rune(text)
copy(p.data[p.position:], textRunes)
l := len(textRunes)
p.position = p.position + l
return l
}
// Appends a single character to the parts instance
func (p *parts) appendRune(char rune) int {
newLen := p.position + 1
if newLen >= cap(p.data) {
p.grow(newLen)
}
p.data[p.position] = char
p.position++
return 1
}
// Returns the current position
func (p *parts) pos() int {
return p.position
}
// Sets the position to 0.
// The internal array will stay the same.
func (p *parts) rewind() {
p.position = 0
}
// Change current position
func (p *parts) skip(forward int) error {
if forward <= 0 {
return fmt.Errorf("forward should always be greater than zero")
}
newPos := p.position + forward
if newPos >= len(p.data) {
return fmt.Errorf("cannot skip after end")
}
p.position = newPos
return nil
}
// Gets the rune at the specific index
func (p *parts) runeAt(index int) (rune, error) {
if index < 0 {
return 0, fmt.Errorf("index should always be greater than or equal to zero")
}
if index >= len(p.data) {
return 0, fmt.Errorf("index cannot be greater than current position")
}
return p.data[index], nil
}
// Sets the rune at the specific position
func (p *parts) setRuneAt(index int, val rune) error {
if index < 0 {
return fmt.Errorf("index should always be greater than or equal to zero")
}
if index >= len(p.data) {
return fmt.Errorf("invalid index")
}
p.data[index] = val
return nil
}
func (p *parts) resize(text string) {
newLen := p.position + len(text)
if newLen > cap(p.data) {
p.grow(newLen)
}
}
func (p *parts) grow(lenToAdd int) {
// Grow times 2 until lenToAdd fits
newLen := len(p.data)
if newLen == 0 {
newLen = 8
}
for newLen < lenToAdd {
newLen = newLen * 2
}
p.data = append(p.data, make([]rune, newLen-len(p.data))...)
}
func (p *parts) part(length int) (string, error) {
if length <= 0 {
return "", nil
}
start := p.position
err := p.skip(length)
if err != nil {
return "", err
}
end := p.position
r := make([]rune, end-start)
copy(r, p.data[start:end])
return string(r), nil
}