From 411b00c3ee166c74f25435ca2ac575dfcf196640 Mon Sep 17 00:00:00 2001 From: fegor101 Date: Fri, 7 Aug 2026 15:29:02 +0300 Subject: [PATCH] feat: add new tests --- lib/potato_pass_core.dart | 8 +-- lib/src/encoding/compact_text_encoder.dart | 79 ++++++++++++++++++++++ test/potato_pass_core_test.dart | 51 ++++++++++++-- 3 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 lib/src/encoding/compact_text_encoder.dart diff --git a/lib/potato_pass_core.dart b/lib/potato_pass_core.dart index b489c0c..bb163d7 100644 --- a/lib/potato_pass_core.dart +++ b/lib/potato_pass_core.dart @@ -1,8 +1,4 @@ -/// Support for doing something awesome. -/// -/// More dartdocs go here. library; -export 'src/potato_pass_core_base.dart'; - -// TODO: Export any libraries intended for clients of this package. +// Экспортируем наш 6-битный кодировщик +export 'src/encoding/compact_text_encoder.dart'; \ No newline at end of file diff --git a/lib/src/encoding/compact_text_encoder.dart b/lib/src/encoding/compact_text_encoder.dart new file mode 100644 index 0000000..9e1eb20 --- /dev/null +++ b/lib/src/encoding/compact_text_encoder.dart @@ -0,0 +1,79 @@ +import 'dart:typed_data'; + +/// Класс для упаковки/распаковки текста в 6-битный формат. +/// 4 символа упаковываются ровно в 3 байта (сжатие ~62% для кириллицы). +abstract class CompactTextEncoder { + /// Словарь из 64 символов (индексы от 0 до 63) + static const String charset = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯABCDEFGHIJKLMNOPQRSTUVWXYZ -.,_'; + + /// Упаковывает строку [text] в байтовый массив Uint8List. + static Uint8List encode(String text) { + if (text.isEmpty) { + return Uint8List(0); + } + + final String upperText = text.toUpperCase(); + final List indices = []; + + // Преобразуем каждый символ в индекс от 0 до 63 + for (int i = 0; i < upperText.length; i++) { + int idx = charset.indexOf(upperText[i]); + // Если символа нет в словаре, заменяем на пробел + if (idx == -1) { + idx = charset.indexOf(' '); + } + indices.add(idx); + } + + // Добиваем длину до кратности 4 символам (символом '_') + while (indices.length % 4 != 0) { + indices.add(charset.indexOf('_')); + } + + final int byteLength = (indices.length ~/ 4) * 3; + final Uint8List result = Uint8List(byteLength); + + int byteIdx = 0; + for (int i = 0; i < indices.length; i += 4) { + int c1 = indices[i]; + int c2 = indices[i + 1]; + int c3 = indices[i + 2]; + int c4 = indices[i + 3]; + + // Упаковка 4 x 6 бит (24 бита) в 3 байта по 8 бит + result[byteIdx++] = (c1 << 2) | (c2 >> 4); + result[byteIdx++] = ((c2 & 0x0F) << 4) | (c3 >> 2); + result[byteIdx++] = ((c3 & 0x03) << 6) | c4; + } + + return result; + } + + /// Распаковывает байтовый массив обратно в строку. + static String decode(Uint8List bytes) { + if (bytes.isEmpty) { + return ''; + } + + final StringBuffer sb = StringBuffer(); + + for (int i = 0; i < bytes.length; i += 3) { + int b1 = bytes[i]; + int b2 = bytes[i + 1]; + int b3 = bytes[i + 2]; + + int c1 = (b1 >> 2) & 0x3F; + int c2 = ((b1 & 0x03) << 4) | ((b2 >> 4) & 0x0F); + int c3 = ((b2 & 0x0F) << 2) | ((b3 >> 6) & 0x03); + int c4 = b3 & 0x3F; + + sb.write(charset[c1]); + sb.write(charset[c2]); + sb.write(charset[c3]); + sb.write(charset[c4]); + } + + // Удаляем заполнители и пробелы по краям + return sb.toString().replaceAll('_', '').trim(); + } +} \ No newline at end of file diff --git a/test/potato_pass_core_test.dart b/test/potato_pass_core_test.dart index 4c6e2cf..551b6f7 100644 --- a/test/potato_pass_core_test.dart +++ b/test/potato_pass_core_test.dart @@ -1,16 +1,53 @@ +import 'dart:typed_data'; import 'package:potato_pass_core/potato_pass_core.dart'; import 'package:test/test.dart'; void main() { - group('A group of tests', () { - final awesome = Awesome(); + group('CompactTextEncoder Tests', () { + test('Кодирование и декодирование русской строки', () { + const input = 'ИВАНОВ ИВАН ИВАНОВИЧ'; + + // Кодируем + final Uint8List encoded = CompactTextEncoder.encode(input); + + // Проверяем декодирование + final String decoded = CompactTextEncoder.decode(encoded); - setUp(() { - // Additional setup goes here. + expect(decoded, equals(input)); }); - test('First Test', () { - expect(awesome.isAwesome, isTrue); + test('Проверка сжатия размера', () { + const input = 'ИВАНОВ ИВАН'; // 11 символов + + // В UTF-8 кириллица весит 2 байта на символ и займут 22 байта + // В 6-битном кодировщике 11 символов округляются до 12 и займут 9 байт + final Uint8List encoded = CompactTextEncoder.encode(input); + + expect(encoded.length, equals(9)); + }); + + test('Обработка строчных букв (автоперевод в верхний регистр)', () { + const input = 'Петров Петр'; + final Uint8List encoded = CompactTextEncoder.encode(input); + final String decoded = CompactTextEncoder.decode(encoded); + + expect(decoded, equals('ПЕТРОВ ПЕТР')); + }); + + test('Тест некодируемых символов', () { + const input = '_.`~!@#\$%^&*()=+\\||//1234567890.';//2 точки и 29 некодирукемый символ (точки чтобы не обрезало) и _ в начале которе + final Uint8List encoded = CompactTextEncoder.encode(input); + final String decoded = CompactTextEncoder.decode(encoded); + + expect(decoded, equals('. .'));//2 точки и 29 пробелов (некодируемые символы заменяются на пробелы) + }); + + test('Тест кодируемости всех символов', () { + const input = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯABCDEFGHIJKLMNOPQRSTUVWXYZ .-,';//Все неслужебные символы + final Uint8List encoded = CompactTextEncoder.encode(input); + final String decoded = CompactTextEncoder.decode(encoded); + + expect(decoded, equals('АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯABCDEFGHIJKLMNOPQRSTUVWXYZ .-,')); }); }); -} +} \ No newline at end of file