23static void storeUleb(std::vector<uint8_t>& result,
size_t value)
26 uint8_t b = value & 0x7F;
33[[nodiscard]]
static constexpr size_t loadUleb(std::span<const uint8_t>& data)
38 assert(!data.empty());
39 uint8_t b = data.front();
40 data = data.subspan(1);
41 result |= size_t(b & 0x7F) << shift;
42 if ((b & 0x80) == 0)
return result;
50template<
int N>
bool comp(
const uint8_t* p,
const uint8_t* q);
52template<>
bool comp<4>(
const uint8_t* p,
const uint8_t* q)
54 return *std::bit_cast<const uint32_t*>(p) ==
55 *std::bit_cast<const uint32_t*>(q);
58template<>
bool comp<8>(
const uint8_t* p,
const uint8_t* q)
60 return *std::bit_cast<const uint64_t*>(p) ==
61 *std::bit_cast<const uint64_t*>(q);
65template<>
bool comp<16>(
const uint8_t* p,
const uint8_t* q)
70 __m128i a = _mm_loadu_si128(std::bit_cast<const __m128i*>(p));
71 __m128i b = _mm_loadu_si128(std::bit_cast<const __m128i*>(q));
72 __m128i d = _mm_cmpeq_epi8(a, b);
73 return _mm_movemask_epi8(d) == 0xffff;
88static std::pair<const uint8_t*, const uint8_t*> scan_mismatch(
89 const uint8_t* p,
const uint8_t* p_end,
const uint8_t* q,
const uint8_t* q_end)
91 assert((p_end - p) == (q_end - q));
97 constexpr ptrdiff_t WORD_SIZE =
106 if (((p_end - p) < (2 * WORD_SIZE)) ||
107 ((std::bit_cast<uintptr_t>(p) & (WORD_SIZE - 1)) !=
108 (std::bit_cast<uintptr_t>(q) & (WORD_SIZE - 1)))) [[unlikely]] {
113 if (std::bit_cast<uintptr_t>(p) & (WORD_SIZE - 1)) [[unlikely]] {
115 if (*p != *q)
return {p, q};
117 }
while (std::bit_cast<uintptr_t>(p) & (WORD_SIZE - 1));
125 auto* sentinel = &
const_cast<uint8_t*
>(p_end)[-WORD_SIZE];
126 auto save = *sentinel;
127 *sentinel = ~q_end[-WORD_SIZE];
129 while (comp<WORD_SIZE>(p, q)) {
130 p += WORD_SIZE; q += WORD_SIZE;
140end:
return std::mismatch(p, p_end, q);
155[[nodiscard]]
static constexpr std::pair<const uint8_t*, const uint8_t*> scan_match(
156 const uint8_t* p,
const uint8_t* p_end,
const uint8_t* q,
const uint8_t* q_end)
158 assert((p_end - p) == (q_end - q));
164 if (p == p_end)
return {p, q};
166 auto* p_last =
const_cast<uint8_t*
>(p_end - 1);
170 while (*p != *q) { ++p; ++q; }
173 if ((p == p_last) && (*p != *q)) { ++p; ++q; }
186[[nodiscard]]
static std::vector<uint8_t> calcDelta(
187 const uint8_t* oldBuf, std::span<const uint8_t> newBuf)
189 std::vector<uint8_t> result;
191 const auto* p = oldBuf;
192 const auto* q = newBuf.data();
193 auto size = newBuf.size();
194 const auto* p_end = p +
size;
195 const auto* q_end = q +
size;
199 std::tie(p, q) = scan_mismatch(p, p_end, q, q_end);
201 storeUleb(result, n1);
208 std::tie(p, q) = scan_match(p + 1, p_end, q + 1, q_end);
212 std::tie(p, q) = scan_mismatch(p, p_end, q, q_end);
214 if ((q != q_end) && (
n3 <= 2))
goto different;
216 storeUleb(result, n2);
217 result.insert(result.end(), q2, q3);
219 if (
n3 != 0) storeUleb(result,
n3);
222 result.shrink_to_fit();
227static void applyDeltaInPlace(std::span<uint8_t> buf, std::span<const uint8_t> delta)
229 while (!buf.empty()) {
230 auto n1 = loadUleb(delta);
231 buf = buf.subspan(n1);
232 if (buf.empty())
break;
234 auto n2 = loadUleb(delta);
236 buf = buf .subspan(n2);
237 delta = delta.subspan(n2);
247 globalAllocSize -= allocSize;
248 std::cout <<
"stat: ~DeltaBlock " << globalAllocSize
249 <<
" (-" << allocSize <<
")\n";
263 assert(!compressed());
266 globalAllocSize += allocSize;
267 std::cout <<
"stat: DeltaBlockCopy " << globalAllocSize
268 <<
" (+" << allocSize <<
")\n";
286 if (compressed())
return;
292 if (dstLen >= size) {
296 compressedSize = dstLen;
297 std::swap(block, buf2);
298 block.
resize(compressedSize);
299 assert(compressed());
306 int delta = compressedSize - allocSize;
307 allocSize = compressedSize;
308 globalAllocSize += delta;
309 std::cout <<
"stat: compress " << globalAllocSize
310 <<
" (" << delta <<
")\n";
316 assert(!compressed());
324 std::shared_ptr<DeltaBlockCopy> prev_,
325 std::span<const uint8_t> data)
326 : prev(
std::move(prev_))
327 , delta(calcDelta(prev->getData(), data))
337 allocSize = delta.size();
338 globalAllocSize += allocSize;
339 std::cout <<
"stat: DeltaBlockDiff " << globalAllocSize
340 <<
" (+" << allocSize <<
")\n";
347 applyDeltaInPlace(dst, delta);
362 const void*
id, std::span<const uint8_t> data)
364 auto size = data.size();
366 [](
const Info& info) {
return std::tuple(info.id, info.size); });
367 if ((it ==
end(infos)) || (it->id !=
id) || (it->size != size)) {
369 it = infos.emplace(it,
id, size);
371 assert(it->id ==
id);
372 assert(it->size == size);
374 auto ref = it->ref.lock();
375 if (it->accSize >= size || !ref) {
383 auto b = std::make_shared<DeltaBlockCopy>(data);
391 auto b = std::make_shared<DeltaBlockDiff>(ref, data);
393 it->accSize += b->getDeltaSize();
399 const void*
id, std::span<const uint8_t> data)
401 auto size = data.size();
403 if ((it ==
end(infos)) || (it->id !=
id)) {
405 it = infos.emplace(it,
id, size);
407 assert(it->size == size);
409 auto last = it->last.lock();
411 auto b = std::make_shared<DeltaBlockCopy>(data);
426 for (
const Info& info : infos) {
427 if (
auto ref = info.ref.lock()) {
428 ref->compress(info.size);
DeltaBlockCopy(std::span< const uint8_t > data)
const uint8_t * getData()
void apply(std::span< uint8_t > dst) const override
void compress(size_t size)
DeltaBlockDiff(std::shared_ptr< DeltaBlockCopy > prev_, std::span< const uint8_t > data)
void apply(std::span< uint8_t > dst) const override
size_t getDeltaSize() const
virtual ~DeltaBlock()=default
std::shared_ptr< DeltaBlock > createNew(const void *id, std::span< const uint8_t > data)
std::shared_ptr< DeltaBlock > createNullDiff(const void *id, std::span< const uint8_t > data)
This class manages the lifetime of a block of memory.
void resize(size_t size)
Grow or shrink the memory block.
const T * data() const
Returns pointer to the start of the memory buffer.
static Sha1Sum calc(std::span< const uint8_t > data)
Easier to use interface, if you can pass all data in one go.
mat3 n3(vec3(1, 0, 3), vec3(4, 5, 6), vec3(7, 8, 9))
int compressBound(int iSize)
int decompress(const uint8_t *src, uint8_t *dst, int compressedSize, int dstCapacity)
int compress(const uint8_t *src, uint8_t *dst, int srcSize)
This file implemented 3 utility functions:
bool comp< 4 >(const uint8_t *p, const uint8_t *q)
bool comp< 8 >(const uint8_t *p, const uint8_t *q)
bool comp(const uint8_t *p, const uint8_t *q)
constexpr bool equal(InputRange1 &&range1, InputRange2 &&range2, Pred pred={}, Proj1 proj1={}, Proj2 proj2={})
constexpr auto copy(InputRange &&range, OutputIter out)
auto lower_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
size_t size(std::string_view utf8)
constexpr auto end(const zstring_view &x)