1
1
package com .avsystem .commons
2
2
package serialization .json
3
3
4
- import java .io .{Reader , StringReader }
5
-
6
4
import com .avsystem .commons .annotation .explicitGenerics
7
5
import com .avsystem .commons .serialization .GenCodec .ReadFailure
6
+ import com .avsystem .commons .serialization ._
8
7
import com .avsystem .commons .serialization .json .JsonStringInput .{AfterElement , AfterElementNothing }
9
- import com .avsystem .commons .serialization .{FieldInput , GenCodec , Input , InputType , ListInput , ObjectInput }
10
8
11
9
object JsonStringInput {
12
10
@ explicitGenerics def read [T : GenCodec ](json : String ): T =
13
- GenCodec .read[T ](new JsonStringInput (new JsonReader (new StringReader ( json) )))
11
+ GenCodec .read[T ](new JsonStringInput (new JsonReader (json)))
14
12
15
13
private [json] object ObjectMarker {
16
14
override def toString = " object"
@@ -79,7 +77,7 @@ class JsonStringInput(reader: JsonReader, callback: AfterElement = AfterElementN
79
77
val result = new Array [Byte ](hex.length / 2 )
80
78
var i = 0
81
79
while (i < result.length) {
82
- result(i) = ((reader.fromHex(hex.codePointAt (2 * i)) << 4 ) | reader.fromHex(hex.codePointAt (2 * i + 1 ))).toByte
80
+ result(i) = ((reader.fromHex(hex.charAt (2 * i)) << 4 ) | reader.fromHex(hex.charAt (2 * i + 1 ))).toByte
83
81
i += 1
84
82
}
85
83
result
@@ -111,9 +109,10 @@ final class JsonListInput(reader: JsonReader, callback: AfterElement) extends Li
111
109
prepareForNext(first = true )
112
110
113
111
private def prepareForNext (first : Boolean ): Unit = {
114
- end = reader.peekNoWs() == ']'
112
+ reader.skipWs()
113
+ end = reader.isNext(']' )
115
114
if (end) {
116
- reader.read ()
115
+ reader.advance ()
117
116
callback.afterElement()
118
117
} else if (! first) {
119
118
reader.pass(',' )
@@ -134,9 +133,10 @@ final class JsonObjectInput(reader: JsonReader, callback: AfterElement) extends
134
133
prepareForNext(first = true )
135
134
136
135
private def prepareForNext (first : Boolean ): Unit = {
137
- end = reader.peekNoWs() == '}'
136
+ reader.skipWs()
137
+ end = reader.isNext('}' )
138
138
if (end) {
139
- reader.read ()
139
+ reader.advance ()
140
140
callback.afterElement()
141
141
} else if (! first) {
142
142
reader.pass(',' )
@@ -157,56 +157,55 @@ final class JsonObjectInput(reader: JsonReader, callback: AfterElement) extends
157
157
prepareForNext(first = false )
158
158
}
159
159
160
- final class JsonReader (reader : Reader ) {
161
- private [this ] var peeked : Int = - 2
162
-
163
- def read (): Int =
164
- if (peeked != - 2 ) {
165
- val res = peeked
166
- peeked = - 2
167
- res
168
- } else reader.read()
169
-
170
- def peek (): Int =
171
- if (peeked != - 2 ) peeked
172
- else {
173
- peeked = reader.read()
174
- peeked
175
- }
160
+ final class JsonReader (json : String ) {
161
+ private [this ] var i : Int = 0
176
162
177
- def skipWs (): Unit = {
178
- while ( Character .isWhitespace(peek())) {
179
- read ()
180
- }
163
+ @ inline def read (): Char = {
164
+ val res = json.charAt(i)
165
+ advance ()
166
+ res
181
167
}
182
168
183
- def peekNoWs (): Int = {
184
- skipWs()
185
- peek()
169
+ @ inline def isNext (ch : Char ): Boolean =
170
+ i < json.length && json.charAt(i) == ch
171
+
172
+ @ inline def isNextDigit : Boolean =
173
+ i < json.length && Character .isDigit(json.charAt(i))
174
+
175
+ @ inline def advance (): Unit = {
176
+ i += 1
186
177
}
187
178
188
- def pass (ch : Int ): Unit = {
179
+ def skipWs (): Unit = {
180
+ while (i < json.length && Character .isWhitespace(json.charAt(i))) {
181
+ i += 1
182
+ }
183
+ }
184
+
185
+ def pass (ch : Char ): Unit = {
189
186
val r = read()
190
187
if (r != ch) throw new ReadFailure (s " ' ${ch.toChar}' expected, got ${if (r == - 1 ) " EOF" else r.toChar}" )
191
188
}
192
189
193
- def tryPass (ch : Int ): Boolean =
194
- if (peek() == ch ) {
195
- read ()
190
+ def tryPass (ch : Char ): Boolean =
191
+ if (isNext(ch) ) {
192
+ advance ()
196
193
true
197
194
} else false
198
195
199
196
private def pass (str : String ): Unit = {
200
- var i = 0
201
- while (i < str.length) {
202
- if (read() != str.charAt(i )) {
197
+ var j = 0
198
+ while (j < str.length) {
199
+ if (! isNext( str.charAt(j) )) {
203
200
throw new ReadFailure (s " expected ' $str' " )
201
+ } else {
202
+ advance()
204
203
}
205
- i += 1
204
+ j += 1
206
205
}
207
206
}
208
207
209
- def fromHex (ch : Int ): Int =
208
+ def fromHex (ch : Char ): Int =
210
209
if (ch >= 'A' && ch <= 'F' ) ch - 'A' + 10
211
210
else if (ch >= 'a' && ch <= 'f' ) ch - 'a' + 10
212
211
else if (ch >= '0' && ch <= '9' ) ch - '0'
@@ -216,56 +215,60 @@ final class JsonReader(reader: Reader) {
216
215
fromHex(read())
217
216
218
217
private def parseNumber (): Any = {
219
- val sb = new JStringBuilder
218
+ val start = i
220
219
var decimal = false
221
220
222
- def advance (): Unit =
223
- sb.appendCodePoint(read())
224
-
225
- if (peek() == '-' ) {
221
+ if (isNext('-' )) {
226
222
advance()
227
223
}
228
224
229
225
def parseDigits (): Unit = {
230
- if (! Character .isDigit(peek()) ) {
226
+ if (! isNextDigit ) {
231
227
throw new ReadFailure (" Expected digit" )
232
228
}
233
- while (Character .isDigit(peek()) ) {
229
+ while (isNextDigit ) {
234
230
advance()
235
231
}
236
232
}
237
233
238
- if (peek() == '0' ) {
234
+ if (isNext( '0' ) ) {
239
235
advance()
240
- } else if (Character .isDigit(peek()) ) {
236
+ } else if (isNextDigit ) {
241
237
parseDigits()
242
238
} else throw new ReadFailure (" Expected '-' or digit" )
243
239
244
- if (peek() == '.' ) {
240
+ if (isNext( '.' ) ) {
245
241
decimal = true
246
242
advance()
247
243
parseDigits()
248
- if (peek() == 'e' || peek() == 'E' ) {
244
+ if (isNext( 'e' ) || isNext( 'E' ) ) {
249
245
advance()
250
- if (peek() == '-' || peek() == '+' ) {
246
+ if (isNext( '-' ) || isNext( '+' ) ) {
251
247
advance()
252
248
parseDigits()
253
249
} else throw new ReadFailure (" Expected '+' or '-'" )
254
250
}
255
251
}
256
252
257
- val str = sb.toString
253
+ val str = json.substring(start, i)
258
254
if (decimal) str.toDouble else str.toInt
259
255
}
260
256
261
257
def parseString (): String = {
262
258
pass('"' )
263
- val sb = new JStringBuilder
259
+ var sb : JStringBuilder = null
260
+ var plainStart = i
264
261
var cont = true
265
262
while (cont) {
266
263
read() match {
267
264
case '"' => cont = false
268
265
case '\\ ' =>
266
+ if (sb == null ) {
267
+ sb = new JStringBuilder
268
+ }
269
+ if (plainStart < i - 1 ) {
270
+ sb.append(json, plainStart, i - 1 )
271
+ }
269
272
val unesc = read() match {
270
273
case '"' => '"'
271
274
case '\\ ' => '\\ '
@@ -277,22 +280,32 @@ final class JsonReader(reader: Reader) {
277
280
case 'u' => ((readHex() << 12 ) + (readHex() << 8 ) + (readHex() << 4 ) + readHex()).toChar
278
281
}
279
282
sb.append(unesc)
280
- case c =>
281
- sb.append(c.toChar)
283
+ plainStart = i
284
+ case _ =>
282
285
}
283
286
}
284
- sb.toString
287
+ if (sb != null ) {
288
+ sb.append(json, plainStart, i - 1 )
289
+ sb.toString
290
+ } else {
291
+ json.substring(plainStart, i - 1 )
292
+ }
285
293
}
286
294
287
- def parseValue (): Any = peekNoWs() match {
288
- case '"' => parseString()
289
- case 't' => pass(" true" ); true
290
- case 'f' => pass(" false" ); false
291
- case 'n' => pass(" null" ); null
292
- case '[' => read(); JsonStringInput .ListMarker
293
- case '{' => read(); JsonStringInput .ObjectMarker
294
- case '-' => parseNumber()
295
- case c if Character .isDigit(c) => parseNumber()
296
- case c => throw new ReadFailure (s " Unexpected character: ' ${c.toChar}' " )
295
+ def parseValue (): Any = {
296
+ skipWs()
297
+ if (i < json.length) json.charAt(i) match {
298
+ case '"' => parseString()
299
+ case 't' => pass(" true" ); true
300
+ case 'f' => pass(" false" ); false
301
+ case 'n' => pass(" null" ); null
302
+ case '[' => read(); JsonStringInput .ListMarker
303
+ case '{' => read(); JsonStringInput .ObjectMarker
304
+ case '-' => parseNumber()
305
+ case c if Character .isDigit(c) => parseNumber()
306
+ case c => throw new ReadFailure (s " Unexpected character: ' ${c.toChar}' " )
307
+ } else {
308
+ throw new ReadFailure (" EOF" )
309
+ }
297
310
}
298
311
}
0 commit comments