description

# parseIPv6Address static method

List<int> parseIPv6Address(

1. String host, [
2. int start = 0,
3. int? end

])

Parses the `host` as an IP version 6 (IPv6) address.

Returns the address as a list of 16 bytes in network byte order (big endian).

Throws a FormatException if `host` is not a valid IPv6 address representation.

Acts on the substring from `start` to `end`. If `end` is omitted, it defaults to the end of the string.

Some examples of IPv6 addresses:

- `::1`
- `FEDC:BA98:7654:3210:FEDC:BA98:7654:3210`
- `3ffe:2a00:100:7031::1`
- `::FFFF:129.144.52.38`
- `2010:836B:4179::836B:4179`

The grammar for IPv6 addresses are:

    IPv6address ::=                           (h16 ":"){6} ls32
                  |                      "::" (h16 ":"){5} ls32
                  | (               h16) "::" (h16 ":"){4} ls32
                  | ((h16 ":"){0,1} h16) "::" (h16 ":"){3} ls32
                  | ((h16 ":"){0,2} h16) "::" (h16 ":"){2} ls32
                  | ((h16 ":"){0,3} h16) "::"  h16 ":"     ls32
                  | ((h16 ":"){0,4} h16) "::"              ls32
                  | ((h16 ":"){0,5} h16) "::"              h16
                  | ((h16 ":"){0,6} h16) "::"
    ls32        ::= (h16 ":" h16) | IPv4address
                    ;; least-significant 32 bits of address
    h16         ::= HEXDIG{1,4}
                    ;; 16 bits of address represented in hexadecimal

That is

- eight 1-to-4-digit hexadecimal numerals separated by `:`, or
- one to seven such `:`-separated numerals, with either one pair is separated by `::`, or a leading or trailing `::`.
- either of the above with a trailing two `:`-separated numerals replaced by an IPv4 address.

An IPv6 address with a zone ID (from RFC 6874) is an IPv6 address followed by `%25` (an escaped `%`) and valid zone characters.

    IPv6addrz ::= IPv6address "%25" ZoneID
    ZoneID    ::= (unreserved | pct-encoded)+
    ```.

## Implementation

    static List<int> parseIPv6Address(String host, [int start = 0, int? end]) {
      end ??= RangeError.checkValidRange(start, end, host.length);
      // An IPv6 address consists of exactly 8 parts of 1-4 hex digits, separated
      // by `:`'s, with the following exceptions:
      //
      //  - One (and only one) wildcard (`::`) may be present, representing a fill
      //    of 0's. The IPv6 `::` is thus 16 bytes of `0`.
      //  - The last two parts may be replaced by an IPv4 "dotted-quad" address.

      // Helper function for reporting a badly formatted IPv6 address.
      Never error(String msg, int? position) {
        throw FormatException('Illegal IPv6 address, $msg', host, position);
      }

      if (end - start < 2) error('address is too short', null);
      Uint8List result = Uint8List(16);
      // Set if seeing a ".", suggesting that there is an IPv4 address.
      int partStart = start;
      int wildcardAt = -1;
      int partCount = 0;
      int hexValue = 0;
      bool decValue = true;
      int cursor = start;
      // Handle leading colons eagerly to make the loop simpler.
      // After this, any colon encountered will be after another colon or
      // a hex part.
      if (host.codeUnitAt(cursor) == _COLON) {
        if (host.codeUnitAt(cursor + 1) == _COLON) {
          wildcardAt = 0;
          partCount = 1;
          partStart = cursor += 2;
        } else {
          error('invalid start colon', cursor);
        }
      }
      // Parse all parts, except a potential last one.
      while (true) {
        var char = cursor >= end ? 0 : host.codeUnitAt(cursor);
        handleDigit:
        {
          int hexDigit;
          if (char ^ _DIGIT_0 case var digit when digit <= 9) {
            hexDigit = digit;
          } else if (char | 0x20 case var letter
              when letter >= _LOWER_CASE_A && letter <= _LOWER_CASE_F) {
            hexDigit = letter - (_LOWER_CASE_A - 10);
            decValue = false;
          } else {
            break handleDigit; // Not a digit.
          }
          if (cursor < partStart + 4) {
            hexValue = hexValue * 16 + hexDigit;
            cursor++;
            continue;
          }
          error('an IPv6 part can contain a maximum of 4 hex digits', partStart);
        }
        if (cursor > partStart) {
          // Non-empty part, value in hexValue.
          if (char == _DOT) {
            if (decValue) {
              if (partCount <= 6) {
                _parseIPv4Address(host, partStart, end, result, partCount * 2);
                cursor = end; // Throws if IPv4 does not extend to end.
                partCount += 2;
                break; // Must be last.
              }
              error('an address must contain at most 8 parts', partStart);
            }
            break; // Any other dot is just an invalid character.
          }
          // Non-empty hex part.
          result[partCount * 2] = hexValue >> 8;
          result[partCount * 2 + 1] = hexValue & 0xFF;
          partCount++;
          if (char == _COLON) {
            if (partCount < 8) {
              // Start new part.
              hexValue = 0;
              decValue = true;
              cursor++;
              partStart = cursor;
              continue;
            }
            error('an address must contain at most 8 parts', cursor);
          }
          break; // Unexpected character or the end sentinel.
        }
        // Empty part, after a colon.
        if (char == _COLON) {
          // Colon after empty part must be after colon.
          // (Leading colons were checked before loop.)
          if (wildcardAt < 0) {
            wildcardAt = partCount;
            partCount++; // A wildcard fills at least one part.
            partStart = ++cursor;
            continue;
          }
          error('only one wildcard `::` is allowed', cursor);
        }
        // Missing hex digits after colon.
        // OK to end right after wildcard, not after another colon.
        if (wildcardAt != partCount - 1) {
          // Missing part after a single `:`.
          // Error even at end.
          error('missing part', cursor);
        }
        break; // Error if not at end.
      }
      if (cursor < end) error('invalid character', cursor);
      if (partCount < 8) {
        if (wildcardAt < 0) {
          error(
            'an address without a wildcard must contain exactly 8 parts',
            end,
          );
        }
        // Move everything after the wildcard to the end of the buffer, and fill
        // the wildcard gap with zeros.
        var partAfterWildcard = wildcardAt + 1;
        int partsAfterWildcard = partCount - partAfterWildcard;
        if (partsAfterWildcard > 0) {
          var positionAfterWildcard = partAfterWildcard * 2;
          var bytesAfterWildcard = partsAfterWildcard * 2;
          var newPositionAfterWildcard = result.length - bytesAfterWildcard;
          result.setRange(
            newPositionAfterWildcard,
            result.length,
            result,
            positionAfterWildcard,
          );
          result.fillRange(positionAfterWildcard, newPositionAfterWildcard, 0);
        }
      }
      return result;
    }