public static function dump($str, $useHtml = false, $sanitizeNonBinary = false)
    {
        self::$useHtml = $useHtml;
        self::$sanitizeNonBinary = $sanitizeNonBinary;
        self::setStr($str);
        $controlCharAs = 'other'; // how should we treat ascii control chars?
        $curBlockType = 'utf8'; // utf8, utf8special, other
        $newBlockType = null;
        $curBlockStart = 0; // string offset
        $strNew = '';
        while (self::$curI < self::$stats['strLen']) {
            $curI = self::$curI;
            $isUtf8 = self::isOffsetUtf8($isSpecial, true);
            if ($isUtf8 && $isSpecial && $controlCharAs !== 'utf8special' && \ord($str[$curI]) < 0x80) {
                if ($controlCharAs == 'other') {
                    $isUtf8 = false;
                } elseif ($controlCharAs == 'utf8') {
                    $isSpecial = false;
                }
            }
            if ($isUtf8) {
                if ($isSpecial) {
                    // control-char or special
                    if ($curBlockType !== 'utf8special') {
                        $newBlockType = 'utf8special';
                    }
                } else {
                    // plain-ol-utf8
                    if ($curBlockType !== 'utf8') {
                        $newBlockType = 'utf8';
                    }
                }
            } else {
                // not a valid utf-8 character
                if ($curBlockType !== 'other') {
                    $newBlockType = 'other';
                }
            }
            if ($newBlockType) {
                $len = $curI - $curBlockStart;
                self::incStat($curBlockType, $len);
                $subStr = \substr(self::$str, $curBlockStart, $len);
                $strNew .= self::dumpBlock($subStr, $curBlockType);
                $curBlockStart = $curI;
                $curBlockType = $newBlockType;
                $newBlockType = null;
            }
        }
        $len = self::$stats['strLen'] - $curBlockStart;
        self::incStat($curBlockType, $len);
        if (self::$stats['strLen']) {
            $percentOther = (self::$stats['bytesOther']) / self::$stats['strLen'] * 100;
            if ($percentOther > 33) {
                $strNew = self::dumpBlock($str, 'other', array('prefix'=>false));
            } else {
                $subStr = \substr(self::$str, $curBlockStart, $len);
                $strNew .= self::dumpBlock($subStr, $curBlockType);
            }
        }
        return $strNew;
    }