mirror of
https://github.com/isar/libmdbx.git
synced 2025-05-21 20:47:46 +08:00
640 lines
25 KiB
C
640 lines
25 KiB
C
/// \copyright SPDX-License-Identifier: Apache-2.0
|
||
/// \author Леонид Юрьев aka Leonid Yuriev <leo@yuriev.ru> \date 2025
|
||
|
||
#include "internals.h"
|
||
|
||
static inline size_t rkl_size2bytes(const size_t size) {
|
||
assert(size > 0 && size <= txl_max * 2);
|
||
size_t bytes = ceil_powerof2(MDBX_ASSUME_MALLOC_OVERHEAD + sizeof(txnid_t) * size, txl_granulate * sizeof(txnid_t)) -
|
||
MDBX_ASSUME_MALLOC_OVERHEAD;
|
||
return bytes;
|
||
}
|
||
|
||
static inline size_t rkl_bytes2size(const size_t bytes) {
|
||
size_t size = bytes / sizeof(txnid_t);
|
||
assert(size > 0 && size <= txl_max * 2);
|
||
return size;
|
||
}
|
||
|
||
void rkl_init(rkl_t *rkl) {
|
||
rkl->list_limit = ARRAY_LENGTH(rkl->inplace);
|
||
rkl->list = rkl->inplace;
|
||
rkl_clear(rkl);
|
||
}
|
||
|
||
void rkl_clear(rkl_t *rkl) {
|
||
rkl->solid_begin = UINT64_MAX;
|
||
rkl->solid_end = 0;
|
||
rkl->list_length = 0;
|
||
}
|
||
|
||
void rkl_destroy(rkl_t *rkl) {
|
||
void *ptr = rkl->list;
|
||
rkl->list = nullptr;
|
||
if (ptr != rkl->inplace)
|
||
osal_free(ptr);
|
||
}
|
||
|
||
static inline bool solid_empty(const rkl_t *rkl) { return !(rkl->solid_begin < rkl->solid_end); }
|
||
|
||
#define RKL_ORDERED(first, last) ((first) < (last))
|
||
|
||
SEARCH_IMPL(rkl_bsearch, txnid_t, txnid_t, RKL_ORDERED)
|
||
|
||
void rkl_destructive_move(rkl_t *src, rkl_t *dst) {
|
||
assert(rkl_check(src));
|
||
dst->solid_begin = src->solid_begin;
|
||
dst->solid_end = src->solid_end;
|
||
dst->list_length = src->list_length;
|
||
if (dst->list != dst->inplace)
|
||
osal_free(dst->list);
|
||
if (src->list != src->inplace) {
|
||
dst->list = src->list;
|
||
dst->list_limit = src->list_limit;
|
||
} else {
|
||
dst->list = dst->inplace;
|
||
dst->list_limit = ARRAY_LENGTH(src->inplace);
|
||
memcpy(dst->inplace, src->list, sizeof(dst->inplace));
|
||
}
|
||
rkl_init(src);
|
||
}
|
||
|
||
static int rkl_resize(rkl_t *rkl, size_t wanna_size) {
|
||
assert(wanna_size > rkl->list_length);
|
||
assert(rkl_check(rkl));
|
||
STATIC_ASSERT(txl_max < INT_MAX / sizeof(txnid_t));
|
||
if (unlikely(wanna_size > txl_max)) {
|
||
ERROR("rkl too long (%zu >= %zu)", wanna_size, (size_t)txl_max);
|
||
return MDBX_TXN_FULL;
|
||
}
|
||
if (unlikely(wanna_size < rkl->list_length)) {
|
||
ERROR("unable shrink rkl to %zu since length is %u", wanna_size, rkl->list_length);
|
||
return MDBX_PROBLEM;
|
||
}
|
||
|
||
if (unlikely(wanna_size <= ARRAY_LENGTH(rkl->inplace))) {
|
||
if (rkl->list != rkl->inplace) {
|
||
assert(rkl->list_limit > ARRAY_LENGTH(rkl->inplace) && rkl->list_length <= ARRAY_LENGTH(rkl->inplace));
|
||
memcpy(rkl->inplace, rkl->list, sizeof(rkl->inplace));
|
||
rkl->list_limit = ARRAY_LENGTH(rkl->inplace);
|
||
osal_free(rkl->list);
|
||
rkl->list = rkl->inplace;
|
||
} else {
|
||
assert(rkl->list_limit == ARRAY_LENGTH(rkl->inplace));
|
||
}
|
||
return MDBX_SUCCESS;
|
||
}
|
||
|
||
if (wanna_size != rkl->list_limit) {
|
||
size_t bytes = rkl_size2bytes(wanna_size);
|
||
void *ptr = (rkl->list == rkl->inplace) ? osal_malloc(bytes) : osal_realloc(rkl->list, bytes);
|
||
if (unlikely(!ptr))
|
||
return MDBX_ENOMEM;
|
||
#ifdef osal_malloc_usable_size
|
||
bytes = osal_malloc_usable_size(ptr);
|
||
#endif /* osal_malloc_usable_size */
|
||
rkl->list_limit = rkl_bytes2size(bytes);
|
||
if (rkl->list == rkl->inplace)
|
||
memcpy(ptr, rkl->inplace, sizeof(rkl->inplace));
|
||
rkl->list = ptr;
|
||
}
|
||
return MDBX_SUCCESS;
|
||
}
|
||
|
||
int rkl_copy(const rkl_t *src, rkl_t *dst) {
|
||
assert(rkl_check(src));
|
||
rkl_init(dst);
|
||
if (!rkl_empty(src)) {
|
||
if (dst->list_limit < src->list_length) {
|
||
int err = rkl_resize(dst, src->list_limit);
|
||
if (unlikely(err != MDBX_SUCCESS))
|
||
return err;
|
||
}
|
||
memcpy(dst->list, src->list, sizeof(txnid_t) * src->list_length);
|
||
dst->list_length = src->list_length;
|
||
dst->solid_begin = src->solid_begin;
|
||
dst->solid_end = src->solid_end;
|
||
}
|
||
return MDBX_SUCCESS;
|
||
}
|
||
|
||
size_t rkl_len(const rkl_t *rkl) { return rkl_empty(rkl) ? 0 : rkl->solid_end - rkl->solid_begin + rkl->list_length; }
|
||
|
||
__hot bool rkl_contain(const rkl_t *rkl, txnid_t id) {
|
||
assert(rkl_check(rkl));
|
||
if (id >= rkl->solid_begin && id < rkl->solid_end)
|
||
return true;
|
||
if (rkl->list_length) {
|
||
const txnid_t *it = rkl_bsearch(rkl->list, rkl->list_length, id);
|
||
const txnid_t *const end = rkl->list + rkl->list_length;
|
||
assert(it >= rkl->list && it <= end);
|
||
if (it != rkl->list)
|
||
assert(RKL_ORDERED(it[-1], id));
|
||
if (it != end) {
|
||
assert(!RKL_ORDERED(it[0], id));
|
||
return *it == id;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
__hot bool rkl_find(const rkl_t *rkl, const txnid_t id, rkl_iter_t *iter) {
|
||
assert(rkl_check(rkl));
|
||
*iter = rkl_iterator(rkl, false);
|
||
if (id >= rkl->solid_begin) {
|
||
if (id < rkl->solid_end) {
|
||
iter->pos = iter->solid_offset + (unsigned)(id - rkl->solid_begin);
|
||
return true;
|
||
}
|
||
iter->pos = (unsigned)(rkl->solid_end - rkl->solid_begin);
|
||
}
|
||
if (rkl->list_length) {
|
||
const txnid_t *it = rkl_bsearch(rkl->list, rkl->list_length, id);
|
||
const txnid_t *const end = rkl->list + rkl->list_length;
|
||
assert(it >= rkl->list && it <= end);
|
||
if (it != rkl->list)
|
||
assert(RKL_ORDERED(it[-1], id));
|
||
iter->pos += (unsigned)(it - rkl->list);
|
||
if (it != end) {
|
||
assert(!RKL_ORDERED(it[0], id));
|
||
return *it == id;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
static inline txnid_t list_remove_first(rkl_t *rkl) {
|
||
assert(rkl->list_length > 0);
|
||
const txnid_t first = rkl->list[0];
|
||
if (--rkl->list_length) {
|
||
/* TODO: Можно подумать о том, чтобы для избавления от memove() добавить headroom или вместо длины и
|
||
* указателя на список использовать три поля: list_begin, list_end и list_buffer. */
|
||
size_t i = 0;
|
||
do
|
||
rkl->list[i] = rkl->list[i + 1];
|
||
while (++i <= rkl->list_length);
|
||
}
|
||
return first;
|
||
}
|
||
|
||
static inline txnid_t after_cut(rkl_t *rkl, const txnid_t out) {
|
||
if (rkl->list_length == 0 && rkl->solid_begin == rkl->solid_end) {
|
||
rkl->solid_end = 0;
|
||
rkl->solid_begin = UINT64_MAX;
|
||
}
|
||
return out;
|
||
}
|
||
|
||
static int extend_solid(rkl_t *rkl, txnid_t solid_begin, txnid_t solid_end, const txnid_t id) {
|
||
if (rkl->list_length) {
|
||
const txnid_t *i = rkl_bsearch(rkl->list, rkl->list_length, id);
|
||
const txnid_t *const end = rkl->list + rkl->list_length;
|
||
/* если начало или конец списка примыкает к непрерывному интервалу,
|
||
* то переносим эти элементы из списка в непрерывный интервал */
|
||
txnid_t *f = (txnid_t *)i;
|
||
while (f > rkl->list && f[-1] >= solid_begin - 1) {
|
||
f -= 1;
|
||
solid_begin -= 1;
|
||
if (unlikely(*f != solid_begin))
|
||
return MDBX_RESULT_TRUE;
|
||
}
|
||
txnid_t *t = (txnid_t *)i;
|
||
while (t < end && *t <= solid_end) {
|
||
if (unlikely(*t != solid_end))
|
||
return MDBX_RESULT_TRUE;
|
||
solid_end += 1;
|
||
t += 1;
|
||
}
|
||
if (f < t) {
|
||
rkl->list_length -= t - f;
|
||
while (t < end)
|
||
*f++ = *t++;
|
||
}
|
||
}
|
||
|
||
rkl->solid_begin = solid_begin;
|
||
rkl->solid_end = solid_end;
|
||
assert(rkl_check(rkl));
|
||
return MDBX_SUCCESS;
|
||
}
|
||
|
||
int rkl_push(rkl_t *rkl, const txnid_t id, const bool known_continuous) {
|
||
assert(id >= MIN_TXNID && id < INVALID_TXNID);
|
||
assert(rkl_check(rkl));
|
||
|
||
if (rkl->solid_begin >= rkl->solid_end) {
|
||
/* непрерывный интервал пуст */
|
||
return extend_solid(rkl, id, id + 1, id);
|
||
} else if (id < rkl->solid_begin) {
|
||
if (known_continuous || id + 1 == rkl->solid_begin)
|
||
/* id примыкает к solid_begin */
|
||
return extend_solid(rkl, id, rkl->solid_end, id);
|
||
} else if (id >= rkl->solid_end) {
|
||
if (known_continuous || id == rkl->solid_end)
|
||
/* id примыкает к solid_end */
|
||
return extend_solid(rkl, rkl->solid_begin, id + 1, id);
|
||
} else {
|
||
/* id входит в интервал между solid_begin и solid_end, т.е. подан дубликат */
|
||
return MDBX_RESULT_TRUE;
|
||
}
|
||
|
||
if (rkl->list_length == 1 && rkl->solid_end == rkl->solid_begin + 1 &&
|
||
(rkl->list[0] == id + 1 || rkl->list[0] == id - 1)) {
|
||
/* В списке один элемент и добавляемый id примыкает к нему, при этом в непрерывном интервале тоже один элемент.
|
||
* Лучше поменять элементы списка и непрерывного интервала. */
|
||
const txnid_t couple = (rkl->list[0] == id - 1) ? id - 1 : id;
|
||
rkl->list[0] = rkl->solid_begin;
|
||
rkl->solid_begin = couple;
|
||
rkl->solid_end = couple + 2;
|
||
assert(rkl_check(rkl));
|
||
return MDBX_SUCCESS;
|
||
}
|
||
|
||
if (unlikely(rkl->list_length == rkl->list_limit)) {
|
||
/* удваиваем размер буфера если закончилось место */
|
||
size_t x2 = (rkl->list_limit + 1) << 1;
|
||
x2 = (x2 > 62) ? x2 : 62;
|
||
x2 = (x2 < txl_max) ? x2 : txl_max;
|
||
x2 = (x2 > rkl->list_length) ? x2 : rkl->list_length + 42;
|
||
int err = rkl_resize(rkl, x2);
|
||
if (unlikely(err != MDBX_SUCCESS))
|
||
return err;
|
||
assert(rkl->list_limit > rkl->list_length);
|
||
}
|
||
|
||
size_t i = rkl->list_length;
|
||
/* ищем место для вставки двигаясь от конца к началу списка, сразу переставляя/раздвигая элементы */
|
||
while (i > 0) {
|
||
if (RKL_ORDERED(id, rkl->list[i - 1])) {
|
||
rkl->list[i] = rkl->list[i - 1];
|
||
i -= 1;
|
||
continue;
|
||
}
|
||
if (unlikely(id == rkl->list[i - 1])) {
|
||
while (++i < rkl->list_length)
|
||
rkl->list[i - 1] = rkl->list[i];
|
||
return MDBX_RESULT_TRUE;
|
||
}
|
||
break;
|
||
}
|
||
|
||
rkl->list[i] = id;
|
||
rkl->list_length++;
|
||
assert(rkl_check(rkl));
|
||
|
||
/* После добавления id в списке могла образоваться длинная последовательность,
|
||
* которую (возможно) стоит обменять с непрерывным интервалом. */
|
||
if (rkl->list_length > (MDBX_DEBUG ? 2 : 16) &&
|
||
((i > 0 && rkl->list[i - 1] == id - 1) || (i + 1 < rkl->list_length && rkl->list[i + 1] == id + 1))) {
|
||
txnid_t new_solid_begin = id;
|
||
size_t from = i;
|
||
while (from > 0 && rkl->list[from - 1] == new_solid_begin - 1) {
|
||
from -= 1;
|
||
new_solid_begin -= 1;
|
||
}
|
||
txnid_t new_solid_end = id + 1;
|
||
size_t to = i + 1;
|
||
while (to < rkl->list_length && rkl->list[to] == new_solid_end) {
|
||
to += 1;
|
||
new_solid_end += 1;
|
||
}
|
||
|
||
const size_t new_solid_len = to - from;
|
||
if (new_solid_len > 3) {
|
||
const size_t old_solid_len = rkl->solid_end - rkl->solid_begin;
|
||
if (new_solid_len > old_solid_len) {
|
||
/* Новая непрерывная последовательность длиннее текущей.
|
||
* Считаем обмен выгодным, если он дешевле пути развития событий с добавлением следующего элемента в список. */
|
||
const size_t old_solid_pos = rkl_bsearch(rkl->list, rkl->list_length, rkl->solid_begin) - rkl->list;
|
||
const size_t swap_cost =
|
||
/* количество элементов списка после изымаемой из списка последовательности,
|
||
* которые нужно переместить */
|
||
rkl->list_length - to +
|
||
/* количество элементов списка после позиции добавляемой в список последовательности,
|
||
* которые нужно переместить */
|
||
((from > old_solid_pos) ? from - old_solid_pos : 0)
|
||
/* количество элементов списка добавляемой последовательности, которые нужно добавить */
|
||
+ old_solid_len;
|
||
/* количество элементов списка, которые нужно переместить для вставки еще-одного/следующего элемента */
|
||
const size_t new_insert_cost = rkl->list_length - i;
|
||
/* coverity[logical_vs_bitwise] */
|
||
if (unlikely(swap_cost < new_insert_cost) || MDBX_DEBUG) {
|
||
/* Изымаемая последовательность длиннее добавляемой, поэтому:
|
||
* - список станет короче;
|
||
* - перемещать хвост нужно всегда к началу;
|
||
* - если начальные элементы потребуется раздвигать,
|
||
* то места хватит и остающиеся элементы в конце не будут перезаписаны. */
|
||
size_t moved = 0;
|
||
if (from > old_solid_pos) {
|
||
/* добавляемая последовательность ближе к началу, нужно раздвинуть элементы в голове для вставки. */
|
||
moved = from - old_solid_pos;
|
||
do {
|
||
from -= 1;
|
||
rkl->list[from + old_solid_len] = rkl->list[from];
|
||
} while (from > old_solid_pos);
|
||
} else if (from + new_solid_len < old_solid_pos) {
|
||
/* добавляемая последовательность дальше от начала,
|
||
* перемещаем часть элементов из хвоста после изымаемой последовательности */
|
||
do
|
||
rkl->list[from++] = rkl->list[to++];
|
||
while (from < old_solid_pos - new_solid_len);
|
||
}
|
||
|
||
/* вставляем последовательноть */
|
||
i = 0;
|
||
do
|
||
rkl->list[from++] = rkl->solid_begin + i++;
|
||
while (i != old_solid_len);
|
||
|
||
/* сдвигаем оставшийся хвост */
|
||
while (to < rkl->list_length)
|
||
rkl->list[moved + from++] = rkl->list[to++];
|
||
|
||
rkl->list_length = rkl->list_length - new_solid_len + old_solid_len;
|
||
rkl->solid_begin = new_solid_begin;
|
||
rkl->solid_end = new_solid_end;
|
||
assert(rkl_check(rkl));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return MDBX_SUCCESS;
|
||
}
|
||
|
||
txnid_t rkl_pop(rkl_t *rkl, const bool highest_not_lowest) {
|
||
assert(rkl_check(rkl));
|
||
|
||
if (rkl->list_length) {
|
||
assert(rkl->solid_begin <= rkl->solid_end);
|
||
if (highest_not_lowest && (solid_empty(rkl) || rkl->solid_end < rkl->list[rkl->list_length - 1]))
|
||
return after_cut(rkl, rkl->list[rkl->list_length -= 1]);
|
||
if (!highest_not_lowest && (solid_empty(rkl) || rkl->solid_begin > rkl->list[0]))
|
||
return after_cut(rkl, list_remove_first(rkl));
|
||
}
|
||
|
||
if (!solid_empty(rkl))
|
||
return after_cut(rkl, highest_not_lowest ? --rkl->solid_end : rkl->solid_begin++);
|
||
|
||
assert(rkl_empty(rkl));
|
||
return 0;
|
||
}
|
||
|
||
txnid_t rkl_lowest(const rkl_t *rkl) {
|
||
if (rkl->list_length)
|
||
return (solid_empty(rkl) || rkl->list[0] < rkl->solid_begin) ? rkl->list[0] : rkl->solid_begin;
|
||
return !solid_empty(rkl) ? rkl->solid_begin : INVALID_TXNID;
|
||
}
|
||
|
||
txnid_t rkl_highest(const rkl_t *rkl) {
|
||
if (rkl->list_length)
|
||
return (solid_empty(rkl) || rkl->list[rkl->list_length - 1] >= rkl->solid_end) ? rkl->list[rkl->list_length - 1]
|
||
: rkl->solid_end - 1;
|
||
return !solid_empty(rkl) ? rkl->solid_end - 1 : 0;
|
||
}
|
||
|
||
int rkl_merge(rkl_t *dst, const rkl_t *src, bool ignore_duplicates) {
|
||
if (src->list_length) {
|
||
size_t i = src->list_length;
|
||
do {
|
||
int err = rkl_push(dst, src->list[i - 1], false);
|
||
if (unlikely(err != MDBX_SUCCESS) && (!ignore_duplicates || err != MDBX_RESULT_TRUE))
|
||
return err;
|
||
} while (--i);
|
||
}
|
||
|
||
txnid_t id = src->solid_begin;
|
||
while (id < src->solid_end) {
|
||
int err = rkl_push(dst, id, false);
|
||
if (unlikely(err != MDBX_SUCCESS) && (!ignore_duplicates || err != MDBX_RESULT_TRUE))
|
||
return err;
|
||
++id;
|
||
}
|
||
return MDBX_SUCCESS;
|
||
}
|
||
|
||
rkl_iter_t rkl_iterator(const rkl_t *rkl, const bool reverse) {
|
||
rkl_iter_t iter = {.rkl = rkl, .pos = reverse ? rkl_len(rkl) : 0, .solid_offset = 0};
|
||
if (!solid_empty(rkl) && rkl->list_length) {
|
||
const txnid_t *it = rkl_bsearch(rkl->list, rkl->list_length, rkl->solid_begin);
|
||
const txnid_t *const end = rkl->list + rkl->list_length;
|
||
assert(it >= rkl->list && it <= end && (it == end || *it > rkl->solid_begin));
|
||
iter.solid_offset = it - rkl->list;
|
||
}
|
||
return iter;
|
||
}
|
||
|
||
txnid_t rkl_turn(rkl_iter_t *iter, const bool reverse) {
|
||
assert((unsigned)reverse == (unsigned)!!reverse);
|
||
size_t pos = iter->pos - reverse;
|
||
if (unlikely(pos >= rkl_len(iter->rkl)))
|
||
return 0;
|
||
|
||
iter->pos = pos + !reverse;
|
||
assert(iter->pos <= rkl_len(iter->rkl));
|
||
|
||
const size_t solid_len = iter->rkl->solid_end - iter->rkl->solid_begin;
|
||
if (iter->rkl->list_length) {
|
||
if (pos < iter->solid_offset)
|
||
return iter->rkl->list[pos];
|
||
else if (pos < iter->solid_offset + solid_len)
|
||
return iter->rkl->solid_begin + pos - iter->solid_offset;
|
||
else
|
||
return iter->rkl->list[pos - solid_len];
|
||
}
|
||
|
||
assert(pos < solid_len);
|
||
return iter->rkl->solid_begin + pos;
|
||
}
|
||
|
||
size_t rkl_left(rkl_iter_t *iter, const bool reverse) {
|
||
assert(iter->pos <= rkl_len(iter->rkl));
|
||
return reverse ? iter->pos : rkl_len(iter->rkl) - iter->pos;
|
||
}
|
||
|
||
#if 1
|
||
#define DEBUG_HOLE(hole) \
|
||
do { \
|
||
} while (0)
|
||
#else
|
||
#define DEBUG_HOLE(hole) \
|
||
do { \
|
||
printf(" return-%sward: %d, ", reverse ? "back" : "for", __LINE__); \
|
||
if (hole.begin == hole.end) \
|
||
printf("empty-hole\n"); \
|
||
else if (hole.end - hole.begin == 1) \
|
||
printf("hole %" PRIaTXN "\n", hole.begin); \
|
||
else \
|
||
printf("hole %" PRIaTXN "-%" PRIaTXN "\n", hole.begin, hole.end - 1); \
|
||
fflush(nullptr); \
|
||
} while (0)
|
||
#endif
|
||
|
||
rkl_hole_t rkl_hole(rkl_iter_t *iter, const bool reverse) {
|
||
assert((unsigned)reverse == (unsigned)!!reverse);
|
||
rkl_hole_t hole;
|
||
const size_t len = rkl_len(iter->rkl);
|
||
size_t pos = iter->pos;
|
||
if (unlikely(pos >= len)) {
|
||
if (len == 0) {
|
||
hole.begin = 1;
|
||
hole.end = MAX_TXNID;
|
||
iter->pos = 0;
|
||
DEBUG_HOLE(hole);
|
||
return hole;
|
||
} else if (pos == len && reverse) {
|
||
/* шаг назад из позиции на конце rkl */
|
||
} else if (reverse) {
|
||
hole.begin = 1;
|
||
hole.end = 1 /* rkl_lowest(iter->rkl); */;
|
||
iter->pos = 0;
|
||
DEBUG_HOLE(hole);
|
||
return hole;
|
||
} else {
|
||
hole.begin = MAX_TXNID /* rkl_highest(iter->rkl) + 1 */;
|
||
hole.end = MAX_TXNID;
|
||
iter->pos = len;
|
||
DEBUG_HOLE(hole);
|
||
return hole;
|
||
}
|
||
}
|
||
|
||
const size_t solid_len = iter->rkl->solid_end - iter->rkl->solid_begin;
|
||
if (iter->rkl->list_length) {
|
||
/* список элементов не пуст */
|
||
txnid_t here, there;
|
||
for (size_t next;; pos = next) {
|
||
next = reverse ? pos - 1 : pos + 1;
|
||
if (pos < iter->solid_offset) {
|
||
/* текущая позиция перед непрерывным интервалом */
|
||
here = iter->rkl->list[pos];
|
||
if (next == iter->solid_offset) {
|
||
/* в следующей позиции начинается непрерывный интерал (при поиске вперед) */
|
||
assert(!reverse);
|
||
hole.begin = here + 1;
|
||
hole.end = iter->rkl->solid_begin;
|
||
next += solid_len;
|
||
assert(hole.begin < hole.end /* зазор обязан быть, иначе это ошибка не-слияния */);
|
||
/* зазор между элементом списка перед сплошным интервалом и началом интервала */
|
||
iter->pos = next - 1;
|
||
DEBUG_HOLE(hole);
|
||
return hole;
|
||
}
|
||
if (next >= len)
|
||
/* уперлись в конец или начало rkl */
|
||
break;
|
||
/* следующая позиция также перед непрерывным интервалом */
|
||
there = iter->rkl->list[next];
|
||
} else if (pos >= iter->solid_offset + solid_len) {
|
||
/* текущая позиция после непрерывного интервала */
|
||
here = (pos < len) ? iter->rkl->list[pos - solid_len] : MAX_TXNID;
|
||
if (next >= len)
|
||
/* уперлись в конец или начало rkl */
|
||
break;
|
||
if (next == iter->solid_offset + solid_len - 1) {
|
||
/* в следующей позиции конец непрерывного интервала (при поиске назад) */
|
||
assert(reverse);
|
||
hole.begin = iter->rkl->solid_end;
|
||
hole.end = here;
|
||
pos = iter->solid_offset;
|
||
assert(hole.begin < hole.end /* зазор обязан быть, иначе это ошибка не-слияния */);
|
||
/* зазор между элементом списка после сплошного интервала и концом интервала */
|
||
iter->pos = pos;
|
||
DEBUG_HOLE(hole);
|
||
return hole;
|
||
}
|
||
/* следующая позиция также после непрерывного интервала */
|
||
there = iter->rkl->list[next - solid_len];
|
||
} else if (reverse) {
|
||
/* текущая позиция внутри непрерывного интервала и поиск назад */
|
||
next = iter->solid_offset - 1;
|
||
here = iter->rkl->solid_begin;
|
||
if (next >= len)
|
||
/* нет элементов списка перед непрерывным интервалом */
|
||
break;
|
||
/* предыдущая позиция перед непрерывным интервалом */
|
||
there = iter->rkl->list[next];
|
||
} else {
|
||
/* текущая позиция внутри непрерывного интервала и поиск вперед */
|
||
next = iter->solid_offset + solid_len;
|
||
here = iter->rkl->solid_end - 1;
|
||
if (next >= len)
|
||
/* нет элементов списка после непрерывного интервала */
|
||
break;
|
||
/* следующая позиция после непрерывного интервала */
|
||
there = iter->rkl->list[next - solid_len];
|
||
}
|
||
|
||
hole.begin = (reverse ? there : here) + 1;
|
||
hole.end = reverse ? here : there;
|
||
if (hole.begin < hole.end) {
|
||
/* есть зазор между текущей и следующей позицией */
|
||
iter->pos = next;
|
||
DEBUG_HOLE(hole);
|
||
return hole;
|
||
}
|
||
}
|
||
|
||
if (reverse) {
|
||
/* уперлись в начало rkl, возвращаем зазор перед началом rkl */
|
||
hole.begin = 1;
|
||
hole.end = here;
|
||
iter->pos = 0;
|
||
DEBUG_HOLE(hole);
|
||
} else {
|
||
/* уперлись в конец rkl, возвращаем зазор после конца rkl */
|
||
hole.begin = here + 1;
|
||
hole.end = MAX_TXNID;
|
||
iter->pos = len;
|
||
DEBUG_HOLE(hole);
|
||
}
|
||
return hole;
|
||
}
|
||
|
||
/* список элементов пуст, но есть непрерывный интервал */
|
||
iter->pos = reverse ? 0 : len;
|
||
if (reverse && pos < len) {
|
||
/* возвращаем зазор перед непрерывным интервалом */
|
||
hole.begin = 1;
|
||
hole.end = iter->rkl->solid_begin;
|
||
DEBUG_HOLE(hole);
|
||
} else {
|
||
/* возвращаем зазор после непрерывного интервала */
|
||
hole.begin = iter->rkl->solid_end;
|
||
hole.end = MAX_TXNID;
|
||
DEBUG_HOLE(hole);
|
||
}
|
||
return hole;
|
||
}
|
||
|
||
bool rkl_check(const rkl_t *rkl) {
|
||
if (!rkl)
|
||
return false;
|
||
if (rkl->list == rkl->inplace && unlikely(rkl->list_limit != ARRAY_LENGTH(rkl->inplace)))
|
||
return false;
|
||
if (unlikely(rkl->list_limit < ARRAY_LENGTH(rkl->inplace)))
|
||
return false;
|
||
|
||
if (rkl_empty(rkl))
|
||
return rkl->list_length == 0 && solid_empty(rkl);
|
||
|
||
if (rkl->list_length) {
|
||
for (size_t i = 1; i < rkl->list_length; ++i)
|
||
if (unlikely(!RKL_ORDERED(rkl->list[i - 1], rkl->list[i])))
|
||
return false;
|
||
if (!solid_empty(rkl) && rkl->solid_begin - 1 <= rkl->list[rkl->list_length - 1] &&
|
||
rkl->solid_end >= rkl->list[0]) {
|
||
/* непрерывный интервал "плавает" внутри списка, т.е. находится между какими-то соседними значениями */
|
||
const txnid_t *it = rkl_bsearch(rkl->list, rkl->list_length, rkl->solid_begin);
|
||
const txnid_t *const end = rkl->list + rkl->list_length;
|
||
if (it < rkl->list || it > end)
|
||
return false;
|
||
if (it > rkl->list && it[-1] >= rkl->solid_begin)
|
||
return false;
|
||
if (it < end && it[0] <= rkl->solid_end)
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|