1
0
Fork 0
forked from dlang/cdcdb
cdcdb/source/cdcdb/core.d

135 lines
4.4 KiB
D
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// Модуль базовых структур и алгоритмов CDC (content-defined chunking).
module cdcdb.core;
import std.digest.sha : SHA256, digest;
/// Описание чанка данных.
///
/// Поля:
/// - `index` — порядковый номер чанка, начиная с 1.
/// - `offset` — смещение чанка в исходном буфере (в байтах).
/// - `size` — размер чанка (в байтах).
/// - `sha256` — SHA-256 содержимого (сырые 32 байта).
struct Chunk
{
size_t index;
size_t offset;
size_t size;
immutable(ubyte)[32] sha256;
}
/// Change Data Capture (CDC) — алгоритм нарезки потока на чанки по содержимому.
///
/// Класс реализует скользящее шифрование (rolling hash) с двумя масками:
/// строгой (`_maskS`) до «нормального» размера и более слабой (`_maskL`) до «максимального».
final class CDC
{
private:
size_t _minSize, _normalSize, _maxSize;
ulong _maskS, _maskL;
// Таблица случайных значений Gear (должна быть сгенерирована отдельно в "gear.d")
mixin(import("gear.d"));
/// Вычисляет длину следующего чанка, начиная с начала `src`.
///
/// Параметры:
/// - `src` — оставшийся участок данных.
///
/// Возвращает: длину чанка в байтах.
///
/// Детали:
/// - Если данных меньше либо равно минимальному размеру — возвращает их длину.
/// - Сначала ищется граница по строгой маске до `_normalSize`, затем по слабой до `_maxSize`.
size_t cut(const(ubyte)[] src) pure nothrow @safe @nogc
{
size_t size = src.length;
if (size == 0)
return 0;
if (size <= _minSize)
return size;
if (size > _maxSize)
size = _maxSize;
auto normalSize = _normalSize;
if (size < normalSize)
normalSize = size;
ulong fingerprint = 0;
size_t index;
// Инициализация без проверки на разрез
while (index < _minSize)
{
fingerprint = (fingerprint << 1) + _gear[src[index]];
++index;
}
// Строгая маска
while (index < normalSize)
{
fingerprint = (fingerprint << 1) + _gear[src[index]];
if ((fingerprint & _maskS) == 0)
return index;
++index;
}
// Слабая маска
while (index < size)
{
fingerprint = (fingerprint << 1) + _gear[src[index]];
if ((fingerprint & _maskL) == 0)
return index;
++index;
}
return size;
}
public:
/// Создаёт экземпляр CDC.
///
/// Параметры:
/// - `minSize` — минимальный размер чанка.
/// - `normalSize` — целевой (нормальный) размер чанка.
/// - `maxSize` — максимальный размер чанка.
/// - `maskS` — строгая маска (для поиска границы до `normalSize`).
/// - `maskL` — слабая маска (для поиска границы до `maxSize`).
///
/// Замечания:
/// - Требуется `0 < minSize < normalSize < maxSize`.
this(size_t minSize, size_t normalSize, size_t maxSize, ulong maskS, ulong maskL) @safe @nogc nothrow
{
assert(minSize > 0 && minSize < normalSize && normalSize < maxSize,
"Некорректные размеры: требуется 0 < min < normal < max");
_minSize = minSize;
_normalSize = normalSize;
_maxSize = maxSize;
_maskS = maskS;
_maskL = maskL;
}
/// Разбивает буфер `data` на последовательность чанков.
///
/// Параметры:
/// - `data` — исходные байты.
///
/// Возвращает: массив `Chunk` в порядке следования.
Chunk[] split(const(ubyte)[] data) @safe nothrow
{
Chunk[] chunks;
if (data.length == 0)
return chunks;
chunks.reserve(data.length / _normalSize);
size_t offset = 0;
size_t index = 1;
while (offset < data.length)
{
auto size = cut(data[offset .. $]);
auto bytes = data[offset .. offset + size];
ubyte[32] hash = digest!SHA256(bytes);
chunks ~= Chunk(index, offset, size, hash);
offset += size;
++index;
}
return chunks;
}
}