toRadixString method

String toRadixString({
  1. int radix = 2,
  2. int chunkSize = 4,
  3. String sepChar = '_',
})

Return the radix encoding of the current LogicValue as a sequence of radix characters prefixed by the length and encoding format. Output format is: <len>'<format><encoded-value>.

ofRadixString can parse a String produced by toRadixString and construct a LogicValue.

Here is the number 1492 printed as a radix string:

  • Binary: 15'b101_1101_0100
  • Quaternary: 15'q11_3110
  • Octal: 15'o2724
  • Decimal: 10'd1492
  • Hex: 15'h05d4

Separators are output according to chunkSize starting from the LSB(right), default is 4 characters. The default separator is '_'. sepChar can be set to another character, but not in radixStringChars, otherwise it will throw an exception.

  • chunkSize = default: 61'h2_9ebc_5f06_5bf7
  • chunkSize = 10: 61'h29e_bc5f065bf7

Leading 0s are omitted in the output string:

  • 25'h1

When a LogicValue has 'x' or 'z' bits, then the radix characters those bits overlap will be expanded into binary form with '<' '>' bracketing them as follows:

  • 35'h7_ZZZZ_Z<zzz0><100z>Z Such a LogicValue cannot be converted to a Decimal (10) radix string and will throw an exception.

If the leading bits are 'z', then the output radix character is 'Z' no matter what the length. When leading, 'Z' indicates one or more 'z' bits to fill the first radix character.

  • 9'bz_zzzz_zzzz = 9'hZZZ

Implementation

String toRadixString(
    {int radix = 2, int chunkSize = 4, String sepChar = '_'}) {
  if (radixStringChars.contains(sepChar)) {
    throw LogicValueConversionException('separation character invalid');
  }
  final radixStr = switch (radix) {
    2 => "'b",
    4 => "'q",
    8 => "'o",
    10 => "'d",
    16 => "'h",
    _ => throw LogicValueConversionException('Unsupported radix: $radix')
  };
  final String reversedStr;
  if (isValid) {
    final radixString =
        toBigInt().toUnsigned(width).toRadixString(radix).toUpperCase();
    reversedStr = _reverse(radixString);
  } else if (radix == 10) {
    final span = (width * math.log(2) / math.log(radix)).floor();
    if (toRadixString().contains(RegExp('[xX]'))) {
      reversedStr = 'X' * span;
    } else {
      reversedStr = 'Z' * span;
    }
  } else {
    final span = (math.log(radix) / math.log(2)).ceil();
    final extendedStr =
        LogicValue.of(this, width: span * (width / span).ceil());
    final buf = StringBuffer();
    for (var i = (extendedStr.width ~/ span) - 1; i >= 0; i--) {
      final binaryChunk = extendedStr.slice((i + 1) * span - 1, i * span);
      var chunkString = binaryChunk.toString(includeWidth: false);
      if (i == extendedStr.width ~/ span - 1) {
        final chunkWidth = chunkString.length;
        chunkString = chunkString.substring(
            chunkWidth - (width - i * span), chunkWidth);
      }
      final s = [
        if (chunkString == 'z' * chunkString.length)
          (span == 1 ? 'z' : 'Z')
        else if (chunkString == 'x' * chunkString.length)
          (span == 1 ? 'x' : 'X')
        else if (chunkString.contains('z') | chunkString.contains('x'))
          '>${_reverse(chunkString)}<'
        else
          binaryChunk.toBigInt().toUnsigned(span).toRadixString(radix)
      ].first;
      buf.write(_reverse(s));
    }
    reversedStr = _reverse(buf.toString());
  }
  final spaceString = _reverse(reversedStr
      .replaceAllMapped(
          RegExp('((>(.){$chunkSize}<)|([a-zA-Z0-9])){$chunkSize}'),
          (match) => '${match.group(0)}$sepChar')
      .replaceAll('$sepChar<', '<'));

  final fullString = spaceString[0] == sepChar
      ? spaceString.substring(1, spaceString.length)
      : spaceString;
  return '$width$radixStr$fullString';
}