Location.fromBytes constructor

Location.fromBytes(
  1. String name,
  2. List<int> rawData
)

Deserialize Location from bytes

Implementation

factory Location.fromBytes(String name, List<int> rawData) {
  final data = rawData is Uint8List ? rawData : Uint8List.fromList(rawData);

  final bdata =
      data.buffer.asByteData(data.offsetInBytes, data.lengthInBytes);

  final magic1 = bdata.getUint32(0);
  if (magic1 != _ziMagic) {
    throw InvalidZoneInfoDataException('Invalid magic header "$magic1"');
  }
  final version1 = bdata.getUint8(4);

  var offset = 20;

  switch (version1) {
    case 0:
      final header = _Header.fromBytes(
          Uint8List.view(bdata.buffer, offset, _Header.size));

      // calculating data offsets
      final dataOffset = offset + _Header.size;
      final transitionAtOffset = dataOffset;
      final transitionZoneOffset =
          transitionAtOffset + header.tzh_timecnt * 5;
      final abbreviationsOffset =
          transitionZoneOffset + header.tzh_typecnt * 6;
      final leapOffset = abbreviationsOffset + header.tzh_charcnt;
      final stdOrWctOffset = leapOffset + header.tzh_leapcnt * 8;
      final utcOrGmtOffset = stdOrWctOffset + header.tzh_ttisstdcnt;

      // read transitions
      final transitionAt = <int>[];
      final transitionZone = <int>[];

      offset = transitionAtOffset;

      for (var i = 0; i < header.tzh_timecnt; i++) {
        transitionAt.add(bdata.getInt32(offset));
        offset += 4;
      }

      for (var i = 0; i < header.tzh_timecnt; i++) {
        transitionZone.add(bdata.getUint8(offset));
        offset += 1;
      }

      // function to read from abbreviation buffer
      final abbreviationsData = data.buffer.asUint8List(
          data.offsetInBytes + abbreviationsOffset, header.tzh_charcnt);
      final abbreviations = <String>[];
      final abbreviationsCache = HashMap<int, int>();
      int readAbbreviation(int offset) {
        var result = abbreviationsCache[offset];
        if (result == null) {
          result = abbreviations.length;
          abbreviationsCache[offset] = result;
          abbreviations.add(_readByteString(abbreviationsData, offset));
        }
        return result;
      }

      // read zones
      final zones = <TimeZone>[];
      offset = transitionZoneOffset;

      for (var i = 0; i < header.tzh_typecnt; i++) {
        final tt_gmtoff = bdata.getInt32(offset);
        final tt_isdst = bdata.getInt8(offset + 4);
        final tt_abbrind = bdata.getUint8(offset + 5);
        offset += 6;

        zones.add(TimeZone(tt_gmtoff,
            isDst: tt_isdst == 1,
            abbreviationIndex: readAbbreviation(tt_abbrind)));
      }

      // read leap seconds
      final leapAt = <int>[];
      final leapDiff = <int>[];

      offset = leapOffset;
      for (var i = 0; i < header.tzh_leapcnt; i++) {
        leapAt.add(bdata.getInt32(offset));
        leapDiff.add(bdata.getInt32(offset + 4));
        offset += 5;
      }

      // read std flags
      final isStd = <int>[];

      offset = stdOrWctOffset;
      for (var i = 0; i < header.tzh_ttisstdcnt; i++) {
        isStd.add(bdata.getUint8(offset));
        offset += 1;
      }

      // read utc flags
      final isUtc = <int>[];

      offset = utcOrGmtOffset;
      for (var i = 0; i < header.tzh_ttisgmtcnt; i++) {
        isUtc.add(bdata.getUint8(offset));
        offset += 1;
      }

      return Location(name, transitionAt, transitionZone, abbreviations,
          zones, leapAt, leapDiff, isStd, isUtc);

    case 50:
    case 51:
      // skip old version header/data
      final header1 = _Header.fromBytes(
          Uint8List.view(bdata.buffer, offset, _Header.size));
      offset += _Header.size + header1.dataLength(4);

      final magic2 = bdata.getUint32(offset);
      if (magic2 != _ziMagic) {
        throw InvalidZoneInfoDataException(
            'Invalid second magic header "$magic2"');
      }

      final version2 = bdata.getUint8(offset + 4);
      if (version2 != version1) {
        throw InvalidZoneInfoDataException(
            'Second version "$version2" doesn\'t match first version '
            '"$version1"');
      }

      offset += 20;

      final header2 = _Header.fromBytes(
          Uint8List.view(bdata.buffer, offset, _Header.size));

      // calculating data offsets
      final dataOffset = offset + _Header.size;
      final transitionAtOffset = dataOffset;
      final transitionZoneOffset =
          transitionAtOffset + header2.tzh_timecnt * 9;
      final abbreviationsOffset =
          transitionZoneOffset + header2.tzh_typecnt * 6;
      final leapOffset = abbreviationsOffset + header2.tzh_charcnt;
      final stdOrWctOffset = leapOffset + header2.tzh_leapcnt * 12;
      final utcOrGmtOffset = stdOrWctOffset + header2.tzh_ttisstdcnt;

      // read transitions
      final transitionAt = <int>[];
      final transitionZone = <int>[];

      offset = transitionAtOffset;

      for (var i = 0; i < header2.tzh_timecnt; i++) {
        transitionAt.add(bdata.getInt64(offset));
        offset += 8;
      }

      for (var i = 0; i < header2.tzh_timecnt; i++) {
        transitionZone.add(bdata.getUint8(offset));
        offset += 1;
      }

      // function to read from abbreviation buffer
      final abbreviationsData = data.buffer.asUint8List(
          data.offsetInBytes + abbreviationsOffset, header2.tzh_charcnt);
      final abbreviations = <String>[];
      final abbreviationsCache = HashMap<int, int>();
      int readAbbreviation(int offset) {
        var result = abbreviationsCache[offset];
        if (result == null) {
          result = abbreviations.length;
          abbreviationsCache[offset] = result;
          abbreviations.add(_readByteString(abbreviationsData, offset));
        }
        return result;
      }

      // read transition info
      final zones = <TimeZone>[];
      offset = transitionZoneOffset;

      for (var i = 0; i < header2.tzh_typecnt; i++) {
        final tt_gmtoff = bdata.getInt32(offset);
        final tt_isdst = bdata.getInt8(offset + 4);
        final tt_abbrind = bdata.getUint8(offset + 5);
        offset += 6;

        zones.add(TimeZone(tt_gmtoff,
            isDst: tt_isdst == 1,
            abbreviationIndex: readAbbreviation(tt_abbrind)));
      }

      // read leap seconds
      final leapAt = <int>[];
      final leapDiff = <int>[];

      offset = leapOffset;
      for (var i = 0; i < header2.tzh_leapcnt; i++) {
        leapAt.add(bdata.getInt64(offset));
        leapDiff.add(bdata.getInt32(offset + 8));
        offset += 9;
      }

      // read std flags
      final isStd = <int>[];

      offset = stdOrWctOffset;
      for (var i = 0; i < header2.tzh_ttisstdcnt; i++) {
        isStd.add(bdata.getUint8(offset));
        offset += 1;
      }

      // read utc flags
      final isUtc = <int>[];

      offset = utcOrGmtOffset;
      for (var i = 0; i < header2.tzh_ttisgmtcnt; i++) {
        isUtc.add(bdata.getUint8(offset));
        offset += 1;
      }

      return Location(name, transitionAt, transitionZone, abbreviations,
          zones, leapAt, leapDiff, isStd, isUtc);

    default:
      throw InvalidZoneInfoDataException('Unknown version: $version1');
  }
}