Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(258)

Unified Diff: sdk/lib/convert/ascii.dart

Issue 22875034: Add ASCII encoding to lib/convert. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address review comments Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | sdk/lib/convert/convert.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/convert/ascii.dart
diff --git a/sdk/lib/convert/latin1.dart b/sdk/lib/convert/ascii.dart
similarity index 50%
copy from sdk/lib/convert/latin1.dart
copy to sdk/lib/convert/ascii.dart
index b09a987bfc6fd8d8f728e1d78ba8f6ed8671b38f..bea9680aea207064ff8311e34b3e61aaa9365c34 100644
--- a/sdk/lib/convert/latin1.dart
+++ b/sdk/lib/convert/ascii.dart
@@ -5,80 +5,80 @@
part of dart.convert;
/**
- * An instance of the default implementation of the [Latin1Codec].
+ * An instance of the default implementation of the [AsciiCodec].
*
- * This instance provides a convenient access to the most common ISO Latin 1
+ * This instance provides a convenient access to the most common ASCII
* use cases.
*
* Examples:
*
- * var encoded = LATIN1.encode("blåbærgrød");
- * var decoded = LATIN1.decode([0x62, 0x6c, 0xe5, 0x62, 0xe6,
- * 0x72, 0x67, 0x72, 0xf8, 0x64]);
+ * var encoded = ASCII.encode("This is ASCII!");
+ * var decoded = ASCII.decode([0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73,
+ * 0x20, 0x41, 0x53, 0x43, 0x49, 0x49, 0x21]);
*/
-const LATIN1 = const Latin1Codec();
+const ASCII = const AsciiCodec();
+
+const int _ASCII_MASK = 0x7F;
/**
- * A [LatinCodec] encodes strings to ISO Latin-1 (aka ISO-8859-1) bytes
- * and decodes Latin-1 bytes to strings.
+ * An [AsciiCodec] allows encoding strings as ASCII bytes
+ * and decoding ASCII bytes to strings.
*/
-class Latin1Codec extends _Encoding {
+class AsciiCodec extends _Encoding {
final bool _allowInvalid;
/**
- * Instantiates a new [Latin1Codec].
+ * Instantiates a new [AsciiCodec].
*
* If [allowInvalid] is true, the [decode] method and the converter
- * returned by [decoder] will default to allowing invalid values. Invalid
- * values are decoded into the Unicode Replacement character (U+FFFD).
- * Calls to the [decode] method can override this default.
+ * returned by [decoder] will default to allowing invalid values.
+ * If allowing invalid values, the values will be decoded into the Unicode
+ * Replacement character (U+FFFD). If not, an exception will be thrown.
+ * Calls to the [decode] method can choose to override this default.
*
* Encoders will not accept invalid (non Latin-1) characters.
*/
- const Latin1Codec({bool allowInvalid: false}) : _allowInvalid = allowInvalid;
+ const AsciiCodec({bool allowInvalid: false}) : _allowInvalid = allowInvalid;
/**
- * Decodes the Latin-1 [bytes] (a list of unsigned 8-bit integers) to the
+ * Decodes the ASCII [bytes] (a list of unsigned 7-bit integers) to the
* corresponding string.
*
- * If [bytes] contains values that are not in the range 0 .. 255, the decoder
+ * If [bytes] contains values that are not in the range 0 .. 127, the decoder
* will eventually throw a [FormatException].
*
* If [allowInvalid] is not provided, it defaults to the value used to create
- * this [Latin1Codec].
+ * this [AsciiCodec].
*/
String decode(List<int> bytes, { bool allowInvalid }) {
if (allowInvalid == null) allowInvalid = _allowInvalid;
if (allowInvalid) {
- return const Latin1Decoder(allowInvalid: true).convert(bytes);
+ return const AsciiDecoder(allowInvalid: true).convert(bytes);
} else {
- return const Latin1Decoder(allowInvalid: false).convert(bytes);
+ return const AsciiDecoder(allowInvalid: false).convert(bytes);
}
}
- Converter<String, List<int>> get encoder => const Latin1Encoder();
+ Converter<String, List<int>> get encoder => const AsciiEncoder();
Converter<List<int>, String> get decoder =>
- _allowInvalid ? const Latin1Decoder(allowInvalid: true)
- : const Latin1Decoder(allowInvalid: false);
+ _allowInvalid ? const AsciiDecoder(allowInvalid: true)
+ : const AsciiDecoder(allowInvalid: false);
}
-/**
- * This class converts strings of only ISO Latin-1 characters to bytes.
- */
-class Latin1Encoder extends Converter<String, List<int>> {
- const Latin1Encoder();
+// Superclass for [AsciiEncoder] and [Latin1Encoder].
+// Generalizes common operations that only differ by a mask;
+class _UnicodeSubsetEncoder extends Converter<String, List<int>> {
+ final int _subsetMask;
+
+ const _UnicodeSubsetEncoder(this._subsetMask);
- /**
- * Converts [string] to its Latin-1 bytes (a list of
- * unsigned 8-bit integers).
- */
List<int> convert(String string) {
// TODO(11971): Use Uint8List when possible.
List result = new List<int>(string.length);
for (int i = 0; i < string.length; i++) {
var codeUnit = string.codeUnitAt(i);
- if ((codeUnit & ~0xFF) != 0) {
- throw new ArgumentError("String contains non-Latin-1 characters.");
+ if ((codeUnit & ~_subsetMask) != 0) {
+ throw new ArgumentError("String contains invalid characters.");
}
result[i] = codeUnit;
}
@@ -96,7 +96,7 @@ class Latin1Encoder extends Converter<String, List<int>> {
if (sink is! ByteConversionSink) {
sink = new ByteConversionSink.from(sink);
}
- return new _Latin1EncoderSink(sink);
+ return new _UnicodeSubsetEncoderSink(_subsetMask, sink);
}
// Override the base-class' bind, to provide a better type.
@@ -104,25 +104,24 @@ class Latin1Encoder extends Converter<String, List<int>> {
}
/**
+ * This class converts strings of only ASCII characters to bytes.
+ */
+class AsciiEncoder extends _UnicodeSubsetEncoder {
+ const AsciiEncoder() : super(_ASCII_MASK);
+}
+
+/**
* This class encodes chunked strings to bytes (unsigned 8-bit
* integers).
*/
-class _Latin1EncoderSink extends StringConversionSinkBase {
- static const _DEFAULT_BYTE_BUFFER_SIZE = 1024;
+class _UnicodeSubsetEncoderSink extends StringConversionSinkBase {
final ByteConversionSink _sink;
+ final int _subsetMask;
- // TODO(11971): Use Uint8List when available.
- List<int> _buffer = new List<int>(_DEFAULT_BYTE_BUFFER_SIZE);
- int _bufferIndex = 0;
-
- _Latin1EncoderSink(this._sink);
+ _UnicodeSubsetEncoderSink(this._subsetMask, this._sink);
void close() {
- if (_bufferIndex > 0) {
- _sink.addSlice(_buffer, 0, _bufferIndex, true);
- } else {
- _sink.close();
- }
+ _sink.close();
}
void addSlice(String source, int start, int end, bool isLast) {
@@ -134,17 +133,15 @@ class _Latin1EncoderSink extends StringConversionSinkBase {
}
for (int i = start; i < end; i++) {
int codeUnit = source.codeUnitAt(i);
- if ((codeUnit & ~0xFF) != 0) {
- throw new ArgumentError("Source contains non-Latin-1 characters.");
- }
- _buffer[_bufferIndex] = codeUnit;
- _bufferIndex++;
- if (_bufferIndex == _buffer.length) {
- _sink.addSlice(_buffer, 0, _bufferIndex, false);
- _bufferIndex = 0;
+ if ((codeUnit & ~_subsetMask) != 0) {
+ throw new ArgumentError(
+ "Source contains invalid character with code point: $codeUnit.");
}
}
- if (isLast) close();
+ _sink.add(source.codeUnits);
+ if (isLast) {
+ close();
+ }
}
}
@@ -152,32 +149,36 @@ class _Latin1EncoderSink extends StringConversionSinkBase {
* This class converts Latin-1 bytes (lists of unsigned 8-bit integers)
* to a string.
*/
-class Latin1Decoder extends Converter<List<int>, String> {
+abstract class _UnicodeSubsetDecoder extends Converter<List<int>, String> {
final bool _allowInvalid;
+ final int _subsetMask;
/**
- * Instantiates a new [Latin1Decoder].
+ * Instantiates a new decoder.
*
- * The optional [allowInvalid] argument defines how [convert] deals
+ * The [_allowInvalid] argument defines how [convert] deals
* with invalid bytes.
*
- * If it is `true`, [convert] replaces invalid bytes with the Unicode
- * Replacement character `U+FFFD` (�).
+ * The [_subsetMask] argument is a bit mask used to define the subset
+ * of Unicode being decoded. Use [_LATIN1_MASK] for Latin-1 (8-bit) or
+ * [_ASCII_MASK] for ASCII (7-bit).
+ *
+ * If [_allowInvalid] is `true`, [convert] replaces invalid bytes with the
+ * Unicode Replacement character `U+FFFD` (�).
* Otherwise it throws a [FormatException].
*/
- const Latin1Decoder({ bool allowInvalid: false })
- : this._allowInvalid = allowInvalid;
+ const _UnicodeSubsetDecoder(this._allowInvalid, this._subsetMask);
/**
- * Converts the Latin=1 [bytes] (a list of unsigned 8-bit integers) to the
+ * Converts the [bytes] (a list of unsigned 7- or 8-bit integers) to the
* corresponding string.
*/
String convert(List<int> bytes) {
for (int i = 0; i < bytes.length; i++) {
int byte = bytes[i];
- if ((byte & ~0xFF) != 0) {
+ if ((byte & ~_subsetMask) != 0) {
if (!_allowInvalid) {
- throw new FormatException("Non-byte in byte list");
+ throw new FormatException("Invalid value in input: $byte");
}
return _convertInvalid(bytes);
}
@@ -189,7 +190,7 @@ class Latin1Decoder extends Converter<List<int>, String> {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
int value = bytes[i];
- if ((value & ~0xFF) != 0) value = 0xFFFD;
+ if ((value & ~_subsetMask) != 0) value = 0xFFFD;
buffer.writeCharCode(value);
}
return buffer.toString();
@@ -217,28 +218,45 @@ class Latin1Decoder extends Converter<List<int>, String> {
Stream<String> bind(Stream<List<int>> stream) => super.bind(stream);
}
-class _Latin1DecoderSink extends ByteConversionSinkBase {
+class AsciiDecoder extends _UnicodeSubsetDecoder {
+ const AsciiDecoder({bool allowInvalid: false})
+ : super(allowInvalid, _ASCII_MASK);
+
+ /**
+ * Starts a chunked conversion.
+ *
+ * The converter works more efficiently if the given [sink] is a
+ * [StringConversionSink].
+ */
+ ByteConversionSink startChunkedConversion(
+ ChunkedConversionSink<String> sink) {
+ StringConversionSink stringSink;
+ if (sink is StringConversionSink) {
+ stringSink = sink;
+ } else {
+ stringSink = new StringConversionSink.from(sink);
+ }
+ // TODO(lrn): Use asUtf16Sink when it becomes available. It
+ // works just as well, is likely to have less decoding overhead,
+ // and make adding U+FFFD easier.
+ // At that time, merge this with _Latin1DecoderSink;
+ return new _AsciiDecoderSink(_allowInvalid, stringSink.asUtf8Sink(false));
+ }
+}
+
+class _AsciiDecoderSink extends ByteConversionSinkBase {
final bool _allowInvalid;
- StringConversionSink _sink;
- _Latin1DecoderSink(this._allowInvalid, this._sink);
+ ByteConversionSink _utf8Sink;
+ _AsciiDecoderSink(this._allowInvalid, this._utf8Sink);
void close() {
- _sink.close();
+ _utf8Sink.close();
}
void add(List<int> source) {
addSlice(source, 0, source.length, false);
}
- void _addSliceToSink(List<int> source, int start, int end, bool isLast) {
- // If _sink was a UTF-16 conversion sink, just add the slice directly with
- // _sink.addSlice(source, start, end, isLast).
- // The code below is an incredibly stupid workaround until a real
- // solution can be made.
- _sink.add(new String.fromCharCodes(source.getRange(start, end)));
- if (isLast) close();
- }
-
void addSlice(List<int> source, int start, int end, bool isLast) {
if (start < 0 || start > source.length) {
throw new RangeError.range(start, 0, source.length);
@@ -247,19 +265,19 @@ class _Latin1DecoderSink extends ByteConversionSinkBase {
throw new RangeError.range(end, start, source.length);
}
for (int i = start; i < end; i++) {
- if ((source[i] & ~0xFF) != 0) {
+ if ((source[i] & ~_ASCII_MASK) != 0) {
if (_allowInvalid) {
- if (i > start) _addSliceToSink(source, start, i, false);
+ if (i > start) _utf8Sink.addSlice(source, start, i, false);
// Add UTF-8 encoding of U+FFFD.
- _addSliceToSink(const[0xFFFD], 0, 1, false);
+ _utf8Sink.add(const<int>[0xEF, 0xBF, 0xBD]);
start = i + 1;
} else {
- throw new FormatException("Source contains non-Latin-1 characters.");
+ throw new FormatException("Source contains non-ASCII bytes.");
}
}
}
if (start < end) {
- _addSliceToSink(source, start, end, isLast);
+ _utf8Sink.addSlice(source, start, end, isLast);
} else if (isLast) {
close();
}
« no previous file with comments | « no previous file | sdk/lib/convert/convert.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698