Split up parts in `package:intl` so they can be migrated incrementally to null safety.

PiperOrigin-RevId: 327788719
diff --git a/lib/intl.dart b/lib/intl.dart
index f4ed71d..cb83005 100644
--- a/lib/intl.dart
+++ b/lib/intl.dart
@@ -20,27 +20,17 @@
 library intl;
 
 import 'dart:async';
-import 'dart:collection';
-import 'dart:convert';
-import 'dart:math';
 
-import 'package:clock/clock.dart';
-
-import 'date_symbols.dart';
-import 'number_symbols.dart';
-import 'number_symbols_data.dart';
-import 'src/date_format_internal.dart';
 import 'src/global_state.dart' as global_state;
+import 'src/intl/date_format.dart' show DateFormat;
 import 'src/intl_helpers.dart' as helpers;
 import 'src/plural_rules.dart' as plural_rules;
 
-part 'src/intl/bidi_formatter.dart';
-part 'src/intl/bidi_utils.dart';
-part 'src/intl/compact_number_format.dart';
-part 'src/intl/date_format.dart';
-part 'src/intl/date_format_field.dart';
-part 'src/intl/date_format_helpers.dart';
-part 'src/intl/number_format.dart';
+export 'src/intl/bidi.dart' show Bidi;
+export 'src/intl/bidi_formatter.dart' show BidiFormatter;
+export 'src/intl/date_format.dart' show DateFormat;
+export 'src/intl/number_format.dart' show NumberFormat, MicroMoney;
+export 'src/intl/text_direction.dart' show TextDirection;
 
 /// The Intl class provides a common entry point for internationalization
 /// related tasks. An Intl instance can be created for a particular locale
diff --git a/lib/src/intl/bidi_utils.dart b/lib/src/intl/bidi.dart
similarity index 90%
rename from lib/src/intl/bidi_utils.dart
rename to lib/src/intl/bidi.dart
index 60a268b..ba028c4 100644
--- a/lib/src/intl/bidi_utils.dart
+++ b/lib/src/intl/bidi.dart
@@ -3,50 +3,18 @@
 // BSD-style license that can be found in the LICENSE file.
 // @dart=2.9
 
-part of intl;
-
-// Suppress naming issues as changing them would be breaking.
-// ignore_for_file: constant_identifier_names
-
 /// Bidi stands for Bi-directional text.  According to
 /// http://en.wikipedia.org/wiki/Bi-directional_text: Bi-directional text is
 /// text containing text in both text directionalities, both right-to-left (RTL)
 /// and left-to-right (LTR). It generally involves text containing different
 /// types of alphabets, but may also refer to boustrophedon, which is changing
 /// text directionality in each row.
-///
-/// This file provides some utility classes for determining directionality of
-/// text, switching CSS layout from LTR to RTL, and other normalizing utilities
-/// needed when switching between RTL and LTR formatting.
-///
-/// It defines the TextDirection class which is used to represent directionality
-/// of text,
-/// In most cases, it is preferable to use bidi_formatter.dart, which provides
-/// bidi functionality in the given directional context, instead of using
-/// bidi_utils.dart directly.
-class TextDirection {
-  static const LTR = TextDirection._('LTR', 'ltr');
-  static const RTL = TextDirection._('RTL', 'rtl');
-  // If the directionality of the text cannot be determined and we are not using
-  // the context direction (or if the context direction is unknown), then the
-  // text falls back on the more common ltr direction.
-  static const UNKNOWN = TextDirection._('UNKNOWN', 'ltr');
 
-  /// Textual representation of the directionality constant. One of
-  /// 'LTR', 'RTL', or 'UNKNOWN'.
-  final String value;
+import '../global_state.dart' as global_state;
+import 'text_direction.dart';
 
-  /// Textual representation of the directionality when used in span tag.
-  final String spanText;
-
-  const TextDirection._(this.value, this.spanText);
-
-  /// Returns true if [otherDirection] is known to be different from this
-  /// direction.
-  bool isDirectionChange(TextDirection otherDirection) =>
-      otherDirection != TextDirection.UNKNOWN && this != otherDirection;
-}
-
+// Suppress naming issues as changing them would be breaking.
+// ignore_for_file: constant_identifier_names
 // ignore: avoid_classes_with_only_static_members
 /// This provides utility methods for working with bidirectional text. All
 /// of the methods are static, and are organized into a class primarily to
@@ -170,7 +138,7 @@
   /// Sindhi (sd) and Uyghur (ug).  The presence of other subtags of the
   /// language code, e.g. regions like EG (Egypt), is ignored.
   static bool isRtlLanguage([String languageString]) {
-    var language = languageString ?? Intl.getCurrentLocale();
+    var language = languageString ?? global_state.getCurrentLocale();
     if (_lastLocaleCheckedForRtl != language) {
       _lastLocaleCheckedForRtl = language;
       _lastRtlCheck = _rtlLocaleRegex.hasMatch(language);
diff --git a/lib/src/intl/bidi_formatter.dart b/lib/src/intl/bidi_formatter.dart
index 7cccfdd..f84cd14 100644
--- a/lib/src/intl/bidi_formatter.dart
+++ b/lib/src/intl/bidi_formatter.dart
@@ -3,7 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 // @dart=2.9
 
-part of intl;
+import 'dart:convert';
+
+import 'bidi.dart';
+import 'text_direction.dart';
 
 // Suppress naming issues as changing them would be breaking.
 // ignore_for_file: non_constant_identifier_names
diff --git a/lib/src/intl/compact_number_format.dart b/lib/src/intl/compact_number_format.dart
index 5ac6442..272ef22 100644
--- a/lib/src/intl/compact_number_format.dart
+++ b/lib/src/intl/compact_number_format.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 // @dart=2.9
 
-part of intl;
+part of 'number_format.dart';
 
 // Suppress naming issues as changes would be breaking.
 // ignore_for_file: constant_identifier_names
diff --git a/lib/src/intl/constants.dart b/lib/src/intl/constants.dart
new file mode 100644
index 0000000..51ac779
--- /dev/null
+++ b/lib/src/intl/constants.dart
@@ -0,0 +1,5 @@
+// Copyright (c) 2020, 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.
+
+final int asciiZeroCodeUnit = '0'.codeUnitAt(0);
diff --git a/lib/src/intl/date_format_helpers.dart b/lib/src/intl/date_builder.dart
similarity index 70%
rename from lib/src/intl/date_format_helpers.dart
rename to lib/src/intl/date_builder.dart
index 3097dc2..7281fb3 100644
--- a/lib/src/intl/date_format_helpers.dart
+++ b/lib/src/intl/date_builder.dart
@@ -3,36 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 // @dart=2.9
 
-part of intl;
+import 'package:clock/clock.dart';
 
-/// Given a month and day number, return the day of the year, all one-based.
-///
-/// For example,
-///  * January 2nd (1, 2) -> 2.
-///  * February 5th (2, 5) -> 36.
-///  * March 1st of a non-leap year (3, 1) -> 60.
-int _dayOfYear(int month, int day, bool leapYear) {
-  if (month == 1) return day;
-  if (month == 2) return day + 31;
-  return ordinalDayFromMarchFirst(month, day) + 59 + (leapYear ? 1 : 0);
-}
-
-/// Return true if this is a leap year. Rely on [DateTime] to do the
-/// underlying calculation, even though it doesn't expose the test to us.
-bool _isLeapYear(DateTime date) {
-  var feb29 = DateTime(date.year, 2, 29);
-  return feb29.month == 2;
-}
-
-/// Return the day of the year counting March 1st as 1, after which the
-/// number of days per month is constant, so it's easier to calculate.
-/// Formula from http://en.wikipedia.org/wiki/Ordinal_date
-int ordinalDayFromMarchFirst(int month, int day) =>
-    ((30.6 * month) - 91.4).floor() + day;
+import 'date_computation.dart' as date_computation;
 
 /// A class for holding onto the data for a date so that it can be built
 /// up incrementally.
-class _DateBuilder {
+class DateBuilder {
   // Default the date values to the EPOCH so that there's a valid date
   // in case the format doesn't set them.
   int year = 1970,
@@ -76,14 +53,14 @@
 
   // We do set it, the analyzer just can't tell.
   // ignore: prefer_final_fields
-  var _dateOnly = false;
+  var dateOnly = false;
 
   /// The function we will call to create a DateTime from its component pieces.
   ///
   /// This is normally only modified in tests that want to introduce errors.
   final _DateTimeConstructor _dateTimeConstructor;
 
-  _DateBuilder(this._locale, this._dateTimeConstructor);
+  DateBuilder(this._locale, this._dateTimeConstructor);
 
   // Functions that exist just to be closurized so we can pass them to a general
   // method.
@@ -151,13 +128,14 @@
     // _correctForErrors, but we may not be able to compensate for a midnight
     // that doesn't exist. So tolerate an hour value of zero or one in these
     // cases.
-    var minimumDate = _dateOnly && date.hour == 1 ? 0 : date.hour;
+    var minimumDate = dateOnly && date.hour == 1 ? 0 : date.hour;
     _verify(hour24, minimumDate, date.hour, 'hour', s, date);
     if (dayOfYear > 0) {
       // We have an ordinal date, compute the corresponding date for the result
       // and compare to that.
-      var leapYear = _isLeapYear(date);
-      var correspondingDay = _dayOfYear(date.month, date.day, leapYear);
+      var leapYear = date_computation.isLeapYear(date);
+      var correspondingDay =
+          date_computation.dayOfYear(date.month, date.day, leapYear);
       _verify(
           dayOfYear, correspondingDay, correspondingDay, 'dayOfYear', s, date);
     } else {
@@ -282,8 +260,9 @@
       return result;
     }
 
-    var leapYear = _isLeapYear(result);
-    var resultDayOfYear = _dayOfYear(result.month, result.day, leapYear);
+    var leapYear = date_computation.isLeapYear(result);
+    var resultDayOfYear =
+        date_computation.dayOfYear(result.month, result.day, leapYear);
 
     // Check for the UTC failure. Are we expecting to produce a local time, but
     // the result is UTC. However, the local time might happen to be the same as
@@ -300,7 +279,7 @@
       return asDate(retries: retries - 1);
     }
 
-    if (_dateOnly && result.hour != 0) {
+    if (dateOnly && result.hour != 0) {
       // This could be a flake, try again.
       var tryAgain = asDate(retries: retries - 1);
       if (tryAgain != result) {
@@ -309,8 +288,9 @@
       }
 
       // Trying again didn't work, try to force the offset.
-      var expectedDayOfYear =
-          dayOfYear == 0 ? _dayOfYear(month, day, leapYear) : dayOfYear;
+      var expectedDayOfYear = dayOfYear == 0
+          ? date_computation.dayOfYear(month, day, leapYear)
+          : dayOfYear;
 
       // If we're _dateOnly, then hours should be zero, but might have been
       // offset to e.g. 11:00pm the previous day. Add that time back in. This
@@ -335,7 +315,7 @@
       // hour takes us back to 11:00pm the day before. In that case the 1:00am
       // answer on the correct date is preferable.
       var adjustedDayOfYear =
-          _dayOfYear(adjusted.month, adjusted.day, leapYear);
+          date_computation.dayOfYear(adjusted.month, adjusted.day, leapYear);
       if (adjustedDayOfYear != expectedDayOfYear) {
         return result;
       }
@@ -346,96 +326,6 @@
   }
 }
 
-/// A simple and not particularly general stream class to make parsing
-/// dates from strings simpler. It is general enough to operate on either
-/// lists or strings.
-// TODO(alanknight): With the improvements to the collection libraries
-// since this was written we might be able to get rid of it entirely
-// in favor of e.g. aString.split('') giving us an iterable of one-character
-// strings, or else make the implementation trivial. And consider renaming,
-// as _Stream is now just confusing with the system Streams.
-class _Stream {
-  dynamic contents;
-  int index = 0;
-
-  _Stream(this.contents);
-
-  bool atEnd() => index >= contents.length;
-
-  dynamic next() => contents[index++];
-
-  /// Return the next [howMany] items, or as many as there are remaining.
-  /// Advance the stream by that many positions.
-  dynamic read([int howMany = 1]) {
-    var result = peek(howMany);
-    index += howMany;
-    return result;
-  }
-
-  /// Does the input start with the given string, if we start from the
-  /// current position.
-  bool startsWith(String pattern) {
-    if (contents is String) return contents.startsWith(pattern, index);
-    return pattern == peek(pattern.length);
-  }
-
-  /// Return the next [howMany] items, or as many as there are remaining.
-  /// Does not modify the stream position.
-  dynamic peek([int howMany = 1]) {
-    dynamic result;
-    if (contents is String) {
-      String stringContents = contents;
-      result = stringContents.substring(
-          index, min(index + howMany, stringContents.length));
-    } else {
-      // Assume List
-      result = contents.sublist(index, index + howMany);
-    }
-    return result;
-  }
-
-  /// Return the remaining contents of the stream
-  dynamic rest() => peek(contents.length - index);
-
-  /// Find the index of the first element for which [f] returns true.
-  /// Advances the stream to that position.
-  int findIndex(bool Function(dynamic) f) {
-    while (!atEnd()) {
-      if (f(next())) return index - 1;
-    }
-    return null;
-  }
-
-  /// Find the indexes of all the elements for which [f] returns true.
-  /// Leaves the stream positioned at the end.
-  List<dynamic> findIndexes(bool Function(dynamic) f) {
-    var results = [];
-    while (!atEnd()) {
-      if (f(next())) results.add(index - 1);
-    }
-    return results;
-  }
-
-  /// Assuming that the contents are characters, read as many digits as we
-  /// can see and then return the corresponding integer, advancing the receiver.
-  ///
-  /// For non-ascii digits, the optional arguments are a regular expression
-  /// [digitMatcher] to find the next integer, and the codeUnit of the local
-  /// zero [zeroDigit].
-  int nextInteger({RegExp digitMatcher, int zeroDigit}) {
-    var string =
-        (digitMatcher ?? DateFormat._asciiDigitMatcher).stringMatch(rest());
-    if (string == null || string.isEmpty) return null;
-    read(string.length);
-    if (zeroDigit != null && zeroDigit != DateFormat._asciiZeroCodeUnit) {
-      // Trying to optimize this, as it might get called a lot.
-      var oldDigits = string.codeUnits;
-      var newDigits = List<int>(string.length);
-      for (var i = 0; i < string.length; i++) {
-        newDigits[i] = oldDigits[i] - zeroDigit + DateFormat._asciiZeroCodeUnit;
-      }
-      string = String.fromCharCodes(newDigits);
-    }
-    return int.parse(string);
-  }
-}
+/// Defines a function type for creating DateTime instances.
+typedef _DateTimeConstructor = DateTime Function(int year, int month, int day,
+    int hour24, int minute, int second, int fractionalSecond, bool utc);
diff --git a/lib/src/intl/date_computation.dart b/lib/src/intl/date_computation.dart
new file mode 100644
index 0000000..ed5566f
--- /dev/null
+++ b/lib/src/intl/date_computation.dart
@@ -0,0 +1,24 @@
+/// Given a month and day number, return the day of the year, all one-based.
+///
+/// For example,
+///  * January 2nd (1, 2) -> 2.
+///  * February 5th (2, 5) -> 36.
+///  * March 1st of a non-leap year (3, 1) -> 60.
+int dayOfYear(int month, int day, bool leapYear) {
+  if (month == 1) return day;
+  if (month == 2) return day + 31;
+  return ordinalDayFromMarchFirst(month, day) + 59 + (leapYear ? 1 : 0);
+}
+
+/// Return true if this is a leap year. Rely on [DateTime] to do the
+/// underlying calculation, even though it doesn't expose the test to us.
+bool isLeapYear(DateTime date) {
+  var feb29 = DateTime(date.year, 2, 29);
+  return feb29.month == 2;
+}
+
+/// Return the day of the year counting March 1st as 1, after which the
+/// number of days per month is constant, so it's easier to calculate.
+/// Formula from http://en.wikipedia.org/wiki/Ordinal_date
+int ordinalDayFromMarchFirst(int month, int day) =>
+    ((30.6 * month) - 91.4).floor() + day;
diff --git a/lib/src/intl/date_format.dart b/lib/src/intl/date_format.dart
index 70e23a0..03d868c 100644
--- a/lib/src/intl/date_format.dart
+++ b/lib/src/intl/date_format.dart
@@ -3,7 +3,17 @@
 // BSD-style license that can be found in the LICENSE file.
 // @dart=2.9
 
-part of intl;
+import 'package:intl/date_symbols.dart';
+import 'package:intl/intl.dart';
+import 'package:intl/src/date_format_internal.dart';
+
+import 'constants.dart' as constants;
+import 'date_builder.dart';
+import 'date_computation.dart' as date_computation;
+import 'intl_stream.dart';
+import 'regexp.dart' as regexp;
+
+part 'date_format_field.dart';
 
 // Suppress naming issues as changes would breaking.
 // ignore_for_file: non_constant_identifier_names, constant_identifier_names
@@ -353,9 +363,9 @@
 
   DateTime _parseLoose(String inputString, bool utc) {
     var dateFields =
-        _DateBuilder(locale ?? Intl.defaultLocale, dateTimeConstructor);
+        DateBuilder(locale ?? Intl.defaultLocale, dateTimeConstructor);
     if (utc) dateFields.utc = true;
-    var stream = _Stream(inputString);
+    var stream = IntlStream(inputString);
     for (var field in _formatFields) {
       field.parseLoose(stream, dateFields);
     }
@@ -382,10 +392,10 @@
     // TODO(alanknight): The Closure code refers to special parsing of numeric
     // values with no delimiters, which we currently don't do. Should we?
     var dateFields =
-        _DateBuilder(locale ?? Intl.defaultLocale, dateTimeConstructor);
+        DateBuilder(locale ?? Intl.defaultLocale, dateTimeConstructor);
     if (utc) dateFields.utc = true;
-    dateFields._dateOnly = dateOnly;
-    var stream = _Stream(inputString);
+    dateFields.dateOnly = dateOnly;
+    var stream = IntlStream(inputString);
     for (var field in _formatFields) {
       field.parse(stream, dateFields);
     }
@@ -777,16 +787,12 @@
     return _digitMatcher;
   }
 
-  /// Hard-code the most common matcher, which has special RegExp syntax.
-  static final RegExp _asciiDigitMatcher = RegExp(r'^\d+');
-
   int _localeZeroCodeUnit;
 
   /// For performance, keep the code unit of the zero digit available.
   int get localeZeroCodeUnit => _localeZeroCodeUnit == null
       ? _localeZeroCodeUnit = localeZero.codeUnitAt(0)
       : _localeZeroCodeUnit;
-  static final int _asciiZeroCodeUnit = '0'.codeUnitAt(0);
 
   String _localeZero;
 
@@ -797,7 +803,7 @@
 
   // Does this use non-ASCII digits, e.g. Eastern Arabic.
   bool get usesNativeDigits =>
-      useNativeDigits && _localeZeroCodeUnit != _asciiZeroCodeUnit;
+      useNativeDigits && _localeZeroCodeUnit != constants.asciiZeroCodeUnit;
 
   /// Does this use ASCII digits
   bool get usesAsciiDigits => !usesNativeDigits;
@@ -809,7 +815,8 @@
     var newDigits = List<int>(numberString.length);
     var oldDigits = numberString.codeUnits;
     for (var i = 0; i < numberString.length; i++) {
-      newDigits[i] = oldDigits[i] + localeZeroCodeUnit - _asciiZeroCodeUnit;
+      newDigits[i] =
+          oldDigits[i] + localeZeroCodeUnit - constants.asciiZeroCodeUnit;
     }
     return String.fromCharCodes(newDigits);
   }
@@ -817,7 +824,7 @@
   /// A regular expression that matches for digits in a particular
   /// locale, defined by the digit for zero in that locale.
   RegExp _initDigitMatcher() {
-    if (usesAsciiDigits) return _asciiDigitMatcher;
+    if (usesAsciiDigits) return regexp.asciiDigitMatcher;
     var localeDigits = Iterable.generate(10, (i) => i)
         .map((i) => localeZeroCodeUnit + i)
         .toList();
diff --git a/lib/src/intl/date_format_field.dart b/lib/src/intl/date_format_field.dart
index ad7f087..0bb0159 100644
--- a/lib/src/intl/date_format_field.dart
+++ b/lib/src/intl/date_format_field.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 // @dart=2.9
 
-part of intl;
+part of 'date_format.dart';
 
 /// This is a private class internal to DateFormat which is used for formatting
 /// particular fields in a template. e.g. if the format is hh:mm:ss then the
@@ -43,15 +43,15 @@
   }
 
   /// Abstract method for subclasses to implementing parsing for their format.
-  void parse(_Stream input, _DateBuilder dateFields);
+  void parse(IntlStream input, DateBuilder dateFields);
 
   /// Abstract method for subclasses to implementing 'loose' parsing for
   /// their format, accepting input case-insensitively, and allowing some
   /// delimiters to be skipped.
-  void parseLoose(_Stream input, _DateBuilder dateFields);
+  void parseLoose(IntlStream input, DateBuilder dateFields);
 
   /// Parse a literal field. We just look for the exact input.
-  void parseLiteral(_Stream input) {
+  void parseLiteral(IntlStream input) {
     var found = input.read(width);
     if (found != pattern) {
       throwFormatException(input);
@@ -66,7 +66,7 @@
   /// field's format specification is also trimmed before matching is
   /// attempted. Therefore, leading and trailing whitespace is optional, and
   /// arbitrary additional whitespace may be added before/after the literal.
-  void parseLiteralLoose(_Stream input) {
+  void parseLiteralLoose(IntlStream input) {
     _trimWhitespace(input);
 
     var found = input.peek(_trimmedPattern.length);
@@ -77,14 +77,14 @@
     _trimWhitespace(input);
   }
 
-  void _trimWhitespace(_Stream input) {
+  void _trimWhitespace(IntlStream input) {
     while (!input.atEnd() && input.peek().trim().isEmpty) {
       input.read();
     }
   }
 
   /// Throw a format exception with an error message indicating the position.
-  void throwFormatException(_Stream stream) {
+  void throwFormatException(IntlStream stream) {
     throw FormatException('Trying to read $this from ${stream.contents} '
         'at position ${stream.index}');
   }
@@ -96,11 +96,11 @@
 class _DateFormatLiteralField extends _DateFormatField {
   _DateFormatLiteralField(pattern, parent) : super(pattern, parent);
 
-  void parse(_Stream input, _DateBuilder dateFields) {
+  void parse(IntlStream input, DateBuilder dateFields) {
     parseLiteral(input);
   }
 
-  void parseLoose(_Stream input, _DateBuilder dateFields) =>
+  void parseLoose(IntlStream input, DateBuilder dateFields) =>
       parseLiteralLoose(input);
 }
 
@@ -116,11 +116,11 @@
     _fullPattern = pattern;
   }
 
-  void parse(_Stream input, _DateBuilder dateFields) {
+  void parse(IntlStream input, DateBuilder dateFields) {
     parseLiteral(input);
   }
 
-  void parseLoose(_Stream input, _DateBuilder dateFields) =>
+  void parseLoose(IntlStream input, DateBuilder dateFields) =>
       parseLiteralLoose(input);
 
   static final _twoEscapedQuotes = RegExp(r"''");
@@ -145,7 +145,7 @@
 
   /// Parse from a list of possibilities, but case-insensitively.
   /// Assumes that input is lower case.
-  int parseEnumeratedString(_Stream input, List<String> possibilities) {
+  int parseEnumeratedString(IntlStream input, List<String> possibilities) {
     var lowercasePossibilities =
         possibilities.map((x) => x.toLowerCase()).toList();
     try {
@@ -216,7 +216,7 @@
 
   /// Parse a day of the week name, case-insensitively.
   /// Assumes that input is lower case. Doesn't do anything
-  void parseDayOfWeek(_Stream input) {
+  void parseDayOfWeek(IntlStream input) {
     // This is IGNORED, but we still have to skip over it the correct amount.
     if (width <= 2) {
       handleNumericField(input, (x) => x);
@@ -247,14 +247,14 @@
 
   /// Parse the date according to our specification and put the result
   /// into the correct place in dateFields.
-  void parse(_Stream input, _DateBuilder dateFields) {
+  void parse(IntlStream input, DateBuilder dateFields) {
     parseField(input, dateFields);
   }
 
   /// Parse the date according to our specification and put the result
   /// into the correct place in dateFields. Allow looser parsing, accepting
   /// case-insensitive input and skipped delimiters.
-  void parseLoose(_Stream input, _DateBuilder dateFields) {
+  void parseLoose(IntlStream input, DateBuilder dateFields) {
     _LoosePatternField(pattern, parent).parse(input, dateFields);
   }
 
@@ -271,7 +271,7 @@
 
   /// Parse a field representing part of a date pattern. Note that we do not
   /// return a value, but rather build up the result in [builder].
-  void parseField(_Stream input, _DateBuilder builder) {
+  void parseField(IntlStream input, DateBuilder builder) {
     try {
       switch (pattern[0]) {
         case 'a':
@@ -415,7 +415,7 @@
   ///
   /// This method handles reading any of the numeric fields. The [offset]
   /// argument allows us to compensate for zero-based versus one-based values.
-  void handleNumericField(_Stream input, void Function(int) setter,
+  void handleNumericField(IntlStream input, void Function(int) setter,
       [int offset = 0]) {
     var result = input.nextInteger(
         digitMatcher: parent.digitMatcher,
@@ -433,8 +433,8 @@
   /// Then after all parsing is done we construct a date from the
   /// arguments. This method handles reading any of string fields from an
   /// enumerated set.
-  int parseEnumeratedString(_Stream input, List<String> possibilities) {
-    var results = _Stream(possibilities)
+  int parseEnumeratedString(IntlStream input, List<String> possibilities) {
+    var results = IntlStream(possibilities)
         .findIndexes((each) => input.peek(each.length) == each);
     if (results.isEmpty) throwFormatException(input);
     results.sort(
@@ -444,7 +444,7 @@
     return longestResult;
   }
 
-  void parseYear(_Stream input, _DateBuilder builder) {
+  void parseYear(IntlStream input, DateBuilder builder) {
     handleNumericField(input, builder.setYear);
     builder.setHasAmbiguousCentury(width == 2);
   }
@@ -462,7 +462,7 @@
     }
   }
 
-  void parseMonth(_Stream input, _DateBuilder dateFields) {
+  void parseMonth(IntlStream input, DateBuilder dateFields) {
     List<String> possibilities;
     switch (width) {
       case 5:
@@ -516,7 +516,7 @@
     return padTo(width, hours);
   }
 
-  void parse1To12Hours(_Stream input, _DateBuilder dateFields) {
+  void parse1To12Hours(IntlStream input, DateBuilder dateFields) {
     handleNumericField(input, dateFields.setHour);
     if (dateFields.hour == 12) dateFields.hour = 0;
   }
@@ -542,7 +542,7 @@
     }
   }
 
-  void parseStandaloneDay(_Stream input) {
+  void parseStandaloneDay(IntlStream input) {
     // This is ignored, but we still have to skip over it the correct amount.
     List<String> possibilities;
     switch (width) {
@@ -608,8 +608,10 @@
     return padTo(width, date.day);
   }
 
-  String formatDayOfYear(DateTime date) =>
-      padTo(width, _dayOfYear(date.month, date.day, _isLeapYear(date)));
+  String formatDayOfYear(DateTime date) => padTo(
+      width,
+      date_computation.dayOfYear(
+          date.month, date.day, date_computation.isLeapYear(date)));
 
   String formatDayOfWeek(DateTime date) {
     // Note that Dart's weekday returns 1 for Monday and 7 for Sunday.
@@ -618,13 +620,13 @@
         : symbols.SHORTWEEKDAYS)[(date.weekday) % 7];
   }
 
-  void parseDayOfWeek(_Stream input) {
+  void parseDayOfWeek(IntlStream input) {
     // This is IGNORED, but we still have to skip over it the correct amount.
     var possibilities = width >= 4 ? symbols.WEEKDAYS : symbols.SHORTWEEKDAYS;
     parseEnumeratedString(input, possibilities);
   }
 
-  void parseEra(_Stream input) {
+  void parseEra(IntlStream input) {
     var possibilities = width >= 4 ? symbols.ERANAMES : symbols.ERAS;
     parseEnumeratedString(input, possibilities);
   }
diff --git a/lib/src/intl/intl_stream.dart b/lib/src/intl/intl_stream.dart
new file mode 100644
index 0000000..3149a53
--- /dev/null
+++ b/lib/src/intl/intl_stream.dart
@@ -0,0 +1,101 @@
+// Copyright (c) 2020, 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 'dart:math';
+
+import 'constants.dart' as constants;
+import 'regexp.dart' as regexp;
+
+/// A simple and not particularly general stream class to make parsing
+/// dates from strings simpler. It is general enough to operate on either
+/// lists or strings.
+// TODO(alanknight): With the improvements to the collection libraries
+// since this was written we might be able to get rid of it entirely
+// in favor of e.g. aString.split('') giving us an iterable of one-character
+// strings, or else make the implementation trivial.
+class IntlStream {
+  dynamic contents;
+  int index = 0;
+
+  IntlStream(this.contents);
+
+  bool atEnd() => index >= contents.length;
+
+  dynamic next() => contents[index++];
+
+  /// Return the next [howMany] items, or as many as there are remaining.
+  /// Advance the stream by that many positions.
+  dynamic read([int howMany = 1]) {
+    var result = peek(howMany);
+    index += howMany;
+    return result;
+  }
+
+  /// Does the input start with the given string, if we start from the
+  /// current position.
+  bool startsWith(String pattern) {
+    if (contents is String) return contents.startsWith(pattern, index);
+    return pattern == peek(pattern.length);
+  }
+
+  /// Return the next [howMany] items, or as many as there are remaining.
+  /// Does not modify the stream position.
+  dynamic peek([int howMany = 1]) {
+    dynamic result;
+    if (contents is String) {
+      String stringContents = contents;
+      result = stringContents.substring(
+          index, min(index + howMany, stringContents.length));
+    } else {
+      // Assume List
+      result = contents.sublist(index, index + howMany);
+    }
+    return result;
+  }
+
+  /// Return the remaining contents of the stream
+  dynamic rest() => peek(contents.length - index);
+
+  /// Find the index of the first element for which [f] returns true.
+  /// Advances the stream to that position.
+  int findIndex(bool Function(dynamic) f) {
+    while (!atEnd()) {
+      if (f(next())) return index - 1;
+    }
+    return null;
+  }
+
+  /// Find the indexes of all the elements for which [f] returns true.
+  /// Leaves the stream positioned at the end.
+  List<dynamic> findIndexes(bool Function(dynamic) f) {
+    var results = [];
+    while (!atEnd()) {
+      if (f(next())) results.add(index - 1);
+    }
+    return results;
+  }
+
+  /// Assuming that the contents are characters, read as many digits as we
+  /// can see and then return the corresponding integer, advancing the receiver.
+  ///
+  /// For non-ascii digits, the optional arguments are a regular expression
+  /// [digitMatcher] to find the next integer, and the codeUnit of the local
+  /// zero [zeroDigit].
+  int nextInteger({RegExp digitMatcher, int zeroDigit}) {
+    var string = (digitMatcher ?? regexp.asciiDigitMatcher).stringMatch(rest());
+    if (string == null || string.isEmpty) return null;
+    read(string.length);
+    if (zeroDigit != null && zeroDigit != constants.asciiZeroCodeUnit) {
+      // Trying to optimize this, as it might get called a lot.
+      var oldDigits = string.codeUnits;
+      var newDigits = List<int>(string.length);
+      for (var i = 0; i < string.length; i++) {
+        newDigits[i] = oldDigits[i] - zeroDigit + constants.asciiZeroCodeUnit;
+      }
+      string = String.fromCharCodes(newDigits);
+    }
+    return int.parse(string);
+  }
+}
diff --git a/lib/src/intl/number_format.dart b/lib/src/intl/number_format.dart
index 61b4ce5..487e2e3 100644
--- a/lib/src/intl/number_format.dart
+++ b/lib/src/intl/number_format.dart
@@ -3,7 +3,16 @@
 // BSD-style license that can be found in the LICENSE file.
 // @dart=2.9
 
-part of intl;
+import 'dart:collection';
+import 'dart:math';
+
+import 'package:intl/intl.dart';
+import 'package:intl/number_symbols.dart';
+import 'package:intl/number_symbols_data.dart';
+
+import 'intl_stream.dart';
+
+part 'compact_number_format.dart';
 
 // ignore_for_file: constant_identifier_names
 
@@ -985,7 +994,7 @@
   final String text;
 
   /// What we use to iterate over the input text.
-  final _Stream input;
+  final IntlStream input;
 
   /// The result of parsing [text] according to [format]. Automatically
   /// populated in the constructor.
@@ -1030,7 +1039,7 @@
   int get _localeZero => format._localeZero;
 
   ///  Create a new [_NumberParser] on which we can call parse().
-  _NumberParser(this.format, this.text) : input = _Stream(text) {
+  _NumberParser(this.format, this.text) : input = IntlStream(text) {
     scale = format._internalMultiplier;
     value = parse();
   }
@@ -1183,7 +1192,7 @@
 
   /// Parse the number portion of the input, i.e. not any prefixes or suffixes,
   /// and assuming NaN and Infinity are already handled.
-  num parseNumber(_Stream input) {
+  num parseNumber(IntlStream input) {
     if (gotNegative) {
       _normalized.write('-');
     }
diff --git a/lib/src/intl/regexp.dart b/lib/src/intl/regexp.dart
new file mode 100644
index 0000000..e6d8e8b
--- /dev/null
+++ b/lib/src/intl/regexp.dart
@@ -0,0 +1,6 @@
+// Copyright (c) 2020, 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.
+
+/// Hard-code the most common matcher, which has special RegExp syntax.
+final RegExp asciiDigitMatcher = RegExp(r'^\d+');
diff --git a/lib/src/intl/text_direction.dart b/lib/src/intl/text_direction.dart
new file mode 100644
index 0000000..7bcb7e4
--- /dev/null
+++ b/lib/src/intl/text_direction.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2020, 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
+
+/// Represents directionality of text.
+///
+/// In most cases, it is preferable to use bidi_formatter.dart, which provides
+/// bidi functionality in the given directional context, instead of using
+/// bidi_utils.dart directly.
+class TextDirection {
+  static const LTR = TextDirection._('LTR', 'ltr');
+  static const RTL = TextDirection._('RTL', 'rtl');
+  // If the directionality of the text cannot be determined and we are not using
+  // the context direction (or if the context direction is unknown), then the
+  // text falls back on the more common ltr direction.
+  static const UNKNOWN = TextDirection._('UNKNOWN', 'ltr');
+
+  /// Textual representation of the directionality constant. One of
+  /// 'LTR', 'RTL', or 'UNKNOWN'.
+  final String value;
+
+  /// Textual representation of the directionality when used in span tag.
+  final String spanText;
+
+  const TextDirection._(this.value, this.spanText);
+
+  /// Returns true if [otherDirection] is known to be different from this
+  /// direction.
+  bool isDirectionChange(TextDirection otherDirection) =>
+      otherDirection != TextDirection.UNKNOWN && this != otherDirection;
+}