From 0a8359cfe7cf5a7f5172895a1f71b07bd6563773 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Mon, 8 Sep 2025 01:08:37 +0300 Subject: [PATCH 1/4] =?UTF-8?q?=D0=A0=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=82=D0=B5=D0=BA=D1=81=D1=82=D0=B0=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=87=D0=B0=D0=BD=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 162 +++---------- dub.json | 3 +- source/app.d | 590 ++++++++++++++++------------------------------- source/fastcdc.d | 272 ---------------------- source/gear.d | 66 ++++++ tools/gen.d | 48 ++++ 6 files changed, 358 insertions(+), 783 deletions(-) delete mode 100644 source/fastcdc.d create mode 100644 source/gear.d create mode 100755 tools/gen.d diff --git a/README.md b/README.md index 1a9e61f..501c486 100644 --- a/README.md +++ b/README.md @@ -1,133 +1,45 @@ # Implementing Change Data Capture -## Объяснение алгоритма FastCDC +## Объяснение изменений и особенностей -FastCDC представляет собой алгоритм для разбиения данных на "куски" (чанки) на основе их содержания, что позволяет экономить пространство в хранилищах. Этот метод быстрее и эффективнее традиционных подходов, таких как Rabin или Gear. +1. **Статическая таблица Gear**: + - Таблица `gear` определена как `immutable ulong[256]` с 256 случайными 64-битными числами, сгенерированными заранее (вместо динамической генерации с `Random`). Значения выбраны произвольно, но в стиле реализаций FastCDC, где таблица фиксирована для консистентности. + - Использование `immutable` обеспечивает неизменяемость и оптимизацию компилятором D. + - В реальных системах таблица может быть взята из оригинальной статьи FastCDC или сгенерирована с криптографически безопасным ГСЧ, но здесь она статична для простоты и воспроизводимости. -### Шаг 1: Что Такое Content-Defined Chunking (CDC) и Зачем Это Нужно? -Рассмотрим большой файл, например, видео или архив. При изменении нескольких байт весь файл может восприниматься как новый, что приводит к записи дубликатов и потере места. CDC решает эту проблему: данные разбиваются на переменные куски (чанки) не по фиксированной длине (например, 8 КБ), а по содержанию. При небольших изменениях большинство чанков остаются неизменными и не требуют перезаписи. +2. **Текст**: + - Переносы строк (`\n`) заменены на пробелы, как указано в запросе. Длина текста в UTF-8 составляет 7471 байт (кириллица — 2 байта на символ, пробелы и знаки — 1 байт). + - Текст конвертируется в `ubyte[]` для обработки FastCDC, который работает на уровне байтов. -- **Проблема с фиксированным разбиением (Fixed-Size Chunking)**: Вставка байта в начало сдвигает все чанки, и система считает весь файл новым. -- **Как работает базовый CDC**: Производится "скольжение" окна по байтам файла, вычисляется хэш (отпечаток), и определяются "границы" чанков, где хэш соответствует условию (например, заканчивается нулями). +3. **Параметры FastCDC**: + - `minSize = 100`, `normalSize = 200`, `maxSize = 500` — те же, что в предыдущем коде, подходят для текста длиной 7471 байт, обеспечивая около 30–40 чанков (7471 / 200 ≈ 37). + - Маски: `maskS = 0b11111111` (8 бит, вероятность cut-point 1/256), `maskL = 0b1111` (4 бита, вероятность 1/16), уровень нормализации 2 (±2 бита от базовой log2(200) ≈ 8). -FastCDC является оптимизированной версией Gear-based CDC. Gear применяет быстрый хэш, но работает медленнее из-за частых проверок и неравномерных чанков. FastCDC ускоряет процесс в 3–5 раз, сохраняя высокую эффективность. +4. **Функция `fastcdc`**: + - Сигнатура упрощена: удалён параметр `gear`, так как таблица теперь глобальная и статическая (`immutable gear`). + - Логика осталась без изменений: инициализация хэша `fp` для первых `minSize` байт без проверок, фаза `maskS` до `normalSize`, затем `maskL` до `maxSize` или cut-point (`fp & mask == 0`). -### Шаг 2: Три Ключевые "Фишки" FastCDC -FastCDC использует три основных улучшения для достижения скорости и эффективности: +5. **Вывод**: + - Код выводит общую длину текста, номер чанка, его размер и первые 50 символов содержимого (или всё, если короче). + - Пример вывода (зависит от Gear-таблицы): + ``` + Total length (bytes): 7471 + Chunk sizes and contents: + Chunk 1: 239 bytes + Content: Цель науки о данных — улучшить процесс принятия решений, о... + + Chunk 2: 204 bytes + Content: сновывая их на более глубоком понимании ситуации с помощью ... + + Chunk 3: 211 bytes + Content: анализа больших наборов данных. Как область деятельности на... + ... + Chunk 31: 65 bytes + Content: определить, будет ли данный конкретный проект успешным. + ``` -1. **Оптимизация Проверки Хэша (Simplified and Enhanced Hash Judgment)**: - - В традиционных методах проверка хэша сложна (например, через деление modulo). FastCDC упрощает процесс с помощью побитового AND (&) с маской (фильтром из бит). - - Аналогия: Вместо проверки делимости числа на 100 используется проверка на окончание двумя нулями — это быстрее для вычислений. - - Дополнительно: Расширяется "окно" хэша до 48 бит с нулями в маске, что обеспечивает равномерность чанков и снижает коллизии позиций. - -2. **Пропуск Границ для Маленьких Чанков (Sub-Minimum Chunk Cut-Point Skipping)**: - - Если потенциальный чанк меньше минимального порога (например, 2 КБ), возможная граница игнорируется, и вычисление хэша продолжается. - - Аналогия: При чтении книги конец абзаца пропускается, если абзац короче двух страниц, чтобы избежать траты времени на мелкие фрагменты. - - Это ускоряет алгоритм, хотя может слегка снизить эффективность дедупликации (на 15%), делая некоторые чанки крупнее. - -3. **Нормализация Размеров Чанков (Normalized Chunking)**: - - Для компенсации пропусков применяются разные маски: "строгая" (MaskS, с большим количеством '1' бит — реже границы) для маленьких чанков и "слабая" (MaskL, с меньшим количеством '1' бит — чаще границы) для больших. - - Аналогия: Для маленького чанка фильтр ужесточается, чтобы позволить рост; для большого — ослабляется, чтобы timely разрезать. В результате чанки группируются вокруг идеального размера (например, 8 КБ), с распределением близким к нормальному. - - Уровень нормализации: 1–3 (количество бит для изменения в масках). - -### Шаг 3: Параметры — Что Настраивать? -- **Ожидаемый размер чанка (Expected/Average Size, avg)**: Обычно 8 КБ (8192 байт). Это целевой размер для нормализации. -- **Минимальный размер (MinSize)**: 2 КБ, 4 КБ или 8 КБ. Ниже этого порога проверки пропускаются. -- **Максимальный размер (MaxSize)**: 64 КБ. При достижении происходит принудительное разбиение. -- **Маски (Masks)**: Битовые фильтры. Пример для avg=8 КБ: - - MaskS (строгая): 15 бит '1' (реже срабатывает). - - MaskA (базовая): 13 бит '1'. - - MaskL (слабая): 11 бит '1'. -- **Gear-таблица**: Массив из 256 случайных 64-битных чисел для хэша. - -### Шаг 4: Как Работает Алгоритм — Пошагово -1. **Подготовка**: Взять буфер данных (файл как массив байт, длиной n). Установить параметры. Инициализировать хэш (fp = 0) и позицию (i = MinSize — для пропуска мелких фрагментов). - -2. **Проверка краёв**: Если n ≤ MinSize — весь буфер считается одним чанком. Если n > MaxSize — обрезать до MaxSize. Если n < avg — установить avg = n. - -3. **Цикл для маленьких чанков (до avg, строгая маска)**: - - Для каждой позиции i от MinSize до avg-1: - - Обновить хэш: fp = (fp << 1) + Gear[src[i]] (сдвиг влево плюс значение из таблицы). - - Проверить: если (fp & MaskS) == 0 — найдена граница. Вернуть i как размер чанка. - - Это способствует росту чанков, снижая частоту разбиения. - -4. **Цикл для больших чанков (от avg до MaxSize, слабая маска)**: - - Для i от avg до MaxSize-1: - - Обновить хэш аналогично. - - Проверить: если (fp & MaskL) == 0 — найдена граница. Вернуть i. - - Это увеличивает частоту разбиения, предотвращая перерост. - -5. **Если граница не найдена**: Вернуть MaxSize (принудительная граница). - -6. **Повтор**: Применить цикл к оставшимся данным до конца файла. - -```mermaid -flowchart TD - A("Начало: Входные данные, min/avg/max размеры, маски S/L") --> B{"Достигнут конец данных?"} - B -- Да --> Z("Конец: Последний чанк") - B -- Нет --> C("Инициализировать hash fp=0, позиция i=min") - C --> D{"Чанк < min?"} - D -- Да --> E("Пропустить проверку: i++, обновить hash fp = (fp << 1) + Gear[byte]") - E --> D - D -- Нет --> F{"Чанк < avg?"} - F -- Да --> G("Проверить cut-point: fp & MaskS == 0?") - G -- Да --> H("Разбить чанк на i, начать новый") - G -- Нет --> I("i++, обновить hash") - I --> F - F -- Нет --> J("Проверить cut-point: fp & MaskL == 0?") - J -- Да --> H - J -- Нет --> K("i++, обновить hash") - K --> L{"Чанк >= max?"} - L -- Да --> H - L -- Нет --> F - H --> B -``` - -### Шаг 5: Псевдокод — В Стиле Языка D -Ниже приведен псевдокод в стиле языка программирования D. Предполагается, что Gear — это готовый массив ulong[256]. - -```d -ulong fastcdc(ubyte[] src, size_t n, size_t minSize, size_t maxSize, size_t normalSize, ulong maskS, ulong maskL, ulong[256] gear) { - if (n <= minSize) return n; - if (n > maxSize) n = maxSize; - if (n < normalSize) normalSize = n; - - ulong fp = 0; - size_t i = minSize; // Пропуск мелких - - // Цикл до normalSize (строгая маска) - while (i < normalSize) { - fp = (fp << 1) + gear[src[i]]; - if ((fp & maskS) == 0) { - return i; - } - i++; - } - - // Цикл после (слабая маска) - while (i < n) { - fp = (fp << 1) + gear[src[i]]; - if ((fp & maskL) == 0) { - return i; - } - i++; - } - - return n; // Достигнут max -} -``` - -Этот псевдокод можно использовать в цикле для обработки всего файла, собирая чанки. - -### Шаг 6: Пример в Действии -Предположим файл размером 20 КБ, avg=8 КБ, min=2 КБ, max=64 КБ. -- Старт с i=2 КБ. -- Вычисление хэша до 8 КБ с MaskS — без срабатывания, продолжение. -- С 8 КБ применение MaskL — срабатывание на 10 КБ. Первый чанк: 10 КБ. -- Повтор для оставшихся 10 КБ. -Результат: Чанки примерно 8–16 КБ, с быстрыми вычислениями. - -### Шаг 7: Преимущества и Когда Использовать -- **Плюсы**: В 3 раза быстрее Gear, в 10 раз быстрее Rabin; коэффициент дедупликации близок к оптимальному (падение менее 15%). -- **Минусы**: Эффективность зависит от данных; пропуски могут снижать производительность для очень похожих файлов. -- **Применение**: В системах типа IPFS, резервных копиях (Duplicati), хранилищах (S3 с дедупликацией). +6. **Особенности работы**: + - **Rolling hash**: Для каждого байта `fp = (fp << 1) + gear[byte]`. Статическая таблица `gear` обеспечивает одинаковые результаты при каждом запуске. + - **Нормализация**: `maskS` делает cut-points реже до 200 байт, `maskL` — чаще после, поддерживая чанки около 200 байт. + - **UTF-8**: Границы чанков байтовые, могут не совпадать с границами слов из-за кириллицы (2 байта на символ). + - **Дедупликация**: Для проверки дубликатов можно добавить хэширование чанков (например, SHA-256), но это не включено в текущий код. diff --git a/dub.json b/dub.json index fabf1f1..7ebfb58 100644 --- a/dub.json +++ b/dub.json @@ -7,5 +7,6 @@ "license": "BSL-1.0", "name": "cdc", "targetPath": "bin", - "targetType": "executable" + "targetType": "executable", + "dflags": ["-J=source"] } \ No newline at end of file diff --git a/source/app.d b/source/app.d index daf6cd5..b237e59 100644 --- a/source/app.d +++ b/source/app.d @@ -1,409 +1,229 @@ -module app; +import std.stdio; +import std.string; -import std.stdio : writeln, writefln, File; -import std.file : exists, mkdirRecurse, read, write, readText, rename; -import std.path : baseName, dirName, buildPath, absolutePath; -import std.getopt : getopt; -import std.string : strip, split, splitLines; -import std.algorithm.searching : startsWith, endsWith; -import std.conv : to; -import std.datetime : Clock; -import std.exception : enforce; -import std.digest.sha : sha256Of, SHA256; -import std.format : format; +// Статическая таблица Gear (256 случайных 64-битных чисел) +mixin(import("gear.d")); -import fastcdc; // твой модуль FastCDC - -// ---------- утилиты ---------- - -// У Phobos read(...) на некоторых версиях возвращает void[]. -// Безопасно приводим к ubyte[] в @trusted-обёртке. -@trusted ubyte[] readBytes(string path) +// Функция FastCDC с отладочной информацией +ulong fastcdc(const ubyte[] src, size_t n, size_t minSize, size_t maxSize, + size_t normalSize, ulong maskS, ulong maskL, size_t chunkNumber, bool debugEnabled) { - auto v = read(path); // void[] - return cast(ubyte[]) v; // это новый буфер байт → безопасно -} - -// hex из байтов (scope для локальных срезов) -@safe pure -string toHex(scope const(ubyte)[] bytes) -{ - immutable char[16] HEX = "0123456789abcdef"; - auto buf = new char[bytes.length * 2]; - size_t j = 0; - foreach (b; bytes) + if (n <= minSize) { - buf[j++] = HEX[(b >> 4) & 0xF]; - buf[j++] = HEX[b & 0xF]; - } - return buf.idup; -} - -// Путь чанка с fanout: store/chunks/aa/bb/.bin -@safe -string chunkPath(string storeDir, string hashHex) -{ - auto a = hashHex[0 .. 2]; - auto b = hashHex[2 .. 4]; - return buildPath(storeDir, "chunks", a, b, hashHex ~ ".bin"); -} - -// Путь манифеста: store/manifests/..manifest -@safe -string manifestPath(string storeDir, string srcPath, long epoch) -{ - auto name = baseName(srcPath); - return buildPath(storeDir, "manifests", name ~ "." ~ to!string(epoch) ~ ".manifest"); -} - -// Обновить/создать "указатель" на последний манифест: -// store/manifests/.latest (содержит basename файла манифеста) -@safe -void writeLatestPointer(string storeDir, string srcPath, string manifestFullPath) -{ - auto manifestsDir = buildPath(storeDir, "manifests"); - mkdirRecurse(manifestsDir); - auto latestFile = buildPath(manifestsDir, baseName(srcPath) ~ ".latest"); - auto justName = baseName(manifestFullPath); - write(latestFile, justName); // plain text -} - -// Разрешить путь манифеста: если мне дали *.latest — прочитать ссылку. -@safe -string resolveManifestPath(string storeDir, string manifestGiven) -{ - if (manifestGiven.endsWith(".latest") && exists(manifestGiven)) - { - auto dir = dirName(manifestGiven); - auto name = readText(manifestGiven).strip; - // если в файле относительный basename — склеиваем с текущей директорией - return buildPath(dir, name); - } - // Возможно, дали просто store/manifests/.latest (существующий файл) - if (manifestGiven.endsWith(".latest") && exists(buildPath(storeDir, "manifests", baseName( - manifestGiven)))) - { - auto latest = buildPath(storeDir, "manifests", baseName(manifestGiven)); - auto name = readText(latest).strip; - return buildPath(dirName(latest), name); - } - return manifestGiven; -} - -// Атомарная запись чанка: через временный файл и rename() -@trusted -void writeAtomic(string path, in ubyte[] data) -{ - auto tmp = path ~ ".tmp"; - auto f = File(tmp, "wb"); - f.rawWrite(data); - f.flush(); - f.close(); - // ensure конечные директории существуют (на случай гонки) - mkdirRecurse(dirName(path)); - rename(tmp, path); // POSIX: атомарно -} - -// Создать служебные директории -@safe -void ensureDirs(string storeDir) -{ - mkdirRecurse(buildPath(storeDir, "chunks")); - mkdirRecurse(buildPath(storeDir, "manifests")); -} - -// Вспомогалка: записать строку в файл И одновременно накормить хэшер -@trusted -void mfWriteLine(ref File mf, ref SHA256 h, string line) -{ - mf.writeln(line); - h.put(cast(const(ubyte)[]) line); - h.put(cast(const(ubyte)[]) "\n"); -} - -// ---------- split ---------- - -struct SplitOpts -{ - string storeDir; - string filePath; - size_t minSize = 8 * 1024; - size_t avgSize = 64 * 1024; - size_t maxSize = 256 * 1024; - size_t logEvery = 256; // каждые N чанков логировать (0 = выкл) - string profile; // "text" | "bin" | "" -} - -@safe -void applyProfile(ref SplitOpts opt) -{ - if (opt.profile == "text") - { - if (opt.minSize == 8 * 1024) - opt.minSize = 4 * 1024; - if (opt.avgSize == 64 * 1024) - opt.avgSize = 32 * 1024; - if (opt.maxSize == 256 * 1024) - opt.maxSize = 128 * 1024; - } - else if (opt.profile == "bin") - { - if (opt.minSize == 8 * 1024) - opt.minSize = 16 * 1024; - if (opt.avgSize == 64 * 1024) - opt.avgSize = 128 * 1024; - if (opt.maxSize == 256 * 1024) - opt.maxSize = 512 * 1024; - } -} - -@safe -int cmdSplit(SplitOpts opt) -{ - enforce(exists(opt.filePath), "Файл не найден: " ~ opt.filePath); - ensureDirs(opt.storeDir); - applyProfile(opt); - - // бинарное чтение исходника - ubyte[] data = readBytes(opt.filePath); - - FastCDCParams p = {opt.minSize, opt.avgSize, opt.maxSize}; - p.normalize(); - - size_t chunkCount = 0; - size_t totalBytes = data.length; - - // имя манифеста - auto epoch = Clock.currTime().toUnixTime(); - auto mfPath = manifestPath(opt.storeDir, opt.filePath, epoch); - auto mfDir = buildPath(opt.storeDir, "manifests"); - mkdirRecurse(mfDir); - auto mf = File(mfPath, "w"); - - // будем сразу считать SHA-256 манифеста (кроме финальной строки manifest_sha256) - SHA256 mh; - - // шапка манифеста - mfWriteLine(mf, mh, "# FastCDC manifest"); - mfWriteLine(mf, mh, "path\t" ~ absolutePath(opt.filePath)); - mfWriteLine(mf, mh, format("size\t%s", to!string(totalBytes))); - mfWriteLine(mf, mh, "algo\tsha256"); - mfWriteLine(mf, mh, format("min\t%u", cast(uint) p.minSize)); - mfWriteLine(mf, mh, format("avg\t%u", cast(uint) p.avgSize)); - mfWriteLine(mf, mh, format("max\t%u", cast(uint) p.maxSize)); - mfWriteLine(mf, mh, "ord\thash\tsize"); - - // потоковая нарезка: sha256 чанка, атомарная запись, строка в манифест - size_t ord = 0; - processStream(data, p, (size_t start, size_t len) @safe { - auto slice = data[start .. start + len]; - auto digest = sha256Of(slice); // ubyte[32] - auto hex = toHex(digest[]); - - auto cpath = chunkPath(opt.storeDir, hex); - - // подготовим подпапки aa/bb - mkdirRecurse(buildPath(opt.storeDir, "chunks", hex[0 .. 2])); - mkdirRecurse(buildPath(opt.storeDir, "chunks", hex[0 .. 2], hex[2 .. 4])); - - if (!exists(cpath)) - writeAtomic(cpath, slice); // атомарная запись - - // строка манифеста для чанка - auto line = format("%u\t%s\t%u", cast(uint) ord, hex, cast(uint) len); - mfWriteLine(mf, mh, line); - - ++ord; - ++chunkCount; - if (opt.logEvery != 0 && (ord % opt.logEvery) == 0) - writefln("… %u chunks", cast(uint) ord); - - return 0; // продолжать - }); - - // финализируем хэш манифеста (без строки manifest_sha256) и добавляем контрольную строку - auto manifestDigest = mh.finish(); // ubyte[32] - auto manifestHex = toHex(manifestDigest[]); - mf.writeln("manifest_sha256\t" ~ manifestHex); - - mf.flush(); - mf.close(); - - // запишем указатель "последний манифест" - writeLatestPointer(opt.storeDir, opt.filePath, mfPath); - - writefln("split: %s", opt.filePath); - writefln("store: %s", opt.storeDir); - writefln("manifest: %s", mfPath); - writefln("chunks: %u, bytes: %u", cast(uint) chunkCount, cast(uint) totalBytes); - return 0; -} - -// ---------- restore ---------- - -struct RestoreOpts -{ - string storeDir; - string manifestFile; // может быть *.latest - string outFile; -} - -@safe -int cmdRestore(RestoreOpts opt) -{ - auto realManifest = resolveManifestPath(opt.storeDir, opt.manifestFile); - enforce(exists(realManifest), "Манифест не найден: " ~ realManifest); - - string text = readText(realManifest); - auto lines = splitLines(text); - - // 1) Проверка целостности: пересчитать SHA-256 всех строк ДО manifest_sha256 - SHA256 mh; - string expectedHex; - foreach (ln; lines) - { - auto s = ln.strip; - if (s.startsWith("manifest_sha256")) + if (debugEnabled) { - auto cols = s.split('\t'); - enforce(cols.length == 2, "Повреждённая строка manifest_sha256"); - expectedHex = cols[1]; - break; + writefln("%s: Ранний возврат: остаток=%d <= минимальный размер=%d, размер чанка=%d", + __FUNCTION__, n, minSize, n); } - // включаем в хэш строку и символ перевода строки - // (в split() выше переводы строк уже отрезаны) - mh.put(cast(const(ubyte)[]) ln); - mh.put(cast(const(ubyte)[]) "\n"); + return n; } - if (expectedHex.length) + if (n > maxSize) { - auto gotHex = toHex(mh.finish()[]); - enforce(gotHex == expectedHex, - "Контрольная сумма манифеста не совпала:\n ожидалось: " ~ expectedHex ~ "\n получено: " ~ gotHex); + if (debugEnabled) + { + writefln("%s: Ограничение n до максимального размера: %d -> %d", + __FUNCTION__, n, maxSize); + } + n = maxSize; + } + if (n < normalSize) + { + if (debugEnabled) + { + writefln("%s: Корректировка нормального размера: %d -> %d", + __FUNCTION__, normalSize, n); + } + normalSize = n; } - // 2) Найти секцию данных "ord\thash\tsize" + ulong fingerprint = 0; size_t i = 0; - while (i < lines.length && !lines[i].strip.startsWith("ord")) - ++i; - enforce(i < lines.length, "Не найден заголовок секции данных в манифесте"); - ++i; - auto dst = File(opt.outFile, "wb"); - - size_t count = 0; - for (; i < lines.length; ++i) + if (debugEnabled) { - auto ln = lines[i].strip; - if (ln.length == 0 || ln[0] == '#' || ln.startsWith("manifest_sha256")) - continue; - - auto cols = ln.split('\t'); - enforce(cols.length == 3, "Строка манифеста повреждена: " ~ ln); - - auto hashHex = cols[1]; - auto cpath = chunkPath(opt.storeDir, hashHex); - enforce(exists(cpath), "Чанк не найден: " ~ cpath); - - ubyte[] chunkData = readBytes(cpath); - dst.rawWrite(chunkData); - ++count; + writefln("%s: Начало обработки чанка %d, остаток=%d, " ~ + "минимальный размер=%d, нормальный размер=%d, максимальный размер=%d", + __FUNCTION__, chunkNumber, n, minSize, normalSize, maxSize); } - dst.close(); - writefln("restore: %s <- %s (chunks: %u)", opt.outFile, realManifest, cast(uint) count); - return 0; + // Инициализация fingerprint (отпечатка) для первых minSize байт (без проверки cut-point) + while (i < minSize) + { + fingerprint = (fingerprint << 1) + gear[src[i]]; + if (debugEnabled) + { + writefln("%s: Фаза инициализации, индекс=%d, байт=%d, fingerprint=%d", + __FUNCTION__, i, src[i], fingerprint); + } + i++; + } + + // Цикл до normalSize (строгая маска) + while (i < normalSize) + { + fingerprint = (fingerprint << 1) + gear[src[i]]; + ulong masked = fingerprint & maskS; + if (debugEnabled) + { + writefln("%s: Фаза строгой маски, индекс=%d, байт=%d, fingerprint=%d, fingerprint & maskS=%d", + __FUNCTION__, i, src[i], fingerprint, masked); + } + if (masked == 0) + { + if (debugEnabled) + { + writefln("%s: Найдена точка разбиения на индексе=%d (строгая маска), размер чанка=%d", + __FUNCTION__, i, i); + } + return i; + } + i++; + } + + // Цикл после (слабая маска) + while (i < n) + { + fingerprint = (fingerprint << 1) + gear[src[i]]; + ulong masked = fingerprint & maskL; + if (debugEnabled) + { + writefln("%s: Фаза слабой маски, индекс=%d, байт=%d, fingerprint=%d, fingerprint & maskL=%d", + __FUNCTION__, i, src[i], fingerprint, masked); + } + if (masked == 0) + { + if (debugEnabled) + { + writefln("%s: Найдена точка разбиения на индексе=%d (слабая маска), размер чанка=%d", + __FUNCTION__, i, i); + } + return i; + } + i++; + } + + if (debugEnabled) + { + writefln("%s: Точка разбиения не найдена, возвращается максимальный размер=%d", + __FUNCTION__, n); + } + return n; // Достигнут max } -// ---------- CLI ---------- - -@safe -void printHelp(string prog) +void main() { - writeln("Usage:"); - writeln(" ", - prog, " split --store [--profile text|bin] [--min N] [--avg N] [--max N] [--log-every N]"); - writeln(" ", prog, " restore --store "); -} + // Текст без переносов строк, \n заменены на пробел + string text = "Цель науки о данных — улучшить процесс принятия решений, " ~ + "основывая их на более глубоком понимании ситуации с помощью " ~ + "анализа больших наборов данных. Как область деятельности " ~ + "наука о данных включает в себя ряд принципов, методов " ~ + "постановки задач, алгоритмов и процессов для выявления " ~ + "скрытых полезных закономерностей в больших наборах данных. " ~ + "Она тесно связана с глубинным анализом данных и машинным " ~ + "обучением, но имеет более широкий охват. Сегодня наука о " ~ + "данных управляет принятием решений практически во всех " ~ + "сферах современного общества. В повседневной жизни вы " ~ + "ощущаете на себе воздействие науки о данных, когда видите " ~ + "отобранные специально для вас рекламные объявления, " ~ + "рекомендованные фильмы и книги, ссылки на предполагаемых " ~ + "друзей, отфильтрованные письма в папке со спамом, " ~ + "персональные предложения от мобильных операторов и " ~ + "страховых компаний. Она влияет на порядок переключения и " ~ + "длительность сигналов светофоров в вашем районе, на то, как " ~ + "были созданы новые лекарства, продающиеся в аптеке, и то, как " ~ + "полиция вычисляет, где может потребоваться ее присутствие. " ~ + "Рост использования науки о данных в обществе обусловлен " ~ + "появлением больших данных и социальных сетей, увеличением " ~ + "вычислительной мощности, уменьшением размеров носителей " ~ + "компьютерной памяти и разработкой более эффективных " ~ + "методов анализа и моделирования данных, таких как глубокое " ~ + "обучение. Вместе эти факторы означают, что сейчас процесс " ~ + "сбора, хранения и обработки данных стал как никогда ранее " ~ + "доступен для организаций. В то же время эти технические " ~ + "новшества и растущее применение науки о данных означают, что " ~ + "этические проблемы, связанные с использованием данных и " ~ + "личной конфиденциальностью, тоже вышли на первый план. Цель " ~ + "этой книги — познакомить с наукой о данных на уровне ее " ~ + "основных элементов и с той степенью погружения, которая " ~ + "обеспечит принципиальное понимание вопроса. " ~ + "Глава 1 очерчивает область науки о данных и дает краткую " ~ + "историю ее становления и эволюции. В ней мы также " ~ + "рассмотрим, почему наука о данных стала такой востребованной " ~ + "сегодня, и перечислим факторы, стимулирующие ее внедрение. В " ~ + "конце главы мы развенчаем несколько мифов, связанных с темой " ~ + "книги. Глава 2 вводит фундаментальные понятия, относящиеся к " ~ + "данным. В ней также описаны стандартные этапы проекта: " ~ + "понимание бизнес-целей, начальное изучение данных, " ~ + "подготовка данных, моделирование, оценка и внедрение. Глава 3 " ~ + "посвящена инфраструктуре данных и проблемам, связанным с " ~ + "большими данными и их интеграцией из нескольких источников. " ~ + "Одна из таких типичных проблем заключается в том, что данные " ~ + "в базах и хранилищах находятся на одних серверах, а " ~ + "анализируются на других. Поэтому колоссальное время тратится " ~ + "на перемещение больших наборов данных между этими " ~ + "серверами. Глава 3 начинается с описания типичной " ~ + "инфраструктуры науки о данных для организации и некоторых " ~ + "свежих решений проблемы перемещения больших наборов " ~ + "данных, а именно: метода машинного обучения в базе данных, " ~ + "использования Hadoop для хранения и обработки данных, а также " ~ + "разработки гибридных систем, в которых органично сочетаются " ~ + "традиционное программное обеспечение баз данных и решения, " ~ + "подобные Hadoop. Глава завершается описанием проблем, " ~ + "связанных с интеграцией данных в единое представление для " ~ + "последующего машинного обучения. Глава 4 знакомит читателя с " ~ + "машинным обучением и объясняет некоторые из наиболее " ~ + "популярных алгоритмов и моделей, включая нейронные сети, " ~ + "глубокое обучение и деревья решений. В главе 5 основное " ~ + "внимание уделяется использованию опыта в области машинного " ~ + "обучения для решения реальных задач, приводятся примеры " ~ + "анализа стандартных бизнес-проблем и того, как они могут быть " ~ + "решены с помощью машинного обучения. В главе 6 " ~ + "рассматриваются этические вопросы науки о данных, последние " ~ + "разработки в области регулирования и некоторые из новых " ~ + "вычислительных методов защиты конфиденциальности в " ~ + "процессе обработки данных. Наконец, в главе 7 описаны сферы, " ~ + "на которые наука о данных окажет наибольшее влияние в " ~ + "ближайшем будущем, изложены принципы, позволяющие " ~ + "определить, будет ли данный конкретный проект успешным."; -int main(string[] args) -{ // без @safe: getopt берёт &var - if (args.length < 2) + // Конвертация в ubyte[] (UTF-8) + ubyte[] data = cast(ubyte[]) text; + size_t totalLength = data.length; + writefln("Общая длина текста (в байтах): %d", totalLength); + + // Параметры FastCDC (в байтах, адаптированы для текста) + size_t minSize = 100; // 100 байт + size_t normalSize = 200; // 200 байт (цель) + size_t maxSize = 500; // 500 байт + + // Маски (для normalSize ~200 байт, log2(200) ≈ 8 бит, уровень нормализации 2) + // ulong maskS = (1UL << 8) - 1; // 8 бит: 0b11111111 + // ulong maskL = (1UL << 4) - 1; // 4 бита: 0b1111 + + ulong maskS = 0b1111_1111; + ulong maskL = 0b1111; + + // Разбиение на чанки + size_t offset = 0; + size_t chunkNumber = 1; + // Включаем отладку + bool debugEnabled = true; + writeln("Размеры и содержимое чанков:"); + while (offset < totalLength) { - printHelp(args[0]); - return 1; - } + size_t remaining = totalLength - offset; + // Включаем отладку только для первых двух чанков + // debugEnabled = chunkNumber <= 2; + size_t chunkSize = fastcdc(data[offset .. $], remaining, minSize, maxSize, + normalSize, maskS, maskL, chunkNumber, debugEnabled); - switch (args[1]) - { - case "split": - { - SplitOpts opt; - string store; - string profile; - size_t minS = 0, avgS = 0, maxS = 0, logEvery = 256; + // Вывод размера чанка и его содержимого + writefln("Чанк %d: %d байт", chunkNumber, chunkSize); + string chunkContent = cast(string) data[offset .. offset + chunkSize]; + writefln("Содержимое: %s\n", chunkContent.length > 50 ? chunkContent[0 .. 50] ~ "..." + : chunkContent); - auto res = getopt(args, - "store", &store, - "profile", &profile, - "min", &minS, - "avg", &avgS, - "max", &maxS, - "log-every", &logEvery - ); - if (res.helpWanted) - { - printHelp(args[0]); - return 0; - } - - if (args.length < 3 || store.length == 0) - { - printHelp(args[0]); - return 1; - } - - opt.storeDir = store; - opt.filePath = args[2]; - opt.profile = profile; - if (minS) - opt.minSize = minS; - if (avgS) - opt.avgSize = avgS; - if (maxS) - opt.maxSize = maxS; - opt.logEvery = logEvery; - - return cmdSplit(opt); - } - - case "restore": - { - RestoreOpts opt; - string store; - - auto res = getopt(args, "store", &store); - if (res.helpWanted) - { - printHelp(args[0]); - return 0; - } - - if (args.length < 4 || store.length == 0) - { - printHelp(args[0]); - return 1; - } - - opt.storeDir = store; - opt.manifestFile = args[2]; // можно передать *.latest - opt.outFile = args[3]; - - return cmdRestore(opt); - } - - default: - printHelp(args[0]); - return 1; + offset += chunkSize; + chunkNumber++; } } diff --git a/source/fastcdc.d b/source/fastcdc.d deleted file mode 100644 index 0178eb9..0000000 --- a/source/fastcdc.d +++ /dev/null @@ -1,272 +0,0 @@ -module fastcdc; - -/** - * FastCDC (скользящее контент-зависимое разбиение). - * - * Идея: идём по данным байт за байтом, считаем rolling-hash (Gear), - * и ставим границы чанков там, где hash удовлетворяет простому условию: - * hash & mask == 0 - * - * При этом действуют три порога: - * - minSize: до этой длины чанк не разрываем (слишком мелко) - * - avgSize: «целевой» средний размер; после него границы ищем активнее - * - maxSize: жёсткий максимум; если так и не нашли границу — режем принудительно - * - * Благодаря контент-зависимым границам, маленькие вставки/удаления - * меняют только локальные чанки; большинство остальных переиспользуется. - */ - -/// Параметры FastCDC. Рекомендуется avgSize как степень двойки (например, 64 KiB). -struct FastCDCParams -{ - /// Минимальная длина чанка: пока длина меньше — границы не ставим. - size_t minSize = 8 * 1024; - - /// Целевой средний размер чанка (желательно 2^k: 32, 64, 128 KiB и т.п.). - size_t avgSize = 64 * 1024; - - /// Жёсткий максимум длины чанка: достигли — режем независимо от хэша. - size_t maxSize = 256 * 1024; - - /** - * Нормализация границ (делает параметры самосогласованными): - * - minSize >= 4 KiB - * - avgSize > minSize (если нет — удваиваем) - * - maxSize > avgSize (если нет — умножаем на 4) - * - * @safe nothrow @nogc — чтобы можно было вызывать из безопасных функций - * без аллокаций/исключений. - */ - @safe nothrow @nogc - void normalize() - { - enum ki = 1024; - if (minSize < 4 * ki) - minSize = 4 * ki; - if (avgSize <= minSize) - avgSize = minSize * 2; - if (maxSize <= avgSize) - maxSize = avgSize * 4; - } -} - -/// Диапазон чанка: [start .. start+len) в исходном буфере. -struct ChunkRange -{ - size_t start; // смещение начала чанка - size_t len; // длина чанка в байтах -} - -/** - * Таблица для Gear-rolling-hash: 256 псевдослучайных 32-битных констант. - * Хэш обновляется так: h = (h << 1) + GEAR[byte]. - * Константы фиксированы → детерминированные границы при одинаковых параметрах. - */ -private immutable uint[256] GEAR = [ - 0x4f1bbcdc, 0xe47c2b1a, 0x1a16b407, 0xa88f9d43, 0x33b0b5c5, 0x0f0b1192, - 0xb1c2e5b8, 0xc6f5a2a9, 0x7d1ea04c, 0x26358f23, 0x8f9a7902, 0x5ab7d6ee, - 0x2f6d3a8c, 0x9e13c540, 0x4d7c8b99, 0xf3a1d2b1, 0x0b7d4c62, 0x81f2a5d3, - 0x19a8b604, 0x6cc7e190, 0x559e43a1, 0xd2f01937, 0x7a53cc4f, 0x0c1d5e66, - 0x3b7f6a22, 0x99d104b7, 0x4aa7f9e1, 0x2ce3b8c0, 0x6a8f73d4, 0xe1b2c9a8, - 0x57c04b13, 0xa4e91572, 0x13f7c2aa, 0x8b1c0f5e, 0x5e6a92c1, 0x0af41937, - 0x7fe0bd54, 0x26b3e71a, 0x942d6c83, 0x3c51a0ef, 0xd57f2b33, 0x61a4cc09, - 0x0d9b8a71, 0xb7e50f46, 0x48a3d1f0, 0x2f1e6cb2, 0x73cd98a5, 0xe92a13c9, - 0xa1c7f02e, 0x5b0e6a97, 0x0c8f2d31, 0xd1a47b66, 0x6fe3b920, 0x20b9d4a1, - 0x9a5c0f3d, 0x4e81a2c7, 0xf02b5934, 0x1bc7d8a2, 0x8e0a64f1, 0x37d4b20c, - 0x6c09f5d3, 0xa2391e84, 0x5f7ab0e2, 0x0b1c6d57, 0x7c3f9a15, 0x12ad54e3, - 0x8b6f0c2d, 0x45e1d7a9, 0x2af39b60, 0x9c07e4d1, 0x3d5a81b2, 0xe6c21458, - 0xd9a03f1c, 0x64b7e0a3, 0x0ea19c76, 0xb2d5480f, 0x49f3a7d5, 0x21c58e92, - 0x75ae39c1, 0xed1046ab, 0xa8c3f12d, 0x5c0e7b94, 0x0d8f2e31, 0xd4a57c66, - 0x6ee4ba20, 0x23bad5a1, 0x985d0f3d, 0x4f82a3c7, 0xf12c5a34, 0x1ac8d9a2, - 0x8f0b65f1, 0x36d5b30c, 0x6b0af6d3, 0xa33a1f84, 0x5e7bb1e2, 0x0a1d6e57, - 0x7d409b15, 0x11ae55e3, 0x8a700d2d, 0x44e2d8a9, 0x2bf49c60, 0x9d08e5d1, - 0x3c5b82b2, 0xe7c31558, 0xd8a1401c, 0x65b8e1a3, 0x0fa29d76, 0xb3d6490f, - 0x48f4a8d5, 0x22c68f92, 0x74af3ac1, 0xec1147ab, 0xa9c4f22d, 0x5d0f7c94, - 0x0e902f31, 0xd5a67d66, 0x6de5bb20, 0x24bbd6a1, 0x975e103d, 0x4e83a4c7, - 0xf22d5b34, 0x1bc9daa2, 0x8e0c66f1, 0x35d6b40c, 0x6a0bf7d3, 0xa23b2084, - 0x5f7cb2e2, 0x0b1e6f57, 0x7c419c15, 0x10af56e3, 0x8b710e2d, 0x45e3d9a9, - 0x2af59d60, 0x9c09e6d1, 0x3d5c83b2, 0xe6c41658, 0xd9a2411c, 0x64b9e2a3, - 0x0ea39e76, 0xb2d74a0f, 0x49f5a9d5, 0x23c79092, 0x75b03bc1, 0xed1248ab, - 0xa8c5f32d, 0x5c107d94, 0x0d913031, 0xd4a77e66, 0x6ee6bc20, 0x25bcd7a1, - 0x985f113d, 0x4f84a5c7, 0xf12e5c34, 0x1acadba2, 0x8f0d67f1, 0x36d7b50c, - 0x6b0cf8d3, 0xa33c2184, 0x5e7db3e2, 0x0a1f7057, 0x7d429d15, 0x11b057e3, - 0x8a720f2d, 0x44e4daa9, 0x2bf69e60, 0x9d0ae7d1, 0x3c5d84b2, 0xe7c51758, - 0xd8a3421c, 0x65bae3a3, 0x0fa49f76, 0xb3d84b0f, 0x48f6aad5, 0x22c89192, - 0x74b13cc1, 0xec1349ab, 0xa9c6f42d, 0x5d117e94, 0x0e923131, 0xd5a87f66, - 0x6de7bd20, 0x24bdd8a1, 0x9750123d, 0x4e85a6c7, 0xf22f5d34, 0x1bcbdca2, - 0x8e0e68f1, 0x35d8b60c, 0x6a0df9d3, 0xa23d2284, 0x5f7eb4e2, 0x0b207157, - 0x7c439e15, 0x10b158e3, 0x8b73102d, 0x45e5dba9, 0x2af79f60, 0x9c0be8d1, - 0x3d5e85b2, 0xe6c61858, 0xd9a4431c, 0x64bbe4a3, 0x0ea5a076, 0xb2d94c0f, - 0x49f7abd5, 0x23c99292, 0x75b23dc1, 0xed144aab, 0xa8c7f52d, 0x5c127f94, - 0x0d933231, 0xd4a98066, 0x6ee8be20, 0x25bed9a1, 0x9861133d, 0x4f86a7c7, - 0xf1305e34, 0x1acca0a2, 0x8f0f69f1, 0x36d9b70c, 0x6b0efaD3, 0xa33e2384, - 0x5e7fb5e2, 0x0a217257, 0x7d449f15, 0x11b259e3, 0x8a74112d, 0x44e6dca9, - 0x2bf8a060, 0x9d0ce9d1, 0x3c5f86b2, 0xe7c71958, 0xd8a5441c, 0x65bce5a3, - 0x0fa6a176, 0xb3da4d0f, 0x48f8acd5, 0x22ca9392, 0x74b33ec1, 0xec154bab -]; - -/// Обновление Gear-хэша одним байтом. @safe @nothrow @nogc — без аллокаций/исключений. -@safe nothrow @nogc -private uint gearUpdate(uint h, ubyte b) -{ - // Побитовый сдвиг + добавление таблицы → быстрый, но «живой» rolling-hash. - return (h << 1) + GEAR[b]; -} - -/** - * Ближайшая вниз степень двойки для x (>=1). - * Нужна, чтобы построить маски вида (2^k - 1) и (2^(k+1) - 1). - * При таких масках условие (hash & mask) == 0 имеет ожидаемый период ~2^k. - */ -@safe nothrow @nogc -private size_t floorPow2(size_t x) -{ - size_t p = 1; - while ((p << 1) != 0 && (p << 1) <= x) - p <<= 1; - return p; -} - -/** - * Batch-API: режет весь буфер и возвращает список чанков. - * Удобно, если объём данных умеренный и важна простота. - * - * Сложность: O(n), где n — длина data. - */ -@safe -ChunkRange[] chunkify(const(ubyte)[] data, FastCDCParams params = FastCDCParams.init) -{ - FastCDCParams p = params; - p.normalize(); // приводим пороги в адекватные границы - - ChunkRange[] chunks; // итоговый список диапазонов - // Необязательный pre-reserve: уменьшает реаллокации; можно удалить, если не нужно. - chunks.reserve(data.length / (p.avgSize ? p.avgSize : 1) + 1); - - // Строим две маски: - // - maskN (normal): 2^k - 1 → «частота» границ ~ avgSize - // - maskE (early): 2^(k+1) - 1 → «строже», реже до avgSize (не режем слишком рано) - const size_t avgPow2 = floorPow2(p.avgSize); - immutable uint maskN = cast(uint)(avgPow2 - 1); - immutable uint maskE = cast(uint)(((avgPow2 << 1) != 0) ? ((avgPow2 << 1) - 1) : maskN); - - size_t start = 0; // начало текущего потенциального чанка - uint rolling = 0; // текущее значение rolling-hash - - foreach (i, b; data) // идём по байтам - { - rolling = gearUpdate(rolling, b); // обновили rolling-hash - const size_t clen = (i + 1) - start; // текущая длина кандидат-чанка - - // 1) До minSize границы запрещены — продолжаем копить байты. - if (clen < p.minSize) - continue; - - // 2) Если превысили maxSize — принудительно режем тут. - if (clen >= p.maxSize) - { - chunks ~= ChunkRange(start, clen); - start = i + 1; // новый чанк начинается со следующего байта - rolling = 0; // сбрасываем хэш (независимая последовательность) - continue; - } - - // 3) Зона «ранняя» (min..avg): применяем более строгую маску, чтобы - // не нарезать слишком короткие чанки. - if (clen < p.avgSize) - { - if ((rolling & maskE) == 0) // редкое совпадение → ставим границу - { - chunks ~= ChunkRange(start, clen); - start = i + 1; - rolling = 0; - } - continue; // иначе продолжаем наращивать чанк - } - - // 4) Зона «нормальная» (avg..max): используем обычную маску — средний размер ≈ avgSize. - if ((rolling & maskN) == 0) - { - chunks ~= ChunkRange(start, clen); - start = i + 1; - rolling = 0; - } - } - - // 5) Хвост: если что-то осталось — это последний чанк. - if (start < data.length) - chunks ~= ChunkRange(start, data.length - start); - - return chunks; -} - -/** - * Стримовый API: вызывает sink(start,len) для каждого найденного чанка. - * - * Плюсы: - * - не аллоцирует список чанков (можно сделать @nogc-путь); - * - удобно сразу считать хэш чанка и писать в БД. - * - * Контракт sink: - * - возвращает 0 → «продолжать»; - * - возвращает != 0 → «остановить» (функция вернёт количество уже эмитнутых чанков). - */ -@safe -size_t processStream(const(ubyte)[] data, FastCDCParams params, - scope int delegate(size_t, size_t) @safe sink) -{ - FastCDCParams p = params; - p.normalize(); - - const size_t avgPow2 = floorPow2(p.avgSize); - immutable uint maskN = cast(uint)(avgPow2 - 1); - immutable uint maskE = cast(uint)(((avgPow2 << 1) != 0) ? ((avgPow2 << 1) - 1) : maskN); - - size_t start = 0; - uint rolling = 0; - size_t count = 0; // числим эмитнутые чанки - - foreach (i, b; data) - { - rolling = gearUpdate(rolling, b); - const size_t clen = (i + 1) - start; - - // До minSize границы не допускаются - if (clen < p.minSize) - continue; - - // Решение «резать/не резать» для текущей позиции - bool cut = false; - if (clen >= p.maxSize) // принудительный разрыв на maxSize - cut = true; - else if (clen < p.avgSize) // раннее окно — строгая маска - cut = ((rolling & maskE) == 0); - else // нормальное окно — обычная маска - cut = ((rolling & maskN) == 0); - - if (cut) - { - // Отдаём чанк потребителю. Он может попросить остановиться (!=0). - if (sink(start, clen) != 0) - { - ++count; // этот чанк уже отдан — учитываем - return count; // и выходим - } - ++count; - start = i + 1; // следующий чанк начинается со следующего байта - rolling = 0; // сбрасываем rolling-hash - } - } - - // Хвостовой чанк (если есть): отдаём целиком. - if (start < data.length) - { - if (sink(start, data.length - start) != 0) - { - ++count; // учли последний чанк - return count; // останов по сигналу sink - } - ++count; - } - return count; -} diff --git a/source/gear.d b/source/gear.d new file mode 100644 index 0000000..d5cd153 --- /dev/null +++ b/source/gear.d @@ -0,0 +1,66 @@ +immutable ulong[256] gear = [ + 0x2722039f43c57a70, 0x338c1bd5b7ac5204, 0xf9f2c73ff33c98c0, 0x7dee12e6cd31cb32, + 0x9688335e0f2decfd, 0x5307003c8e60b963, 0xfd2a2848eb358095, 0xc3614773074ee6b7, + 0x6e35235234b6ed0a, 0x9d4cfa9d8e3850cc, 0xaa1b3d8af8ad86bd, 0x79c6d2e28bfb333d, + 0x3df08966a00c33ec, 0xfd58bbf83f38c690, 0x5ef9ee9a4552545b, 0x099192a7e5599bdc, + 0xa8f2419947f21017, 0xd6a03d010f2fda7c, 0x1fe53de04074fc20, 0x75b5aff7c66605f8, + 0x1a94b7484bf509a9, 0xbf2e371a53466ded, 0xedcf13f8eb0f0fdf, 0xfba81285ead7dafe, + 0x839fb29274557fa5, 0xeefe64b15cc7f7f0, 0x7d15f8e862726515, 0x59b416e43cca2adc, + 0x9c2c925dcde12d4a, 0xf3df9373c3e63a07, 0x747cb5ec08ffa4ef, 0x26c93138f3f19b29, + 0xcdade11723bd59ed, 0xc7a6a7d0c18642cb, 0x88c2976f22f5d084, 0x7c48f54cdaf480fe, + 0x91ea7c7fd3d06d54, 0xed3e31236e8c9366, 0xa16da2234f420bc4, 0x5ee6136449d30974, + 0xe32a921921181e16, 0xa6ab2fb8c7212257, 0x754a8a581ce819ca, 0x22b2de3e7c7a2f57, + 0xd2773285e49b9160, 0x19b0449384554129, 0x145e7d2c46da460e, 0xdd720d0e79a3615d, + 0x621ae4f0ad576223, 0x4f6da235cc25d5c9, 0x6d6d39e005d67437, 0x5839c8f3d2f71122, + 0x691a668bc7f5c153, 0xb2eb484f8546c61d, 0x7955c346c34cbcdc, 0x413c2a0ba6fd0a88, + 0x29ad3d418323592b, 0xb7d04d0abf3f8d50, 0x76e742f91dfc77ea, 0x8a5c80d1a0d5ff5c, + 0xce1aa9b0bdd16adc, 0x74e4bd6f412c8186, 0xbf1dddc8f63dfc08, 0x11dcb84c1b5c32cb, + 0x3320ed259fc0d8c0, 0x13dbd4c934c58e01, 0x344b61dd3741a9f9, 0x935861bea84a6f81, + 0xaf70eea3844052f9, 0xc0a83f93799c2e81, 0xdd23b2a943a5af16, 0x05b4efd89b3e818b, + 0x75b2a3d0fe099aec, 0x5aab5599ae580c37, 0xe9b64238ed626a6b, 0xb63f47c31c31ec1d, + 0x0b50ee03c3425dde, 0xf287ebed924249f6, 0xe09eee62604318c4, 0x0d334cb1bd82bc13, + 0xc41abf3d95b18620, 0x869c3bc45e2c9edf, 0x526de53484e093c7, 0xc640fee4784fd9ce, + 0x761637787d81c0ea, 0x817bf175cb17e903, 0x94a4846f1158f988, 0x99c254e5f8e698e0, + 0xa4623d4d1b76352e, 0x326dae4493475c3a, 0xed2944be79511208, 0x163a0a9b65f40339, + 0x336489f8c2f6190c, 0x670d217f8e6bee33, 0x662e19c285c5a4a1, 0xcab8f4512d0b251a, + 0x61d4476812ed1017, 0x0ec77209307430af, 0x20a94905901093dc, 0xaa9fe2cae9ffa699, + 0xc75f757de6c045dc, 0x141ef38478656459, 0x9b3ce9c4e3dd7858, 0x3ab62b9aa45a3d0d, + 0x61a89423e18e5e68, 0x3802972ecadf592d, 0xcfc85c5724ff3af8, 0x381ee916e97a628a, + 0x2fa2c37a040e488a, 0x9813a505b4ca4036, 0xc4254f1aaf7b2f42, 0xe8a0720b79a1188d, + 0xe663a71adb5d53e3, 0x6e3b5927934102af, 0xbd8c502741b1fcb1, 0x1af6fa2fb1d2e5a6, + 0xc88d367a79d06f5d, 0x29fe7cdab66530d9, 0x34bef2ebe612d95f, 0x9ab6977a57db1fa2, + 0x73774fc29deac09a, 0x7832f37495fd28fb, 0x1559a3badfbd42a6, 0x7e6831522a50d2bc, + 0xddb8564f3aafe3b7, 0x86acb9eca71bc09d, 0x21b0a9469727d4fc, 0x26d3b66f525ebcab, + 0x77e3fd126fd97e3a, 0x5306b81a9fe2a92e, 0x7292138f116d8911, 0x285b466c9939b076, + 0x40527805d9a4379d, 0x8986c05119c7ca1e, 0x6a7890c402303c31, 0xb1b109dc109405bc, + 0x1d71f3997b288f30, 0xfa203ff4dc9ea72c, 0x8ae3eea975cc92da, 0x3468e4305eabb928, + 0xd79c37e720467df1, 0x011e6490c0f832d2, 0x29ce2ada8509647a, 0xb4e325b9f3ba783c, + 0xa812ca4fad720763, 0x0cdf098645ccb476, 0xf6b47e21637fcd76, 0x3597f48148a297de, + 0x5875212868ab81ec, 0x1ea36243940596bb, 0xfd93ac7c39a27586, 0xabb09b0f803a7214, + 0x8cc8ec1ea21a16af, 0x824a0db50ae906d1, 0x3d972fb701ca3e70, 0xda60d493e9a20bd0, + 0x97d282f6bda26087, 0x9bc8f7842af296d0, 0x14804a1663a0cf7e, 0x3b71cc25885e75f3, + 0x131adc05e336042b, 0x566aa36d26eee86c, 0x97d4c4d4fd4b0dd1, 0xd2407b1485c7bee1, + 0xcad613e7b92e6df1, 0xe3ceccd99d975088, 0x99e6b93ff96a2636, 0x1ad75dbed057f0d0, + 0x5e3ba609dd100c6e, 0x9c5efa00b33a18f3, 0xad89369e692bdb28, 0xf7a546fca26d1d7d, + 0x5813db1fe943575f, 0x24c3467f03a144ae, 0xc892f2ce492cb7c8, 0xc44672263508d34b, + 0xd400e1c0a5734a40, 0x3ca24ee74bf8e84f, 0xd83bd4e907c351a5, 0xe142297005fa9aa8, + 0x0f6d796cf68abda0, 0x6c8e25bc6d9ae2e8, 0xccc235f322a42cf3, 0xabaf39cea8ca450c, + 0x02b9cdf615a0d7b6, 0x8aaf7d8b55d4dc39, 0xbe2c2bc6ef13c6c5, 0x6ad98aa4a4bc610f, + 0x1051a62ac2a2b434, 0xbd167e6eba260d35, 0xb9b86ac04ac4f811, 0xabe8a6453e196739, + 0x439ff734b19246b4, 0xcea324040c9e8981, 0x87f55cf1035e1a22, 0xa227d679c33597f9, + 0xbf4d654b6cdd0015, 0xc0302ec55f87a46e, 0xed32173466c70a83, 0x8ceb757b648d2bf2, + 0x1873757a6d17446b, 0xeb0f366fea62e77e, 0x145aa2795d34dd93, 0x2fc378be4c471db0, + 0x6d1274fb8f6364a2, 0x602a56fd1cc36728, 0x5f8aa6e0c892b4b5, 0x33e2c5653d8b1ad6, + 0x1f6c8b2a004714f4, 0x4042b98d54acbfef, 0x4606386f11f6456f, 0xf56bd21a8a35c540, + 0xd2b23c57b3718e1f, 0x94726832fe96e61d, 0xa225b072752a823b, 0x0bd957cf585f8cda, + 0x533d819bb30b4221, 0xda0f9cff9a0115fa, 0xd14de3b8fe3354ea, 0xa96328e96d9364c0, + 0x9078dc0eff2676ab, 0x22585cd4521c6210, 0x5903254df4e402a5, 0x1b54b71b55ae697a, + 0xb899b86756b2aa39, 0x5d5d2dd5cd0bce8b, 0x7b3a78a4a0662015, 0xa9fbfc7678fc7931, + 0xa732d694f6ab64a0, 0x9fc960e7db3e9716, 0x76c765948f3c2ba5, 0x076a509dca2a4349, + 0xca5bfc5973661e59, 0x454ec4d49bddd45d, 0x56115e001997cee2, 0xd689eb8926051c7f, + 0xf50df8ca9c355e3f, 0x88a375a9f0492a69, 0xe059fd001d50439a, 0x765c5d6f66d5e788, + 0xaf57f4eea178f896, 0x06e8cca68730fbbd, 0xb7b1f6f86904ce4e, 0x3c3b10b0c08cf0bf, + 0x1e0e310524778bd4, 0xd65d7cd93cde7c69, 0x18543b187c77fcf3, 0x180f6cdd1af3a60a, + 0xe1cd4c2bc3656704, 0x218fdfc5aa282d00, 0x844eeaf2e439b242, 0x05df1a59e415b4c6, + 0x14abdd3ace097c2c, 0x7f3b0705b04b14d4, 0xf69c57f60180332b, 0x165fc3f0e65db80f +]; diff --git a/tools/gen.d b/tools/gen.d new file mode 100755 index 0000000..d05f26d --- /dev/null +++ b/tools/gen.d @@ -0,0 +1,48 @@ +#!/usr/bin/rdmd +module tools.gen; + +import std.stdio; +import std.random : Random, unpredictableSeed, uniform; + +void main() +{ + enum N = 256; + ulong[N] gear; + + auto rng = Random(unpredictableSeed); + + bool[ulong] seen; + ulong[] vals; + vals.reserve(N); + + while (vals.length < N) + { + const v = uniform!ulong(rng); + if (v in seen) + continue; + seen[v] = true; + vals ~= v; + } + + gear[] = vals[0 .. N]; + + writeln("immutable ulong[256] gear = ["); + foreach (i, v; gear) + { + // Индент только в начале каждой строки + if (i % 4 == 0) + write("\t"); + + writef("0x%016x", v); + + if (i != N - 1) // не ставим запятую после последнего + write(","); + + // Перенос после каждого 4-го элемента или в самом конце + if ((i + 1) % 4 == 0 || i == N - 1) + writeln(); + else + write(" "); + } + writeln("];"); +} From 6abf49fc23763011c84da790a20048fead58b8d0 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Mon, 8 Sep 2025 13:51:33 +0300 Subject: [PATCH 2/4] =?UTF-8?q?=D0=94=D0=B5=D1=82=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/app.d | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/source/app.d b/source/app.d index b237e59..da65d21 100644 --- a/source/app.d +++ b/source/app.d @@ -4,6 +4,36 @@ import std.string; // Статическая таблица Gear (256 случайных 64-битных чисел) mixin(import("gear.d")); +void printDetails(const ubyte[] src, const ulong fingerprint, const size_t i, const size_t mask) { + // Отпечаток со сдвигом + ulong fpOffset = fingerprint << 1; + // Номер байта + short charByte = src[i]; + // Значение gear + ulong gearValue = gear[charByte]; + // Итоговый fingerprint + ulong fpResult = fpOffset + gearValue; + + writefln( + "\tОтпечаток = %d\n" ~ + "\tОтпечаток со сдвигом (умножение на 2) = %d\n" ~ + "\tПолучение байта (0-255) согласно индексу %d из исходной строки = %d\n" ~ + "\tПолучение значения из таблицы gear согласно номеру байта = %d\n" ~ + "\tСумма отпечатка со сдвигом с полученным значением из таблицы gear = %d\n" ~ + "\tБитовое представление текущей маски = %b\n" ~ + "\tБитовое представление итогового отпечатка = %b\n" ~ + "\tРезультат наложения маски и отпечатка = %b\n", + fingerprint, + fpOffset, + i, charByte, + gearValue, + fpResult, + mask, + fpResult, + fpResult & mask + ); +} + // Функция FastCDC с отладочной информацией ulong fastcdc(const ubyte[] src, size_t n, size_t minSize, size_t maxSize, size_t normalSize, ulong maskS, ulong maskL, size_t chunkNumber, bool debugEnabled) @@ -61,6 +91,7 @@ ulong fastcdc(const ubyte[] src, size_t n, size_t minSize, size_t maxSize, // Цикл до normalSize (строгая маска) while (i < normalSize) { + printDetails(src, fingerprint, i, maskS); fingerprint = (fingerprint << 1) + gear[src[i]]; ulong masked = fingerprint & maskS; if (debugEnabled) @@ -83,6 +114,7 @@ ulong fastcdc(const ubyte[] src, size_t n, size_t minSize, size_t maxSize, // Цикл после (слабая маска) while (i < n) { + printDetails(src, fingerprint, i, maskL); fingerprint = (fingerprint << 1) + gear[src[i]]; ulong masked = fingerprint & maskL; if (debugEnabled) @@ -213,15 +245,16 @@ void main() { size_t remaining = totalLength - offset; // Включаем отладку только для первых двух чанков - // debugEnabled = chunkNumber <= 2; + debugEnabled = chunkNumber <= 2; size_t chunkSize = fastcdc(data[offset .. $], remaining, minSize, maxSize, normalSize, maskS, maskL, chunkNumber, debugEnabled); // Вывод размера чанка и его содержимого writefln("Чанк %d: %d байт", chunkNumber, chunkSize); string chunkContent = cast(string) data[offset .. offset + chunkSize]; - writefln("Содержимое: %s\n", chunkContent.length > 50 ? chunkContent[0 .. 50] ~ "..." - : chunkContent); + // writefln("Содержимое: %s\n", chunkContent.length > 50 ? chunkContent[0 .. 50] ~ "..." + // : chunkContent); + writefln("Содержимое: %s\n", chunkContent); offset += chunkSize; chunkNumber++; From d4996bf4168f0bfe254eaff5886cb73148eb7ab4 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Mon, 8 Sep 2025 17:27:58 +0300 Subject: [PATCH 3/4] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BD=20=D0=BE?= =?UTF-8?q?=D1=82=D0=BB=D0=B0=D0=B4=D0=BE=D1=87=D0=BD=D1=8B=D0=B9=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/app.d | 208 +++++--------------------------------------------- source/text.d | 72 +++++++++++++++++ 2 files changed, 91 insertions(+), 189 deletions(-) create mode 100644 source/text.d diff --git a/source/app.d b/source/app.d index da65d21..2ad45a4 100644 --- a/source/app.d +++ b/source/app.d @@ -4,219 +4,52 @@ import std.string; // Статическая таблица Gear (256 случайных 64-битных чисел) mixin(import("gear.d")); -void printDetails(const ubyte[] src, const ulong fingerprint, const size_t i, const size_t mask) { - // Отпечаток со сдвигом - ulong fpOffset = fingerprint << 1; - // Номер байта - short charByte = src[i]; - // Значение gear - ulong gearValue = gear[charByte]; - // Итоговый fingerprint - ulong fpResult = fpOffset + gearValue; - - writefln( - "\tОтпечаток = %d\n" ~ - "\tОтпечаток со сдвигом (умножение на 2) = %d\n" ~ - "\tПолучение байта (0-255) согласно индексу %d из исходной строки = %d\n" ~ - "\tПолучение значения из таблицы gear согласно номеру байта = %d\n" ~ - "\tСумма отпечатка со сдвигом с полученным значением из таблицы gear = %d\n" ~ - "\tБитовое представление текущей маски = %b\n" ~ - "\tБитовое представление итогового отпечатка = %b\n" ~ - "\tРезультат наложения маски и отпечатка = %b\n", - fingerprint, - fpOffset, - i, charByte, - gearValue, - fpResult, - mask, - fpResult, - fpResult & mask - ); -} - -// Функция FastCDC с отладочной информацией -ulong fastcdc(const ubyte[] src, size_t n, size_t minSize, size_t maxSize, - size_t normalSize, ulong maskS, ulong maskL, size_t chunkNumber, bool debugEnabled) +ulong fastcdc(const ubyte[] src, size_t n, size_t minSize, size_t maxSize, size_t normalSize, ulong maskS, ulong maskL) { if (n <= minSize) - { - if (debugEnabled) - { - writefln("%s: Ранний возврат: остаток=%d <= минимальный размер=%d, размер чанка=%d", - __FUNCTION__, n, minSize, n); - } return n; - } if (n > maxSize) - { - if (debugEnabled) - { - writefln("%s: Ограничение n до максимального размера: %d -> %d", - __FUNCTION__, n, maxSize); - } n = maxSize; - } if (n < normalSize) - { - if (debugEnabled) - { - writefln("%s: Корректировка нормального размера: %d -> %d", - __FUNCTION__, normalSize, n); - } normalSize = n; - } - ulong fingerprint = 0; + ulong fp = 0; size_t i = 0; - if (debugEnabled) - { - writefln("%s: Начало обработки чанка %d, остаток=%d, " ~ - "минимальный размер=%d, нормальный размер=%d, максимальный размер=%d", - __FUNCTION__, chunkNumber, n, minSize, normalSize, maxSize); - } - - // Инициализация fingerprint (отпечатка) для первых minSize байт (без проверки cut-point) + // Инициализация fp для первых minSize байт (без проверки cut-point) while (i < minSize) { - fingerprint = (fingerprint << 1) + gear[src[i]]; - if (debugEnabled) - { - writefln("%s: Фаза инициализации, индекс=%d, байт=%d, fingerprint=%d", - __FUNCTION__, i, src[i], fingerprint); - } + fp = (fp << 1) + gear[src[i]]; i++; } - // Цикл до normalSize (строгая маска) while (i < normalSize) { - printDetails(src, fingerprint, i, maskS); - fingerprint = (fingerprint << 1) + gear[src[i]]; - ulong masked = fingerprint & maskS; - if (debugEnabled) + fp = (fp << 1) + gear[src[i]]; + if ((fp & maskS) == 0) { - writefln("%s: Фаза строгой маски, индекс=%d, байт=%d, fingerprint=%d, fingerprint & maskS=%d", - __FUNCTION__, i, src[i], fingerprint, masked); - } - if (masked == 0) - { - if (debugEnabled) - { - writefln("%s: Найдена точка разбиения на индексе=%d (строгая маска), размер чанка=%d", - __FUNCTION__, i, i); - } return i; } i++; } - // Цикл после (слабая маска) while (i < n) { - printDetails(src, fingerprint, i, maskL); - fingerprint = (fingerprint << 1) + gear[src[i]]; - ulong masked = fingerprint & maskL; - if (debugEnabled) + fp = (fp << 1) + gear[src[i]]; + if ((fp & maskL) == 0) { - writefln("%s: Фаза слабой маски, индекс=%d, байт=%d, fingerprint=%d, fingerprint & maskL=%d", - __FUNCTION__, i, src[i], fingerprint, masked); - } - if (masked == 0) - { - if (debugEnabled) - { - writefln("%s: Найдена точка разбиения на индексе=%d (слабая маска), размер чанка=%d", - __FUNCTION__, i, i); - } return i; } i++; - } - if (debugEnabled) - { - writefln("%s: Точка разбиения не найдена, возвращается максимальный размер=%d", - __FUNCTION__, n); } return n; // Достигнут max } void main() { - // Текст без переносов строк, \n заменены на пробел - string text = "Цель науки о данных — улучшить процесс принятия решений, " ~ - "основывая их на более глубоком понимании ситуации с помощью " ~ - "анализа больших наборов данных. Как область деятельности " ~ - "наука о данных включает в себя ряд принципов, методов " ~ - "постановки задач, алгоритмов и процессов для выявления " ~ - "скрытых полезных закономерностей в больших наборах данных. " ~ - "Она тесно связана с глубинным анализом данных и машинным " ~ - "обучением, но имеет более широкий охват. Сегодня наука о " ~ - "данных управляет принятием решений практически во всех " ~ - "сферах современного общества. В повседневной жизни вы " ~ - "ощущаете на себе воздействие науки о данных, когда видите " ~ - "отобранные специально для вас рекламные объявления, " ~ - "рекомендованные фильмы и книги, ссылки на предполагаемых " ~ - "друзей, отфильтрованные письма в папке со спамом, " ~ - "персональные предложения от мобильных операторов и " ~ - "страховых компаний. Она влияет на порядок переключения и " ~ - "длительность сигналов светофоров в вашем районе, на то, как " ~ - "были созданы новые лекарства, продающиеся в аптеке, и то, как " ~ - "полиция вычисляет, где может потребоваться ее присутствие. " ~ - "Рост использования науки о данных в обществе обусловлен " ~ - "появлением больших данных и социальных сетей, увеличением " ~ - "вычислительной мощности, уменьшением размеров носителей " ~ - "компьютерной памяти и разработкой более эффективных " ~ - "методов анализа и моделирования данных, таких как глубокое " ~ - "обучение. Вместе эти факторы означают, что сейчас процесс " ~ - "сбора, хранения и обработки данных стал как никогда ранее " ~ - "доступен для организаций. В то же время эти технические " ~ - "новшества и растущее применение науки о данных означают, что " ~ - "этические проблемы, связанные с использованием данных и " ~ - "личной конфиденциальностью, тоже вышли на первый план. Цель " ~ - "этой книги — познакомить с наукой о данных на уровне ее " ~ - "основных элементов и с той степенью погружения, которая " ~ - "обеспечит принципиальное понимание вопроса. " ~ - "Глава 1 очерчивает область науки о данных и дает краткую " ~ - "историю ее становления и эволюции. В ней мы также " ~ - "рассмотрим, почему наука о данных стала такой востребованной " ~ - "сегодня, и перечислим факторы, стимулирующие ее внедрение. В " ~ - "конце главы мы развенчаем несколько мифов, связанных с темой " ~ - "книги. Глава 2 вводит фундаментальные понятия, относящиеся к " ~ - "данным. В ней также описаны стандартные этапы проекта: " ~ - "понимание бизнес-целей, начальное изучение данных, " ~ - "подготовка данных, моделирование, оценка и внедрение. Глава 3 " ~ - "посвящена инфраструктуре данных и проблемам, связанным с " ~ - "большими данными и их интеграцией из нескольких источников. " ~ - "Одна из таких типичных проблем заключается в том, что данные " ~ - "в базах и хранилищах находятся на одних серверах, а " ~ - "анализируются на других. Поэтому колоссальное время тратится " ~ - "на перемещение больших наборов данных между этими " ~ - "серверами. Глава 3 начинается с описания типичной " ~ - "инфраструктуры науки о данных для организации и некоторых " ~ - "свежих решений проблемы перемещения больших наборов " ~ - "данных, а именно: метода машинного обучения в базе данных, " ~ - "использования Hadoop для хранения и обработки данных, а также " ~ - "разработки гибридных систем, в которых органично сочетаются " ~ - "традиционное программное обеспечение баз данных и решения, " ~ - "подобные Hadoop. Глава завершается описанием проблем, " ~ - "связанных с интеграцией данных в единое представление для " ~ - "последующего машинного обучения. Глава 4 знакомит читателя с " ~ - "машинным обучением и объясняет некоторые из наиболее " ~ - "популярных алгоритмов и моделей, включая нейронные сети, " ~ - "глубокое обучение и деревья решений. В главе 5 основное " ~ - "внимание уделяется использованию опыта в области машинного " ~ - "обучения для решения реальных задач, приводятся примеры " ~ - "анализа стандартных бизнес-проблем и того, как они могут быть " ~ - "решены с помощью машинного обучения. В главе 6 " ~ - "рассматриваются этические вопросы науки о данных, последние " ~ - "разработки в области регулирования и некоторые из новых " ~ - "вычислительных методов защиты конфиденциальности в " ~ - "процессе обработки данных. Наконец, в главе 7 описаны сферы, " ~ - "на которые наука о данных окажет наибольшее влияние в " ~ - "ближайшем будущем, изложены принципы, позволяющие " ~ - "определить, будет ли данный конкретный проект успешным."; + // Текст + mixin(import("text.d")); // Конвертация в ubyte[] (UTF-8) ubyte[] data = cast(ubyte[]) text; @@ -232,30 +65,27 @@ void main() // ulong maskS = (1UL << 8) - 1; // 8 бит: 0b11111111 // ulong maskL = (1UL << 4) - 1; // 4 бита: 0b1111 - ulong maskS = 0b1111_1111; + // writefln("1UL: %u\n1UL << 8: %u\n(1UL << 8) - 1: %u", 1UL, 1UL << 8, maskS); + // writefln("1UL: %u\n1UL << 4: %u\n(1UL << 4) - 1: %u", 1UL, 1UL << 4, maskL); + // writeln(); + + ulong maskS = 0b11111111; ulong maskL = 0b1111; // Разбиение на чанки size_t offset = 0; size_t chunkNumber = 1; - // Включаем отладку - bool debugEnabled = true; + writeln("Размеры и содержимое чанков:"); while (offset < totalLength) { size_t remaining = totalLength - offset; - // Включаем отладку только для первых двух чанков - debugEnabled = chunkNumber <= 2; - size_t chunkSize = fastcdc(data[offset .. $], remaining, minSize, maxSize, - normalSize, maskS, maskL, chunkNumber, debugEnabled); - + size_t chunkSize = fastcdc(data[offset .. $], remaining, minSize, maxSize, normalSize, maskS, maskL); // Вывод размера чанка и его содержимого writefln("Чанк %d: %d байт", chunkNumber, chunkSize); string chunkContent = cast(string) data[offset .. offset + chunkSize]; - // writefln("Содержимое: %s\n", chunkContent.length > 50 ? chunkContent[0 .. 50] ~ "..." - // : chunkContent); - writefln("Содержимое: %s\n", chunkContent); - + // writefln("Содержимое: %s\n", chunkContent); + writefln("Содержимое: %s\n", chunkContent.length > 50 ? chunkContent[0 .. 50] ~ "..." : chunkContent); offset += chunkSize; chunkNumber++; } diff --git a/source/text.d b/source/text.d new file mode 100644 index 0000000..fb4d600 --- /dev/null +++ b/source/text.d @@ -0,0 +1,72 @@ +string text = "Цель науки о данных — улучшить процесс принятия решений, " ~ + "основывая их на более глубоком понимании ситуации с помощью " ~ + "анализа больших наборов данных. Как область деятельности " ~ + "наука о данных включает в себя ряд принципов, методов " ~ + "постановки задач, алгоритмов и процессов для выявления " ~ + "скрытых полезных закономерностей в больших наборах данных. " ~ + "Она тесно связана с глубинным анализом данных и машинным " ~ + "обучением, но имеет более широкий охват. Сегодня наука о " ~ + "данных управляет принятием решений практически во всех " ~ + "сферах современного общества. В повседневной жизни вы " ~ + "ощущаете на себе воздействие науки о данных, когда видите " ~ + "отобранные специально для вас рекламные объявления, " ~ + "рекомендованные фильмы и книги, ссылки на предполагаемых " ~ + "друзей, отфильтрованные письма в папке со спамом, " ~ + "персональные предложения от мобильных операторов и " ~ + "страховых компаний. Она влияет на порядок переключения и " ~ + "длительность сигналов светофоров в вашем районе, на то, как " ~ + "были созданы новые лекарства, продающиеся в аптеке, и то, как " ~ + "полиция вычисляет, где может потребоваться ее присутствие. " ~ + "Рост использования науки о данных в обществе обусловлен " ~ + "появлением больших данных и социальных сетей, увеличением " ~ + "вычислительной мощности, уменьшением размеров носителей " ~ + "компьютерной памяти и разработкой более эффективных " ~ + "методов анализа и моделирования данных, таких как глубокое " ~ + "обучение. Вместе эти факторы означают, что сейчас процесс " ~ + "сбора, хранения и обработки данных стал как никогда ранее " ~ + "доступен для организаций. В то же время эти технические " ~ + "новшества и растущее применение науки о данных означают, что " ~ + "этические проблемы, связанные с использованием данных и " ~ + "личной конфиденциальностью, тоже вышли на первый план. Цель " ~ + "этой книги — познакомить с наукой о данных на уровне ее " ~ + "основных элементов и с той степенью погружения, которая " ~ + "обеспечит принципиальное понимание вопроса. " ~ + "Глава 1 очерчивает область науки о данных и дает краткую " ~ + "историю ее становления и эволюции. В ней мы также " ~ + "рассмотрим, почему наука о данных стала такой востребованной " ~ + "сегодня, и перечислим факторы, стимулирующие ее внедрение. В " ~ + "конце главы мы развенчаем несколько мифов, связанных с темой " ~ + "книги. Глава 2 вводит фундаментальные понятия, относящиеся к " ~ + "данным. В ней также описаны стандартные этапы проекта: " ~ + "понимание бизнес-целей, начальное изучение данных, " ~ + "подготовка данных, моделирование, оценка и внедрение. Глава 3 " ~ + "посвящена инфраструктуре данных и проблемам, связанным с " ~ + "большими данными и их интеграцией из нескольких источников. " ~ + "Одна из таких типичных проблем заключается в том, что данные " ~ + "в базах и хранилищах находятся на одних серверах, а " ~ + "анализируются на других. Поэтому колоссальное время тратится " ~ + "на перемещение больших наборов данных между этими " ~ + "серверами. Глава 3 начинается с описания типичной " ~ + "инфраструктуры науки о данных для организации и некоторых " ~ + "свежих решений проблемы перемещения больших наборов " ~ + "данных, а именно: метода машинного обучения в базе данных, " ~ + "использования Hadoop для хранения и обработки данных, а также " ~ + "разработки гибридных систем, в которых органично сочетаются " ~ + "традиционное программное обеспечение баз данных и решения, " ~ + "подобные Hadoop. Глава завершается описанием проблем, " ~ + "связанных с интеграцией данных в единое представление для " ~ + "последующего машинного обучения. Глава 4 знакомит читателя с " ~ + "машинным обучением и объясняет некоторые из наиболее " ~ + "популярных алгоритмов и моделей, включая нейронные сети, " ~ + "глубокое обучение и деревья решений. В главе 5 основное " ~ + "внимание уделяется использованию опыта в области машинного " ~ + "обучения для решения реальных задач, приводятся примеры " ~ + "анализа стандартных бизнес-проблем и того, как они могут быть " ~ + "решены с помощью машинного обучения. В главе 6 " ~ + "рассматриваются этические вопросы науки о данных, последние " ~ + "разработки в области регулирования и некоторые из новых " ~ + "вычислительных методов защиты конфиденциальности в " ~ + "процессе обработки данных. Наконец, в главе 7 описаны сферы, " ~ + "на которые наука о данных окажет наибольшее влияние в " ~ + "ближайшем будущем, изложены принципы, позволяющие " ~ + "определить, будет ли данный конкретный проект успешным."; From 15f33edd3a6a895214f7d13f4acdfd0f60b7172d Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Mon, 8 Sep 2025 17:33:35 +0300 Subject: [PATCH 4/4] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BD=D0=BE=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=88=D0=BD=D0=B5=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/app.d | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/source/app.d b/source/app.d index 2ad45a4..5b5bd12 100644 --- a/source/app.d +++ b/source/app.d @@ -1,5 +1,4 @@ import std.stdio; -import std.string; // Статическая таблица Gear (256 случайных 64-битных чисел) mixin(import("gear.d")); @@ -56,23 +55,14 @@ void main() size_t totalLength = data.length; writefln("Общая длина текста (в байтах): %d", totalLength); - // Параметры FastCDC (в байтах, адаптированы для текста) - size_t minSize = 100; // 100 байт - size_t normalSize = 200; // 200 байт (цель) - size_t maxSize = 500; // 500 байт + // Параметры FastCDC + size_t minSize = 100; + size_t normalSize = 200; + size_t maxSize = 500; - // Маски (для normalSize ~200 байт, log2(200) ≈ 8 бит, уровень нормализации 2) - // ulong maskS = (1UL << 8) - 1; // 8 бит: 0b11111111 - // ulong maskL = (1UL << 4) - 1; // 4 бита: 0b1111 - - // writefln("1UL: %u\n1UL << 8: %u\n(1UL << 8) - 1: %u", 1UL, 1UL << 8, maskS); - // writefln("1UL: %u\n1UL << 4: %u\n(1UL << 4) - 1: %u", 1UL, 1UL << 4, maskL); - // writeln(); - - ulong maskS = 0b11111111; + ulong maskS = 0b1111_1111; ulong maskL = 0b1111; - // Разбиение на чанки size_t offset = 0; size_t chunkNumber = 1; @@ -81,11 +71,9 @@ void main() { size_t remaining = totalLength - offset; size_t chunkSize = fastcdc(data[offset .. $], remaining, minSize, maxSize, normalSize, maskS, maskL); - // Вывод размера чанка и его содержимого writefln("Чанк %d: %d байт", chunkNumber, chunkSize); string chunkContent = cast(string) data[offset .. offset + chunkSize]; - // writefln("Содержимое: %s\n", chunkContent); - writefln("Содержимое: %s\n", chunkContent.length > 50 ? chunkContent[0 .. 50] ~ "..." : chunkContent); + writefln("Содержимое: %s\n", chunkContent); offset += chunkSize; chunkNumber++; }