1 /*
2 * Copyright 2017 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 @file:Suppress("ConvertTwoComparisonsToRangeCheck", "NOTHING_TO_INLINE")
18
19 package trebuchet.util
20
21 import trebuchet.io.DataSlice
22 import java.util.regex.Matcher
23
isDigitnull24 inline fun Byte.isDigit() = this >= '0'.toByte() && this <= '9'.toByte()
25
26 open class BufferReaderState(var buffer: ByteArray, var index: Int, var endIndexExclusive: Int) {
27 constructor() : this(DataSlice.EmptyBuffer, 0, 0)
28
29 inline fun peek() = buffer[index]
30
31 inline fun isDigit() = buffer[index].isDigit()
32
33 inline fun skip() {
34 index++
35 }
36
37 inline fun skipCount(count: Int) {
38 index += count
39 }
40
41 inline fun skipChar(char: Byte) {
42 while (index < endIndexExclusive && buffer[index] == char) { index++ }
43 }
44
45 inline fun skipSingle(char: Byte) {
46 if (peek() == char) skip()
47 }
48
49 inline fun skipUntil(cb: (Byte) -> Boolean) {
50 while (index < endIndexExclusive && !cb(peek())) { index++ }
51 }
52
53 inline fun end() { index = endIndexExclusive }
54
55 inline fun skipTo(search: StringSearch) {
56 val foundAt = search.find(buffer, index, endIndexExclusive)
57 index = if (foundAt != -1) foundAt else endIndexExclusive
58 }
59
60 inline fun readByte() = buffer[index++]
61 }
62
63 class PreviewReader : BufferReaderState() {
64 val startIndex = index
rewindnull65 inline fun rewind() {
66 index--
67 if (index < startIndex) { throw IndexOutOfBoundsException() }
68 }
rewindUntilnull69 inline fun rewindUntil(cb: (Byte) -> Boolean) {
70 while (!cb(peek())) { rewind() }
71 }
72 }
73
74 class MatchResult(val reader: BufferReader) {
75 var matcher: Matcher? = null
76 var startIndex: Int = 0
77
intnull78 fun int(group: Int): Int {
79 reader.index = startIndex + matcher!!.start(group)
80 return reader.readInt()
81 }
82
intOrnull83 fun intOr(group: Int, default: Int): Int {
84 return if (matcher!!.start(group) == -1) default else int(group)
85 }
86
doublenull87 fun double(group: Int): Double {
88 reader.index = startIndex + matcher!!.start(group)
89 return reader.readDouble()
90 }
91
longnull92 fun long(group: Int): Long {
93 reader.index = startIndex + matcher!!.start(group)
94 return reader.readLong()
95 }
96
stringnull97 fun string(group: Int): String {
98 reader.index = startIndex + matcher!!.start(group)
99 val endAt = startIndex + matcher!!.end(group)
100 return reader.stringTo { index = endAt }
101 }
102
slicenull103 fun slice(group: Int): DataSlice {
104 reader.index = startIndex + matcher!!.start(group)
105 val endAt = startIndex + matcher!!.end(group)
106 return reader.sliceTo { index = endAt }
107 }
108
readernull109 fun reader(group: Int): BufferReader {
110 reader.index = startIndex + matcher!!.start(group)
111 return reader
112 }
113
readnull114 fun <T> read(group: Int, cb: PreviewReader.() -> T): T {
115 val tempPreview = reader.tempPreview
116 tempPreview.buffer = reader.buffer
117 tempPreview.index = startIndex + matcher!!.start(group)
118 tempPreview.endIndexExclusive = startIndex + matcher!!.end(group)
119 return cb.invoke(tempPreview)
120 }
121 }
122
123 class BufferReader : BufferReaderState() {
124 companion object {
125 val PowerOf10s = intArrayOf(1, 10, 100, 1000, 10_000, 100_000, 1_000_000)
126 }
127
128 var stringCache: StringCache? = null
129 val tempSlice = DataSlice()
130 val tempPreview = PreviewReader()
131 val tempMatchResult = MatchResult(this)
132
133 val charWrapper = object : CharSequence {
134 override val length: Int
135 get() = endIndexExclusive - index
136
getnull137 override fun get(index: Int): Char {
138 return buffer[this@BufferReader.index + index].toChar()
139 }
140
subSequencenull141 override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
142 TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
143 }
144
145 }
146
resetnull147 fun reset(slice: DataSlice, stringCache: StringCache?) {
148 this.buffer = slice.buffer
149 this.index = slice.startIndex
150 this.endIndexExclusive = slice.endIndex
151 this.stringCache = stringCache
152 }
153
readnull154 inline fun <T> read(slice: DataSlice, stringCache: StringCache? = null, init: BufferReader.() -> T): T {
155 this.reset(slice, stringCache)
156 val ret = this.init()
157 this.stringCache = null
158 return ret
159 }
160
readIntnull161 fun readInt(): Int {
162 return readLong().toInt();
163 }
164
readLongnull165 fun readLong(): Long {
166 var value: Long = 0
167 var foundDigit = false
168 val startIndex = index
169 while (index < endIndexExclusive) {
170 val c = buffer[index] - '0'.toByte()
171 if (c >= 0 && c <= 9) {
172 foundDigit = true
173 value *= 10
174 value += c
175 } else if (foundDigit) {
176 return value
177 }
178 index++
179 }
180 if (foundDigit) {
181 return value
182 }
183 throw NumberFormatException(
184 "${String(buffer, startIndex, index - startIndex)} is not an int")
185 }
186
readDoublenull187 fun readDouble(): Double {
188 skipUntil { it == '.'.toByte() || isDigit() }
189 var result = readInt().toDouble()
190 if (peek() == '.'.toByte()) {
191 skip()
192 val startI = index
193 val second = readInt().toDouble()
194 val magnitude = index - startI
195 result += (second / PowerOf10s[magnitude])
196 }
197 return result
198 }
199
stringTonull200 inline fun stringTo(init: PreviewReader.() -> Unit): String {
201 val slice = sliceTo(tempSlice, init)
202 return stringCache?.stringFor(slice) ?: slice.toString()
203 }
204
sliceTonull205 inline fun sliceTo(slice: DataSlice = DataSlice(), init: PreviewReader.() -> Unit): DataSlice {
206 tempPreview.buffer = buffer
207 tempPreview.index = index
208 tempPreview.endIndexExclusive = endIndexExclusive
209 tempPreview.init()
210 slice.set(buffer, index, tempPreview.index)
211 index = tempPreview.index
212 return slice
213 }
214
matchnull215 inline fun match(matcher: Matcher, result: MatchResult.() -> Unit) {
216 if (!tryMatch(matcher, result)) {
217 println("RE failed on '${stringTo { end() }}'")
218 }
219 }
220
tryMatchnull221 inline fun tryMatch(matcher: Matcher, result: MatchResult.() -> Unit): Boolean {
222 matcher.reset(charWrapper)
223 if (matcher.matches()) {
224 tempMatchResult.matcher = matcher
225 tempMatchResult.startIndex = index
226 result.invoke(tempMatchResult)
227 tempMatchResult.matcher = null
228 return true
229 }
230 return false
231 }
232 }
233
234