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