119#define USE_FASTPATH_CHUNKS_FIRST
129#ifdef USE_FASTPATH_CHUNKS_FIRST
130# define USE_FASTPATH_CHUNKS_LAST
139#define USE_ALIGN_CHUNKS_TEST
145#define USE_HASH_TABLE_ACCUMULATE
147#ifdef USE_HASH_TABLE_ACCUMULATE
156# define BCHUNK_HASH_TABLE_ACCUMULATE_STEPS_DEFAULT 3
157# define BCHUNK_HASH_TABLE_ACCUMULATE_STEPS_32BITS 4
158# define BCHUNK_HASH_TABLE_ACCUMULATE_STEPS_16BITS 5
167# define BCHUNK_HASH_TABLE_ACCUMULATE_STEPS_8BITS 6
173# define BCHUNK_HASH_LEN 16
179#define USE_HASH_TABLE_KEY_CACHE
180#ifdef USE_HASH_TABLE_KEY_CACHE
181# define HASH_TABLE_KEY_UNSET ((hash_key)-1)
182# define HASH_TABLE_KEY_FALLBACK ((hash_key)-2)
193#define USE_HASH_TABLE_DEDUPLICATE
198#define BCHUNK_HASH_TABLE_MUL 3
211#define USE_MERGE_CHUNKS
213#ifdef USE_MERGE_CHUNKS
215# define BCHUNK_SIZE_MIN_DIV 8
224# define BCHUNK_SIZE_MAX_MUL 2
256#ifdef USE_HASH_TABLE_ACCUMULATE
320#ifdef USE_HASH_TABLE_KEY_CACHE
360#ifdef USE_HASH_TABLE_KEY_CACHE
369 memcpy(data_copy, data, data_len);
370 return bchunk_new(bs_mem, data_copy, data_len);
376 if (chunk->
users == 1) {
386 const uchar *data_base,
387 const size_t data_base_len,
392 return (memcmp(&data_base[offset], chunk->
data, chunk->
data_len) == 0);
396 const uchar *data_base,
397 const size_t data_base_len,
400 if (offset +
size_t(chunk->
data_len) <= data_base_len) {
419 chunk_list->
users = 0;
426 if (chunk_list->
users == 1) {
431 cref_next = cref->next;
439 chunk_list->
users -= 1;
443#ifdef USE_VALIDATE_LIST_SIZE
445# define ASSERT_CHUNKLIST_SIZE(chunk_list, n) BLI_assert(bchunk_list_size(chunk_list) == n)
448#ifndef ASSERT_CHUNKLIST_SIZE
449# define ASSERT_CHUNKLIST_SIZE(chunk_list, n) (EXPR_NOP(chunk_list), EXPR_NOP(n))
452#ifdef USE_VALIDATE_LIST_DATA_PARTIAL
453static size_t bchunk_list_data_check(
const BChunkList *chunk_list,
const uchar *data)
457 if (memcmp(&data[offset], cref->link->data, cref->link->data_len) != 0) {
460 offset += cref->link->data_len;
464# define ASSERT_CHUNKLIST_DATA(chunk_list, data) \
465 BLI_assert(bchunk_list_data_check(chunk_list, data))
467# define ASSERT_CHUNKLIST_DATA(chunk_list, data) (EXPR_NOP(chunk_list), EXPR_NOP(data))
470#ifdef USE_MERGE_CHUNKS
476 if (cref && cref->
prev) {
484 if (data_merge_len <= info->chunk_byte_size_max) {
494 memcpy(data_merge, chunk_prev->
data, chunk_prev->
data_len);
513 const size_t data_prev_len =
split;
514 const size_t data_curr_len = data_merge_len -
split;
518 if (data_prev_len <= chunk_prev->data_len) {
519 const size_t data_curr_shrink_len = chunk_prev->
data_len - data_prev_len;
522 memcpy(data_prev, chunk_prev->
data, data_prev_len);
525 memcpy(data_curr, &chunk_prev->
data[data_prev_len], data_curr_shrink_len);
526 memcpy(&data_curr[data_curr_shrink_len], chunk_curr->
data, chunk_curr->
data_len);
529 BLI_assert(data_curr_len <= chunk_curr->data_len);
532 const size_t data_prev_grow_len = data_prev_len - chunk_prev->
data_len;
535 memcpy(data_prev, chunk_prev->
data, chunk_prev->
data_len);
536 memcpy(&data_prev[chunk_prev->
data_len], chunk_curr->
data, data_prev_grow_len);
539 memcpy(data_curr, &chunk_curr->
data[data_prev_grow_len], data_curr_len);
566 const size_t data_len,
567 size_t *r_data_trim_len,
568 size_t *r_data_last_chunk_len)
570 size_t data_last_chunk_len = 0;
571 size_t data_trim_len = data_len;
573#ifdef USE_MERGE_CHUNKS
577 data_trim_len = data_trim_len - data_last_chunk_len;
578 if (data_last_chunk_len) {
579 if (data_last_chunk_len < info->chunk_byte_size_min) {
588 data_last_chunk_len = data_len;
594 data_trim_len = data_trim_len - data_last_chunk_len;
597 BLI_assert(data_trim_len + data_last_chunk_len == data_len);
599 *r_data_trim_len = data_trim_len;
600 *r_data_last_chunk_len = data_last_chunk_len;
623 const size_t data_len)
627#ifdef USE_MERGE_CHUNKS
628 BLI_assert(data_len <= info->chunk_byte_size_max);
635 const size_t data_merge_len = chunk_prev->
data_len + data_len;
640 memcpy(&data_merge[chunk_prev->
data_len], data, data_len);
646 memcpy(data_merge, chunk_prev->
data, chunk_prev->
data_len);
647 memcpy(&data_merge[chunk_prev->
data_len], data, data_len);
664# ifdef USE_MERGE_CHUNKS
683 size_t data_trim_len, data_last_chunk_len;
686 if (data_trim_len != 0) {
695 while (i_prev != data_trim_len) {
702 if (data_last_chunk_len) {
710 if (data_last_chunk_len) {
716#ifdef USE_MERGE_CHUNKS
731#ifdef USE_MERGE_CHUNKS
742 const size_t data_len)
746 size_t data_trim_len, data_last_chunk_len;
750 while (i_prev != data_trim_len) {
757 if (data_last_chunk_len) {
763#ifdef USE_MERGE_CHUNKS
772# ifdef USE_MERGE_CHUNKS
793#define HASH_INIT (5381)
803 const signed char *p;
806 for (p = (
const signed char *)key; n--; p++) {
815#ifdef USE_HASH_TABLE_ACCUMULATE
817 const uchar *data_slice,
818 const size_t data_slice_len,
822 for (
size_t i = 0, i_step = 0; i_step < data_slice_len; i++, i_step += info->
chunk_stride) {
828 for (
size_t i = 0; i < data_slice_len; i++) {
840 const size_t data_len,
843 const size_t hash_array_len = data_len / info->
chunk_stride;
846 size_t i_next = hash_array_len - i;
852 BLI_assert(data_trim_len <= cref->link->data_len);
856 }
while ((i < hash_array_len) && (cref !=
nullptr));
868 hash_array[i_dst] += ((hash_array[i_ahead] << 3) ^ (hash_array[i_dst] >> 1));
874 if (
UNLIKELY(iter_steps > hash_array_len)) {
875 iter_steps = hash_array_len;
878 const size_t hash_array_search_len = hash_array_len - iter_steps;
879 while (iter_steps != 0) {
880 const size_t hash_offset = iter_steps;
881 for (
size_t i = 0; i < hash_array_search_len; i++) {
895 if (
UNLIKELY(!(iter_steps <= hash_array_len))) {
897 iter_steps = hash_array_len;
901 size_t iter_steps_sub = iter_steps;
903 while (iter_steps != 0) {
904 const size_t hash_array_search_len = hash_array_len - iter_steps_sub;
905 const size_t hash_offset = iter_steps;
906 for (
uint i = 0; i < hash_array_search_len; i++) {
910 iter_steps_sub += iter_steps;
918 const size_t hash_store_len)
927# ifdef USE_HASH_TABLE_KEY_CACHE
957# ifdef USE_HASH_TABLE_KEY_CACHE
967 const size_t table_len,
968 const size_t i_table_start,
970 const size_t data_len,
976 const BTableRef *tref = table[key_index];
977 if (tref !=
nullptr) {
978 const size_t size_left = data_len - offset;
981# ifdef USE_HASH_TABLE_KEY_CACHE
986 if (chunk_test->
data_len <= size_left) {
993 }
while ((tref = tref->
next));
1008# ifdef USE_HASH_TABLE_KEY_CACHE
1031 const size_t table_len,
1034 const size_t data_len,
1035 const size_t offset,
1038 const size_t data_hash_len = BCHUNK_HASH_LEN * info->
chunk_stride;
1040 const size_t size_left = data_len - offset;
1041 const hash_key key =
hash_data(&data[offset], std::min(data_hash_len, size_left));
1043 for (
BTableRef *tref = table[key_index]; tref; tref = tref->
next) {
1045# ifdef USE_HASH_TABLE_KEY_CACHE
1050 if (chunk_test->
data_len <= size_left) {
1091 const BChunkRef *cref_match_first =
nullptr;
1093 uint chunk_list_reference_skip_len = 0;
1094 size_t chunk_list_reference_skip_bytes = 0;
1097#ifdef USE_FASTPATH_CHUNKS_FIRST
1099 bool full_match =
true;
1104 cref_match_first = cref;
1105 chunk_list_reference_skip_len += 1;
1106 chunk_list_reference_skip_bytes += cref->
link->
data_len;
1130 if (cref_match_first !=
nullptr) {
1131 size_t chunk_size_step = 0;
1135 chunk_size_step += chunk->
data_len;
1139 if (cref == cref_match_first) {
1149 i_prev = chunk_size_step;
1166#define data_len_original invalid_usage
1167#ifdef data_len_original
1171 const BChunkRef *chunk_list_reference_last =
nullptr;
1173#ifdef USE_FASTPATH_CHUNKS_LAST
1176 while ((cref->
prev !=
nullptr) && (cref != cref_match_first) &&
1180 size_t offset = data_len - chunk_test->
data_len;
1183 chunk_list_reference_last = cref;
1184 chunk_list_reference_skip_len += 1;
1185 chunk_list_reference_skip_bytes += cref->
link->
data_len;
1205 bool use_aligned =
false;
1207#ifdef USE_ALIGN_CHUNKS_TEST
1210 if (data_len - i_prev <= chunk_list->total_expanded_size / 4) {
1224 const BChunkRef *cref = cref_match_first ? cref_match_first->
next :
1227 while (i_prev != data_len) {
1231 if ((cref != chunk_list_reference_last) &&
1250 (chunk_list_reference->
chunk_refs_len >= chunk_list_reference_skip_len) &&
1262#ifdef USE_HASH_TABLE_ACCUMULATE
1263 size_t i_table_start = i_prev;
1264 const size_t table_hash_array_len = (data_len - i_prev) / info->
chunk_stride;
1266 MEM_mallocN(
sizeof(*table_hash_array) * table_hash_array_len, __func__));
1272 uint i_table_start = 0;
1273 hash_key *table_hash_array =
nullptr;
1276 const uint chunk_list_reference_remaining_len = (chunk_list_reference->
chunk_refs_len -
1277 chunk_list_reference_skip_len) +
1281 uint table_ref_stack_n = 0;
1285 MEM_callocN(table_len *
sizeof(*table), __func__));
1290#ifdef USE_HASH_TABLE_ACCUMULATE
1298 chunk_list_reference_skip_bytes;
1300 if (cref_match_first) {
1301 cref = cref_match_first;
1302 chunk_list_reference_bytes_remaining += cref->
link->
data_len;
1308#ifdef USE_PARANOID_CHECKS
1310 size_t test_bytes_len = 0;
1312 while (cr != chunk_list_reference_last) {
1316 BLI_assert(test_bytes_len == chunk_list_reference_bytes_remaining);
1320 while ((cref != chunk_list_reference_last) &&
1333 BTableRef *tref_prev = table[key_index];
1334 BLI_assert(table_ref_stack_n < chunk_list_reference_remaining_len);
1335#ifdef USE_HASH_TABLE_DEDUPLICATE
1336 bool is_duplicate =
false;
1344# ifdef USE_HASH_TABLE_KEY_CACHE
1345 if (key == chunk_b->
key)
1348 if (chunk_a != chunk_b) {
1351 is_duplicate =
true;
1357 }
while ((tref = tref->
next));
1363 BTableRef *tref = &table_ref_stack[table_ref_stack_n++];
1365 tref->
next = tref_prev;
1366 table[key_index] = tref;
1369 chunk_list_reference_bytes_remaining -= cref->
link->
data_len;
1373 BLI_assert(table_ref_stack_n <= chunk_list_reference_remaining_len);
1375#ifdef USE_HASH_TABLE_ACCUMULATE
1382 for (
size_t i = i_prev; i < data_len;) {
1386 info, table, table_len, i_table_start, data, data_len, i, table_hash_array);
1387 if (cref_found !=
nullptr) {
1406 while (!
ELEM(cref_found->
next,
nullptr, chunk_list_reference_last)) {
1407 cref_found = cref_found->
next;
1431#ifdef USE_HASH_TABLE_ACCUMULATE
1449 if (i_prev != data_len) {
1457#ifdef USE_FASTPATH_CHUNKS_LAST
1458 if (chunk_list_reference_last !=
nullptr) {
1460 const BChunkRef *cref = chunk_list_reference_last;
1461 while (cref !=
nullptr) {
1473#undef data_len_original
1498 BArrayStore *bs = MEM_cnew<BArrayStore>(__func__);
1504#ifdef USE_MERGE_CHUNKS
1509#ifdef USE_HASH_TABLE_ACCUMULATE
1512 if (stride <=
sizeof(
int8_t)) {
1515 else if (stride <=
sizeof(
int16_t)) {
1518 else if (stride <=
sizeof(
int32_t)) {
1566 state_next =
state->next;
1601 size_t size_accum = 0;
1603 size_accum +=
state->chunk_list->total_expanded_size;
1610 size_t size_total = 0;
1616 size_total += size_t(chunk->
data_len);
1629 const size_t data_len,
1635#ifdef USE_PARANOID_CHECKS
1636 if (state_reference) {
1642 if (state_reference) {
1645 (
const uchar *)data,
1655 chunk_list->
users += 1;
1658 state->chunk_list = chunk_list;
1662#ifdef USE_PARANOID_CHECKS
1664 size_t data_test_len;
1667 BLI_assert(memcmp(data_test, data, data_len) == 0);
1677#ifdef USE_PARANOID_CHECKS
1689 return state->chunk_list->total_expanded_size;
1694#ifdef USE_PARANOID_CHECKS
1695 size_t data_test_len = 0;
1712 void *data =
MEM_mallocN(
state->chunk_list->total_expanded_size, __func__);
1714 *r_data_len =
state->chunk_list->total_expanded_size;
1727 size_t total_expanded_size = 0;
1731 return total_expanded_size;
1751#ifdef USE_MERGE_CHUNKS
1779#define GHASH_PTR_ADD_USER(gh, pt) \
1782 if (BLI_ghash_ensure_p((gh), (pt), &val)) { \
1783 *((int *)val) += 1; \
1786 *((int *)val) = 1; \
1840#undef GHASH_PTR_ADD_USER
Efficient in-memory storage of multiple similar arrays.
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
#define GHASH_ITER(gh_iter_, ghash_)
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
unsigned int BLI_ghash_len(const GHash *gh) ATTR_WARN_UNUSED_RESULT
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1)
void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL()
void * BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
void BLI_mempool_free(BLI_mempool *pool, void *addr) ATTR_NONNULL(1
BLI_mempool * BLI_mempool_create(unsigned int esize, unsigned int elem_num, unsigned int pchunk, unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
int BLI_mempool_len(const BLI_mempool *pool) ATTR_NONNULL(1)
void BLI_mempool_destroy(BLI_mempool *pool) ATTR_NONNULL(1)
void BLI_mempool_clear(BLI_mempool *pool) ATTR_NONNULL(1)
#define UNUSED_VARS_NDEBUG(...)
#define POINTER_AS_INT(i)
static void split(const char *text, const char *seps, char ***str, int *count)
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
static void bchunk_list_calc_trim_len(const BArrayInfo *info, const size_t data_len, size_t *r_data_trim_len, size_t *r_data_last_chunk_len)
#define BCHUNK_HASH_TABLE_MUL
static hash_key key_from_chunk_ref(const BArrayInfo *info, const BChunkRef *cref, hash_key *hash_store, const size_t hash_store_len)
static const BChunkRef * table_lookup(const BArrayInfo *info, BTableRef **table, const size_t table_len, const size_t i_table_start, const uchar *data, const size_t data_len, const size_t offset, const hash_key *table_hash_array)
static void array_store_free_data(BArrayStore *bs)
#define USE_HASH_TABLE_ACCUMULATE
static void hash_array_from_cref(const BArrayInfo *info, const BChunkRef *cref, const size_t data_len, hash_key *hash_array)
#define GHASH_PTR_ADD_USER(gh, pt)
BLI_INLINE void hash_accum_impl(hash_key *hash_array, const size_t i_dst, const size_t i_ahead)
#define BCHUNK_HASH_TABLE_ACCUMULATE_STEPS_DEFAULT
static bool bchunk_data_compare(const BChunk *chunk, const uchar *data_base, const size_t data_base_len, const size_t offset)
static hash_key hash_data(const uchar *key, size_t n)
static void bchunk_list_fill_from_array(const BArrayInfo *info, BArrayMemory *bs_mem, BChunkList *chunk_list, const uchar *data, const size_t data_len)
void BLI_array_store_state_remove(BArrayStore *bs, BArrayState *state)
#define ASSERT_CHUNKLIST_DATA(chunk_list, data)
static void bchunk_decref(BArrayMemory *bs_mem, BChunk *chunk)
static void hash_accum_single(hash_key *hash_array, const size_t hash_array_len, size_t iter_steps)
#define BCHUNK_HASH_TABLE_ACCUMULATE_STEPS_8BITS
BLI_INLINE hash_key hash_data_single(const uchar p)
static BChunkList * bchunk_list_from_data_merge(const BArrayInfo *info, BArrayMemory *bs_mem, const uchar *data, const size_t data_len_original, const BChunkList *chunk_list_reference)
static void bchunk_list_append_data_n(const BArrayInfo *info, BArrayMemory *bs_mem, BChunkList *chunk_list, const uchar *data, size_t data_len)
size_t BLI_array_store_calc_size_expanded_get(const BArrayStore *bs)
#define HASH_TABLE_KEY_UNSET
BArrayStore * BLI_array_store_create(uint stride, uint chunk_count)
void BLI_array_store_clear(BArrayStore *bs)
static void bchunk_list_decref(BArrayMemory *bs_mem, BChunkList *chunk_list)
static size_t bchunk_list_size(const BChunkList *chunk_list)
#define BCHUNK_SIZE_MAX_MUL
bool BLI_array_store_is_valid(BArrayStore *bs)
#define HASH_TABLE_KEY_FALLBACK
size_t BLI_array_store_state_size_get(BArrayState *state)
#define data_len_original
static void bchunk_list_ensure_min_size_last(const BArrayInfo *info, BArrayMemory *bs_mem, BChunkList *chunk_list)
static void hash_accum(hash_key *hash_array, const size_t hash_array_len, size_t iter_steps)
#define ASSERT_CHUNKLIST_SIZE(chunk_list, n)
void * BLI_array_store_state_data_get_alloc(BArrayState *state, size_t *r_data_len)
#define BCHUNK_HASH_TABLE_ACCUMULATE_STEPS_32BITS
static BChunk * bchunk_new(BArrayMemory *bs_mem, const uchar *data, const size_t data_len)
static void hash_array_from_data(const BArrayInfo *info, const uchar *data_slice, const size_t data_slice_len, hash_key *hash_array)
static void bchunk_list_append_only(BArrayMemory *bs_mem, BChunkList *chunk_list, BChunk *chunk)
void BLI_array_store_state_data_get(const BArrayState *state, void *data)
void BLI_array_store_destroy(BArrayStore *bs)
BArrayState * BLI_array_store_state_add(BArrayStore *bs, const void *data, const size_t data_len, const BArrayState *state_reference)
size_t BLI_array_store_calc_size_compacted_get(const BArrayStore *bs)
static void bchunk_list_append(const BArrayInfo *info, BArrayMemory *bs_mem, BChunkList *chunk_list, BChunk *chunk)
static BChunk * bchunk_new_copydata(BArrayMemory *bs_mem, const uchar *data, const size_t data_len)
static void bchunk_list_append_data(const BArrayInfo *info, BArrayMemory *bs_mem, BChunkList *chunk_list, const uchar *data, const size_t data_len)
BLI_INLINE bool bchunk_data_compare_unchecked(const BChunk *chunk, const uchar *data_base, const size_t data_base_len, const size_t offset)
#define BCHUNK_SIZE_MIN_DIV
#define BCHUNK_HASH_TABLE_ACCUMULATE_STEPS_16BITS
static BChunkList * bchunk_list_new(BArrayMemory *bs_mem, size_t total_expanded_size)
void *(* MEM_mallocN)(size_t len, const char *str)
size_t(* MEM_allocN_len)(const void *vmemh)
void MEM_freeN(void *vmemh)
void *(* MEM_callocN)(size_t len, const char *str)
size_t chunk_byte_size_max
size_t chunk_byte_size_min
size_t accum_read_ahead_bytes
size_t accum_read_ahead_len
size_t total_expanded_size