-
Notifications
You must be signed in to change notification settings - Fork 0
/
iterator.go
132 lines (109 loc) · 2.79 KB
/
iterator.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
// This Go implementation started with C# SpanSplitEnumerator<T>: https://github.com/dotnet/runtime/pull/104534
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// https://github.com/dotnet/runtime/blob/main/LICENSE.TXT
package split
type seq interface {
~string | ~[]byte
}
type mode byte
const (
any mode = iota
sequence
emptySeparator
)
type funcs[T seq] struct {
index func(s T, sep T) int
indexAny func(s T, chars string) int
decodeRune func(p T) (r rune, size int)
}
func split[T seq](s T, sep T, funcs funcs[T]) *Iterator[T] {
var mode = sequence
if len(sep) == 0 {
mode = emptySeparator
}
return &Iterator[T]{
funcs: funcs,
input: s,
separators: sep,
mode: mode,
}
}
func splitAny[T seq](s T, separators T, funcs funcs[T]) *Iterator[T] {
var mode = any
if len(separators) == 0 {
mode = emptySeparator
}
return &Iterator[T]{
funcs: funcs,
input: s,
separators: separators,
mode: mode,
}
}
// Iterator is an iterator over subslices of `[]byte` or `string`. See [Iterator.Next] and [Iterator.Value].
type Iterator[T seq] struct {
funcs[T]
input T
separators T
mode mode
start, end int
cursor int
done bool
}
// Value retrieves the value of the current subslice. Use it with [Iterator.Next].
func (it *Iterator[T]) Value() T {
return it.input[it.start:it.end]
}
// Next tests whether there are any remaining subslices.
//
// Intended for use in a for loop. Inside the loop, retrieve the current subslice with [Iterator.Value].
func (it *Iterator[T]) Next() bool {
var index int
var separatorLength = 1
var slice = it.input[it.cursor:]
if it.done {
return false
}
switch it.mode {
case any:
index = it.indexAny(slice, string(it.separators))
case sequence:
index = it.index(slice, it.separators)
separatorLength = len(it.separators)
case emptySeparator:
_, index = it.decodeRune(slice)
if index == 0 {
return false
}
separatorLength = 0
}
it.start = it.cursor
if index >= 0 {
it.end = it.start + index
it.cursor = it.end + separatorLength
} else {
it.cursor = len(it.input)
it.end = len(it.input)
it.done = true
}
return true
}
// ToArray collects all the subslices into an array.
//
// This is a convenience method, and the result should identical to strings|bytes.Split from the standard library.
// You should just use strings|bytes.Split if your goal is an array of results.
func (it *Iterator[T]) ToArray() []T {
var result []T = make([]T, 0, len(it.input)/4)
for it.Next() {
result = append(result, it.Value())
}
return result
}
// Reset resets the iterator to the start of the string/bytes.
func (it *Iterator[T]) Reset() {
it.cursor = 0
it.start = 0
it.end = 0
it.done = false
}