| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.convert; | 5 part of dart.convert; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Error thrown by JSON serialization if an object cannot be serialized. | 8 * Error thrown by JSON serialization if an object cannot be serialized. |
| 9 * | 9 * |
| 10 * The [unsupportedObject] field holds that object that failed to be serialized. | 10 * The [unsupportedObject] field holds that object that failed to be serialized. |
| 11 * | 11 * |
| 12 * If an object isn't directly serializable, the serializer calls the `toJson` | 12 * If an object isn't directly serializable, the serializer calls the `toJson` |
| 13 * method on the object. If that call fails, the error will be stored in the | 13 * method on the object. If that call fails, the error will be stored in the |
| 14 * [cause] field. If the call returns an object that isn't directly | 14 * [cause] field. If the call returns an object that isn't directly |
| 15 * serializable, the [cause] is null. | 15 * serializable, the [cause] is null. |
| 16 */ | 16 */ |
| 17 class JsonUnsupportedObjectError extends Error { | 17 class JsonUnsupportedObjectError extends Error { |
| 18 /** The object that could not be serialized. */ | 18 /** The object that could not be serialized. */ |
| 19 final unsupportedObject; | 19 final unsupportedObject; |
| 20 /** The exception thrown when trying to convert the object. */ | 20 /** The exception thrown when trying to convert the object. */ |
| 21 final cause; | 21 final cause; |
| 22 | 22 |
| 23 JsonUnsupportedObjectError(this.unsupportedObject, { this.cause }); | 23 JsonUnsupportedObjectError(this.unsupportedObject, {this.cause}); |
| 24 | 24 |
| 25 String toString() { | 25 String toString() { |
| 26 if (cause != null) { | 26 if (cause != null) { |
| 27 return "Converting object to an encodable object failed."; | 27 return "Converting object to an encodable object failed."; |
| 28 } else { | 28 } else { |
| 29 return "Converting object did not return an encodable object."; | 29 return "Converting object did not return an encodable object."; |
| 30 } | 30 } |
| 31 } | 31 } |
| 32 } | 32 } |
| 33 | 33 |
| 34 | |
| 35 /** | 34 /** |
| 36 * Reports that an object could not be stringified due to cyclic references. | 35 * Reports that an object could not be stringified due to cyclic references. |
| 37 * | 36 * |
| 38 * An object that references itself cannot be serialized by [stringify]. | 37 * An object that references itself cannot be serialized by [stringify]. |
| 39 * When the cycle is detected, a [JsonCyclicError] is thrown. | 38 * When the cycle is detected, a [JsonCyclicError] is thrown. |
| 40 */ | 39 */ |
| 41 class JsonCyclicError extends JsonUnsupportedObjectError { | 40 class JsonCyclicError extends JsonUnsupportedObjectError { |
| 42 /** The first object that was detected as part of a cycle. */ | 41 /** The first object that was detected as part of a cycle. */ |
| 43 JsonCyclicError(Object object): super(object); | 42 JsonCyclicError(Object object) : super(object); |
| 44 String toString() => "Cyclic error in JSON stringify"; | 43 String toString() => "Cyclic error in JSON stringify"; |
| 45 } | 44 } |
| 46 | 45 |
| 47 | |
| 48 /** | 46 /** |
| 49 * An instance of the default implementation of the [JsonCodec]. | 47 * An instance of the default implementation of the [JsonCodec]. |
| 50 * | 48 * |
| 51 * This instance provides a convenient access to the most common JSON | 49 * This instance provides a convenient access to the most common JSON |
| 52 * use cases. | 50 * use cases. |
| 53 * | 51 * |
| 54 * Examples: | 52 * Examples: |
| 55 * | 53 * |
| 56 * var encoded = JSON.encode([1, 2, { "a": null }]); | 54 * var encoded = JSON.encode([1, 2, { "a": null }]); |
| 57 * var decoded = JSON.decode('["foo", { "bar": 499 }]'); | 55 * var decoded = JSON.decode('["foo", { "bar": 499 }]'); |
| 58 */ | 56 */ |
| 59 const JsonCodec JSON = const JsonCodec(); | 57 const JsonCodec JSON = const JsonCodec(); |
| 60 | 58 |
| 61 typedef _Reviver(var key, var value); | 59 typedef _Reviver(var key, var value); |
| 62 typedef _ToEncodable(var o); | 60 typedef _ToEncodable(var o); |
| 63 | 61 |
| 64 | |
| 65 /** | 62 /** |
| 66 * A [JsonCodec] encodes JSON objects to strings and decodes strings to | 63 * A [JsonCodec] encodes JSON objects to strings and decodes strings to |
| 67 * JSON objects. | 64 * JSON objects. |
| 68 * | 65 * |
| 69 * Examples: | 66 * Examples: |
| 70 * | 67 * |
| 71 * var encoded = JSON.encode([1, 2, { "a": null }]); | 68 * var encoded = JSON.encode([1, 2, { "a": null }]); |
| 72 * var decoded = JSON.decode('["foo", { "bar": 499 }]'); | 69 * var decoded = JSON.decode('["foo", { "bar": 499 }]'); |
| 73 */ | 70 */ |
| 74 class JsonCodec extends Codec<Object, String> { | 71 class JsonCodec extends Codec<Object, String> { |
| (...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 251 * The converter works more efficiently if the given [sink] is a | 248 * The converter works more efficiently if the given [sink] is a |
| 252 * [StringConversionSink]. | 249 * [StringConversionSink]. |
| 253 * | 250 * |
| 254 * Returns a chunked-conversion sink that accepts at most one object. It is | 251 * Returns a chunked-conversion sink that accepts at most one object. It is |
| 255 * an error to invoke `add` more than once on the returned sink. | 252 * an error to invoke `add` more than once on the returned sink. |
| 256 */ | 253 */ |
| 257 ChunkedConversionSink<Object> startChunkedConversion(Sink<String> sink) { | 254 ChunkedConversionSink<Object> startChunkedConversion(Sink<String> sink) { |
| 258 if (sink is! StringConversionSink) { | 255 if (sink is! StringConversionSink) { |
| 259 sink = new StringConversionSink.from(sink); | 256 sink = new StringConversionSink.from(sink); |
| 260 } else if (sink is _Utf8EncoderSink) { | 257 } else if (sink is _Utf8EncoderSink) { |
| 261 return new _JsonUtf8EncoderSink(sink._sink, _toEncodable, | 258 return new _JsonUtf8EncoderSink( |
| 262 JsonUtf8Encoder._utf8Encode(indent), | 259 sink._sink, |
| 263 JsonUtf8Encoder.DEFAULT_BUFFER_SIZE); | 260 _toEncodable, |
| 261 JsonUtf8Encoder._utf8Encode(indent), |
| 262 JsonUtf8Encoder.DEFAULT_BUFFER_SIZE); |
| 264 } | 263 } |
| 265 return new _JsonEncoderSink(sink, _toEncodable, indent); | 264 return new _JsonEncoderSink(sink, _toEncodable, indent); |
| 266 } | 265 } |
| 267 | 266 |
| 268 // Override the base class's bind, to provide a better type. | 267 // Override the base class's bind, to provide a better type. |
| 269 Stream<String> bind(Stream<Object> stream) => super.bind(stream); | 268 Stream<String> bind(Stream<Object> stream) => super.bind(stream); |
| 270 | 269 |
| 271 Converter<Object, T> fuse<T>(Converter<String, T> other) { | 270 Converter<Object, T> fuse<T>(Converter<String, T> other) { |
| 272 if (other is Utf8Encoder) { | 271 if (other is Utf8Encoder) { |
| 273 return new JsonUtf8Encoder(indent, _toEncodable) | 272 return new JsonUtf8Encoder(indent, _toEncodable) |
| 274 as dynamic/*=Converter<Object, T>*/; | 273 as dynamic/*=Converter<Object, T>*/; |
| 275 } | 274 } |
| 276 return super.fuse<T>(other); | 275 return super.fuse<T>(other); |
| 277 } | 276 } |
| 278 } | 277 } |
| 279 | 278 |
| 280 /** | 279 /** |
| 281 * Encoder that encodes a single object as a UTF-8 encoded JSON string. | 280 * Encoder that encodes a single object as a UTF-8 encoded JSON string. |
| 282 * | 281 * |
| 283 * This encoder works equivalently to first converting the object to | 282 * This encoder works equivalently to first converting the object to |
| 284 * a JSON string, and then UTF-8 encoding the string, but without | 283 * a JSON string, and then UTF-8 encoding the string, but without |
| 285 * creating an intermediate string. | 284 * creating an intermediate string. |
| 286 */ | 285 */ |
| 287 class JsonUtf8Encoder extends Converter<Object, List<int>> | 286 class JsonUtf8Encoder extends Converter<Object, List<int>> |
| 288 implements ChunkedConverter<Object, List<int>, Object, List<int>> { | 287 implements ChunkedConverter<Object, List<int>, Object, List<int>> { |
| 289 | |
| 290 /** Default buffer size used by the JSON-to-UTF-8 encoder. */ | 288 /** Default buffer size used by the JSON-to-UTF-8 encoder. */ |
| 291 static const int DEFAULT_BUFFER_SIZE = 256; | 289 static const int DEFAULT_BUFFER_SIZE = 256; |
| 292 /** Indentation used in pretty-print mode, `null` if not pretty. */ | 290 /** Indentation used in pretty-print mode, `null` if not pretty. */ |
| 293 final List<int> _indent; | 291 final List<int> _indent; |
| 294 /** Function called with each un-encodable object encountered. */ | 292 /** Function called with each un-encodable object encountered. */ |
| 295 final _ToEncodable _toEncodable; | 293 final _ToEncodable _toEncodable; |
| 296 /** UTF-8 buffer size. */ | 294 /** UTF-8 buffer size. */ |
| 297 final int _bufferSize; | 295 final int _bufferSize; |
| 298 | 296 |
| 299 /** | 297 /** |
| (...skipping 14 matching lines...) Expand all Loading... |
| 314 * | 312 * |
| 315 * The JSON encoder handles numbers, strings, booleans, null, lists and maps | 313 * The JSON encoder handles numbers, strings, booleans, null, lists and maps |
| 316 * directly. | 314 * directly. |
| 317 * | 315 * |
| 318 * Any other object is attempted converted by [toEncodable] to an object that | 316 * Any other object is attempted converted by [toEncodable] to an object that |
| 319 * is of one of the convertible types. | 317 * is of one of the convertible types. |
| 320 * | 318 * |
| 321 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on the | 319 * If [toEncodable] is omitted, it defaults to calling `.toJson()` on the |
| 322 * object. | 320 * object. |
| 323 */ | 321 */ |
| 324 JsonUtf8Encoder([String indent, | 322 JsonUtf8Encoder( |
| 325 toEncodable(object), | 323 [String indent, |
| 326 int bufferSize = DEFAULT_BUFFER_SIZE]) | 324 toEncodable(object), |
| 325 int bufferSize = DEFAULT_BUFFER_SIZE]) |
| 327 : _indent = _utf8Encode(indent), | 326 : _indent = _utf8Encode(indent), |
| 328 _toEncodable = toEncodable, | 327 _toEncodable = toEncodable, |
| 329 _bufferSize = bufferSize; | 328 _bufferSize = bufferSize; |
| 330 | 329 |
| 331 static List<int> _utf8Encode(String string) { | 330 static List<int> _utf8Encode(String string) { |
| 332 if (string == null) return null; | 331 if (string == null) return null; |
| 333 if (string.isEmpty) return new Uint8List(0); | 332 if (string.isEmpty) return new Uint8List(0); |
| 334 checkAscii: { | 333 checkAscii: |
| 334 { |
| 335 for (int i = 0; i < string.length; i++) { | 335 for (int i = 0; i < string.length; i++) { |
| 336 if (string.codeUnitAt(i) >= 0x80) break checkAscii; | 336 if (string.codeUnitAt(i) >= 0x80) break checkAscii; |
| 337 } | 337 } |
| 338 return string.codeUnits; | 338 return string.codeUnits; |
| 339 } | 339 } |
| 340 return UTF8.encode(string); | 340 return UTF8.encode(string); |
| 341 } | 341 } |
| 342 | 342 |
| 343 /** Convert [object] into UTF-8 encoded JSON. */ | 343 /** Convert [object] into UTF-8 encoded JSON. */ |
| 344 List<int> convert(Object object) { | 344 List<int> convert(Object object) { |
| 345 List<List<int>> bytes = []; | 345 List<List<int>> bytes = []; |
| 346 // The `stringify` function always converts into chunks. | 346 // The `stringify` function always converts into chunks. |
| 347 // Collect the chunks into the `bytes` list, then combine them afterwards. | 347 // Collect the chunks into the `bytes` list, then combine them afterwards. |
| 348 void addChunk(Uint8List chunk, int start, int end) { | 348 void addChunk(Uint8List chunk, int start, int end) { |
| 349 if (start > 0 || end < chunk.length) { | 349 if (start > 0 || end < chunk.length) { |
| 350 int length = end - start; | 350 int length = end - start; |
| 351 chunk = new Uint8List.view(chunk.buffer, | 351 chunk = new Uint8List.view( |
| 352 chunk.offsetInBytes + start, | 352 chunk.buffer, chunk.offsetInBytes + start, length); |
| 353 length); | |
| 354 } | 353 } |
| 355 bytes.add(chunk); | 354 bytes.add(chunk); |
| 356 } | 355 } |
| 357 _JsonUtf8Stringifier.stringify(object, | 356 |
| 358 _indent, | 357 _JsonUtf8Stringifier.stringify( |
| 359 _toEncodable, | 358 object, _indent, _toEncodable, _bufferSize, addChunk); |
| 360 _bufferSize, | |
| 361 addChunk); | |
| 362 if (bytes.length == 1) return bytes[0]; | 359 if (bytes.length == 1) return bytes[0]; |
| 363 int length = 0; | 360 int length = 0; |
| 364 for (int i = 0; i < bytes.length; i++) { | 361 for (int i = 0; i < bytes.length; i++) { |
| 365 length += bytes[i].length; | 362 length += bytes[i].length; |
| 366 } | 363 } |
| 367 Uint8List result = new Uint8List(length); | 364 Uint8List result = new Uint8List(length); |
| 368 for (int i = 0, offset = 0; i < bytes.length; i++) { | 365 for (int i = 0, offset = 0; i < bytes.length; i++) { |
| 369 var byteList = bytes[i]; | 366 var byteList = bytes[i]; |
| 370 int end = offset + byteList.length; | 367 int end = offset + byteList.length; |
| 371 result.setRange(offset, end, byteList); | 368 result.setRange(offset, end, byteList); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 382 * The argument [sink] will receive byte lists in sizes depending on the | 379 * The argument [sink] will receive byte lists in sizes depending on the |
| 383 * `bufferSize` passed to the constructor when creating this encoder. | 380 * `bufferSize` passed to the constructor when creating this encoder. |
| 384 */ | 381 */ |
| 385 ChunkedConversionSink<Object> startChunkedConversion(Sink<List<int>> sink) { | 382 ChunkedConversionSink<Object> startChunkedConversion(Sink<List<int>> sink) { |
| 386 ByteConversionSink byteSink; | 383 ByteConversionSink byteSink; |
| 387 if (sink is ByteConversionSink) { | 384 if (sink is ByteConversionSink) { |
| 388 byteSink = sink; | 385 byteSink = sink; |
| 389 } else { | 386 } else { |
| 390 byteSink = new ByteConversionSink.from(sink); | 387 byteSink = new ByteConversionSink.from(sink); |
| 391 } | 388 } |
| 392 return new _JsonUtf8EncoderSink(byteSink, _toEncodable, | 389 return new _JsonUtf8EncoderSink( |
| 393 _indent, _bufferSize); | 390 byteSink, _toEncodable, _indent, _bufferSize); |
| 394 } | 391 } |
| 395 | 392 |
| 396 // Override the base class's bind, to provide a better type. | 393 // Override the base class's bind, to provide a better type. |
| 397 Stream<List<int>> bind(Stream<Object> stream) { | 394 Stream<List<int>> bind(Stream<Object> stream) { |
| 398 return super.bind(stream); | 395 return super.bind(stream); |
| 399 } | 396 } |
| 400 } | 397 } |
| 401 | 398 |
| 402 /** | 399 /** |
| 403 * Implements the chunked conversion from object to its JSON representation. | 400 * Implements the chunked conversion from object to its JSON representation. |
| (...skipping 18 matching lines...) Expand all Loading... |
| 422 void add(Object o) { | 419 void add(Object o) { |
| 423 if (_isDone) { | 420 if (_isDone) { |
| 424 throw new StateError("Only one call to add allowed"); | 421 throw new StateError("Only one call to add allowed"); |
| 425 } | 422 } |
| 426 _isDone = true; | 423 _isDone = true; |
| 427 ClosableStringSink stringSink = _sink.asStringSink(); | 424 ClosableStringSink stringSink = _sink.asStringSink(); |
| 428 _JsonStringStringifier.printOn(o, stringSink, _toEncodable, _indent); | 425 _JsonStringStringifier.printOn(o, stringSink, _toEncodable, _indent); |
| 429 stringSink.close(); | 426 stringSink.close(); |
| 430 } | 427 } |
| 431 | 428 |
| 432 void close() { /* do nothing */ } | 429 void close() {/* do nothing */} |
| 433 } | 430 } |
| 434 | 431 |
| 435 /** | 432 /** |
| 436 * Sink returned when starting a chunked conversion from object to bytes. | 433 * Sink returned when starting a chunked conversion from object to bytes. |
| 437 */ | 434 */ |
| 438 class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> { | 435 class _JsonUtf8EncoderSink extends ChunkedConversionSink<Object> { |
| 439 /** The byte sink receiveing the encoded chunks. */ | 436 /** The byte sink receiveing the encoded chunks. */ |
| 440 final ByteConversionSink _sink; | 437 final ByteConversionSink _sink; |
| 441 final List<int> _indent; | 438 final List<int> _indent; |
| 442 final _ToEncodable _toEncodable; | 439 final _ToEncodable _toEncodable; |
| 443 final int _bufferSize; | 440 final int _bufferSize; |
| 444 bool _isDone = false; | 441 bool _isDone = false; |
| 445 _JsonUtf8EncoderSink(this._sink, this._toEncodable, this._indent, | 442 _JsonUtf8EncoderSink( |
| 446 this._bufferSize); | 443 this._sink, this._toEncodable, this._indent, this._bufferSize); |
| 447 | 444 |
| 448 /** Callback called for each slice of result bytes. */ | 445 /** Callback called for each slice of result bytes. */ |
| 449 void _addChunk(Uint8List chunk, int start, int end) { | 446 void _addChunk(Uint8List chunk, int start, int end) { |
| 450 _sink.addSlice(chunk, start, end, false); | 447 _sink.addSlice(chunk, start, end, false); |
| 451 } | 448 } |
| 452 | 449 |
| 453 void add(Object object) { | 450 void add(Object object) { |
| 454 if (_isDone) { | 451 if (_isDone) { |
| 455 throw new StateError("Only one call to add allowed"); | 452 throw new StateError("Only one call to add allowed"); |
| 456 } | 453 } |
| 457 _isDone = true; | 454 _isDone = true; |
| 458 _JsonUtf8Stringifier.stringify(object, _indent, _toEncodable, | 455 _JsonUtf8Stringifier.stringify( |
| 459 _bufferSize, | 456 object, _indent, _toEncodable, _bufferSize, _addChunk); |
| 460 _addChunk); | |
| 461 _sink.close(); | 457 _sink.close(); |
| 462 } | 458 } |
| 463 | 459 |
| 464 void close() { | 460 void close() { |
| 465 if (!_isDone) { | 461 if (!_isDone) { |
| 466 _isDone = true; | 462 _isDone = true; |
| 467 _sink.close(); | 463 _sink.close(); |
| 468 } | 464 } |
| 469 } | 465 } |
| 470 } | 466 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 506 */ | 502 */ |
| 507 external StringConversionSink startChunkedConversion(Sink<Object> sink); | 503 external StringConversionSink startChunkedConversion(Sink<Object> sink); |
| 508 | 504 |
| 509 // Override the base class's bind, to provide a better type. | 505 // Override the base class's bind, to provide a better type. |
| 510 Stream<Object> bind(Stream<String> stream) => super.bind(stream); | 506 Stream<Object> bind(Stream<String> stream) => super.bind(stream); |
| 511 } | 507 } |
| 512 | 508 |
| 513 // Internal optimized JSON parsing implementation. | 509 // Internal optimized JSON parsing implementation. |
| 514 external _parseJson(String source, reviver(key, value)); | 510 external _parseJson(String source, reviver(key, value)); |
| 515 | 511 |
| 516 | |
| 517 // Implementation of encoder/stringifier. | 512 // Implementation of encoder/stringifier. |
| 518 | 513 |
| 519 dynamic _defaultToEncodable(dynamic object) => object.toJson(); | 514 dynamic _defaultToEncodable(dynamic object) => object.toJson(); |
| 520 | 515 |
| 521 /** | 516 /** |
| 522 * JSON encoder that traverses an object structure and writes JSON source. | 517 * JSON encoder that traverses an object structure and writes JSON source. |
| 523 * | 518 * |
| 524 * This is an abstract implementation that doesn't decide on the output | 519 * This is an abstract implementation that doesn't decide on the output |
| 525 * format, but writes the JSON through abstract methods like [writeString]. | 520 * format, but writes the JSON through abstract methods like [writeString]. |
| 526 */ | 521 */ |
| 527 abstract class _JsonStringifier { | 522 abstract class _JsonStringifier { |
| 528 // Character code constants. | 523 // Character code constants. |
| 529 static const int BACKSPACE = 0x08; | 524 static const int BACKSPACE = 0x08; |
| 530 static const int TAB = 0x09; | 525 static const int TAB = 0x09; |
| 531 static const int NEWLINE = 0x0a; | 526 static const int NEWLINE = 0x0a; |
| 532 static const int CARRIAGE_RETURN = 0x0d; | 527 static const int CARRIAGE_RETURN = 0x0d; |
| 533 static const int FORM_FEED = 0x0c; | 528 static const int FORM_FEED = 0x0c; |
| 534 static const int QUOTE = 0x22; | 529 static const int QUOTE = 0x22; |
| 535 static const int CHAR_0 = 0x30; | 530 static const int CHAR_0 = 0x30; |
| 536 static const int BACKSLASH = 0x5c; | 531 static const int BACKSLASH = 0x5c; |
| 537 static const int CHAR_b = 0x62; | 532 static const int CHAR_b = 0x62; |
| 538 static const int CHAR_f = 0x66; | 533 static const int CHAR_f = 0x66; |
| 539 static const int CHAR_n = 0x6e; | 534 static const int CHAR_n = 0x6e; |
| 540 static const int CHAR_r = 0x72; | 535 static const int CHAR_r = 0x72; |
| 541 static const int CHAR_t = 0x74; | 536 static const int CHAR_t = 0x74; |
| 542 static const int CHAR_u = 0x75; | 537 static const int CHAR_u = 0x75; |
| 543 | 538 |
| 544 /** List of objects currently being traversed. Used to detect cycles. */ | 539 /** List of objects currently being traversed. Used to detect cycles. */ |
| 545 final List _seen = new List(); | 540 final List _seen = new List(); |
| 546 /** Function called for each un-encodable object encountered. */ | 541 /** Function called for each un-encodable object encountered. */ |
| 547 final _ToEncodable _toEncodable; | 542 final _ToEncodable _toEncodable; |
| 548 | 543 |
| 549 _JsonStringifier(toEncodable(o)) | 544 _JsonStringifier(toEncodable(o)) |
| 550 : _toEncodable = toEncodable ?? _defaultToEncodable; | 545 : _toEncodable = toEncodable ?? _defaultToEncodable; |
| 551 | 546 |
| 552 /** Append a string to the JSON output. */ | 547 /** Append a string to the JSON output. */ |
| (...skipping 15 matching lines...) Expand all Loading... |
| 568 int offset = 0; | 563 int offset = 0; |
| 569 final int length = s.length; | 564 final int length = s.length; |
| 570 for (int i = 0; i < length; i++) { | 565 for (int i = 0; i < length; i++) { |
| 571 int charCode = s.codeUnitAt(i); | 566 int charCode = s.codeUnitAt(i); |
| 572 if (charCode > BACKSLASH) continue; | 567 if (charCode > BACKSLASH) continue; |
| 573 if (charCode < 32) { | 568 if (charCode < 32) { |
| 574 if (i > offset) writeStringSlice(s, offset, i); | 569 if (i > offset) writeStringSlice(s, offset, i); |
| 575 offset = i + 1; | 570 offset = i + 1; |
| 576 writeCharCode(BACKSLASH); | 571 writeCharCode(BACKSLASH); |
| 577 switch (charCode) { | 572 switch (charCode) { |
| 578 case BACKSPACE: | 573 case BACKSPACE: |
| 579 writeCharCode(CHAR_b); | 574 writeCharCode(CHAR_b); |
| 580 break; | 575 break; |
| 581 case TAB: | 576 case TAB: |
| 582 writeCharCode(CHAR_t); | 577 writeCharCode(CHAR_t); |
| 583 break; | 578 break; |
| 584 case NEWLINE: | 579 case NEWLINE: |
| 585 writeCharCode(CHAR_n); | 580 writeCharCode(CHAR_n); |
| 586 break; | 581 break; |
| 587 case FORM_FEED: | 582 case FORM_FEED: |
| 588 writeCharCode(CHAR_f); | 583 writeCharCode(CHAR_f); |
| 589 break; | 584 break; |
| 590 case CARRIAGE_RETURN: | 585 case CARRIAGE_RETURN: |
| 591 writeCharCode(CHAR_r); | 586 writeCharCode(CHAR_r); |
| 592 break; | 587 break; |
| 593 default: | 588 default: |
| 594 writeCharCode(CHAR_u); | 589 writeCharCode(CHAR_u); |
| 595 writeCharCode(CHAR_0); | 590 writeCharCode(CHAR_0); |
| 596 writeCharCode(CHAR_0); | 591 writeCharCode(CHAR_0); |
| 597 writeCharCode(hexDigit((charCode >> 4) & 0xf)); | 592 writeCharCode(hexDigit((charCode >> 4) & 0xf)); |
| 598 writeCharCode(hexDigit(charCode & 0xf)); | 593 writeCharCode(hexDigit(charCode & 0xf)); |
| 599 break; | 594 break; |
| 600 } | 595 } |
| 601 } else if (charCode == QUOTE || charCode == BACKSLASH) { | 596 } else if (charCode == QUOTE || charCode == BACKSLASH) { |
| 602 if (i > offset) writeStringSlice(s, offset, i); | 597 if (i > offset) writeStringSlice(s, offset, i); |
| 603 offset = i + 1; | 598 offset = i + 1; |
| 604 writeCharCode(BACKSLASH); | 599 writeCharCode(BACKSLASH); |
| 605 writeCharCode(charCode); | 600 writeCharCode(charCode); |
| 606 } | 601 } |
| 607 } | 602 } |
| 608 if (offset == 0) { | 603 if (offset == 0) { |
| 609 writeString(s); | 604 writeString(s); |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 671 bool writeJsonValue(object) { | 666 bool writeJsonValue(object) { |
| 672 if (object is num) { | 667 if (object is num) { |
| 673 if (!object.isFinite) return false; | 668 if (!object.isFinite) return false; |
| 674 writeNumber(object); | 669 writeNumber(object); |
| 675 return true; | 670 return true; |
| 676 } else if (identical(object, true)) { | 671 } else if (identical(object, true)) { |
| 677 writeString('true'); | 672 writeString('true'); |
| 678 return true; | 673 return true; |
| 679 } else if (identical(object, false)) { | 674 } else if (identical(object, false)) { |
| 680 writeString('false'); | 675 writeString('false'); |
| 681 return true; | 676 return true; |
| 682 } else if (object == null) { | 677 } else if (object == null) { |
| 683 writeString('null'); | 678 writeString('null'); |
| 684 return true; | 679 return true; |
| 685 } else if (object is String) { | 680 } else if (object is String) { |
| 686 writeString('"'); | 681 writeString('"'); |
| 687 writeStringContent(object); | 682 writeStringContent(object); |
| 688 writeString('"'); | 683 writeString('"'); |
| 689 return true; | 684 return true; |
| 690 } else if (object is List) { | 685 } else if (object is List) { |
| 691 _checkCycle(object); | 686 _checkCycle(object); |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 855 } else { | 850 } else { |
| 856 stringifier = | 851 stringifier = |
| 857 new _JsonStringStringifierPretty(output, toEncodable, indent); | 852 new _JsonStringStringifierPretty(output, toEncodable, indent); |
| 858 } | 853 } |
| 859 stringifier.writeObject(object); | 854 stringifier.writeObject(object); |
| 860 } | 855 } |
| 861 | 856 |
| 862 void writeNumber(num number) { | 857 void writeNumber(num number) { |
| 863 _sink.write(number.toString()); | 858 _sink.write(number.toString()); |
| 864 } | 859 } |
| 860 |
| 865 void writeString(String string) { | 861 void writeString(String string) { |
| 866 _sink.write(string); | 862 _sink.write(string); |
| 867 } | 863 } |
| 864 |
| 868 void writeStringSlice(String string, int start, int end) { | 865 void writeStringSlice(String string, int start, int end) { |
| 869 _sink.write(string.substring(start, end)); | 866 _sink.write(string.substring(start, end)); |
| 870 } | 867 } |
| 868 |
| 871 void writeCharCode(int charCode) { | 869 void writeCharCode(int charCode) { |
| 872 _sink.writeCharCode(charCode); | 870 _sink.writeCharCode(charCode); |
| 873 } | 871 } |
| 874 } | 872 } |
| 875 | 873 |
| 876 class _JsonStringStringifierPretty extends _JsonStringStringifier | 874 class _JsonStringStringifierPretty extends _JsonStringStringifier |
| 877 with _JsonPrettyPrintMixin { | 875 with _JsonPrettyPrintMixin { |
| 878 final String _indent; | 876 final String _indent; |
| 879 | 877 |
| 880 _JsonStringStringifierPretty(StringSink sink, toEncodable(o), this._indent) | 878 _JsonStringStringifierPretty(StringSink sink, toEncodable(o), this._indent) |
| 881 : super(sink, toEncodable); | 879 : super(sink, toEncodable); |
| 882 | 880 |
| 883 void writeIndentation(int count) { | 881 void writeIndentation(int count) { |
| 884 for (int i = 0; i < count; i++) writeString(_indent); | 882 for (int i = 0; i < count; i++) writeString(_indent); |
| 885 } | 883 } |
| 886 } | 884 } |
| 887 | 885 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 908 * Convert [object] to UTF-8 encoded JSON. | 906 * Convert [object] to UTF-8 encoded JSON. |
| 909 * | 907 * |
| 910 * Calls [addChunk] with slices of UTF-8 code units. | 908 * Calls [addChunk] with slices of UTF-8 code units. |
| 911 * These will typically have size [bufferSize], but may be shorter. | 909 * These will typically have size [bufferSize], but may be shorter. |
| 912 * The buffers are not reused, so the [addChunk] call may keep and reuse the | 910 * The buffers are not reused, so the [addChunk] call may keep and reuse the |
| 913 * chunks. | 911 * chunks. |
| 914 * | 912 * |
| 915 * If [indent] is non-`null`, the result will be "pretty-printed" with extra | 913 * If [indent] is non-`null`, the result will be "pretty-printed" with extra |
| 916 * newlines and indentation, using [indent] as the indentation. | 914 * newlines and indentation, using [indent] as the indentation. |
| 917 */ | 915 */ |
| 918 static void stringify(Object object, | 916 static void stringify(Object object, List<int> indent, toEncodable(o), |
| 919 List<int> indent, | 917 int bufferSize, void addChunk(Uint8List chunk, int start, int end)) { |
| 920 toEncodable(o), | |
| 921 int bufferSize, | |
| 922 void addChunk(Uint8List chunk, int start, int end)) { | |
| 923 _JsonUtf8Stringifier stringifier; | 918 _JsonUtf8Stringifier stringifier; |
| 924 if (indent != null) { | 919 if (indent != null) { |
| 925 stringifier = new _JsonUtf8StringifierPretty(toEncodable, indent, | 920 stringifier = new _JsonUtf8StringifierPretty( |
| 926 bufferSize, addChunk); | 921 toEncodable, indent, bufferSize, addChunk); |
| 927 } else { | 922 } else { |
| 928 stringifier = new _JsonUtf8Stringifier(toEncodable, bufferSize, addChunk); | 923 stringifier = new _JsonUtf8Stringifier(toEncodable, bufferSize, addChunk); |
| 929 } | 924 } |
| 930 stringifier.writeObject(object); | 925 stringifier.writeObject(object); |
| 931 stringifier.flush(); | 926 stringifier.flush(); |
| 932 } | 927 } |
| 933 | 928 |
| 934 /** | 929 /** |
| 935 * Must be called at the end to push the last chunk to the [addChunk] | 930 * Must be called at the end to push the last chunk to the [addChunk] |
| 936 * callback. | 931 * callback. |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1026 index = 0; | 1021 index = 0; |
| 1027 } | 1022 } |
| 1028 buffer[index++] = byte; | 1023 buffer[index++] = byte; |
| 1029 } | 1024 } |
| 1030 } | 1025 } |
| 1031 | 1026 |
| 1032 /** | 1027 /** |
| 1033 * Pretty-printing version of [_JsonUtf8Stringifier]. | 1028 * Pretty-printing version of [_JsonUtf8Stringifier]. |
| 1034 */ | 1029 */ |
| 1035 class _JsonUtf8StringifierPretty extends _JsonUtf8Stringifier | 1030 class _JsonUtf8StringifierPretty extends _JsonUtf8Stringifier |
| 1036 with _JsonPrettyPrintMixin { | 1031 with _JsonPrettyPrintMixin { |
| 1037 final List<int> indent; | 1032 final List<int> indent; |
| 1038 _JsonUtf8StringifierPretty( | 1033 _JsonUtf8StringifierPretty(toEncodable(o), this.indent, bufferSize, |
| 1039 toEncodable(o), this.indent, | 1034 void addChunk(Uint8List buffer, int start, int end)) |
| 1040 bufferSize, void addChunk(Uint8List buffer, int start, int end)) | |
| 1041 : super(toEncodable, bufferSize, addChunk); | 1035 : super(toEncodable, bufferSize, addChunk); |
| 1042 | 1036 |
| 1043 void writeIndentation(int count) { | 1037 void writeIndentation(int count) { |
| 1044 List<int> indent = this.indent; | 1038 List<int> indent = this.indent; |
| 1045 int indentLength = indent.length; | 1039 int indentLength = indent.length; |
| 1046 if (indentLength == 1) { | 1040 if (indentLength == 1) { |
| 1047 int char = indent[0]; | 1041 int char = indent[0]; |
| 1048 while (count > 0) { | 1042 while (count > 0) { |
| 1049 writeByte(char); | 1043 writeByte(char); |
| 1050 count -= 1; | 1044 count -= 1; |
| 1051 } | 1045 } |
| 1052 return; | 1046 return; |
| 1053 } | 1047 } |
| 1054 while (count > 0) { | 1048 while (count > 0) { |
| 1055 count--; | 1049 count--; |
| 1056 int end = index + indentLength; | 1050 int end = index + indentLength; |
| 1057 if (end <= buffer.length) { | 1051 if (end <= buffer.length) { |
| 1058 buffer.setRange(index, end, indent); | 1052 buffer.setRange(index, end, indent); |
| 1059 index = end; | 1053 index = end; |
| 1060 } else { | 1054 } else { |
| 1061 for (int i = 0; i < indentLength; i++) { | 1055 for (int i = 0; i < indentLength; i++) { |
| 1062 writeByte(indent[i]); | 1056 writeByte(indent[i]); |
| 1063 } | 1057 } |
| 1064 } | 1058 } |
| 1065 } | 1059 } |
| 1066 } | 1060 } |
| 1067 } | 1061 } |
| OLD | NEW |