convert.dart 2.67 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Hide the original utf8 [Codec] so that we can export our own implementation
// which adds additional error handling.
import 'dart:convert' hide utf8;
8
import 'dart:convert' as cnv show Utf8Decoder, utf8;
9

10 11
import 'package:meta/meta.dart';

12
import 'base/common.dart';
13
export 'dart:convert' hide Utf8Codec, Utf8Decoder, utf8;
14

15 16 17 18 19 20 21 22
/// The original utf8 encoding for testing overrides only.
///
/// Attempting to use the flutter tool utf8 decoder will surface an analyzer
/// warning that overrides cannot change the default value of a named
/// parameter.
@visibleForTesting
const Encoding utf8ForTesting = cnv.utf8;

23
/// A [Codec] which reports malformed bytes when decoding.
24 25 26 27
///
/// Occasionally people end up in a situation where we try to decode bytes
/// that aren't UTF-8 and we're not quite sure how this is happening.
/// This tells people to report a bug when they see this.
28
class Utf8Codec extends Encoding {
29 30 31
  const Utf8Codec({this.reportErrors = true});

  final bool reportErrors;
32 33

  @override
34 35 36
  Converter<List<int>, String> get decoder => reportErrors
    ? const Utf8Decoder()
    : const Utf8Decoder(reportErrors: false);
37 38 39 40 41 42 43 44

  @override
  Converter<String, List<int>> get encoder => cnv.utf8.encoder;

  @override
  String get name => cnv.utf8.name;
}

45
const Encoding utf8 = Utf8Codec();
46

47 48 49 50 51
class Utf8Decoder extends Converter<List<int>, String> {
  const Utf8Decoder({this.reportErrors = true});

  static const cnv.Utf8Decoder _systemDecoder =
      cnv.Utf8Decoder(allowMalformed: true);
52 53 54 55

  final bool reportErrors;

  @override
56 57 58
  String convert(List<int> input, [int start = 0, int? end]) {
    final String result = _systemDecoder.convert(input, start, end);
    // Finding a Unicode replacement character indicates that the input
59 60 61
    // was malformed.
    if (reportErrors && result.contains('\u{FFFD}')) {
      throwToolExit(
62
        'Bad UTF-8 encoding (U+FFFD; REPLACEMENT CHARACTER) found while decoding string: $result. '
63 64 65
        'The Flutter team would greatly appreciate if you could file a bug explaining '
        'exactly what you were doing when this happened:\n'
        'https://github.com/flutter/flutter/issues/new/choose\n'
66
        'The source bytes were:\n$input\n\n');
67 68 69
    }
    return result;
  }
70 71 72 73 74 75 76 77 78 79 80

  @override
  ByteConversionSink startChunkedConversion(Sink<String> sink) =>
      _systemDecoder.startChunkedConversion(sink);

  @override
  Stream<String> bind(Stream<List<int>> stream) => _systemDecoder.bind(stream);

  @override
  Converter<List<int>, T> fuse<T>(Converter<String, T> other) =>
      _systemDecoder.fuse(other);
81
}