| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // @dart = 2.9 |
| |
| import "package:expect/expect.dart"; |
| import 'dart:convert'; |
| |
| var latin1Strings = ["pure ascii", "blåbærgrød", "\x00 edge cases \xff"]; |
| |
| var nonLatin1Strings = ["Edge case \u{100}", "Edge case super-BMP \u{10000}"]; |
| |
| void main() { |
| // Build longer versions of the example strings. |
| for (int i = 0, n = latin1Strings.length; i < n; i++) { |
| var string = latin1Strings[i]; |
| while (string.length < 1024) { |
| string += string; |
| } |
| latin1Strings.add(string); |
| } |
| for (int i = 0, n = nonLatin1Strings.length; i < n; i++) { |
| var string = nonLatin1Strings[i]; |
| while (string.length < 1024) { |
| string += string; |
| } |
| nonLatin1Strings.add(string); |
| } |
| testDirectConversions(); |
| testChunkedConversions(); |
| } |
| |
| void testDirectConversions() { |
| for (var codec in [latin1, new Latin1Codec()]) { |
| for (var latin1String in latin1Strings) { |
| List bytes = codec.encoder.convert(latin1String); |
| Expect.listEquals(latin1String.codeUnits.toList(), bytes, latin1String); |
| String roundTripString = codec.decoder.convert(bytes); |
| Expect.equals(latin1String, roundTripString); |
| roundTripString = codec.decode(bytes); |
| Expect.equals(latin1String, roundTripString); |
| } |
| |
| for (var nonLatin1String in nonLatin1Strings) { |
| Expect.throws(() { |
| print(codec.encoder.convert(nonLatin1String)); |
| }, null, nonLatin1String); |
| } |
| |
| var encode = codec.encoder.convert; |
| Expect.listEquals([0x42, 0xff, 0x44], encode("AB\xffDE", 1, 4)); |
| Expect.listEquals([0x42, 0xff, 0x44, 0x45], encode("AB\xffDE", 1)); |
| Expect.listEquals([0x42, 0xff, 0x44], encode("\u3000B\xffD\u3000", 1, 4)); |
| Expect.throws(() { |
| encode("\u3000B\xffD\u3000", 0, 4); |
| }); |
| Expect.throws(() { |
| encode("\u3000B\xffD\u3000", 1); |
| }); |
| Expect.throws(() { |
| encode("\u3000B\xffD\u3000", 1, 5); |
| }); |
| Expect.throws(() { |
| encode("\u3000B\xffD\u3000", -1, 4); |
| }); |
| Expect.throws(() { |
| encode("\u3000B\xffD\u3000", 1, -1); |
| }); |
| Expect.throws(() { |
| encode("\u3000B\xffD\u3000", 3, 2); |
| }); |
| |
| var decode = codec.decoder.convert; |
| Expect.equals("B\xffD", decode([0x41, 0x42, 0xff, 0x44, 0x45], 1, 4)); |
| Expect.equals("B\xffDE", decode([0x41, 0x42, 0xff, 0x44, 0x45], 1)); |
| Expect.equals("B\xffD", decode([0xFF, 0x42, 0xff, 0x44, 0xFF], 1, 4)); |
| Expect.throws(() { |
| decode([0x3000, 0x42, 0xff, 0x44, 0x3000], 0, 4); |
| }); |
| Expect.throws(() { |
| decode([0x3000, 0x42, 0xff, 0x44, 0x3000], 1); |
| }); |
| Expect.throws(() { |
| decode([0x3000, 0x42, 0xff, 0x44, 0x3000], 1, 5); |
| }); |
| Expect.throws(() { |
| decode([0x3000, 0x42, 0xff, 0x44, 0x3000], -1, 4); |
| }); |
| Expect.throws(() { |
| decode([0x3000, 0x42, 0xff, 0x44, 0x3000], 1, -1); |
| }); |
| Expect.throws(() { |
| decode([0x3000, 0x42, 0xff, 0x44, 0x3000], 3, 2); |
| }); |
| } |
| |
| var allowInvalidCodec = new Latin1Codec(allowInvalid: true); |
| var invalidBytes = [0, 1, 0xff, 0xdead, 0]; |
| String decoded = allowInvalidCodec.decode(invalidBytes); |
| Expect.equals("\x00\x01\xFF\uFFFD\x00", decoded); |
| decoded = allowInvalidCodec.decoder.convert(invalidBytes); |
| Expect.equals("\x00\x01\xFF\uFFFD\x00", decoded); |
| decoded = latin1.decode(invalidBytes, allowInvalid: true); |
| Expect.equals("\x00\x01\xFF\uFFFD\x00", decoded); |
| } |
| |
| List<int> encode( |
| String str, int chunkSize, Converter<String, List<int>> converter) { |
| List<int> bytes = <int>[]; |
| var byteSink = new ByteConversionSink.withCallback(bytes.addAll); |
| var stringConversionSink = converter.startChunkedConversion(byteSink); |
| for (int i = 0; i < str.length; i += chunkSize) { |
| if (i + chunkSize <= str.length) { |
| stringConversionSink.add(str.substring(i, i + chunkSize)); |
| } else { |
| stringConversionSink.add(str.substring(i)); |
| } |
| } |
| stringConversionSink.close(); |
| return bytes; |
| } |
| |
| String decode( |
| List<int> bytes, int chunkSize, Converter<List<int>, String> converter) { |
| StringBuffer buf = new StringBuffer(); |
| var stringSink = new StringConversionSink.fromStringSink(buf); |
| var byteConversionSink = converter.startChunkedConversion(stringSink); |
| for (int i = 0; i < bytes.length; i += chunkSize) { |
| if (i + chunkSize <= bytes.length) { |
| byteConversionSink.add(bytes.sublist(i, i + chunkSize)); |
| } else { |
| byteConversionSink.add(bytes.sublist(i)); |
| } |
| } |
| byteConversionSink.close(); |
| return buf.toString(); |
| } |
| |
| void testChunkedConversions() { |
| // Check encoding. |
| for (var converter in [ |
| latin1.encoder, |
| new Latin1Codec().encoder, |
| new Latin1Encoder() |
| ]) { |
| for (int chunkSize in [1, 2, 5, 50]) { |
| for (var latin1String in latin1Strings) { |
| var units = latin1String.codeUnits.toList(); |
| List bytes = encode(latin1String, chunkSize, converter); |
| Expect.listEquals(units, bytes); |
| } |
| for (var nonLatin1String in nonLatin1Strings) { |
| Expect.throws(() { |
| encode(nonLatin1String, chunkSize, converter); |
| }); |
| } |
| } |
| } |
| // Check decoding. |
| for (var converter in [ |
| latin1.decoder, |
| new Latin1Codec().decoder, |
| new Latin1Decoder() |
| ]) { |
| for (int chunkSize in [1, 2, 5, 50]) { |
| for (var latin1String in latin1Strings) { |
| var units = latin1String.codeUnits.toList(); |
| Expect.equals(latin1String, decode(units, chunkSize, converter)); |
| } |
| for (var nonLatin1String in nonLatin1Strings) { |
| var units = nonLatin1String.codeUnits.toList(); |
| Expect.throws(() { |
| decode(units, chunkSize, converter); |
| }); |
| } |
| } |
| } |
| } |