import 'dart:convert'; import 'dart:typed_data'; class Smaz { static const int _verbatimSingle = 254; static const int _verbatimRun = 255; static const List _rcb = [ " ", "the", "e", "t", "a", "of", "o", "and", "i", "n", "s", "e ", "r", " th", " t", "in", "he", "th", "h", "he ", "to", "\r\n", "l", "s ", "d", " a", "an", "er", "c", " o", "d ", "on", " of", "re", "of ", "t ", ", ", "is", "u", "at", " ", "n ", "or", "which", "f", "m", "as", "it", "that", "\n", "was", "en", " ", " w", "es", " an", " i", "\r", "f ", "g", "p", "nd", " s", "nd ", "ed ", "w", "ed", "http://", "for", "te", "ing", "y ", "The", " c", "ti", "r ", "his", "st", " in", "ar", "nt", ",", " to", "y", "ng", " h", "with", "le", "al", "to ", "b", "ou", "be", "were", " b", "se", "o ", "ent", "ha", "ng ", "their", "\"", "hi", "from", " f", "in ", "de", "ion", "me", "v", ".", "ve", "all", "re ", "ri", "ro", "is ", "co", "f t", "are", "ea", ". ", "her", " m", "er ", " p", "es ", "by", "they", "di", "ra", "ic", "not", "s, ", "d t", "at ", "ce", "la", "h ", "ne", "as ", "tio", "on ", "n t", "io", "we", " a ", "om", ", a", "s o", "ur", "li", "ll", "ch", "had", "this", "e t", "g ", "e\r\n", " wh", "ere", " co", "e o", "a ", "us", " d", "ss", "\n\r\n", "\r\n\r", "=\"", " be", " e", "s a", "ma", "one", "t t", "or ", "but", "el", "so", "l ", "e s", "s,", "no", "ter", " wa", "iv", "ho", "e a", " r", "hat", "s t", "ns", "ch ", "wh", "tr", "ut", "/", "have", "ly ", "ta", " ha", " on", "tha", "-", " l", "ati", "en ", "pe", " re", "there", "ass", "si", " fo", "wa", "ec", "our", "who", "its", "z", "fo", "rs", ">", "ot", "un", "<", "im", "th ", "nc", "ate", "><", "ver", "ad", " we", "ly", "ee", " n", "id", " cl", "ac", "il", " _rcbBytes = _rcb .map((s) => Uint8List.fromList(ascii.encode(s))) .toList(growable: false); static final int _maxEntryLen = _rcbBytes.fold(0, (maxLen, entry) { return entry.length > maxLen ? entry.length : maxLen; }); static String encodeIfSmaller(String text) { if (text.isEmpty || text.startsWith('s:')) return text; final originalBytes = Uint8List.fromList(utf8.encode(text)); final compressed = compressBytes(originalBytes); final encoded = base64Encode(compressed); final candidate = 's:$encoded'; if (utf8.encode(candidate).length < originalBytes.length) { return candidate; } return text; } static String? tryDecodePrefixed(String text) { final trimmedLeft = text.trimLeft(); if (!trimmedLeft.startsWith('s:') || trimmedLeft.length <= 2) return null; final encoded = trimmedLeft.substring(2); try { final compressed = _decodeBase64Flexible(encoded); final decompressed = decompressBytes(compressed); return utf8.decode(decompressed, allowMalformed: true); } catch (_) { return null; } } static Uint8List compressBytes(Uint8List input) { final out = BytesBuilder(copy: false); final verbatim = []; int index = 0; void flushVerbatim() { if (verbatim.isEmpty) return; if (verbatim.length == 1) { out.addByte(_verbatimSingle); out.addByte(verbatim[0]); } else { out.addByte(_verbatimRun); out.addByte(verbatim.length - 1); out.add(verbatim); } verbatim.clear(); } while (index < input.length) { int bestLen = 0; int bestCode = -1; final remaining = input.length - index; final maxLen = remaining < _maxEntryLen ? remaining : _maxEntryLen; for (int code = 0; code < _rcbBytes.length; code++) { final entry = _rcbBytes[code]; final entryLen = entry.length; if (entryLen == 0 || entryLen > maxLen || entryLen <= bestLen) { continue; } if (_matches(input, index, entry)) { bestLen = entryLen; bestCode = code; if (bestLen == maxLen) { break; } } } if (bestCode >= 0) { flushVerbatim(); out.addByte(bestCode); index += bestLen; continue; } verbatim.add(input[index]); index++; if (verbatim.length == 256) { flushVerbatim(); } } flushVerbatim(); return out.toBytes(); } static Uint8List decompressBytes(Uint8List input) { final out = BytesBuilder(copy: false); int index = 0; while (index < input.length) { final code = input[index]; if (code == _verbatimSingle) { if (index + 1 >= input.length) { throw const FormatException( 'Invalid SMAZ stream: truncated verbatim byte.', ); } out.addByte(input[index + 1]); index += 2; } else if (code == _verbatimRun) { if (index + 1 >= input.length) { throw const FormatException( 'Invalid SMAZ stream: truncated verbatim length.', ); } final len = input[index + 1] + 1; final end = index + 2 + len; if (end > input.length) { throw const FormatException( 'Invalid SMAZ stream: truncated verbatim run.', ); } out.add(input.sublist(index + 2, end)); index = end; } else { if (code >= _rcbBytes.length) { throw const FormatException( 'Invalid SMAZ stream: code out of range.', ); } out.add(_rcbBytes[code]); index += 1; } } return out.toBytes(); } static bool _matches(Uint8List input, int offset, Uint8List entry) { final len = entry.length; if (offset + len > input.length) return false; for (int i = 0; i < len; i++) { if (input[offset + i] != entry[i]) return false; } return true; } static Uint8List _decodeBase64Flexible(String encoded) { final trimmed = encoded.trim(); try { return base64Decode(trimmed); } catch (_) { // Try base64url with missing padding. var normalized = trimmed.replaceAll('-', '+').replaceAll('_', '/'); final pad = normalized.length % 4; if (pad != 0) { normalized = normalized.padRight(normalized.length + (4 - pad), '='); } return base64Decode(normalized); } } }