Compare commits

...

96 Commits

Author SHA1 Message Date
Леонид Юрьев (Leonid Yuriev)
aea40fb79f mdbx: выпуск 0.12.10 "СЭМ"
Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов
в память Героя России гвардии майора Дмитрия Семёнова с позывным "СЭМ".

Значимые исправления и доработки:
---------------------------------

 - Устранение унаследованной от LMDB ошибки приводящей к повреждению БД при использовании `MDBX_DUPFIXED`.

 - Исправление ложной ошибки `MDBX_CORRUPTED (-30796)` в сценарии работы
   в режиме `MDBX_DUPFIXED` и нечетной длинной мульти-значений.

 - Исправление недочета корректировки сопутствующих курсоров при разделении страницы
   по сценарию добавления пустой страницы слева.

 - Доработка `rebalance()` ради уменьшения WAF.

 - Исправление assert-проверки внутри `check_txn()` для случая завершенных транзакций в режиме `MDBX_NO_TLS`.
   Последствий ошибки, кроме срабатывания assert-проверки в отладочных сборках, нет.

 - Устранение ошибки при открытии БД на файловой системе только-для-чтения.

 - Удалены излишне строгие проверки в утилите `mdbx_chk`, которые
   приводили к ложно-позитивным ошибкам при проверке БД после серии
   последних доработок.

Более подробная информация в [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 19 commits, 57 files changed, 751 insertions(+), 331 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2024-03-13 14:57:38 +03:00
Леонид Юрьев (Leonid Yuriev)
0741c81cfd mdbx-chk: не считаем ошибкой под-страницы без данных пользователя.
Коммитом 36a7e7ac24 был скорректирован
подсчет места занимаемого заголовками и данными. В частности, байты
занимаемые заголовков вложенной под-страницы теперь относится к
заголовкам, а не пользовательским данным.

Однако, в случае когда на под-странице, созданной для хранения куста
мульти-значений (MDBX_DUPSORT), после серии удалений остаётся одно
значение, которое в MDBX может нулевой длины, получается под-страница с
нулевым количеством пользовательских данных. Этот коммит исправляет
2024-03-12 09:02:50 +03:00
Леонид Юрьев (Leonid Yuriev)
781b3f64d5 mdbx-chk: не считаем ошибочными/проблемными записи нулевой длины в GC.
Технически такие записи не являются проблемными, а образовываются в
случае когда внутри update_gc() резервируется больше места, чем реально
остается номеров свободных страниц для возврата в GC.

Изначально такое избыточное резервирование считалось алгоритмическим
недостатком update_gc(). Поэтому утилита mdbx_chk была временно
доработана для выявления таких случаев в ходе стохастических тестов.

Постепенно все реальные недочеты update_gc() (если не считать
запутанности и неочевидности кода) были устранены, формирование пустых
записей в GC не наблюдалось и излишне строгий контроль в mdbx_chk не
создавал проблем.

В ходе же последних точечных доработок была предпринята попытка еще
немного уменьшить затраты ЦПУ внутри update_gc(), в частности уменьшить
кол-во циклов/повторов посредством улучшения сходимости, а также
уменьшить WAF. При этом образование пустых записей в GC стало возможным
в достаточно редких ситуациях, когда (например) для возврата в GC
остается только одна страница и добавление записи единичной длины
приводит к перебалансировке или разделению листовой страницы по
легковесному пути, без вовлечения других страниц дерева и без
переработки дополнительных записей GC, но с поглощением остававшейся на
возврат страницы.

Проще говоря, в актуальная версии MDBX пустые записи в GC могут
образовываться, когда это энергетически выгодно. Тогда как в предыдущих
выпусках в таких ситуациях выполнялось более дорогое обновление GC с
переработкой и возвратом дополнительных записей.
2024-03-12 02:38:17 +03:00
Леонид Юрьев (Leonid Yuriev)
446d6c9421 mdbx: исправление ошибки открытия БД на ФС только-для-чтения. 2024-03-11 00:34:04 +03:00
Леонид Юрьев (Leonid Yuriev)
fff3fbd866 mdbx: обновление ChangeLog (подготовка к выпуску). 2024-03-06 22:58:31 +03:00
Леонид Юрьев (Leonid Yuriev)
3e850981c7 mdbx-test: расширение стохастического теста dupfixed-сценариями (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
36a7e7ac24 mdbx-chk: исправление подсчета места затраченного на выравнивание в случае нечетного кол-ва dupfixed-элементов нечетного размера (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
2d7fe42327 mdbx: корректировка условия в assert-проверке для MDBX_TXN_DRAINED_GC (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
2b88c6261f mdbx-test: добавление в jitter простого теста txn_reset+txn_renew (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
e515bd56e9 mdbx: исправление assert-проверки внутри check_txn() для случая завершенных транзакций в режиме MDBX_NO_TLS (backport).
По сообщению о проблеме https://t.me/libmdbx/5424
2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
20160ae98f mdbx: доработка rebalance() ради уменьшения WAF (backport).
После предыдущей серии доработок весной 2021 года, функция `rebalance()`
обеспечивала слияние мало заполненной страницы с менее заполненной
соседней, одновременно пытаясь не вовлекать соседних страниц, если те
еще не были скопированы/клонированы/изменены в текущей транзакции.

В целом, реализованная тактика представляется успешной. Однако, при
обновлении GC она иногда приводила к исчерпанию подготовленного резерва
извлеченных из GC страниц. Это не является проблемой, если не считать
вероятность срабатывания `assert(txn->mt_flags & MDBX_TXN_DRAINED_GC)`
в отладочных сборках.

Тем не менее, из этой ситуации можно сделать вывод, что поведение
`rebalance()`, как минимум, может быть обогащено опцией уменьшения WAF
ценой меньшей сбалансированности дерева. Технически при этом слияние
выполняется преимущественно с грязной страницей, если на ней достаточно
места и соседняя страница с другой стороны еще чистая.

ВАЖНО: Соответствующая опция в `enum MDBX_option_t` будет добавлена чуть
позже в следующую версию, а в текущих ветках `master` и `stable` это
именение поведение будет заглушено.
2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
ea97fbae74 mdbx: устранение yнаследованной от LMDB ошибки приводящей к повреждению БД при использовании MDBX_DUPFIXED (backport).
Тезисно:

 - Использование DUPFIXED (включая INTEGERDUP) могло приводить к
   повреждению БД и/или потере данных. Этот коммит устраняет эту угрозу.

 - Вероятность проявления существенно увеличивается с увеличением
   размера/длины мульти-значений/дубликатов (не ключей).

 - В MDBX проблема унаследована от LMDB, где существует более 11 лет,
   начиная с коммита ccc4d23e74
   и до настоящего времени.

Для вложенных страниц типа LEAF2 (которые содержат только значения
одинаковой длины, без таблицы смещений к ним), упомянутым выше коммитом,
было добавлено резервирование места (что в целом спорно, но в некоторых
сценариях позволяет уменьшить накладные расходы). Ошибка была в том, что
в коде не исключалась возможность превышения размера страницы БД, что
далее приводило к арифметическому переполнению, повреждению БД и/или
просписи памяти.
2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
a0a4af7701 mdbx: исправление me_dxb_mmap.current > me_dxb_mmap.limit и срабатывания соответствующей assert-проверки (backport).
Устранение упущения приводящего к нелогичной ситуации `me_dxb_mmap.curren > me_dxb_mmap.limit` при "дребезге" размера БД.
В текущем понимании, последствий кроме срабатывания assert-проверки нет, а вероятность проявления близка к нулю.
2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
74f2bc813b mdbx: продолжение очистки/рефакторинга унаследованных ребусов в cursor_put_nochecklen() (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
1a18369015 mdbx-test: фиксация транзакции при ошибках теста для последующего анализа БД (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
e2f2fd8652 mdbx: рефакторинг node_shrink() для ясности исходного кода (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
fb6be62046 mdbx: исправление недочета корректировки сопутствующих курсоров при разделении страницы по сценарию добавления пустой страницы слева (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
f7e6bd770a mdbx-test: соответствие протоколируемых имен тестов опциям командной строки (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
ba5c74e54d mdbx-test: добавление extra/dupfixed_addodd (backport). 2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
049b71c148 mdbx: исправление ложной ошибки MDBX_CORRUPTED (-30796) в сценарии "odd dupfixed" (backport).
Повреждение БД и/или потери данных не происходило, проблема лишь в
возврате ложной ошибки.

Благодарю пользователя/разработчика @Dvirsw (https://t.me/Dvirsw) за
сообщения о проблеме и предоставление минимального/оптимального сценария
воспроизведения.

--

Проблема была из-за излишнего условия при контроле внутренего поля
mp_upper в ходе проверки структуры страниц БД.

Поле mp_upper указывает на нижнуюю границу заполнения страницы от конца
к началу. Вследствие того, что значения ключей выравниваетня на четную
границу, это поле четно во всех случаях за исключением LEAF2-страницы
(листовая страница вложенного дерева для множественных значений
финсированной/одинаковой длины одного ключа), на которой размещено
нечетное количество значений нечетной длины.

Ошибка не проявлялась в большинстве случаев (в том числе в
стохастических тестах), так как штатно лишняя проверка производилась
только при чтении страницы и перебалансировке ключей, но не при каждом
добавлении значения. Тем не менее, сценарии тестов требуют
доработки/расширения для явного добавления нечетных dupfixed-сценариев.
2024-03-06 13:18:58 +03:00
Леонид Юрьев (Leonid Yuriev)
185e43f3a8 mdbx: выпуск 0.12.9 "Ясень-4"
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов.

Исправления и доработки:
------------------------

 - Ликвидация зависимости от ранее удаленной опции `MDBX_ENABLE_PREFAULT`, из-за
   чего опция `MDBX_ENABLE_MINCORE` не включалась автоматически, что приводило
   к не-активации соответствующего улучшения и не-достижению  декларируемого уровня
   производительности в сценариях использования в режиме `MDBX_WRITEMAP`.

 - Исправление авто-установки `MDBX_ENV_CHECKPID` при отключении использования
   функционала `madvise()` посредством опции сборки `MDBX_ENABLE_MADVISE=0`.
   Из-за чего при поддержке системой `madvise(MADV_DONTFORK)` не включался контроль pid.

 - Добавлена проверка переданного ключа на `NULL` при обработке `MDBX_GET_MULTIPLE`.

 - Добавлена проверка номеров корневых страниц в `coherency_check()`.

 - Обеспечен `const` для начала и конца диапазона в аргументах `mdbx_estimate_range()`.

 - Из разрабатываемой версии перенесены не-нарушающие совместимости доработки C++ API:

     - добавлен тип `mdbx::cursor::estimation_result`, а поведение методов
      `cursor::estimate()` унифицировано с `cursor::move()`;
     - для предотвращения незаметного неверного использования API, для инициализации
       возвращаемых по ссылке срезов, вместо пустых срезов задействован `slice::invalid()`;
     - добавлены дополнительные C++ операторы преобразования к типам C API;
     - для совместимости со старыми стандартами C++ и старыми версиями STL перенесены
       в public классы `buffer::move_assign_alloc` и `buffer::copy_assign_alloc`;
     - добавлен тип `mdbx::default_buffer`;
     - для срезов и буферов добавлены методы `hex_decode()`, `base64_decode()`, `base58_decode()`;
     - добавлен тип `mdbx::comparator` и функций `mdbx::default_comparator()`;
     - добавлены статические методы `buffer::hex()`, `base64()`, `base58()`;
     - для транзакций и курсоров добавлены методы `get_/set_context`;
     - добавлен метод `cursor::clone()`;

 - Поддержка base58 приведена в соответствии с черновиком RFC.

 - Переработка/исправление `to_hex()` и `from_hex()`.

 - Уменьшение `MDBX_opt_rp_augment_limit` по умолчанию до 1/3 от текущего количества страниц в БД.

Более подробная информация в [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 32 commits, 8 files changed, 667 insertions(+), 401 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2023-12-11 23:24:05 +03:00
Леонид Юрьев (Leonid Yuriev)
990d4ea042 mdbx: обновление ChangeLog. 2023-11-28 21:48:56 +03:00
Леонид Юрьев (Leonid Yuriev)
8d67d23224 mdbx: исправление несущественной опечатки в комментарии. 2023-11-28 21:45:41 +03:00
Леонид Юрьев (Leonid Yuriev)
4a7a2034c0 mdbx: новый размер MDBX_opt_rp_augment_limit по умолчанию в 1/3 от текущего кол-ва страниц в БД. 2023-11-28 21:45:21 +03:00
Леонид Юрьев (Leonid Yuriev)
d963f3a971 mdbx: обновление ChangeLog. 2023-11-28 09:36:29 +03:00
Леонид Юрьев (Leonid Yuriev)
fb15c8ca0b mdbx++: дополнительные операторы преобразования к типам C API (backport). 2023-11-28 09:36:29 +03:00
Леонид Юрьев (Leonid Yuriev)
b6034e8045 mdbx-test: правка тестов для совместимости с проблемными версиями glibc и glibc++ (backport). 2023-11-28 09:36:29 +03:00
Леонид Юрьев (Leonid Yuriev)
ae01a8e891 mdbx++: перенос в public типов buffer::move_assign_alloc и buffer::copy_assign_alloc для старых стандартов C++ (backport). 2023-11-28 09:36:29 +03:00
Леонид Юрьев (Leonid Yuriev)
1277fe965d mdbx++: переделка поддержки base58 по RFC-draft (backport). 2023-11-28 09:36:10 +03:00
Леонид Юрьев (Leonid Yuriev)
7fc6a1b658 mdbx++: переработка/исправление to_hex() (backport). 2023-11-28 01:18:24 +03:00
Леонид Юрьев (Leonid Yuriev)
b1abcb8260 mdbx++: использование slice::invalid() для предотвращения незаметного неверного использования API (backport). 2023-11-28 01:18:24 +03:00
Леонид Юрьев (Leonid Yuriev)
7cd3dbbccb mdbx++: добавление mdbx::default_buffer (backport). 2023-11-28 01:18:24 +03:00
Леонид Юрьев (Leonid Yuriev)
352dd75ee8 mdbx++: исправление опечатки в doxygen-описании (backport). 2023-11-28 01:16:38 +03:00
Леонид Юрьев (Leonid Yuriev)
cf1541e4d7 mdbx++: добавление get/set_context для транзакций и курсоров (backport). 2023-11-28 01:16:38 +03:00
Леонид Юрьев (Leonid Yuriev)
446dbc9d6c mdbx++: добавление cursor::clone() (backport). 2023-11-28 01:13:24 +03:00
Леонид Юрьев (Leonid Yuriev)
64a5ad8c04 mdbx++: добавление методов buffer::hex_decode(), base64_decode(), base58_decode() (backport). 2023-11-28 01:13:24 +03:00
Леонид Юрьев (Leonid Yuriev)
0fd0d527d9 mdbx++: добавление типа mdbx::comparator и функций mdbx::default_comparator() (backport). 2023-11-28 01:13:24 +03:00
Леонид Юрьев (Leonid Yuriev)
4bed5d1779 mdbx++: добавление статических методов buffer::hex(), base64(), base58() (backport). 2023-11-28 01:13:24 +03:00
Леонид Юрьев (Leonid Yuriev)
6e4473777e mdbx: проверка pid только для запроса активной env (backport). 2023-11-28 01:10:23 +03:00
Леонид Юрьев (Leonid Yuriev)
5abb6a9bbf mdbx-doc: удаление устаревшего упоминания MDBX_EAGAIN. 2023-11-17 21:18:21 +03:00
Леонид Юрьев (Leonid Yuriev)
c014685c01 mdbx: обновление ChangeLog. 2023-11-12 21:46:52 +03:00
Леонид Юрьев (Leonid Yuriev)
476da5f8cf mdbx++: добавление cursor::estimation_result и переделка cursor::estimate() (backport). 2023-11-12 21:30:09 +03:00
Леонид Юрьев (Leonid Yuriev)
b905a6a391 mdbx: const для начала и конца диапазона в аргументах mdbx_estimate_range() (backport). 2023-11-12 21:30:09 +03:00
Леонид Юрьев (Leonid Yuriev)
d94f34b2c0 mdbx: рефакторинг обработки MDBX_GET_MULTIPLE добавление проверки key на NULL (backport). 2023-11-12 21:30:09 +03:00
Леонид Юрьев (Leonid Yuriev)
f5ce7322c4 mdbx: исправление авто-установки MDBX_ENV_CHECKPID для случая MDBX_ENABLE_MADVISE=0 (backport). 2023-11-12 21:30:09 +03:00
Леонид Юрьев (Leonid Yuriev)
ab5d290f11 mdbx: проверка номеров корневых страниц в coherency_check() (backport). 2023-11-12 21:29:18 +03:00
Леонид Юрьев (Leonid Yuriev)
7eb2f4130e mdbx: использование const MDBX_txn где это возможно в API (backport). 2023-11-12 19:33:27 +03:00
Леонид Юрьев (Leonid Yuriev)
ed8c7ead4e mdbx: ликвидация ошибочной зависимости от удаленной опции MDBX_ENABLE_PREFAULT. 2023-10-29 12:20:54 +03:00
Леонид Юрьев (Leonid Yuriev)
080875cd6d mdbx: Обновление ChangeLog. 2023-10-23 18:13:35 +03:00
Леонид Юрьев (Leonid Yuriev)
753cfd00eb mdbx: обновление патча для старых версий buildroot. 2023-10-23 18:12:21 +03:00
Леонид Юрьев (Leonid Yuriev)
311a6e5d10 mdbx++: добавление забытого исключения mdbx::duplicated_lck_file. 2023-10-23 16:33:34 +03:00
Леонид Юрьев (Leonid Yuriev)
e58b582639 mdbx: исправление MDBX_LAST_ADDED_ERRCODE. 2023-10-23 16:33:30 +03:00
Леонид Юрьев (Leonid Yuriev)
e2ed55853d mdbx: удаление устаревших mdbx_set_compare() и mdbx_set_dupsort(). 2023-10-23 16:17:05 +03:00
Леонид Юрьев (Leonid Yuriev)
02c7cf2a9c mdbx: выпуск 0.12.8 "Владимир Уткин"
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов,
в день 100-летия со дня рождения выдающегося советского и российского ученого и конструктора [Влади́мира Фёдоровича У́ткина](https://ru.wikipedia.org/wiki/Уткин,_Владимир_Фёдорович).

Исправления и доработки:
------------------------

 - Устранение регресса/ошибки в пути обработки `put(MDBX_MULTIPLE)` при пакетном/оптовом
   помещении в БД множественных значений одного ключа (aka multi-value или dupsort).
   Проявление проблемы зависит от компилятора и опций оптимизации/кодогенерации, но с большой вероятностью возвращется
   ошибка `MDBX_BAD_VALSIZE` (`-30781`), а в отладочных сборках срабатывает проверка `cASSERT(mc, !"Invalid key-size")`.
   Сценарии приводящие к другим проявлениям на данный момент не известны.

 - Реализована перезапись в `mdbx_put(MDBX_CURRENT)` всех текущих мульти-значений ключа
   при отсутствии флага `MDBX_NOOVERWRITE`. Ранее в такой ситуации возвращалась ошибка `MDBX_EMULTIVAL`.
   В текущем понимании новое поведение более удобно и не создаёт проблем совместимости с ранее написанным кодом.

 - Добавлена возможность использовать `mdbx_cursor_get(MDBX_GET_MULTIPLE)` без предварительной установки
   курсора, совмещая операцию пакетного получения данных с позиционированием курсора на передаваемый ключ.

 - Микрооптимизация и рефакторинг `cursor_put_nochecklen()` в продолжение исправления
   регресса/ошибки в пути обработки `put(MDBX_MULTIPLE)`.

 - Уточнение формулировок в описании API, в том числе пояснений о `SIGSEGV`
   и недопустимости прямого изменения данных.

Более подробная информация в [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

git diff' stat: 24 commits, 18 files changed, 624 insertions(+), 94 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2023-10-17 18:16:29 +03:00
Леонид Юрьев (Leonid Yuriev)
83f19fc993 mdbx: обновление ChangeLog. 2023-10-14 14:37:08 +03:00
Леонид Юрьев (Leonid Yuriev)
d440485156 mdbx-doc: добавление определений макросов для Doxygen. 2023-10-14 14:36:14 +03:00
Леонид Юрьев (Leonid Yuriev)
25ec8e253f mdbx-doc: уточнение формулировок в описании API. 2023-10-14 14:16:49 +03:00
Леонид Юрьев (Leonid Yuriev)
248208cf5d mdbx: обновлене ChangeLog (планирование релиза). 2023-10-11 11:14:28 +03:00
Леонид Юрьев (Leonid Yuriev)
f02a0ffa21 mdbx: возможность использования MDBX_GET_MULTIPLE без предварительной установки курсора. 2023-10-10 22:34:05 +03:00
Леонид Юрьев (Leonid Yuriev)
2b0eae08f5 mdbx: обновление ChangeLog. 2023-10-09 22:12:06 +03:00
Леонид Юрьев (Leonid Yuriev)
5d9740bbcf mdbx-cmake: использование add_mdbx_option() для вывода информации об mdbx-опциях при сборке. 2023-10-09 21:49:58 +03:00
Леонид Юрьев (Leonid Yuriev)
39f2bb142a mdbx: сокращение излишнего вызова osal_thread_self(). 2023-10-09 20:58:01 +03:00
Леонид Юрьев (Leonid Yuriev)
e9b10db255 mdbx++: доработка использования filesystem для старых компиляторов. 2023-10-09 07:34:01 +03:00
Леонид Юрьев (Leonid Yuriev)
687622b8b1 mdbx: устранение предупреждений Valgrind при логировании в отладочных сборках.
Достаточно запутано:

 - Внутри `update_gc()` используется создание записей с резервированием
   посредством `put(MDBX_RESERVE)` в циклах с ранним выходом и последующим
   заполнением.

 - При этом в случае раннего выхода (из цикла из-за изменения набора
   страниц) зарезервированное место в добавленных записях остается
   незаполненным/неиницилизированным (подкрашенным в Valgrind или ASAN).

 - Чтение этих незаполненных/неиницилизированных данных штатно не
   происходит, но в отладочных сборках при включении детального уровне
   логирования выполняется отладочный вывод значений ключей и данных при
   позиционировании курсоров.

 - В свою очередь, `update_gc()` либо удаляет, либо заполняет
   зарезервированные записи, но для этого требуется позиционирование
   курсора, что в отладочных сборках приводит к чтению
   незаполненных/неиницилизированных записей и печали Valgrind/ASAN.

Теперь внутри `update_gc()` в отладочных сборках с поддержкой Valgrind
или ASAN место в резервируемых записях явно инициализируется.
2023-10-08 18:31:12 +03:00
Леонид Юрьев (Leonid Yuriev)
fd8a99acff mdbx: доработка mdbx_dump_val() используемой для логирования и отладки.
- Обеспечении терминирующего нуля даже при нехватке буфера и
   опосредованных предупреждений Valgrind из-за чтения внутри strlen()
   неинициализированных данных при последующем логировании/печати.

 - Ускорение за счет отказа от использования snpruintf().
2023-10-08 17:43:13 +03:00
Леонид Юрьев (Leonid Yuriev)
e21e91ad1f mdbx-doc: уточнение формулировок о SIGSEGV и недопустимости прямого изменения данных. 2023-10-08 11:55:30 +03:00
Леонид Юрьев (Leonid Yuriev)
6027348651 mdbx: обновление ChangeLog. 2023-10-08 09:42:56 +03:00
Леонид Юрьев (Leonid Yuriev)
1aead6869a mdbx: костыль для глушения/игнорирования EDEADLK в ряде сценариев при использовании Valgrind или ASAN.
Достаточно запутанно:

 - Для полноценного контроля при использовании Valgrind или ASAN
   требуется закрашивать/отравлять отображение файла БД выше границы
   распределенных страниц.

 - Производить такое подкрашивание/отравление необходимо в синхронизации
   с пишущими транзакциями и запросами на изменение геометрии, в том числе
   при изменении размера БД и/или геометрии другим процессом.

 - Для такой синхронизации логично и проще всего использовать основной
   мьютекс/механизм блокировки пишущих транзакций, что и происходит внутри
   txn_valgrind().

 - Однако, в этой схеме может возникать ошибка EDEADLK, когда
   txn_valgrind() вызывается при завершении читающей транзакции
   выполняющейся с дополнительной блокировкой пишущих транзакций.

 - Как таковая ошибка EDEADLK при этом проблем не создаёт и поэтому
   просто игнорируется. Но утилита mdbx_chk при работе в кооперативном
   (не эксклюзивном) режиме чтения-записи использует именно такой сценарий,
   а возникающую при этом ошибку EDEADLK засчитывает как проблему при
   проверке.

 = В результате, при использовании Valgrind или ASAN утилита mdbx_chk
   запущенная с опциями `-wc` всегда завершается неудачей из-за как минимум
   одной проблемы в ходе проверки. Что внешне выглядит как
   недочет/ошибка/регресс и создает проблемы при автоматизированном
   тестировании.

Добавленный костыль использует atomic-счетчик, который инкремируется до
и декремируется после попытки захвата блокировки изнутри txn_valgrind().
В свою очередь, код обрабатывающий ошибку захвата блокировки, игнорирует
EDEADLK при ненулевом значении счетчика. Активируется костыль только при
сборке с поддержкой Valgrind или включенном ASAN, и не оказывает
никакого влияния в остальных случаях.
2023-10-07 23:37:51 +03:00
Леонид Юрьев (Leonid Yuriev)
45721d4064 mdbx-test: устранение жалобы Valgrind на утечку памяти в одном из тестов.
Перед выходом из теста не разрушался курсор.
2023-10-07 18:28:38 +03:00
Леонид Юрьев (Leonid Yuriev)
6de15514df mdbx: устранение жалобы Valgrind на чтение неинициализированной памяти.
Маркер steady/weak в прототипе/заготовке мета-страницы не
инициализировался, но опосредованно читался кодом проверки
когерентности unified buffer/page cache.

Прочитанное не-инициализированное/случайное значение использовалось в
условии одного из ветвлений, но не оказывало какого-либо влияния, так
как в данном контексте все пути приводят к одному инварианту результата.
2023-10-07 18:27:32 +03:00
Леонид Юрьев (Leonid Yuriev)
215bee9ab7 mdbx: обновление ChangeLog. 2023-10-07 10:22:34 +03:00
Леонид Юрьев (Leonid Yuriev)
7d3f136a3a mdbx-cmake: добавление extra-тестов в область видимости ctest. 2023-10-07 09:08:34 +03:00
Леонид Юрьев (Leonid Yuriev)
eb348ca34c mdbx-test-extra: добавление теста dupfixed_multiple. 2023-10-07 09:08:28 +03:00
Леонид Юрьев (Leonid Yuriev)
cb48ee8f3d mdbx: перезапись в mdbx_put() всех мульти-значений ключа при отсутствии флага MDBX_NOOVERWRITE. 2023-10-07 09:08:28 +03:00
Леонид Юрьев (Leonid Yuriev)
a387284458 mdbx: микро-оптимизация и рефакториг cursor_put_nochecklen().
- удалены переменные-флаги dupdata_flag и do_sub;
 - вместо dupdata_flag используется условие dkey.iov_base != nullptr;
 - вместо do_sub используется условие flags & F_DUPDATA;
 - очищено использование dkey, добавлена инициализация dkey.iov_base в ключевых точках;
 - декларация части переменных перенеса ближе к месту использования.
2023-10-07 09:08:28 +03:00
Леонид Юрьев (Leonid Yuriev)
e7ae8214fd mdbx: исправление cursor_put_nochecklen(MDBX_MULTIPLE). 2023-10-06 21:57:25 +03:00
Леонид Юрьев (Leonid Yuriev)
e195f5bcf7 mdbx++: перегрузка txn::put_multiple() и добавление контроля POD. 2023-10-06 21:56:21 +03:00
Леонид Юрьев (Leonid Yuriev)
c256e8358c mdbx++: добавление slice::as_pod<typename>(). 2023-10-06 12:07:38 +03:00
Леонид Юрьев (Leonid Yuriev)
bc6d320bb2 mdbx: исправление несущественных предупреждений при MDBX_ENABLE_PROFGC=ON.
Thanks @Alain (reported via https://t.me/libmdbx).
2023-10-04 08:18:10 +03:00
Леонид Юрьев (Leonid Yuriev)
7b12e7323f mdbx: выпуск 0.12.7 "Артек"
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов,
в день основания международного детского центра [«Арте́к»](https://ru.wikipedia.org/wiki/Артек).

Исправления и доработки:
------------------------

  - Исправление опечатки в имени переменной внутри `mdbx_env_turn_for_recovery()`.
  - Обходное решение проблем сборки посредством GCC с использование опций `-m32 -arch=i686 -Ofast`.
  - Доработка режима "восстановления" БД и переключения на заданную мета-страницу.

  Более подробная информация в [ChangeLog](https://libmdbx.dqdkfa.ru/md__change_log.html).

Мелочи:
-------

 - Незначительное уточнение CMake-пробника для `std::filesystem`,
   проверяющего необходимость линковки с дополнительными библиотеками C++.
 - Устранение минорных предупреждений старых компиляторов в тестах.
 - Устранение причины ложно-позитивного предупреждения новых версий GCC в C++ API.
 - Исправление ссылки на репозиторий бенчмарка ioarena.
 - Добавление перекрестных ссылок в doxygen-документацию по C++ API.
 - Уточнение ограничений в разделе [Restrictions & Caveats](https://libmdbx.dqdkfa.ru/intro.html#restrictions).
 - Исправление ссылок на описание `mdbx_canary_put()`.

14 files changed, 222 insertions(+), 56 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
2023-06-16 20:04:01 +03:00
Леонид Юрьев (Leonid Yuriev)
45aa39c68b mdbx: обновление ChangeLog. 2023-06-09 00:04:27 +03:00
Леонид Юрьев (Leonid Yuriev)
d02bdcf2bd mdbx: костыль для GCC при сборке с -m32 -arch=i686 -Ofast.
Обходное решение проблем сборки посредством GCC с использование опций `-m32 -arch=i686 -Ofast`.

Проблема обусловлена ошибкой GCC, из-за которой конструкция `__attribute__((__target__("sse2")))`
не включает полноценное использование инструкций SSE и SSE2, если это не было сделано посредством
опций командной строки, но была использована опция `-Ofast`.

В результате сборка заканчивалась сообщением об ошибке:
    gcc/i686-buildroot-linux-gnu/12.2.0/include/xmmintrin.h: In function 'diffcmp2mask_sse2':
    gcc/i686-buildroot-linux-gnu/12.2.0/include/xmmintrin.h:814:1: error: inlining failed in call to 'always_inline' '_mm_movemask_ps': target specific option mismatch
      814 | _mm_movemask_ps (__m128 __A)
2023-06-09 00:02:31 +03:00
Леонид Юрьев (Leonid Yuriev)
5561cec9c5 mdbx: дополнительный static_assert для контроля выравнивания 64-битного atomic-типа. 2023-06-09 00:02:31 +03:00
Леонид Юрьев (Leonid Yuriev)
ff6674b377 mdbx: не делаем неявных обновлений БД (изменения размера или статуса мета-страниц) в режиме восстановления.
Это позволяет обезопасить БД (снизить шанс её разрушения) если
пользователь при попытке восстановления, либо просто в качестве
эксперимента, задал утилите `mdbx_chk` неверную или опасную комбинацию
параметров.

При этом обычная проверка, как и явное переключение мета-страниц,
работают по-прежнему.
2023-06-09 00:02:31 +03:00
Леонид Юрьев (Leonid Yuriev)
ca6f04c52a mdbx: не учитываем geo.next при сверке геометрии после открытия БД.
Полная сверка геометрии на совпадение (включая geo.next) не является
ошибкой, но может приводить к выводу бессмысленного предупреждения о
пропуске обновлении/перезаписи геометрии при открытии БД в режиме
восстановления (с явным указанием мета-страницы).
2023-06-09 00:02:31 +03:00
Леонид Юрьев (Leonid Yuriev)
db6cf469c9 mdbx: доработка mdbx_env_turn_for_recovery() чтобы не обновлять мета-страницы при отсутствии изменений. 2023-06-09 00:02:31 +03:00
Леонид Юрьев (Leonid Yuriev)
d516e903d4 mdbx: исправление очепятки в mdbx_env_turn_for_recovery().
Исправление опечатки в имени переменной внутри `mdbx_env_turn_for_recovery()`,
что приводило к неверному поведению в некоторых ситуациях.

С точки зрения пользователя, с учетом актуальных сценариев использования
утилиты `mdbx_chk`, был только один специфический/редкий сценарий
проявления ошибки/проблемы - когда выполнялась проверка и активация
слабой/weak мета-страницы с НЕ-последней транзакцией после системной
аварии машины, где БД использовалась в хрупком/небезопасном режиме.
В сценарии, при успешной проверке целевой страницы и её последующей
активации выводилось сообщение об ошибке, связанной со срабатыванием
механизма контроля не-когерентности кэша файловой системы и отображенных
в ОЗУ данных БД. При этом БД успешно восстанавливалось и не было
каких-либо негативных последствия, кроме самого сообщения об ошибке.

Технически же ошибка проявлялась при "переключении" на мета-страницу,
когда у хотя-бы одной из двух других мета-страниц номер транзакции был
больше:

  * Если содержимое других мета-страниц было корректным, а номера
    связанных транзакций были больше, то результирующий номер транзакции в
    целевой/активируемой мета-страницы устанавливается без учета этих
    мета-страниц и мог быть меньше-или-равным.

  * В результате, если такие мета-страницы были в статусе слабых/weak, то
    при закрытии БД после переключения могла срабатывать защита от
    не-когерентности unified buffer/page cache, а в отладочных сборках могла
    срабатывать assert-проверка.

  * Если же такие мета-страницы были в статусе сильных/steady, то
    переключение на новую мета-страницу могло не давать эффекта либо
    приводить к появлению двух мета-страниц с одинаковым номером транзакции,
    что является ошибочной ситуацией.
2023-06-09 00:01:41 +03:00
Леонид Юрьев (Leonid Yuriev)
7aaae2ecd5 mdbx-doc: исправление ссылок на mdbx_canary_put(). 2023-06-01 08:48:38 +03:00
Леонид Юрьев (Leonid Yuriev)
bf1c753be3 mdbx: обновление ChangeLog. 2023-05-26 18:10:47 +03:00
Леонид Юрьев (Leonid Yuriev)
79edab2adf mdbx-doc: уточнение ограничений в разделе "Restrictions & Caveats". 2023-05-25 12:54:55 +03:00
Леонид Юрьев (Leonid Yuriev)
37792cc568 mdbx: обновление ChangeLog. 2023-05-23 15:45:27 +03:00
Леонид Юрьев (Leonid Yuriev)
e8d2a5bd09 mdbx++: добавление пары перекрестных ссылок в doxygen-документацию. 2023-05-23 15:35:36 +03:00
Леонид Юрьев (Leonid Yuriev)
2c2612ba23 mdbx: fix link to ioarena repo. 2023-05-14 15:57:28 +03:00
Леонид Юрьев (Leonid Yuriev)
60b483025c mdbx++: устранение ложно-позитивного предупреждения новых версий GCC. 2023-05-14 01:23:48 +03:00
Леонид Юрьев (Leonid Yuriev)
2abf80a199 mdbx-test-extra: устранение минорных предупреждений старых компиляторов. 2023-05-14 01:07:15 +03:00
Леонид Юрьев (Leonid Yuriev)
4fd21d2f7b mdbx-cmake: незначительное уточнение пробника для std::filesystem. 2023-05-14 01:06:52 +03:00
64 changed files with 2237 additions and 855 deletions

View File

@@ -1,5 +1,5 @@
##
## Copyright 2020-2023 Leonid Yuriev <leo@yuriev.ru>
## Copyright 2020-2024 Leonid Yuriev <leo@yuriev.ru>
## and other libmdbx authors: please see AUTHORS file.
## All rights reserved.
##
@@ -512,23 +512,25 @@ else()
mark_as_advanced(MDBX_USE_OFDLOCKS)
set(MDBX_AVOID_MSYNC_DEFAULT OFF)
endif()
option(MDBX_AVOID_MSYNC "Controls dirty pages tracking, spilling and persisting in MDBX_WRITEMAP mode" ${MDBX_AVOID_MSYNC_DEFAULT})
add_mdbx_option(MDBX_AVOID_MSYNC "Controls dirty pages tracking, spilling and persisting in MDBX_WRITEMAP mode" ${MDBX_AVOID_MSYNC_DEFAULT})
add_mdbx_option(MDBX_LOCKING "Locking method (Windows=-1, SysV=5, POSIX=1988, POSIX=2001, POSIX=2008, Futexes=1995)" AUTO)
mark_as_advanced(MDBX_LOCKING)
add_mdbx_option(MDBX_TRUST_RTC "Does a system have battery-backed Real-Time Clock or just a fake" AUTO)
mark_as_advanced(MDBX_TRUST_RTC)
option(MDBX_FORCE_ASSERTIONS "Force enable assertion checking" OFF)
option(MDBX_DISABLE_VALIDATION "Disable some checks to reduce an overhead and detection probability of database corruption to a values closer to the LMDB" OFF)
option(MDBX_ENABLE_REFUND "Zerocost auto-compactification during write-transactions" ON)
option(MDBX_ENABLE_MADVISE "Using POSIX' madvise() and/or similar hints" ON)
add_mdbx_option(MDBX_FORCE_ASSERTIONS "Force enable assertion checking" OFF)
add_mdbx_option(MDBX_DISABLE_VALIDATION "Disable some checks to reduce an overhead and detection probability of database corruption to a values closer to the LMDB" OFF)
mark_as_advanced(MDBX_DISABLE_VALIDATION)
add_mdbx_option(MDBX_ENABLE_REFUND "Zerocost auto-compactification during write-transactions" ON)
add_mdbx_option(MDBX_ENABLE_MADVISE "Using POSIX' madvise() and/or similar hints" ON)
if (CMAKE_TARGET_BITNESS GREATER 32)
set(MDBX_BIGFOOT_DEFAULT ON)
else()
set(MDBX_BIGFOOT_DEFAULT OFF)
endif()
option(MDBX_ENABLE_BIGFOOT "Chunking long list of retired pages during huge transactions commit to avoid use sequences of pages" ${MDBX_BIGFOOT_DEFAULT})
option(MDBX_ENABLE_PGOP_STAT "Gathering statistics for page operations" ON)
option(MDBX_ENABLE_PROFGC "Profiling of GC search and updates" OFF)
add_mdbx_option(MDBX_ENABLE_BIGFOOT "Chunking long list of retired pages during huge transactions commit to avoid use sequences of pages" ${MDBX_BIGFOOT_DEFAULT})
add_mdbx_option(MDBX_ENABLE_PGOP_STAT "Gathering statistics for page operations" ON)
add_mdbx_option(MDBX_ENABLE_PROFGC "Profiling of GC search and updates" OFF)
mark_as_advanced(MDBX_ENABLE_PROFGC)
if(NOT MDBX_AMALGAMATED_SOURCE)
if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE_UPPERCASE STREQUAL "DEBUG")

View File

@@ -1,4 +1,4 @@
Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>.
Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
Copyright 2011-2015 Howard Chu, Symas Corp.
Copyright 2015,2016 Peter-Service R&D LLC.
All rights reserved.

View File

@@ -1,9 +1,296 @@
ChangeLog
---------
=========
English version [by Google](https://gitflic-ru.translate.goog/project/erthink/libmdbx/blob?file=ChangeLog.md&_x_tr_sl=ru&_x_tr_tl=en)
and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md).
## v0.12.10 "СЭМ" от 2024-03-12
Поддерживающий выпуск с исправлением обнаруженных ошибок и устранением недочетов
в память Героя России гвардии майора Дмитрия Семёнова с позывным "СЭМ".
```
git diff' stat: 19 commits, 57 files changed, 751 insertions(+), 331 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
Благодарности:
- [Dvir H](https://t.me/Dvirsw) за [сообщение](https://t.me/libmdbx/5368)
об ошибке `MDBX_CORRUPTED` в сценарии работы в режиме `MDBX_DUPFIXED` и нечетной длинной
мульти-значений, с предоставлением точного минимального сценария воспроизведения.
Значимые исправления и доработки:
- Устранение унаследованной от LMDB ошибки приводящей к повреждению БД при использовании `MDBX_DUPFIXED`.
Проблема была выявлена при расширении тестов сценариями с длинными мульти-значениями в режиме `MDBX_DUPFIXED`.
Сообщений о проявлении этой ошибки в эксплуатационных/продуктовых средах не поступало.
На LEAF2-страниц, используемых для компактного хранения мульти-значений фиксированной длины,
выполнялось резервирование места без учета возможности превышения размера страниц,
с последующим переполнением, повреждением структуры БД и/или повреждением содержимого ОЗУ.
Вероятность проявления ошибки существенно увеличивалась с увеличением размера/длины
мульти-значений/дубликатов и уменьшением размера страницы БД. Поэтому при использовании `MDBX_INTEGERDUP`
вероятность проявления близка к нулю, а сценарий такого проявления найти не удалось.
В MDBX ошибка присутствовала с момента отделения проекта от LMDB,
где эта ошибка присутствует более 11 лети, по настоящее время.
- Исправление ложной ошибки `MDBX_CORRUPTED (-30796)` в сценарии работы
в режиме `MDBX_DUPFIXED` и нечетной длинной мульти-значений.
- Исправление недочета корректировки сопутствующих курсоров при разделении страницы
по сценарию добавления пустой страницы слева.
- Доработка `rebalance()` ради уменьшения WAF. Новый функционал, включая
контролируемую пользователем опцию `enum MDBX_option_t`, будет доступен
в выпусках ветки `0.13.x`, а в этом выпуске доработка сводится к тактике
не-вовленичения чистой страницы при нехватке запаса страниц в ходе обновления GC,
за счет ухудшения баланса дерева страниц.
- Устранение упущения приводящего к нелогичной ситуации
`me_dxb_mmap.curren > me_dxb_mmap.limit` при "дребезге" размера БД.
В текущем понимании, последствий кроме срабатывания assert-проверки нет, а
вероятность проявления близка к нулю.
- Исправление в функционале обхода дерева, используемого утилитой
`mdbx_chk`, подсчета места затраченного на выравнивание на вложенной
под-странице, в случае нечетного количества dupfixed-элементов нечетного
размера.
Сообщений о проявлении этой ошибки в эксплуатационных/продуктовых средах не поступало.
- Исправление assert-проверки внутри `check_txn()` для случая завершенных транзакций в режиме `MDBX_NO_TLS`.
Последствий ошибки, кроме срабатывания assert-проверки в отладочных сборках, нет.
- Устранение ошибки при открытии БД на файловой системе только-для-чтения.
- Удалены излишне строгие проверки в утилите `mdbx_chk`, которые
приводили к ложно-позитивным ошибкам при проверке БД после серии
последних доработок. Подробности см в комментариях к коммитам [781b3f64d52b73cbaeb00a55811d1247c25624a8](https://gitflic.ru/project/erthink/libmdbx/commit/781b3f64d52b73cbaeb00a55811d1247c25624a8)
и [0741c81cfd8dc0864fcf55e04192b2207c8f68f7](https://gitflic.ru/project/erthink/libmdbx/commit/0741c81cfd8dc0864fcf55e04192b2207c8f68f7).
Прочее:
- Расширение стохастического теста dupfixed-сценариями.
- Корректировка условия в assert-проверке для `MDBX_TXN_DRAINED_GC`.
- Добавление в jitter-сценарий простого теста сброса и перезапуска читающих транзакций.
- Вынужденное продолжение очистки/рефакторинга унаследованных ребусов в `cursor_put_nochecklen()`.
- Фиксация транзакции при ошибках теста для последующего анализа содержимого БД.
- Сопутствующий рефакторинг `node_shrink()` для ясности исходного кода.
- Приведение в соответствие протоколируемых имен тестов опциям командной строки.
- Добавление cmoke-теста `extra/dupfixed_addodd`.
--------------------------------------------------------------------------------
## v0.12.9 "Ясень-4" от 2023-12-11
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов.
```
git diff' stat: 32 commits, 8 files changed, 667 insertions(+), 401 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
Исправления и доработки:
- Ликвидация зависимости от ранее удаленной опции `MDBX_ENABLE_PREFAULT`, из-за
чего опция `MDBX_ENABLE_MINCORE` не включалась автоматически, что приводило
к не-активации соответствующего улучшения и не-достижению декларируемого уровня
производительности в сценариях использования в режиме `MDBX_WRITEMAP`.
- Исправление авто-установки `MDBX_ENV_CHECKPID` при отключении использования
функционала `madvise()` посредством опции сборки `MDBX_ENABLE_MADVISE=0`.
Из-за чего при поддержке системой `madvise(MADV_DONTFORK)` не включался контроль pid.
- Добавлена проверка переданного ключа на `NULL` при обработке `MDBX_GET_MULTIPLE`.
- Добавлена проверка номеров корневых страниц в `coherency_check()`.
- Обеспечен `const` для начала и конца диапазона в аргументах `mdbx_estimate_range()`.
- Из разрабатываемой версии перенесены не-нарушающие совместимости доработки C++ API:
- добавлен тип `mdbx::cursor::estimation_result`, а поведение методов
`cursor::estimate()` унифицировано с `cursor::move()`;
- для предотвращения незаметного неверного использования API, для инициализации
возвращаемых по ссылке срезов, вместо пустых срезов задействован `slice::invalid()`;
- добавлены дополнительные C++ операторы преобразования к типам C API;
- для совместимости со старыми стандартами C++ и старыми версиями STL перенесены
в public классы `buffer::move_assign_alloc` и `buffer::copy_assign_alloc`;
- добавлен тип `mdbx::default_buffer`;
- для срезов и буферов добавлены методы `hex_decode()`, `base64_decode()`, `base58_decode()`;
- добавлен тип `mdbx::comparator` и функций `mdbx::default_comparator()`;
- добавлены статические методы `buffer::hex()`, `base64()`, `base58()`;
- для транзакций и курсоров добавлены методы `get_/set_context`;
- добавлен метод `cursor::clone()`;
- Поддержка base58 приведена в соответствии с черновиком RFC.
- Переработка/исправление `to_hex()` и `from_hex()`.
- Уменьшение `MDBX_opt_rp_augment_limit` по умолчанию до 1/3 от текущего количества страниц в БД.
Мелочи:
- Удаление устаревших `mdbx_set_compare()` и `mdbx_set_dupsort()`.
- Корректировка определения `MDBX_LAST_ADDED_ERRCODE`.
- Добавление в C++ API забытого исключения `mdbx::duplicated_lck_file`.
- Обновление патча для старых версий buildroot.
- Использование в API `const MDBX_txn` где это возможно.
- Удаление устаревшего упоминания `MDBX_EAGAIN`.
- Проверка pid процесса только в функциях API требующих активной среды/env.
- Исправление опечаток в комментариях, в том числе в doxygen-описании.
- В тестах для совместимости с проблемными версиями glibc и glibc++
устранено использование `std::stoull()`.
--------------------------------------------------------------------------------
## v0.12.8 "Владимир Уткин" от 2023-10-17
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов,
в день 100-летия со дня рождения выдающегося советского и российского ученого и конструктора [Влади́мира Фёдоровича У́ткина](https://ru.wikipedia.org/wiki/Уткин,_Владимир_Фёдорович).
```
git diff' stat: 24 commits, 18 files changed, 624 insertions(+), 94 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
Благодарности:
- [Alain Picard](https://github.com/castortech) за сообщение о проблеме
с обработкой `MDBX_MULTIPLE` и помощь в тестировании.
Исправления и доработки:
- Устранение регресса/ошибки в пути обработки `put(MDBX_MULTIPLE)` при пакетном/оптовом
помещении в БД множественных значений одного ключа (aka multi-value или dupsort).
Проявление проблемы зависит от компилятора и опций оптимизации/кодогенерации, но с большой вероятностью возвращется
ошибка `MDBX_BAD_VALSIZE` (`-30781`), а в отладочных сборках срабатывает проверка `cASSERT(mc, !"Invalid key-size")`.
Сценарии приводящие к другим проявлениям на данный момент не известны.
- Реализована перезапись в `mdbx_put(MDBX_CURRENT)` всех текущих мульти-значений ключа
при отсутствии флага `MDBX_NOOVERWRITE`. Ранее в такой ситуации возвращалась ошибка `MDBX_EMULTIVAL`.
В текущем понимании новое поведение более удобно и не создаёт проблем совместимости с ранее написанным кодом.
- Добавлена возможность использовать `mdbx_cursor_get(MDBX_GET_MULTIPLE)` без предварительной установки
курсора, совмещая операцию пакетного получения данных с позиционированием курсора на передаваемый ключ.
- Микрооптимизация и рефакторинг `cursor_put_nochecklen()` в продолжение исправления
регресса/ошибки в пути обработки `put(MDBX_MULTIPLE)`.
- Уточнение формулировок в описании API, в том числе пояснений о `SIGSEGV`
и недопустимости прямого изменения данных.
Мелочи:
- Исправление несущественных предупреждений при `MDBX_ENABLE_PROFGC=ON`.
- Добавление `slice::as_pod<typename>()` в C++ API.
- Добавление перегрузки `txn::put_multiple()` и контроля POD в C++ API.
- Добавление smoke-теста для `put(MDBX_MULTIPLE)`.
- Добавление дополнительных smoke-тестов в область видимости ctest.
- Устранение жалоб Valgrind на инвариантное чтение неинициализированной памяти
и утечки памяти в одном из тестов.
- Костыль для глушения/игнорирования `EDEADLK` в ряде сценариев при
использовании Valgrind или ASAN. В частности, это устраняет
ложно-негативный результат проверки БД посредством `mdbx_chk -wc`,
т.е. проверку БД в кооперативном (не эксклюзивном) режиме чтения-записи
в сборках с поддержкой Valgrind или включеным ASAN. Для более подробной
информации см. [соответствующий коммит](https://gitflic.ru/project/erthink/libmdbx/commit/1aead6869a7eff1a85e400ab3eeecb4c8b904fe6).
- Доработка `mdbx_dump_val()` используемой для логирования и отладки.
- Устранение предупреждений Valgrind при логировании в отладочных сборках.
- Доработка использования `filesystem` для старых компиляторов.
- Сокращение излишнего вызова `osal_thread_self()`.
- Вывод информации о большинстве mdbx-опций при сборке посредством CMake.
- Добавление определений макросов для Doxygen.
--------------------------------------------------------------------------------
## v0.12.7 "Артек" от 2023-06-16
Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением
недочетов, в день основания международного детского центра [«Арте́к»](https://ru.wikipedia.org/wiki/Артек).
```
14 files changed, 222 insertions(+), 56 deletions(-)
Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
```
Исправления и доработки:
- Исправление опечатки в имени переменной внутри `mdbx_env_turn_for_recovery()`,
что приводило к неверному поведению в некоторых ситуациях.
С точки зрения пользователя, с учетом актуальных сценариев использования
утилиты `mdbx_chk`, был только один специфический/редкий сценарий
проявления ошибки/проблемы - когда выполнялась проверка и активация
слабой/weak мета-страницы с НЕ-последней транзакцией после системной
аварии машины, где БД использовалась в хрупком/небезопасном режиме.
В сценарии, при успешной проверке целевой страницы и её последующей
активации выводилось сообщение об ошибке, связанной со срабатыванием
механизма контроля не-когерентности кэша файловой системы и отображенных
в ОЗУ данных БД. При этом БД успешно восстанавливалось и не было
каких-либо негативных последствия, кроме самого сообщения об ошибке.
Технически же ошибка проявлялась при "переключении" на мета-страницу,
когда у хотя-бы одной из двух других мета-страниц номер транзакции был
больше:
* Если содержимое других мета-страниц было корректным, а номера
связанных транзакций были больше, то результирующий номер транзакции в
целевой/активируемой мета-страницы устанавливается без учета этих
мета-страниц и мог быть меньше-или-равным.
* В результате, если такие мета-страницы были в статусе слабых/weak, то
при закрытии БД после переключения могла срабатывать защита от
не-когерентности unified buffer/page cache, а в отладочных сборках могла
срабатывать assert-проверка.
* Если же такие мета-страницы были в статусе сильных/steady, то
переключение на новую мета-страницу могло не давать эффекта либо
приводить к появлению двух мета-страниц с одинаковым номером транзакции,
что является ошибочной ситуацией.
- Обходное решение проблем сборки посредством GCC с использование опций `-m32 -arch=i686 -Ofast`.
Проблема обусловлена ошибкой GCC, из-за которой конструкция `__attribute__((__target__("sse2")))`
не включает полноценное использование инструкций SSE и SSE2, если это не было сделано посредством
опций командной строки, но была использована опция `-Ofast`. В результате сборка заканчивалась
сообщением об ошибке:
`error: inlining failed in call to 'always_inline' '_mm_movemask_ps': target specific option mismatch`
- Доработка режима "восстановления" БД и переключения на заданную мета-страницу:
* Устранение обновления без необходимости мета-страницы с увеличением номера транзакции;
* Устранение вывода (логирования) бессмысленного/лишнего предупреждения о пропуске обновления геометрии БД;
* Более ожидаемое и безопасное поведение при проверке БД с указанием целевой мета-страницы в режиме чтения-записи.
Теперь при открытии БД посредством `mdbx_env_open_for_recovery()` не
выполняется неявное изменение/обновление БД, в том числе при закрытии
БД. Это позволяет обезопасить БД (снизить шанс её разрушения) если
пользователь при попытке восстановления, либо просто в качестве
эксперимента, задал утилите `mdbx_chk` неверную или опасную комбинацию
параметров. При этом обычная проверка, как и явное переключение
мета-страниц, работают по-прежнему.
Мелочи:
- Незначительное уточнение CMake-пробника для `std::filesystem`,
проверяющего необходимость линковки с дополнительными библиотеками C++.
- Устранение минорных предупреждений старых компиляторов в тестах.
- Устранение причины ложно-позитивного предупреждения новых версий GCC в C++ API.
- Исправление ссылки на репозиторий бенчмарка ioarena.
- Добавление перекрестных ссылок в doxygen-документацию по C++ API.
- Уточнение ограничений в разделе [Restrictions & Caveats](https://libmdbx.dqdkfa.ru/intro.html#restrictions).
- Исправление ссылок на описание `mdbx_canary_put()`.
--------------------------------------------------------------------------------
## v0.12.6 "ЦСКА" от 2023-04-29
@@ -237,7 +524,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
- Добавлен явный выбор `tls_model("local-dynamic")` для обхода проблемы
`relocation R_X86_64_TPOFF32 against FOO cannot be used with -shared`
из-за ошибки в CLANG приводящей к использованию неверного режима `ls_model`.
из-за ошибки в CLANG приводящей к использованию неверного режима `tls_model`.
- Изменение тактики слияния страниц при удалении.
Теперь слияние выполняется преимущественно с уже измененной/грязной страницей.
@@ -354,7 +641,7 @@ Signed-off-by: Леонид Юрьев (Leonid Yuriev) <leo@yuriev.ru>
суммарный размер пары ключ-значение.
- Реализовано использование асинхронной (overlapped) записи в Windows,
включая использования небуфферизированного ввода-вывода и `WriteGather()`.
включая использования небуферизированного ввода-вывода и `WriteGather()`.
Это позволяет сократить накладные расходы и частично обойти проблемы
Windows с низкой производительностью ввода-вывода, включая большие
задержки `FlushFileBuffers()`. Новый код также обеспечивает консолидацию
@@ -483,7 +770,7 @@ New:
- Added the `gcrtime_seconds16dot16` counter to the "Page Operation Statistics" that accumulates time spent for GC searching and reclaiming.
- Copy-with-compactification now clears/zeroes unused gaps inside database pages.
- The `C` and `C++` APIs has been extended and/or refined to simplify using `wchar_t` pathnames.
On Windows the `mdbx_env_openW()`, ``mdbx_env_get_pathW()`()`, `mdbx_env_copyW()`, `mdbx_env_open_for_recoveryW()` are available for now,
On Windows the `mdbx_env_openW()`, `mdbx_env_get_pathW()`, `mdbx_env_copyW()`, `mdbx_env_open_for_recoveryW()` are available for now,
but the `mdbx_env_get_path()` has been replaced in favor of `mdbx_env_get_pathW()`.
- Added explicit error message for Buildroot's Microblaze toolchain maintainers.
- Added `MDBX_MANAGE_BUILD_FLAGS` build options for CMake.

View File

@@ -277,7 +277,7 @@ the user's point of view.
> and up to 30% faster when _libmdbx_ compiled with specific build options
> which downgrades several runtime checks to be match with LMDB behaviour.
>
> These and other results could be easily reproduced with [ioArena](https://abf.io/erthink/ioarena.git) just by `make bench-quartet` command,
> These and other results could be easily reproduced with [ioArena](https://abf.io/erthink/ioarena) just by `make bench-quartet` command,
> including comparisons with [RockDB](https://en.wikipedia.org/wiki/RocksDB)
> and [WiredTiger](https://en.wikipedia.org/wiki/WiredTiger).
@@ -659,7 +659,7 @@ Bindings
Performance comparison
======================
All benchmarks were done in 2015 by [IOArena](https://abf.io/erthink/ioarena.git)
All benchmarks were done in 2015 by [IOArena](https://abf.io/erthink/ioarena)
and multiple [scripts](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015)
runs on Lenovo Carbon-2 laptop, i7-4600U 2.1 GHz (2 physical cores, 4 HyperThreading cores), 8 Gb RAM,
SSD SAMSUNG MZNTD512HAGL-000L1 (DXT23L0Q) 512 Gb.

View File

@@ -1,4 +1,4 @@
## Copyright (c) 2012-2023 Leonid Yuriev <leo@yuriev.ru>.
## Copyright (c) 2012-2024 Leonid Yuriev <leo@yuriev.ru>.
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.
@@ -1039,9 +1039,15 @@ macro(probe_libcxx_filesystem)
#endif
int main(int argc, const char*argv[]) {
fs::path probe(argv[0]);
std::string str(argv[0]);
fs::path probe(str);
if (argc != 1) throw fs::filesystem_error(std::string("fake"), std::error_code());
return fs::is_directory(probe.relative_path());
int r = fs::is_directory(probe.relative_path());
for (auto const& i : fs::directory_iterator(probe)) {
++r;
(void)i;
}
return r;
}
]])
set(LIBCXX_FILESYSTEM "")

View File

@@ -1,4 +1,4 @@
## Copyright (c) 2012-2023 Leonid Yuriev <leo@yuriev.ru>.
## Copyright (c) 2012-2024 Leonid Yuriev <leo@yuriev.ru>.
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
## Copyright (c) 2012-2023 Leonid Yuriev <leo@yuriev.ru>.
## Copyright (c) 2012-2024 Leonid Yuriev <leo@yuriev.ru>.
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.

View File

@@ -2224,14 +2224,24 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED = DOXYGEN MDBX_DECLARE_EXCEPTION \
MDBX_NOTHROW_PURE_FUNCTION MDBX_PURE_FUNCTION \
MDBX_NOTHROW_CONST_FUNCTION \
MDBX_CXX01_CONSTEXPR MDBX_CXX01_CONSTEXPR_VAR \
MDBX_CXX11_CONSTEXPR MDBX_CXX11_CONSTEXPR_VAR \
MDBX_CXX14_CONSTEXPR MDBX_CXX14_CONSTEXPR_VAR \
MDBX_CXX17_CONSTEXPR MDBX_CXX20_CONSTEXPR \
MDBX_CXX17_NOEXCEPT
PREDEFINED = DOXYGEN \
MDBX_CXX20_CONCEPT(CONCEPT,NAME)="CONCEPT NAME" \
MDBX_STD_FILESYSTEM_PATH=::mdbx::filesystem::path \
MDBX_U128_TYPE=uint128_t MDBX_I128_TYPE=int128_t \
MDBX_DECLARE_EXCEPTION(NAME)="struct LIBMDBX_API_TYPE NAME : public exception{NAME(const ::mdbx::error &); virtual ~NAME() noexcept; }" \
MDBX_PURE_FUNCTION=[[gnu::pure]] \
MDBX_NOTHROW_PURE_FUNCTION="[[gnu::pure, gnu::nothrow]]" \
MDBX_CONST_FUNCTION=[[gnu::const]] \
MDBX_NOTHROW_CONST_FUNCTION="[[gnu::const, gnu::nothrow]]" \
MDBX_CXX01_CONSTEXPR=constexpr MDBX_CXX01_CONSTEXPR_VAR=constexpr \
MDBX_CXX11_CONSTEXPR=constexpr MDBX_CXX11_CONSTEXPR_VAR=constexpr \
MDBX_CXX14_CONSTEXPR=constexpr MDBX_CXX14_CONSTEXPR_VAR=constexpr \
MDBX_CXX17_CONSTEXPR=constexpr MDBX_CXX20_CONSTEXPR=constexpr \
MDBX_CXX17_NOEXCEPT=noexcept MDBX_IF_CONSTEXPR=constexpr \
MDBX_CXX20_LIKELY=[[likely]] MDBX_CXX20_UNLIKELY=[[unlikely]] \
MDBX_MAYBE_UNUSED=[[maybe_unused]] \
MDBX_DEPRECATED=[[deprecated]]
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The

View File

@@ -35,10 +35,11 @@ or debugging of a client application while retaining an active read
transaction. LMDB this results in `MDB_MAP_FULL` error and subsequent write
performance degradation.
MDBX mostly solve "long-lived" readers issue by using the Handle-Slow-Readers
\ref MDBX_hsr_func callback which allows to abort long-lived read transactions,
and using the \ref MDBX_LIFORECLAIM mode which addresses subsequent performance degradation.
The "next" version of libmdbx (\ref MithrilDB) will completely solve this.
MDBX mostly solve "long-lived" readers issue by using the
Handle-Slow-Readers \ref MDBX_hsr_func callback which allows to abort
long-lived read transactions, and using the \ref MDBX_LIFORECLAIM mode
which addresses subsequent performance degradation. The "next" version
of libmdbx (\ref MithrilDB) will completely solve this.
- Avoid suspending a process with active transactions. These would then be
"long-lived" as above.
@@ -80,6 +81,19 @@ list of pages to be retired.
Both of these issues will be addressed in MithrilDB.
#### Since v0.12.1 and later
Some aspects related to GC have been refined and improved, in particular:
- The search for free consecutive/adjacent pages through GC has been
significantly speeded, including acceleration using
NOEN/SSE2/AVX2/AVX512 instructions.
- The `Big Foot` feature which significantly reduces GC overhead for
processing large lists of retired pages from huge transactions. Now
libmdbx avoid creating large chunks of PNLs (page number lists) which
required a long sequences of free pages, aka large/overflow pages. Thus
avoiding searching, allocating and storing such sequences inside GC.
## Space reservation
An MDBX database configuration will often reserve considerable unused
@@ -87,6 +101,10 @@ memory address space and maybe file size for future growth. This does
not use actual memory or disk space, but users may need to understand
the difference so they won't be scared off.
However, on 64-bit systems with a relative small amount of RAM, such
reservation can deplete system resources (trigger ENOMEM error, etc)
when setting an inadequately large upper DB size using \ref
mdbx_env_set_geometry() or \ref mdbx::env::geometry. So just avoid this.
## Remote filesystems
Do not use MDBX databases on remote filesystems, even between processes

132
mdbx.h
View File

@@ -25,7 +25,7 @@ _The Future will (be) [Positive](https://www.ptsecurity.com). Всё будет
\section copyright LICENSE & COPYRIGHT
\authors Copyright (c) 2015-2023, Leonid Yuriev <leo@yuriev.ru>
\authors Copyright (c) 2015-2024, Leonid Yuriev <leo@yuriev.ru>
and other _libmdbx_ authors: please see [AUTHORS](./AUTHORS) file.
\copyright Redistribution and use in source and binary forms, with or without
@@ -1876,7 +1876,8 @@ enum MDBX_error_t {
MDBX_BAD_RSLOT = -30783,
/** Transaction is not valid for requested operation,
* e.g. had errored and be must aborted, has a child, or is invalid */
* e.g. had errored and be must aborted, has a child/nested transaction,
* or is invalid */
MDBX_BAD_TXN = -30782,
/** Invalid size or alignment of key or data for target database,
@@ -1936,7 +1937,7 @@ enum MDBX_error_t {
MDBX_DUPLICATED_CLK = -30413,
/* The last of MDBX-added error codes */
MDBX_LAST_ADDED_ERRCODE = MDBX_TXN_OVERLAPPING,
MDBX_LAST_ADDED_ERRCODE = MDBX_DUPLICATED_CLK,
#if defined(_WIN32) || defined(_WIN64)
MDBX_ENODATA = ERROR_HANDLE_EOF,
@@ -2118,7 +2119,8 @@ enum MDBX_option_t {
* growth, or/and to the inability of put long values.
*
* The `MDBX_opt_rp_augment_limit` controls described limit for the current
* process. Default is 262144, it is usually enough for most cases. */
* process. By default this limit adjusted dynamically to 1/3 of current
* quantity of DB pages, which is usually enough for most cases. */
MDBX_opt_rp_augment_limit,
/** \brief Controls the in-process limit to grow a cache of dirty
@@ -2351,7 +2353,6 @@ LIBMDBX_API int mdbx_env_get_option(const MDBX_env *env,
* doesn't exist.
* \retval MDBX_EACCES The user didn't have permission to access
* the environment files.
* \retval MDBX_EAGAIN The environment was locked by another process.
* \retval MDBX_BUSY The \ref MDBX_EXCLUSIVE flag was specified and the
* environment is in use by another process,
* or the current process tries to open environment
@@ -2699,11 +2700,12 @@ MDBX_DEPRECATED LIBMDBX_INLINE_API(int, mdbx_env_info,
* success. The \ref MDBX_RESULT_TRUE means no data pending for flush
* to disk, and 0 otherwise. Some possible errors are:
*
* \retval MDBX_EACCES the environment is read-only.
* \retval MDBX_BUSY the environment is used by other thread
* \retval MDBX_EACCES The environment is read-only.
* \retval MDBX_BUSY The environment is used by other thread
* and `nonblock=true`.
* \retval MDBX_EINVAL an invalid parameter was specified.
* \retval MDBX_EIO an error occurred during synchronization. */
* \retval MDBX_EINVAL An invalid parameter was specified.
* \retval MDBX_EIO An error occurred during the flushing/writing data
* to a storage medium/disk. */
LIBMDBX_API int mdbx_env_sync_ex(MDBX_env *env, bool force, bool nonblock);
/** \brief The shortcut to calling \ref mdbx_env_sync_ex() with
@@ -2846,9 +2848,9 @@ LIBMDBX_INLINE_API(int, mdbx_env_get_syncperiod,
*
* Only a single thread may call this function. All transactions, databases,
* and cursors must already be closed before calling this function. Attempts
* to use any such handles after calling this function will cause a `SIGSEGV`.
* The environment handle will be freed and must not be used again after this
* call.
* to use any such handles after calling this function is UB and would cause
* a `SIGSEGV`. The environment handle will be freed and must not be used again
* after this call.
*
* \param [in] env An environment handle returned by
* \ref mdbx_env_create().
@@ -2878,7 +2880,8 @@ LIBMDBX_INLINE_API(int, mdbx_env_get_syncperiod,
* is expected, i.e. \ref MDBX_env instance was freed in
* proper manner.
*
* \retval MDBX_EIO An error occurred during synchronization. */
* \retval MDBX_EIO An error occurred during the flushing/writing data
* to a storage medium/disk. */
LIBMDBX_API int mdbx_env_close_ex(MDBX_env *env, bool dont_sync);
/** \brief The shortcut to calling \ref mdbx_env_close_ex() with
@@ -3918,7 +3921,8 @@ LIBMDBX_API int mdbx_txn_commit_ex(MDBX_txn *txn, MDBX_commit_latency *latency);
* by current thread.
* \retval MDBX_EINVAL Transaction handle is NULL.
* \retval MDBX_ENOSPC No more disk space.
* \retval MDBX_EIO A system-level I/O error occurred.
* \retval MDBX_EIO An error occurred during the flushing/writing
* data to a storage medium/disk.
* \retval MDBX_ENOMEM Out of memory. */
LIBMDBX_INLINE_API(int, mdbx_txn_commit, (MDBX_txn * txn)) {
return mdbx_txn_commit_ex(txn, NULL);
@@ -4031,7 +4035,7 @@ LIBMDBX_API int mdbx_txn_renew(MDBX_txn *txn);
/** \brief The fours integers markers (aka "canary") associated with the
* environment.
* \ingroup c_crud
* \see mdbx_canary_set()
* \see mdbx_canary_put()
* \see mdbx_canary_get()
*
* The `x`, `y` and `z` values could be set by \ref mdbx_canary_put(), while the
@@ -4069,10 +4073,10 @@ LIBMDBX_API int mdbx_canary_put(MDBX_txn *txn, const MDBX_canary *canary);
/** \brief Returns fours integers markers (aka "canary") associated with the
* environment.
* \ingroup c_crud
* \see mdbx_canary_set()
* \see mdbx_canary_put()
*
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
* \param [in] canary The address of an MDBX_canary structure where the
* \param [in] canary The address of an \ref MDBX_canary structure where the
* information will be copied.
*
* \returns A non-zero error value on failure and 0 on success. */
@@ -4084,9 +4088,9 @@ LIBMDBX_API int mdbx_canary_get(const MDBX_txn *txn, MDBX_canary *canary);
* \see mdbx_get_datacmp \see mdbx_dcmp()
*
* \anchor avoid_custom_comparators
* It is recommend not using custom comparison functions, but instead
* converting the keys to one of the forms that are suitable for built-in
* comparators (for instance take look to the \ref value2key).
* \deprecated It is recommend not using custom comparison functions, but
* instead converting the keys to one of the forms that are suitable for
* built-in comparators (for instance take look to the \ref value2key).
* The reasons to not using custom comparators are:
* - The order of records could not be validated without your code.
* So `mdbx_chk` utility will reports "wrong order" errors
@@ -4289,8 +4293,8 @@ mdbx_int64_from_key(const MDBX_val);
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread.
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *stat,
size_t bytes);
LIBMDBX_API int mdbx_dbi_stat(const MDBX_txn *txn, MDBX_dbi dbi,
MDBX_stat *stat, size_t bytes);
/** \brief Retrieve depth (bitmask) information of nested dupsort (multi-value)
* B+trees for given database.
@@ -4307,7 +4311,7 @@ LIBMDBX_API int mdbx_dbi_stat(MDBX_txn *txn, MDBX_dbi dbi, MDBX_stat *stat,
* by current thread.
* \retval MDBX_EINVAL An invalid parameter was specified.
* \retval MDBX_RESULT_TRUE The dbi isn't a dupsort (multi-value) database. */
LIBMDBX_API int mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi,
LIBMDBX_API int mdbx_dbi_dupsort_depthmask(const MDBX_txn *txn, MDBX_dbi dbi,
uint32_t *mask);
/** \brief DBI state bits returted by \ref mdbx_dbi_flags_ex()
@@ -4316,7 +4320,7 @@ LIBMDBX_API int mdbx_dbi_dupsort_depthmask(MDBX_txn *txn, MDBX_dbi dbi,
enum MDBX_dbi_state_t {
/** DB was written in this txn */
MDBX_DBI_DIRTY = 0x01,
/** Named-DB record is older than txnID */
/** Cached Named-DB record is older than txnID */
MDBX_DBI_STALE = 0x02,
/** Named-DB handle opened in this txn */
MDBX_DBI_FRESH = 0x04,
@@ -4339,13 +4343,13 @@ DEFINE_ENUM_FLAG_OPERATORS(MDBX_dbi_state_t)
* \param [out] state Address where the state will be returned.
*
* \returns A non-zero error value on failure and 0 on success. */
LIBMDBX_API int mdbx_dbi_flags_ex(MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags,
unsigned *state);
LIBMDBX_API int mdbx_dbi_flags_ex(const MDBX_txn *txn, MDBX_dbi dbi,
unsigned *flags, unsigned *state);
/** \brief The shortcut to calling \ref mdbx_dbi_flags_ex() with `state=NULL`
* for discarding it result.
* \ingroup c_statinfo */
LIBMDBX_INLINE_API(int, mdbx_dbi_flags,
(MDBX_txn * txn, MDBX_dbi dbi, unsigned *flags)) {
(const MDBX_txn *txn, MDBX_dbi dbi, unsigned *flags)) {
unsigned state;
return mdbx_dbi_flags_ex(txn, dbi, flags, &state);
}
@@ -4398,9 +4402,14 @@ LIBMDBX_API int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, bool del);
* items requires the use of \ref mdbx_cursor_get().
*
* \note The memory pointed to by the returned values is owned by the
* database. The caller need not dispose of the memory, and may not
* modify it in any way. For values returned in a read-only transaction
* any modification attempts will cause a `SIGSEGV`.
* database. The caller MUST not dispose of the memory, and MUST not modify it
* in any way regardless in a read-only nor read-write transactions!
* For case a database opened without the \ref MDBX_WRITEMAP modification
* attempts likely will cause a `SIGSEGV`. However, when a database opened with
* the \ref MDBX_WRITEMAP or in case values returned inside read-write
* transaction are located on a "dirty" (modified and pending to commit) pages,
* such modification will silently accepted and likely will lead to DB and/or
* data corruption.
*
* \note Values returned from the database are valid only until a
* subsequent update operation, or the end of the transaction.
@@ -4416,7 +4425,7 @@ LIBMDBX_API int mdbx_drop(MDBX_txn *txn, MDBX_dbi dbi, bool del);
* by current thread.
* \retval MDBX_NOTFOUND The key was not in the database.
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
LIBMDBX_API int mdbx_get(const MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
MDBX_val *data);
/** \brief Get items from a database
@@ -4449,7 +4458,7 @@ LIBMDBX_API int mdbx_get(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
* by current thread.
* \retval MDBX_NOTFOUND The key was not in the database.
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
LIBMDBX_API int mdbx_get_ex(const MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
MDBX_val *data, size_t *values_count);
/** \brief Get equal or great item from a database.
@@ -4480,7 +4489,7 @@ LIBMDBX_API int mdbx_get_ex(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key,
* by current thread.
* \retval MDBX_NOTFOUND The key was not in the database.
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_get_equal_or_great(MDBX_txn *txn, MDBX_dbi dbi,
LIBMDBX_API int mdbx_get_equal_or_great(const MDBX_txn *txn, MDBX_dbi dbi,
MDBX_val *key, MDBX_val *data);
/** \brief Store items into a database.
@@ -4650,7 +4659,7 @@ LIBMDBX_API int mdbx_replace_ex(MDBX_txn *txn, MDBX_dbi dbi,
LIBMDBX_API int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
const MDBX_val *data);
/** \brief Create a cursor handle but not bind it to transaction nor DBI handle.
/** \brief Create a cursor handle but not bind it to transaction nor DBI-handle.
* \ingroup c_cursors
*
* A cursor cannot be used when its database handle is closed. Nor when its
@@ -4674,7 +4683,7 @@ LIBMDBX_API int mdbx_del(MDBX_txn *txn, MDBX_dbi dbi, const MDBX_val *key,
* \returns Created cursor handle or NULL in case out of memory. */
LIBMDBX_API MDBX_cursor *mdbx_cursor_create(void *context);
/** \brief Set application information associated with the \ref MDBX_cursor.
/** \brief Set application information associated with the cursor.
* \ingroup c_cursors
* \see mdbx_cursor_get_userctx()
*
@@ -4697,11 +4706,11 @@ LIBMDBX_API int mdbx_cursor_set_userctx(MDBX_cursor *cursor, void *ctx);
MDBX_NOTHROW_PURE_FUNCTION LIBMDBX_API void *
mdbx_cursor_get_userctx(const MDBX_cursor *cursor);
/** \brief Bind cursor to specified transaction and DBI handle.
/** \brief Bind cursor to specified transaction and DBI-handle.
* \ingroup c_cursors
*
* Using of the `mdbx_cursor_bind()` is equivalent to calling
* \ref mdbx_cursor_renew() but with specifying an arbitrary dbi handle.
* \ref mdbx_cursor_renew() but with specifying an arbitrary DBI-handle.
*
* A cursor may be associated with a new transaction, and referencing a new or
* the same database handle as it was created with. This may be done whether the
@@ -4715,14 +4724,14 @@ mdbx_cursor_get_userctx(const MDBX_cursor *cursor);
*
* \param [in] txn A transaction handle returned by \ref mdbx_txn_begin().
* \param [in] dbi A database handle returned by \ref mdbx_dbi_open().
* \param [out] cursor A cursor handle returned by \ref mdbx_cursor_create().
* \param [in] cursor A cursor handle returned by \ref mdbx_cursor_create().
*
* \returns A non-zero error value on failure and 0 on success,
* some possible errors are:
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread.
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *cursor,
LIBMDBX_API int mdbx_cursor_bind(const MDBX_txn *txn, MDBX_cursor *cursor,
MDBX_dbi dbi);
/** \brief Create a cursor handle for the specified transaction and DBI handle.
@@ -4755,7 +4764,7 @@ LIBMDBX_API int mdbx_cursor_bind(MDBX_txn *txn, MDBX_cursor *cursor,
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread.
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi,
LIBMDBX_API int mdbx_cursor_open(const MDBX_txn *txn, MDBX_dbi dbi,
MDBX_cursor **cursor);
/** \brief Close a cursor handle.
@@ -4774,15 +4783,14 @@ LIBMDBX_API int mdbx_cursor_open(MDBX_txn *txn, MDBX_dbi dbi,
* or \ref mdbx_cursor_create(). */
LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
/** \brief Renew a cursor handle.
/** \brief Renew a cursor handle for use within the given transaction.
* \ingroup c_cursors
*
* The cursor may be associated with a new transaction, and referencing a new or
* the same database handle as it was created with. This may be done whether the
* previous transaction is live or dead.
* A cursor may be associated with a new transaction whether the previous
* transaction is running or finished.
*
* Using of the `mdbx_cursor_renew()` is equivalent to calling
* \ref mdbx_cursor_bind() with the DBI handle that previously
* \ref mdbx_cursor_bind() with the DBI-handle that previously
* the cursor was used with.
*
* \note In contrast to LMDB, the MDBX allow any cursor to be re-used by using
@@ -4796,8 +4804,10 @@ LIBMDBX_API void mdbx_cursor_close(MDBX_cursor *cursor);
* some possible errors are:
* \retval MDBX_THREAD_MISMATCH Given transaction is not owned
* by current thread.
* \retval MDBX_EINVAL An invalid parameter was specified. */
LIBMDBX_API int mdbx_cursor_renew(MDBX_txn *txn, MDBX_cursor *cursor);
* \retval MDBX_EINVAL An invalid parameter was specified.
* \retval MDBX_BAD_DBI The cursor was not bound to a DBI-handle
* or such a handle became invalid. */
LIBMDBX_API int mdbx_cursor_renew(const MDBX_txn *txn, MDBX_cursor *cursor);
/** \brief Return the cursor's transaction handle.
* \ingroup c_cursors
@@ -4834,6 +4844,16 @@ LIBMDBX_API int mdbx_cursor_copy(const MDBX_cursor *src, MDBX_cursor *dest);
* to which data refers.
* \see mdbx_get()
*
* \note The memory pointed to by the returned values is owned by the
* database. The caller MUST not dispose of the memory, and MUST not modify it
* in any way regardless in a read-only nor read-write transactions!
* For case a database opened without the \ref MDBX_WRITEMAP modification
* attempts likely will cause a `SIGSEGV`. However, when a database opened with
* the \ref MDBX_WRITEMAP or in case values returned inside read-write
* transaction are located on a "dirty" (modified and pending to commit) pages,
* such modification will silently accepted and likely will lead to DB and/or
* data corruption.
*
* \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open().
* \param [in,out] key The key for a retrieved item.
* \param [in,out] data The data of a retrieved item.
@@ -4860,6 +4880,16 @@ LIBMDBX_API int mdbx_cursor_get(MDBX_cursor *cursor, MDBX_val *key,
* array to which `pairs` refers.
* \see mdbx_cursor_get()
*
* \note The memory pointed to by the returned values is owned by the
* database. The caller MUST not dispose of the memory, and MUST not modify it
* in any way regardless in a read-only nor read-write transactions!
* For case a database opened without the \ref MDBX_WRITEMAP modification
* attempts likely will cause a `SIGSEGV`. However, when a database opened with
* the \ref MDBX_WRITEMAP or in case values returned inside read-write
* transaction are located on a "dirty" (modified and pending to commit) pages,
* such modification will silently accepted and likely will lead to DB and/or
* data corruption.
*
* \param [in] cursor A cursor handle returned by \ref mdbx_cursor_open().
* \param [out] count The number of key and value item returned, on success
* it always be the even because the key-value
@@ -5156,9 +5186,11 @@ LIBMDBX_API int mdbx_estimate_move(const MDBX_cursor *cursor, MDBX_val *key,
* \param [out] distance_items A pointer to store range estimation result.
*
* \returns A non-zero error value on failure and 0 on success. */
LIBMDBX_API int mdbx_estimate_range(MDBX_txn *txn, MDBX_dbi dbi,
MDBX_val *begin_key, MDBX_val *begin_data,
MDBX_val *end_key, MDBX_val *end_data,
LIBMDBX_API int mdbx_estimate_range(const MDBX_txn *txn, MDBX_dbi dbi,
const MDBX_val *begin_key,
const MDBX_val *begin_data,
const MDBX_val *end_key,
const MDBX_val *end_data,
ptrdiff_t *distance_items);
/** \brief The EPSILON value for mdbx_estimate_range()

377
mdbx.h++
View File

@@ -1,7 +1,7 @@
/// \file mdbx.h++
/// \brief The libmdbx C++ API header file.
///
/// \author Copyright (c) 2020-2023, Leonid Yuriev <leo@yuriev.ru>.
/// \author Copyright (c) 2020-2024, Leonid Yuriev <leo@yuriev.ru>.
/// \copyright SPDX-License-Identifier: Apache-2.0
///
/// Tested with:
@@ -80,7 +80,8 @@
#if defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L
#include <filesystem>
#elif __has_include(<experimental/filesystem>)
#elif defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L && \
__has_include(<experimental/filesystem>)
#include <experimental/filesystem>
#endif
@@ -361,13 +362,17 @@ using default_allocator = polymorphic_allocator;
using default_allocator = legacy_allocator;
#endif /* __cpp_lib_memory_resource >= 201603L */
/// \brief Default singe-byte string.
template <class ALLOCATOR = legacy_allocator>
/// \brief Default buffer.
using default_buffer = buffer<default_allocator, default_capacity_policy>;
/// \brief Default single-byte string.
template <class ALLOCATOR = default_allocator>
using string = ::std::basic_string<char, ::std::char_traits<char>, ALLOCATOR>;
using filehandle = ::mdbx_filehandle_t;
#if defined(DOXYGEN) || \
(defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L && \
defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L && \
(!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || \
__MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) && \
(!defined(__IPHONE_OS_VERSION_MIN_REQUIRED) || \
@@ -394,6 +399,16 @@ using path = ::std::wstring;
using path = ::std::string;
#endif /* mdbx::path */
#if defined(__SIZEOF_INT128__) || \
(defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128)
#ifndef MDBX_U128_TYPE
#define MDBX_U128_TYPE __uint128_t
#endif /* MDBX_U128_TYPE */
#ifndef MDBX_I128_TYPE
#define MDBX_I128_TYPE __int128_t
#endif /* MDBX_I128_TYPE */
#endif /* __SIZEOF_INT128__ || _INTEGRAL_MAX_BITS >= 128 */
#if __cplusplus >= 201103L || defined(DOXYGEN)
/// \brief Duration in 1/65536 units of second.
using duration = ::std::chrono::duration<unsigned, ::std::ratio<1, 65536>>;
@@ -546,12 +561,14 @@ MDBX_DECLARE_EXCEPTION(something_busy);
MDBX_DECLARE_EXCEPTION(thread_mismatch);
MDBX_DECLARE_EXCEPTION(transaction_full);
MDBX_DECLARE_EXCEPTION(transaction_overlapping);
MDBX_DECLARE_EXCEPTION(duplicated_lck_file);
#undef MDBX_DECLARE_EXCEPTION
[[noreturn]] LIBMDBX_API void throw_too_small_target_buffer();
[[noreturn]] LIBMDBX_API void throw_max_length_exceeded();
[[noreturn]] LIBMDBX_API void throw_out_range();
[[noreturn]] LIBMDBX_API void throw_allocators_mismatch();
[[noreturn]] LIBMDBX_API void throw_bad_value_size();
static MDBX_CXX14_CONSTEXPR size_t check_length(size_t bytes);
static MDBX_CXX14_CONSTEXPR size_t check_length(size_t headroom,
size_t payload);
@@ -719,6 +736,8 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val {
slice &operator=(const slice &) noexcept = default;
inline slice &operator=(slice &&src) noexcept;
inline slice &operator=(::MDBX_val &&src);
operator MDBX_val *() noexcept { return this; }
operator const MDBX_val *() const noexcept { return this; }
#if defined(DOXYGEN) || \
(defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L)
@@ -1029,6 +1048,35 @@ struct LIBMDBX_API_TYPE slice : public ::MDBX_val {
return slice(size_t(-1));
}
template <typename POD> MDBX_CXX14_CONSTEXPR POD as_pod() const {
static_assert(::std::is_standard_layout<POD>::value &&
!::std::is_pointer<POD>::value,
"Must be a standard layout type!");
if (MDBX_LIKELY(size() == sizeof(POD)))
MDBX_CXX20_LIKELY {
POD r;
memcpy(&r, data(), sizeof(r));
return r;
}
throw_bad_value_size();
}
#ifdef MDBX_U128_TYPE
MDBX_U128_TYPE as_uint128() const;
#endif /* MDBX_U128_TYPE */
uint64_t as_uint64() const;
uint32_t as_uint32() const;
uint16_t as_uint16() const;
uint8_t as_uint8() const;
#ifdef MDBX_I128_TYPE
MDBX_I128_TYPE as_int128() const;
#endif /* MDBX_I128_TYPE */
int64_t as_int64() const;
int32_t as_int32() const;
int16_t as_int16() const;
int8_t as_int8() const;
protected:
MDBX_CXX11_CONSTEXPR slice(size_t invalid_length) noexcept
: ::MDBX_val({nullptr, invalid_length}) {}
@@ -1279,8 +1327,7 @@ struct LIBMDBX_API to_base58 {
/// \brief Returns the buffer size in bytes needed for
/// [Base58](https://en.wikipedia.org/wiki/Base58) dump of passed slice.
MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept {
const size_t bytes =
source.length() / 8 * 11 + (source.length() % 8 * 43 + 31) / 32;
const size_t bytes = (source.length() * 11 + 7) / 8;
return wrap_width ? bytes + bytes / wrap_width : bytes;
}
@@ -1445,7 +1492,7 @@ struct LIBMDBX_API from_base58 {
/// [Base58](https://en.wikipedia.org/wiki/Base58) dump from a passed slice to
/// decoded data.
MDBX_CXX11_CONSTEXPR size_t envisage_result_length() const noexcept {
return source.length() / 11 * 8 + source.length() % 11 * 32 / 43;
return source.length() /* могут быть все нули кодируемые один-к-одному */;
}
/// \brief Fills the destination with data decoded from
@@ -1533,10 +1580,6 @@ public:
private:
friend class txn;
struct silo;
using move_assign_alloc =
allocation_aware_details::move_assign_alloc<silo, allocator_type>;
using copy_assign_alloc =
allocation_aware_details::copy_assign_alloc<silo, allocator_type>;
using swap_alloc = allocation_aware_details::swap_alloc<silo, allocator_type>;
struct silo /* Empty Base Class Optimization */ : public allocator_type {
MDBX_CXX20_CONSTEXPR const allocator_type &get_allocator() const noexcept {
@@ -2028,6 +2071,11 @@ public:
/// \todo buffer& operator>>(buffer&, ...) for reading (delegated to slice)
/// \todo template<class X> key(X) for encoding keys while writing
using move_assign_alloc =
allocation_aware_details::move_assign_alloc<silo, allocator_type>;
using copy_assign_alloc =
allocation_aware_details::copy_assign_alloc<silo, allocator_type>;
/// \brief Returns the associated allocator.
MDBX_CXX20_CONSTEXPR allocator_type get_allocator() const {
return silo_.get_allocator();
@@ -2292,6 +2340,130 @@ public:
return buffer(::mdbx::slice::wrap(pod), make_reference, allocator);
}
template <typename POD> MDBX_CXX14_CONSTEXPR POD as_pod() const {
return slice_.as_pod<POD>();
}
/// \brief Returns a new buffer with a hexadecimal dump of the slice content.
static buffer hex(const ::mdbx::slice &source, bool uppercase = false,
unsigned wrap_width = 0,
const allocator_type &allocator = allocator_type()) {
return source.template encode_hex<ALLOCATOR, CAPACITY_POLICY>(
uppercase, wrap_width, allocator);
}
/// \brief Returns a new buffer with a
/// [Base58](https://en.wikipedia.org/wiki/Base58) dump of the slice content.
static buffer base58(const ::mdbx::slice &source, unsigned wrap_width = 0,
const allocator_type &allocator = allocator_type()) {
return source.template encode_base58<ALLOCATOR, CAPACITY_POLICY>(wrap_width,
allocator);
}
/// \brief Returns a new buffer with a
/// [Base64](https://en.wikipedia.org/wiki/Base64) dump of the slice content.
static buffer base64(const ::mdbx::slice &source, unsigned wrap_width = 0,
const allocator_type &allocator = allocator_type()) {
return source.template encode_base64<ALLOCATOR, CAPACITY_POLICY>(wrap_width,
allocator);
}
/// \brief Returns a new buffer with a hexadecimal dump of the given pod.
template <typename POD>
static buffer hex(const POD &pod, bool uppercase = false,
unsigned wrap_width = 0,
const allocator_type &allocator = allocator_type()) {
return hex(mdbx::slice::wrap(pod), uppercase, wrap_width, allocator);
}
/// \brief Returns a new buffer with a
/// [Base58](https://en.wikipedia.org/wiki/Base58) dump of the given pod.
template <typename POD>
static buffer base58(const POD &pod, unsigned wrap_width = 0,
const allocator_type &allocator = allocator_type()) {
return base58(mdbx::slice::wrap(pod), wrap_width, allocator);
}
/// \brief Returns a new buffer with a
/// [Base64](https://en.wikipedia.org/wiki/Base64) dump of the given pod.
template <typename POD>
static buffer base64(const POD &pod, unsigned wrap_width = 0,
const allocator_type &allocator = allocator_type()) {
return base64(mdbx::slice::wrap(pod), wrap_width, allocator);
}
/// \brief Returns a new buffer with a hexadecimal dump of the slice content.
buffer encode_hex(bool uppercase = false, unsigned wrap_width = 0,
const allocator_type &allocator = allocator_type()) const {
return slice().template encode_hex<ALLOCATOR, CAPACITY_POLICY>(
uppercase, wrap_width, allocator);
}
/// \brief Returns a new buffer with a
/// [Base58](https://en.wikipedia.org/wiki/Base58) dump of the slice content.
buffer
encode_base58(unsigned wrap_width = 0,
const allocator_type &allocator = allocator_type()) const {
return slice().template encode_base58<ALLOCATOR, CAPACITY_POLICY>(
wrap_width, allocator);
}
/// \brief Returns a new buffer with a
/// [Base64](https://en.wikipedia.org/wiki/Base64) dump of the slice content.
buffer
encode_base64(unsigned wrap_width = 0,
const allocator_type &allocator = allocator_type()) const {
return slice().template encode_base64<ALLOCATOR, CAPACITY_POLICY>(
wrap_width, allocator);
}
/// \brief Decodes hexadecimal dump from the slice content to returned buffer.
static buffer hex_decode(const ::mdbx::slice &source,
bool ignore_spaces = false,
const allocator_type &allocator = allocator_type()) {
return source.template hex_decode<ALLOCATOR, CAPACITY_POLICY>(ignore_spaces,
allocator);
}
/// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump
/// from the slice content to returned buffer.
static buffer
base58_decode(const ::mdbx::slice &source, bool ignore_spaces = false,
const allocator_type &allocator = allocator_type()) {
return source.template base58_decode<ALLOCATOR, CAPACITY_POLICY>(
ignore_spaces, allocator);
}
/// \brief Decodes [Base64](https://en.wikipedia.org/wiki/Base64) dump
/// from the slice content to returned buffer.
static buffer
base64_decode(const ::mdbx::slice &source, bool ignore_spaces = false,
const allocator_type &allocator = allocator_type()) {
return source.template base64_decode<ALLOCATOR, CAPACITY_POLICY>(
ignore_spaces, allocator);
}
/// \brief Decodes hexadecimal dump
/// from the buffer content to new returned buffer.
buffer hex_decode(bool ignore_spaces = false,
const allocator_type &allocator = allocator_type()) const {
return hex_decode(slice(), ignore_spaces, allocator);
}
/// \brief Decodes [Base58](https://en.wikipedia.org/wiki/Base58) dump
/// from the buffer content to new returned buffer.
buffer
base58_decode(bool ignore_spaces = false,
const allocator_type &allocator = allocator_type()) const {
return base58_decode(slice(), ignore_spaces, allocator);
}
/// \brief Decodes [Base64](https://en.wikipedia.org/wiki/Base64) dump
/// from the buffer content to new returned buffer.
buffer
base64_decode(bool ignore_spaces = false,
const allocator_type &allocator = allocator_type()) const {
return base64_decode(slice(), ignore_spaces, allocator);
}
/// \brief Reserves storage space.
void reserve(size_t wanna_headroom, size_t wanna_tailroom) {
wanna_headroom = ::std::min(::std::max(headroom(), wanna_headroom),
@@ -2936,6 +3108,7 @@ struct LIBMDBX_API_TYPE map_handle {
map_handle(const map_handle &) noexcept = default;
map_handle &operator=(const map_handle &) noexcept = default;
operator bool() const noexcept { return dbi != 0; }
operator MDBX_dbi() const { return dbi; }
using flags = ::MDBX_db_flags_t;
using state = ::MDBX_dbi_state_t;
@@ -2961,6 +3134,14 @@ struct LIBMDBX_API_TYPE map_handle {
};
};
using comparator = ::MDBX_cmp_func *;
inline comparator default_comparator(key_mode mode) noexcept {
return ::mdbx_get_keycmp(static_cast<MDBX_db_flags_t>(mode));
}
inline comparator default_comparator(value_mode mode) noexcept {
return ::mdbx_get_keycmp(static_cast<MDBX_db_flags_t>(mode));
}
/// \brief Key-value pairs put mode.
enum put_mode {
insert_unique = MDBX_NOOVERWRITE, ///< Insert only unique keys.
@@ -3000,7 +3181,11 @@ public:
//----------------------------------------------------------------------------
/// Database geometry for size management.
/// \brief Database geometry for size management.
/// \see env_managed::create_parameters
/// \see env_managed::env_managed(const ::std::string &pathname, const
/// create_parameters &, const operate_parameters &, bool accede)
struct LIBMDBX_API_TYPE geometry {
enum : int64_t {
default_value = -1, ///< Means "keep current or use default"
@@ -3394,7 +3579,7 @@ public:
inline void *get_context() const noexcept;
/// \brief Sets the application context associated with the environment.
inline env &set_context(void *);
inline env &set_context(void *your_context);
/// \brief Sets threshold to force flush the data buffers to disk, for
/// non-sync durability modes.
@@ -3659,6 +3844,8 @@ public:
bool accede = true);
/// \brief Additional parameters for creating a new database.
/// \see env_managed(const ::std::string &pathname, const create_parameters &,
/// const operate_parameters &, bool accede)
struct create_parameters {
env::geometry geometry;
mdbx_mode_t file_mode_bits{0640};
@@ -3750,6 +3937,12 @@ public:
/// \brief Return the transaction's ID.
inline uint64_t id() const;
/// \brief Returns the application context associated with the transaction.
inline void *get_context() const noexcept;
/// \brief Sets the application context associated with the transaction.
inline txn &set_context(void *your_context);
/// \brief Checks whether the given data is on a dirty page.
inline bool is_dirty(const void *ptr) const;
@@ -3786,7 +3979,7 @@ public:
txn_managed start_nested();
/// \brief Opens cursor for specified key-value map handle.
inline cursor_managed open_cursor(map_handle map);
inline cursor_managed open_cursor(map_handle map) const;
/// \brief Open existing key-value map.
inline map_handle open_map(
@@ -3969,16 +4162,28 @@ public:
size_t values_count, put_mode mode,
bool allow_partial = false);
template <typename VALUE>
size_t put_multiple(map_handle map, const slice &key,
const VALUE *values_array, size_t values_count,
put_mode mode, bool allow_partial = false) {
static_assert(::std::is_standard_layout<VALUE>::value &&
!::std::is_pointer<VALUE>::value &&
!::std::is_array<VALUE>::value,
"Must be a standard layout type!");
return put_multiple(map, key, sizeof(VALUE), values_array, values_count,
mode, allow_partial);
}
template <typename VALUE>
void put_multiple(map_handle map, const slice &key,
const ::std::vector<VALUE> &vector, put_mode mode) {
put_multiple(map, key, sizeof(VALUE), vector.data(), vector.size(), mode,
false);
put_multiple(map, key, vector.data(), vector.size(), mode);
}
inline ptrdiff_t estimate(map_handle map, pair from, pair to) const;
inline ptrdiff_t estimate(map_handle map, slice from, slice to) const;
inline ptrdiff_t estimate_from_first(map_handle map, slice to) const;
inline ptrdiff_t estimate_to_last(map_handle map, slice from) const;
inline ptrdiff_t estimate(map_handle map, const pair &from,
const pair &to) const;
inline ptrdiff_t estimate(map_handle map, const slice &from,
const slice &to) const;
inline ptrdiff_t estimate_from_first(map_handle map, const slice &to) const;
inline ptrdiff_t estimate_to_last(map_handle map, const slice &from) const;
};
/// \brief Managed database transaction.
@@ -4059,6 +4264,7 @@ public:
inline cursor &operator=(cursor &&other) noexcept;
inline cursor(cursor &&other) noexcept;
inline ~cursor() noexcept;
inline cursor_managed clone(void *your_context = nullptr) const;
MDBX_CXX14_CONSTEXPR operator bool() const noexcept;
MDBX_CXX14_CONSTEXPR operator const MDBX_cursor *() const;
MDBX_CXX14_CONSTEXPR operator MDBX_cursor *();
@@ -4067,6 +4273,12 @@ public:
friend MDBX_CXX11_CONSTEXPR bool operator!=(const cursor &a,
const cursor &b) noexcept;
/// \brief Returns the application context associated with the cursor.
inline void *get_context() const noexcept;
/// \brief Sets the application context associated with the cursor.
inline cursor &set_context(void *your_context);
enum move_operation {
first = MDBX_FIRST,
last = MDBX_LAST,
@@ -4091,10 +4303,13 @@ public:
struct move_result : public pair_result {
inline move_result(const cursor &cursor, bool throw_notfound);
inline move_result(cursor &cursor, move_operation operation,
bool throw_notfound);
inline move_result(cursor &cursor, move_operation operation,
const slice &key, bool throw_notfound);
move_result(cursor &cursor, move_operation operation, bool throw_notfound)
: move_result(cursor, operation, slice::invalid(), slice::invalid(),
throw_notfound) {}
move_result(cursor &cursor, move_operation operation, const slice &key,
bool throw_notfound)
: move_result(cursor, operation, key, slice::invalid(),
throw_notfound) {}
inline move_result(cursor &cursor, move_operation operation,
const slice &key, const slice &value,
bool throw_notfound);
@@ -4102,6 +4317,20 @@ public:
move_result &operator=(const move_result &) noexcept = default;
};
struct estimate_result : public pair {
ptrdiff_t approximate_quantity;
estimate_result(const cursor &cursor, move_operation operation)
: estimate_result(cursor, operation, slice::invalid(),
slice::invalid()) {}
estimate_result(const cursor &cursor, move_operation operation,
const slice &key)
: estimate_result(cursor, operation, key, slice::invalid()) {}
inline estimate_result(const cursor &cursor, move_operation operation,
const slice &key, const slice &value);
estimate_result(const estimate_result &) noexcept = default;
estimate_result &operator=(const estimate_result &) noexcept = default;
};
protected:
inline bool move(move_operation operation, MDBX_val *key, MDBX_val *value,
bool throw_notfound) const
@@ -4146,19 +4375,20 @@ public:
inline bool eof() const;
inline bool on_first() const;
inline bool on_last() const;
inline ptrdiff_t estimate(slice key, slice value) const;
inline ptrdiff_t estimate(slice key) const;
inline ptrdiff_t estimate(move_operation operation) const;
inline estimate_result estimate(const slice &key, const slice &value) const;
inline estimate_result estimate(const slice &key) const;
inline estimate_result estimate(move_operation operation) const;
inline estimate_result estimate(move_operation operation, slice &key) const;
//----------------------------------------------------------------------------
/// \brief Renew/bind a cursor with a new transaction and previously used
/// key-value map handle.
inline void renew(::mdbx::txn &txn);
inline void renew(const ::mdbx::txn &txn);
/// \brief Bind/renew a cursor with a new transaction and specified key-value
/// map handle.
inline void bind(::mdbx::txn &txn, ::mdbx::map_handle map_handle);
inline void bind(const ::mdbx::txn &txn, ::mdbx::map_handle map_handle);
/// \brief Returns the cursor's transaction.
inline ::mdbx::txn txn() const;
@@ -4212,7 +4442,8 @@ class LIBMDBX_API_TYPE cursor_managed : public cursor {
public:
/// \brief Creates a new managed cursor with underlying object.
cursor_managed() : cursor_managed(::mdbx_cursor_create(nullptr)) {
cursor_managed(void *your_context = nullptr)
: cursor_managed(::mdbx_cursor_create(your_context)) {
if (MDBX_UNLIKELY(!handle_))
MDBX_CXX20_UNLIKELY error::throw_exception(MDBX_ENOMEM);
}
@@ -5134,7 +5365,7 @@ inline filehandle env::get_filehandle() const {
}
inline MDBX_env_flags_t env::get_flags() const {
unsigned bits;
unsigned bits = 0;
error::success_or_throw(::mdbx_env_get_flags(handle_, &bits));
return MDBX_env_flags_t(bits);
}
@@ -5354,6 +5585,15 @@ MDBX_CXX11_CONSTEXPR bool operator!=(const txn &a, const txn &b) noexcept {
return a.handle_ != b.handle_;
}
inline void *txn::get_context() const noexcept {
return mdbx_txn_get_userctx(handle_);
}
inline txn &txn::set_context(void *ptr) {
error::success_or_throw(::mdbx_txn_set_userctx(handle_, ptr));
return *this;
}
inline bool txn::is_dirty(const void *ptr) const {
int err = ::mdbx_is_dirty(handle_, ptr);
switch (err) {
@@ -5394,7 +5634,7 @@ inline txn::info txn::get_info(bool scan_reader_lock_table) const {
return r;
}
inline cursor_managed txn::open_cursor(map_handle map) {
inline cursor_managed txn::open_cursor(map_handle map) const {
MDBX_cursor *ptr;
error::success_or_throw(::mdbx_cursor_open(handle_, map.dbi, &ptr));
return cursor_managed(ptr);
@@ -5792,28 +6032,32 @@ inline size_t txn::put_multiple(map_handle map, const slice &key,
return args[1].iov_len /* done item count */;
}
inline ptrdiff_t txn::estimate(map_handle map, pair from, pair to) const {
inline ptrdiff_t txn::estimate(map_handle map, const pair &from,
const pair &to) const {
ptrdiff_t result;
error::success_or_throw(mdbx_estimate_range(
handle_, map.dbi, &from.key, &from.value, &to.key, &to.value, &result));
return result;
}
inline ptrdiff_t txn::estimate(map_handle map, slice from, slice to) const {
inline ptrdiff_t txn::estimate(map_handle map, const slice &from,
const slice &to) const {
ptrdiff_t result;
error::success_or_throw(mdbx_estimate_range(handle_, map.dbi, &from, nullptr,
&to, nullptr, &result));
return result;
}
inline ptrdiff_t txn::estimate_from_first(map_handle map, slice to) const {
inline ptrdiff_t txn::estimate_from_first(map_handle map,
const slice &to) const {
ptrdiff_t result;
error::success_or_throw(mdbx_estimate_range(handle_, map.dbi, nullptr,
nullptr, &to, nullptr, &result));
return result;
}
inline ptrdiff_t txn::estimate_to_last(map_handle map, slice from) const {
inline ptrdiff_t txn::estimate_to_last(map_handle map,
const slice &from) const {
ptrdiff_t result;
error::success_or_throw(mdbx_estimate_range(handle_, map.dbi, &from, nullptr,
nullptr, nullptr, &result));
@@ -5824,6 +6068,21 @@ inline ptrdiff_t txn::estimate_to_last(map_handle map, slice from) const {
MDBX_CXX11_CONSTEXPR cursor::cursor(MDBX_cursor *ptr) noexcept : handle_(ptr) {}
inline cursor_managed cursor::clone(void *your_context) const {
cursor_managed clone(your_context);
error::success_or_throw(::mdbx_cursor_copy(handle_, clone.handle_));
return clone;
}
inline void *cursor::get_context() const noexcept {
return mdbx_cursor_get_userctx(handle_);
}
inline cursor &cursor::set_context(void *ptr) {
error::success_or_throw(::mdbx_cursor_set_userctx(handle_, ptr));
return *this;
}
inline cursor &cursor::operator=(cursor &&other) noexcept {
handle_ = other.handle_;
other.handle_ = nullptr;
@@ -5862,22 +6121,8 @@ MDBX_CXX11_CONSTEXPR bool operator!=(const cursor &a,
inline cursor::move_result::move_result(const cursor &cursor,
bool throw_notfound)
: pair_result(key, value, false) {
done = cursor.move(get_current, &key, &value, throw_notfound);
}
inline cursor::move_result::move_result(cursor &cursor,
move_operation operation,
bool throw_notfound)
: pair_result(key, value, false) {
done = cursor.move(operation, &key, &value, throw_notfound);
}
inline cursor::move_result::move_result(cursor &cursor,
move_operation operation,
const slice &key, bool throw_notfound)
: pair_result(key, slice(), false) {
this->done = cursor.move(operation, &this->key, &this->value, throw_notfound);
: pair_result(slice(), slice(), false) {
done = cursor.move(get_current, &this->key, &this->value, throw_notfound);
}
inline cursor::move_result::move_result(cursor &cursor,
@@ -5904,6 +6149,14 @@ inline bool cursor::move(move_operation operation, MDBX_val *key,
}
}
inline cursor::estimate_result::estimate_result(const cursor &cursor,
move_operation operation,
const slice &key,
const slice &value)
: pair(key, value), approximate_quantity(PTRDIFF_MIN) {
approximate_quantity = cursor.estimate(operation, &this->key, &this->value);
}
inline ptrdiff_t cursor::estimate(move_operation operation, MDBX_val *key,
MDBX_val *value) const {
ptrdiff_t result;
@@ -6026,24 +6279,26 @@ inline bool cursor::on_last() const {
return error::boolean_or_throw(::mdbx_cursor_on_last(*this));
}
inline ptrdiff_t cursor::estimate(slice key, slice value) const {
return estimate(multi_exactkey_lowerboundvalue, &key, &value);
inline cursor::estimate_result cursor::estimate(const slice &key,
const slice &value) const {
return estimate_result(*this, multi_exactkey_lowerboundvalue, key, value);
}
inline ptrdiff_t cursor::estimate(slice key) const {
return estimate(key_lowerbound, &key, nullptr);
inline cursor::estimate_result cursor::estimate(const slice &key) const {
return estimate_result(*this, key_lowerbound, key);
}
inline ptrdiff_t cursor::estimate(move_operation operation) const {
slice unused_key;
return estimate(operation, &unused_key, nullptr);
inline cursor::estimate_result
cursor::estimate(move_operation operation) const {
return estimate_result(*this, operation);
}
inline void cursor::renew(::mdbx::txn &txn) {
inline void cursor::renew(const ::mdbx::txn &txn) {
error::success_or_throw(::mdbx_cursor_renew(txn, handle_));
}
inline void cursor::bind(::mdbx::txn &txn, ::mdbx::map_handle map_handle) {
inline void cursor::bind(const ::mdbx::txn &txn,
::mdbx::map_handle map_handle) {
error::success_or_throw(::mdbx_cursor_bind(txn, handle_, map_handle.dbi));
}

View File

@@ -1,7 +1,7 @@
From 790cbdc02c2597650964a564e05fbb5af503adc9 Mon Sep 17 00:00:00 2001
From 3efdf07a80f750c23de126ac80e78fb0545a1b63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=9B=D0=B5=D0=BE=D0=BD=D0=B8=D0=B4=20=D0=AE=D1=80=D1=8C?=
=?UTF-8?q?=D0=B5=D0=B2=20=28Leonid=20Yuriev=29?= <leo@yuriev.ru>
Date: Wed, 19 Apr 2023 13:34:37 +0300
Date: Mon, 23 Oct 2023 18:07:13 +0300
Subject: [PATCH] package/libmdbx: new package (library/database).
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
@@ -15,8 +15,9 @@ This patch adds libmdbx:
in terms of reliability, features and performance.
- https://gitflic.ru/project/erthink/libmdbx
The v0.12.5 "Dynamo" is stable release of frontward _libmdbx_ branch with new superior features
on the day of 100 anniversary of USSR' «Dynamo» sports and fitness society.
The v0.12.8 "Vladimir Utkin" is stable release of frontward _libmdbx_
branch with new superior features on the day of 100 anniversary of the birth
of the outstanding Soviet and Russian scientist and engineer Vladimir Fedorovich Utkin.
The complete ChangeLog: https://gitflic.ru/project/erthink/libmdbx/blob?file=ChangeLog.md
@@ -111,18 +112,18 @@ index 0000000000..a9a4ac45c5
+ !BR2_TOOLCHAIN_GCC_AT_LEAST_4_4
diff --git a/package/libmdbx/libmdbx.hash b/package/libmdbx/libmdbx.hash
new file mode 100644
index 0000000000..7a5b19952e
index 0000000000..82cf28d6a9
--- /dev/null
+++ b/package/libmdbx/libmdbx.hash
@@ -0,0 +1,5 @@
+# Hashes from: https://libmdbx.dqdkfa.ru/release/SHA256SUMS
+sha256 9c3abaaf9079a9518bb7155734817a2e286fffea46f7cc0825dfbd1cf9174075 libmdbx-amalgamated-0.12.5.tar.xz
+sha256 c78c56c53708bbfc519bf53ebf520d1f09d30ee6427a4bedf713316696e671d0 libmdbx-amalgamated-0.12.8.tar.xz
+
+# Locally calculated
+sha256 310fe25c858a9515fc8c8d7d1f24a67c9496f84a91e0a0e41ea9975b1371e569 LICENSE
diff --git a/package/libmdbx/libmdbx.mk b/package/libmdbx/libmdbx.mk
new file mode 100644
index 0000000000..9a196eda60
index 0000000000..d198fe5b22
--- /dev/null
+++ b/package/libmdbx/libmdbx.mk
@@ -0,0 +1,42 @@
@@ -132,7 +133,7 @@ index 0000000000..9a196eda60
+#
+################################################################################
+
+LIBMDBX_VERSION = 0.12.5
+LIBMDBX_VERSION = 0.12.8
+LIBMDBX_SOURCE = libmdbx-amalgamated-$(LIBMDBX_VERSION).tar.xz
+LIBMDBX_SITE = https://libmdbx.dqdkfa.ru/release
+LIBMDBX_SUPPORTS_IN_SOURCE_BUILD = NO
@@ -169,5 +170,5 @@ index 0000000000..9a196eda60
+
+$(eval $(cmake-package))
--
2.34.1
2.42.0

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

1042
src/core.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
@@ -1376,8 +1376,12 @@ struct MDBX_env {
struct MDBX_lockinfo *me_lck;
unsigned me_psize; /* DB page size, initialized from me_os_psize */
unsigned me_leaf_nodemax; /* max size of a leaf-node */
unsigned me_branch_nodemax; /* max size of a branch-node */
uint16_t me_leaf_nodemax; /* max size of a leaf-node */
uint16_t me_branch_nodemax; /* max size of a branch-node */
uint16_t me_subpage_limit;
uint16_t me_subpage_room_threshold;
uint16_t me_subpage_reserve_prereq;
uint16_t me_subpage_reserve_limit;
atomic_pgno_t me_mlocked_pgno;
uint8_t me_psize2log; /* log2 of DB page size */
int8_t me_stuck_meta; /* recovery-only: target meta page or less that zero */
@@ -1482,6 +1486,7 @@ struct MDBX_env {
int me_valgrind_handle;
#endif
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
MDBX_atomic_uint32_t me_ignore_EDEADLK;
pgno_t me_poison_edge;
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
@@ -822,6 +822,11 @@ __cold static int mdbx_ipclock_failed(MDBX_env *env, osal_ipclock_t *ipc,
#error "FIXME"
#endif /* MDBX_LOCKING */
#if defined(MDBX_USE_VALGRIND) || defined(__SANITIZE_ADDRESS__)
if (rc == EDEADLK && atomic_load32(&env->me_ignore_EDEADLK, mo_Relaxed) > 0)
return rc;
#endif /* MDBX_USE_VALGRIND || __SANITIZE_ADDRESS__ */
ERROR("mutex (un)lock failed, %s", mdbx_strerror(err));
if (rc != EDEADLK)
env->me_flags |= MDBX_FATAL_ERROR;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,6 +1,6 @@
.\" Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_CHK 1 "2023-04-29" "MDBX 0.12.6"
.TH MDBX_CHK 1 "2024-03-13" "MDBX 0.12.10"
.SH NAME
mdbx_chk \- MDBX checking tool
.SH SYNOPSIS

View File

@@ -1,8 +1,8 @@
.\" Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_COPY 1 "2023-04-29" "MDBX 0.12.6"
.TH MDBX_COPY 1 "2024-03-13" "MDBX 0.12.10"
.SH NAME
mdbx_copy \- MDBX environment copy tool
.SH SYNOPSIS

View File

@@ -1,7 +1,7 @@
.\" Copyright 2021-2023 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2021-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2014-2021 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_DROP 1 "2023-04-29" "MDBX 0.12.6"
.TH MDBX_DROP 1 "2024-03-13" "MDBX 0.12.10"
.SH NAME
mdbx_drop \- MDBX database delete tool
.SH SYNOPSIS

View File

@@ -1,8 +1,8 @@
.\" Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_DUMP 1 "2023-04-29" "MDBX 0.12.6"
.TH MDBX_DUMP 1 "2024-03-13" "MDBX 0.12.10"
.SH NAME
mdbx_dump \- MDBX environment export tool
.SH SYNOPSIS

View File

@@ -1,8 +1,8 @@
.\" Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_LOAD 1 "2023-04-29" "MDBX 0.12.6"
.TH MDBX_LOAD 1 "2024-03-13" "MDBX 0.12.10"
.SH NAME
mdbx_load \- MDBX environment import tool
.SH SYNOPSIS

View File

@@ -1,8 +1,8 @@
.\" Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>.
.\" Copyright 2015,2016 Peter-Service R&D LLC <http://billing.ru/>.
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.TH MDBX_STAT 1 "2023-04-29" "MDBX 0.12.6"
.TH MDBX_STAT 1 "2024-03-13" "MDBX 0.12.10"
.SH NAME
mdbx_stat \- MDBX environment status tool
.SH SYNOPSIS

View File

@@ -1,5 +1,5 @@
//
// Copyright (c) 2020-2023, Leonid Yuriev <leo@yuriev.ru>.
// Copyright (c) 2020-2024, Leonid Yuriev <leo@yuriev.ru>.
// SPDX-License-Identifier: Apache-2.0
//
// Non-inline part of the libmdbx C++ API
@@ -207,6 +207,44 @@ __cold bug::~bug() noexcept {}
#endif /* Unused*/
struct line_wrapper {
char *line, *ptr;
line_wrapper(char *buf) noexcept : line(buf), ptr(buf) {}
void put(char c, size_t wrap_width) noexcept {
*ptr++ = c;
if (wrap_width && ptr >= wrap_width + line) {
*ptr++ = '\n';
line = ptr;
}
}
void put(const ::mdbx::slice &chunk, size_t wrap_width) noexcept {
if (!wrap_width || wrap_width > (ptr - line) + chunk.length()) {
memcpy(ptr, chunk.data(), chunk.length());
ptr += chunk.length();
} else {
for (size_t i = 0; i < chunk.length(); ++i)
put(chunk.char_ptr()[i], wrap_width);
}
}
};
template <typename TYPE, unsigned INPLACE_BYTES = unsigned(sizeof(void *) * 64)>
struct temp_buffer {
TYPE inplace[(INPLACE_BYTES + sizeof(TYPE) - 1) / sizeof(TYPE)];
const size_t size;
TYPE *const area;
temp_buffer(size_t bytes)
: size((bytes + sizeof(TYPE) - 1) / sizeof(TYPE)),
area((bytes > sizeof(inplace)) ? new TYPE[size] : inplace) {
memset(area, 0, sizeof(TYPE) * size);
}
~temp_buffer() {
if (area != inplace)
delete[] area;
}
TYPE *end() const { return area + size; }
};
} // namespace
//------------------------------------------------------------------------------
@@ -233,6 +271,10 @@ namespace mdbx {
"into an incompatible memory allocation scheme.");
}
[[noreturn]] __cold void throw_bad_value_size() {
throw bad_value_size(MDBX_BAD_VALSIZE);
}
__cold exception::exception(const ::mdbx::error &error) noexcept
: base(error.what()), error_(error) {}
@@ -281,7 +323,7 @@ DEFINE_EXCEPTION(something_busy)
DEFINE_EXCEPTION(thread_mismatch)
DEFINE_EXCEPTION(transaction_full)
DEFINE_EXCEPTION(transaction_overlapping)
DEFINE_EXCEPTION(duplicated_lck_file)
#undef DEFINE_EXCEPTION
__cold const char *error::what() const noexcept {
@@ -367,6 +409,7 @@ __cold void error::throw_exception() const {
CASE_EXCEPTION(thread_mismatch, MDBX_THREAD_MISMATCH);
CASE_EXCEPTION(transaction_full, MDBX_TXN_FULL);
CASE_EXCEPTION(transaction_overlapping, MDBX_TXN_OVERLAPPING);
CASE_EXCEPTION(duplicated_lck_file, MDBX_DUPLICATED_CLK);
#undef CASE_EXCEPTION
default:
if (is_mdbx_error())
@@ -483,6 +526,109 @@ bool slice::is_printable(bool disable_utf8) const noexcept {
return true;
}
#ifdef MDBX_U128_TYPE
MDBX_U128_TYPE slice::as_uint128() const {
static_assert(sizeof(MDBX_U128_TYPE) == 16, "WTF?");
if (size() == 16) {
MDBX_U128_TYPE r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint64();
}
#endif /* MDBX_U128_TYPE */
uint64_t slice::as_uint64() const {
static_assert(sizeof(uint64_t) == 8, "WTF?");
if (size() == 8) {
uint64_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint32();
}
uint32_t slice::as_uint32() const {
static_assert(sizeof(uint32_t) == 4, "WTF?");
if (size() == 4) {
uint32_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint16();
}
uint16_t slice::as_uint16() const {
static_assert(sizeof(uint16_t) == 2, "WTF?");
if (size() == 2) {
uint16_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_uint8();
}
uint8_t slice::as_uint8() const {
static_assert(sizeof(uint8_t) == 1, "WTF?");
if (size() == 1)
return *static_cast<const uint8_t *>(data());
else if (size() == 0)
return 0;
else
MDBX_CXX20_UNLIKELY throw_bad_value_size();
}
#ifdef MDBX_I128_TYPE
MDBX_I128_TYPE slice::as_int128() const {
static_assert(sizeof(MDBX_I128_TYPE) == 16, "WTF?");
if (size() == 16) {
MDBX_I128_TYPE r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int64();
}
#endif /* MDBX_I128_TYPE */
int64_t slice::as_int64() const {
static_assert(sizeof(int64_t) == 8, "WTF?");
if (size() == 8) {
uint64_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int32();
}
int32_t slice::as_int32() const {
static_assert(sizeof(int32_t) == 4, "WTF?");
if (size() == 4) {
int32_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int16();
}
int16_t slice::as_int16() const {
static_assert(sizeof(int16_t) == 2, "WTF?");
if (size() == 2) {
int16_t r;
memcpy(&r, data(), sizeof(r));
return r;
} else
return as_int8();
}
int8_t slice::as_int8() const {
if (size() == 1)
return *static_cast<const int8_t *>(data());
else if (size() == 0)
return 0;
else
MDBX_CXX20_UNLIKELY throw_bad_value_size();
}
//------------------------------------------------------------------------------
char *to_hex::write_bytes(char *__restrict const dest, size_t dest_size) const {
@@ -491,7 +637,7 @@ char *to_hex::write_bytes(char *__restrict const dest, size_t dest_size) const {
auto ptr = dest;
auto src = source.byte_ptr();
const char alphabase = (uppercase ? 'A' : 'a') - 10;
const char alpha_shift = (uppercase ? 'A' : 'a') - '9' - 1;
auto line = ptr;
for (const auto end = source.end_byte_ptr(); src != end; ++src) {
if (wrap_width && size_t(ptr - line) >= wrap_width) {
@@ -500,8 +646,8 @@ char *to_hex::write_bytes(char *__restrict const dest, size_t dest_size) const {
}
const int8_t hi = *src >> 4;
const int8_t lo = *src & 15;
ptr[0] = char(alphabase + hi + (((hi - 10) >> 7) & -7));
ptr[1] = char(alphabase + lo + (((lo - 10) >> 7) & -7));
ptr[0] = char('0' + hi + (((9 - hi) >> 7) & alpha_shift));
ptr[1] = char('0' + lo + (((9 - lo) >> 7) & alpha_shift));
ptr += 2;
assert(ptr <= dest + dest_size);
}
@@ -513,7 +659,7 @@ char *to_hex::write_bytes(char *__restrict const dest, size_t dest_size) const {
MDBX_CXX20_LIKELY {
::std::ostream::sentry sentry(out);
auto src = source.byte_ptr();
const char alphabase = (uppercase ? 'A' : 'a') - 10;
const char alpha_shift = (uppercase ? 'A' : 'a') - '9' - 1;
unsigned width = 0;
for (const auto end = source.end_byte_ptr(); src != end; ++src) {
if (wrap_width && width >= wrap_width) {
@@ -522,8 +668,8 @@ char *to_hex::write_bytes(char *__restrict const dest, size_t dest_size) const {
}
const int8_t hi = *src >> 4;
const int8_t lo = *src & 15;
out.put(char(alphabase + hi + (((hi - 10) >> 7) & -7)));
out.put(char(alphabase + lo + (((lo - 10) >> 7) & -7)));
out.put(char('0' + hi + (((9 - hi) >> 7) & alpha_shift)));
out.put(char('0' + lo + (((9 - lo) >> 7) & alpha_shift)));
width += 2;
}
}
@@ -554,11 +700,11 @@ char *from_hex::write_bytes(char *__restrict const dest,
int8_t hi = src[0];
hi = (hi | 0x20) - 'a';
hi += 10 + ((hi >> 7) & 7);
hi += 10 + ((hi >> 7) & 39);
int8_t lo = src[1];
lo = (lo | 0x20) - 'a';
lo += 10 + ((lo >> 7) & 7);
lo += 10 + ((lo >> 7) & 39);
*ptr++ = hi << 4 | lo;
src += 2;
@@ -601,156 +747,135 @@ enum : signed char {
IL /* invalid */ = -1
};
static const byte b58_alphabet[58] = {
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
#ifndef bswap64
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
static inline uint64_t bswap64(uint64_t v) noexcept {
#if __GNUC_PREREQ(4, 4) || __CLANG_PREREQ(4, 0) || \
__has_builtin(__builtin_bswap64)
return __builtin_bswap64(v);
#elif defined(_MSC_VER) && !defined(__clang__)
return _byteswap_uint64(v);
#elif defined(__bswap_64)
return __bswap_64(v);
#elif defined(bswap_64)
return bswap_64(v);
#if MDBX_WORDBITS > 32
using b58_uint = uint_fast64_t;
#else
return v << 56 | v >> 56 | ((v << 40) & UINT64_C(0x00ff000000000000)) |
((v << 24) & UINT64_C(0x0000ff0000000000)) |
((v << 8) & UINT64_C(0x000000ff00000000)) |
((v >> 8) & UINT64_C(0x00000000ff000000)) |
((v >> 24) & UINT64_C(0x0000000000ff0000)) |
((v >> 40) & UINT64_C(0x000000000000ff00));
using b58_uint = uint_fast32_t;
#endif
}
#endif /* __BYTE_ORDER__ */
#endif /* ifndef bswap64 */
static inline char b58_8to11(uint64_t &v) noexcept {
const unsigned i = unsigned(v % 58);
struct b58_buffer : public temp_buffer<b58_uint> {
b58_buffer(size_t bytes, size_t estimation_ratio_numerator,
size_t estimation_ratio_denominator, size_t extra = 0)
: temp_buffer((/* пересчитываем по указанной пропорции */
bytes = (bytes * estimation_ratio_numerator +
estimation_ratio_denominator - 1) /
estimation_ratio_denominator,
/* учитываем резервный старший байт в каждом слове */
((bytes + sizeof(b58_uint) - 2) / (sizeof(b58_uint) - 1) *
sizeof(b58_uint) +
extra) *
sizeof(b58_uint))) {}
};
static byte b58_8to11(b58_uint &v) noexcept {
static const char b58_alphabet[58] = {
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
const auto i = size_t(v % 58);
v /= 58;
return b58_alphabet[i];
}
static slice b58_encode(b58_buffer &buf, const byte *begin, const byte *end) {
auto high = buf.end();
const auto modulo =
b58_uint((sizeof(b58_uint) > 4) ? UINT64_C(0x1A636A90B07A00) /* 58^9 */
: UINT32_C(0xACAD10) /* 58^4 */);
static_assert(sizeof(modulo) == 4 || sizeof(modulo) == 8, "WTF?");
while (begin < end) {
b58_uint carry = *begin++;
auto ptr = buf.end();
do {
assert(ptr > buf.area);
carry += *--ptr << CHAR_BIT;
*ptr = carry % modulo;
carry /= modulo;
} while (carry || ptr > high);
high = ptr;
}
byte *output = static_cast<byte *>(static_cast<void *>(buf.area));
auto ptr = output;
for (auto porous = high; porous < buf.end();) {
auto chunk = *porous++;
static_assert(sizeof(chunk) == 4 || sizeof(chunk) == 8, "WTF?");
assert(chunk < modulo);
if (sizeof(chunk) > 4) {
ptr[8] = b58_8to11(chunk);
ptr[7] = b58_8to11(chunk);
ptr[6] = b58_8to11(chunk);
ptr[5] = b58_8to11(chunk);
ptr[4] = b58_8to11(chunk);
ptr[3] = b58_8to11(chunk);
ptr[2] = b58_8to11(chunk);
ptr[1] = b58_8to11(chunk);
ptr[0] = b58_8to11(chunk);
ptr += 9;
} else {
ptr[3] = b58_8to11(chunk);
ptr[2] = b58_8to11(chunk);
ptr[1] = b58_8to11(chunk);
ptr[0] = b58_8to11(chunk);
ptr += 4;
}
assert(static_cast<void *>(ptr) < static_cast<void *>(porous));
}
while (output < ptr && *output == '1')
++output;
return slice(output, ptr);
}
char *to_base58::write_bytes(char *__restrict const dest,
size_t dest_size) const {
if (MDBX_UNLIKELY(envisage_result_length() > dest_size))
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
auto ptr = dest;
auto src = source.byte_ptr();
size_t left = source.length();
auto line = ptr;
while (MDBX_LIKELY(left > 7)) {
uint64_t v;
std::memcpy(&v, src, 8);
src += 8;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
v = bswap64(v);
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#else
#error "FIXME: Unsupported byte order"
#endif /* __BYTE_ORDER__ */
ptr[10] = b58_8to11(v);
ptr[9] = b58_8to11(v);
ptr[8] = b58_8to11(v);
ptr[7] = b58_8to11(v);
ptr[6] = b58_8to11(v);
ptr[5] = b58_8to11(v);
ptr[4] = b58_8to11(v);
ptr[3] = b58_8to11(v);
ptr[2] = b58_8to11(v);
ptr[1] = b58_8to11(v);
ptr[0] = b58_8to11(v);
assert(v == 0);
ptr += 11;
left -= 8;
if (wrap_width && size_t(ptr - line) >= wrap_width && left) {
*ptr = '\n';
line = ++ptr;
}
assert(ptr <= dest + dest_size);
auto begin = source.byte_ptr();
auto end = source.end_byte_ptr();
line_wrapper wrapper(dest);
while (MDBX_LIKELY(begin < end) && *begin == 0) {
wrapper.put('1', wrap_width);
assert(wrapper.ptr <= dest + dest_size);
++begin;
}
if (left) {
uint64_t v = 0;
unsigned parrots = 31;
do {
v = (v << 8) + *src++;
parrots += 43;
} while (--left);
auto tail = ptr += parrots >> 5;
assert(ptr <= dest + dest_size);
do {
*--tail = b58_8to11(v);
parrots -= 32;
} while (parrots > 31);
assert(v == 0);
}
return ptr;
b58_buffer buf(end - begin, 11, 8);
wrapper.put(b58_encode(buf, begin, end), wrap_width);
return wrapper.ptr;
}
::std::ostream &to_base58::output(::std::ostream &out) const {
if (MDBX_LIKELY(!is_empty()))
MDBX_CXX20_LIKELY {
::std::ostream::sentry sentry(out);
auto src = source.byte_ptr();
size_t left = source.length();
auto begin = source.byte_ptr();
auto end = source.end_byte_ptr();
unsigned width = 0;
std::array<char, 11> buf;
while (MDBX_LIKELY(left > 7)) {
uint64_t v;
std::memcpy(&v, src, 8);
src += 8;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
v = bswap64(v);
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#else
#error "FIXME: Unsupported byte order"
#endif /* __BYTE_ORDER__ */
buf[10] = b58_8to11(v);
buf[9] = b58_8to11(v);
buf[8] = b58_8to11(v);
buf[7] = b58_8to11(v);
buf[6] = b58_8to11(v);
buf[5] = b58_8to11(v);
buf[4] = b58_8to11(v);
buf[3] = b58_8to11(v);
buf[2] = b58_8to11(v);
buf[1] = b58_8to11(v);
buf[0] = b58_8to11(v);
assert(v == 0);
out.write(&buf.front(), 11);
left -= 8;
if (wrap_width && (width += 11) >= wrap_width && left) {
while (MDBX_LIKELY(begin < end) && *begin == 0) {
out.put('1');
if (wrap_width && ++width >= wrap_width) {
out << ::std::endl;
width = 0;
}
++begin;
}
if (left) {
uint64_t v = 0;
unsigned parrots = 31;
do {
v = (v << 8) + *src++;
parrots += 43;
} while (--left);
auto ptr = buf.end();
do {
*--ptr = b58_8to11(v);
parrots -= 32;
} while (parrots > 31);
assert(v == 0);
out.write(&*ptr, buf.end() - ptr);
b58_buffer buf(end - begin, 11, 8);
const auto chunk = b58_encode(buf, begin, end);
if (!wrap_width || wrap_width > width + chunk.length())
out.write(chunk.char_ptr(), chunk.length());
else {
for (size_t i = 0; i < chunk.length(); ++i) {
out.put(chunk.char_ptr()[i]);
if (wrap_width && ++width >= wrap_width) {
out << ::std::endl;
width = 0;
}
}
}
}
return out;
@@ -776,10 +901,46 @@ const signed char b58_map[256] = {
IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, IL // f0
};
static inline signed char b58_11to8(uint64_t &v, const byte c) noexcept {
const signed char m = b58_map[c];
v = v * 58 + m;
return m;
static slice b58_decode(b58_buffer &buf, const byte *begin, const byte *end,
bool ignore_spaces) {
auto high = buf.end();
while (begin < end) {
const auto c = b58_map[*begin++];
if (MDBX_LIKELY(c >= 0)) {
b58_uint carry = c;
auto ptr = buf.end();
do {
assert(ptr > buf.area);
carry += *--ptr * 58;
*ptr = carry & (~b58_uint(0) >> CHAR_BIT);
carry >>= CHAR_BIT * (sizeof(carry) - 1);
} while (carry || ptr > high);
high = ptr;
} else if (MDBX_UNLIKELY(!ignore_spaces || !isspace(begin[-1])))
MDBX_CXX20_UNLIKELY
throw std::domain_error("mdbx::from_base58:: invalid base58 string");
}
byte *output = static_cast<byte *>(static_cast<void *>(buf.area));
auto ptr = output;
for (auto porous = high; porous < buf.end(); ++porous) {
auto chunk = *porous;
static_assert(sizeof(chunk) == 4 || sizeof(chunk) == 8, "WTF?");
assert(chunk <= (~b58_uint(0) >> CHAR_BIT));
if (sizeof(chunk) > 4) {
*ptr++ = byte(uint_fast64_t(chunk) >> CHAR_BIT * 6);
*ptr++ = byte(uint_fast64_t(chunk) >> CHAR_BIT * 5);
*ptr++ = byte(uint_fast64_t(chunk) >> CHAR_BIT * 4);
*ptr++ = byte(chunk >> CHAR_BIT * 3);
}
*ptr++ = byte(chunk >> CHAR_BIT * 2);
*ptr++ = byte(chunk >> CHAR_BIT * 1);
*ptr++ = byte(chunk >> CHAR_BIT * 0);
}
while (output < ptr && *output == 0)
++output;
return slice(output, ptr);
}
char *from_base58::write_bytes(char *__restrict const dest,
@@ -788,98 +949,33 @@ char *from_base58::write_bytes(char *__restrict const dest,
MDBX_CXX20_UNLIKELY throw_too_small_target_buffer();
auto ptr = dest;
auto src = source.byte_ptr();
for (auto left = source.length(); left > 0;) {
if (MDBX_UNLIKELY(isspace(*src)) && ignore_spaces) {
++src;
--left;
continue;
}
if (MDBX_LIKELY(left > 10)) {
uint64_t v = 0;
if (MDBX_UNLIKELY((b58_11to8(v, src[0]) | b58_11to8(v, src[1]) |
b58_11to8(v, src[2]) | b58_11to8(v, src[3]) |
b58_11to8(v, src[4]) | b58_11to8(v, src[5]) |
b58_11to8(v, src[6]) | b58_11to8(v, src[7]) |
b58_11to8(v, src[8]) | b58_11to8(v, src[9]) |
b58_11to8(v, src[10])) < 0))
MDBX_CXX20_UNLIKELY goto bailout;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
v = bswap64(v);
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#else
#error "FIXME: Unsupported byte order"
#endif /* __BYTE_ORDER__ */
std::memcpy(ptr, &v, 8);
ptr += 8;
src += 11;
left -= 11;
assert(ptr <= dest + dest_size);
continue;
}
constexpr unsigned invalid_length_mask = 1 << 1 | 1 << 4 | 1 << 8;
if (MDBX_UNLIKELY(invalid_length_mask & (1 << left)))
MDBX_CXX20_UNLIKELY goto bailout;
uint64_t v = 1;
unsigned parrots = 0;
do {
if (MDBX_UNLIKELY(b58_11to8(v, *src++) < 0))
MDBX_CXX20_UNLIKELY goto bailout;
parrots += 32;
} while (--left);
auto tail = ptr += parrots / 43;
assert(ptr <= dest + dest_size);
do {
*--tail = byte(v);
v >>= 8;
} while (v > 255);
break;
auto begin = source.byte_ptr();
auto const end = source.end_byte_ptr();
while (begin < end && *begin <= '1') {
if (MDBX_LIKELY(*begin == '1'))
MDBX_CXX20_LIKELY *ptr++ = 0;
else if (MDBX_UNLIKELY(!ignore_spaces || !isspace(*begin)))
MDBX_CXX20_UNLIKELY
throw std::domain_error("mdbx::from_base58:: invalid base58 string");
++begin;
}
return ptr;
bailout:
throw std::domain_error("mdbx::from_base58:: invalid base58 string");
b58_buffer buf(end - begin, 47, 64);
auto slice = b58_decode(buf, begin, end, ignore_spaces);
memcpy(ptr, slice.data(), slice.length());
return ptr + slice.length();
}
bool from_base58::is_erroneous() const noexcept {
bool got = false;
auto src = source.byte_ptr();
for (auto left = source.length(); left > 0;) {
if (MDBX_UNLIKELY(*src <= ' ') &&
MDBX_LIKELY(ignore_spaces && isspace(*src))) {
++src;
--left;
continue;
}
if (MDBX_LIKELY(left > 10)) {
if (MDBX_UNLIKELY((b58_map[src[0]] | b58_map[src[1]] | b58_map[src[2]] |
b58_map[src[3]] | b58_map[src[4]] | b58_map[src[5]] |
b58_map[src[6]] | b58_map[src[7]] | b58_map[src[8]] |
b58_map[src[9]] | b58_map[src[10]]) < 0))
MDBX_CXX20_UNLIKELY return true;
src += 11;
left -= 11;
got = true;
continue;
}
constexpr unsigned invalid_length_mask = 1 << 1 | 1 << 4 | 1 << 8;
if (invalid_length_mask & (1 << left))
return false;
do
if (MDBX_UNLIKELY(b58_map[*src++] < 0))
MDBX_CXX20_UNLIKELY return true;
while (--left);
got = true;
break;
auto begin = source.byte_ptr();
auto const end = source.end_byte_ptr();
while (begin < end) {
if (MDBX_UNLIKELY(b58_map[*begin] < 0 &&
!(ignore_spaces && isspace(*begin))))
return true;
++begin;
}
return !got;
return false;
}
//------------------------------------------------------------------------------

View File

@@ -1,7 +1,7 @@
/* mdbx_chk.c - memory-mapped database check tool */
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
@@ -537,26 +537,14 @@ static int pgvisitor(const uint64_t pgno, const unsigned pgnumber,
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
}
if (payload_bytes < 1) {
if (nentries > 1) {
problem_add("page", pgno, "zero size-of-entry",
"%s-page: payload %" PRIuPTR " bytes, %" PRIuPTR " entries",
pagetype_caption, payload_bytes, nentries);
/* if ((size_t)header_bytes + unused_bytes < page_size) {
// LY: hush a misuse error
page_bytes = page_size;
} */
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
} else {
problem_add("page", pgno, "empty",
"%s-page: payload %" PRIuPTR " bytes, %" PRIuPTR
" entries, deep %i",
pagetype_caption, payload_bytes, nentries, deep);
dbi->pages.empty += 1;
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
}
if (nentries < 1 || (pagetype == MDBX_page_branch && nentries < 2)) {
problem_add("page", pgno, nentries ? "half-empty" : "empty",
"%s-page: payload %" PRIuPTR " bytes, %" PRIuPTR
" entries, deep %i",
pagetype_caption, payload_bytes, nentries, deep);
dbi->pages.empty += 1;
data_tree_problems += !is_gc_tree;
gc_tree_problems += is_gc_tree;
}
if (pgnumber) {
@@ -611,7 +599,7 @@ static int handle_freedb(const uint64_t record_number, const MDBX_val *key,
problem_add("entry", txnid, "wrong idl size", "%" PRIuPTR,
data->iov_len);
size_t number = (data->iov_len >= sizeof(pgno_t)) ? *iptr++ : 0;
if (number < 1 || number > MDBX_PGL_LIMIT)
if (number > MDBX_PGL_LIMIT)
problem_add("entry", txnid, "wrong idl length", "%" PRIuPTR, number);
else if ((number + 1) * sizeof(pgno_t) > data->iov_len) {
problem_add("entry", txnid, "trimmed idl",

View File

@@ -1,7 +1,7 @@
/* mdbx_copy.c - memory-mapped database backup tool */
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,7 +1,7 @@
/* mdbx_drop.c - memory-mapped database delete tool */
/*
* Copyright 2021-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2021-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
*
* Copyright 2016-2021 Howard Chu, Symas Corp.

View File

@@ -1,7 +1,7 @@
/* mdbx_dump.c - memory-mapped database dump tool */
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,7 +1,7 @@
/* mdbx_load.c - memory-mapped database load tool */
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,7 +1,7 @@
/* mdbx_stat.c - memory-mapped database status tool */
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -28,9 +28,17 @@
#define MDBX_OSX_SPEED_INSTEADOF_DURABILITY MDBX_OSX_WANNA_DURABILITY
#endif /* MDBX_OSX_SPEED_INSTEADOF_DURABILITY */
/** Controls using of POSIX' madvise() and/or similar hints. */
#ifndef MDBX_ENABLE_MADVISE
#define MDBX_ENABLE_MADVISE 1
#elif !(MDBX_ENABLE_MADVISE == 0 || MDBX_ENABLE_MADVISE == 1)
#error MDBX_ENABLE_MADVISE must be defined as 0 or 1
#endif /* MDBX_ENABLE_MADVISE */
/** Controls checking PID against reuse DB environment after the fork() */
#ifndef MDBX_ENV_CHECKPID
#if defined(MADV_DONTFORK) || defined(_WIN32) || defined(_WIN64)
#if (defined(MADV_DONTFORK) && MDBX_ENABLE_MADVISE) || defined(_WIN32) || \
defined(_WIN64)
/* PID check could be omitted:
* - on Linux when madvise(MADV_DONTFORK) is available, i.e. after the fork()
* mapped pages will not be available for child process.
@@ -96,8 +104,7 @@
/** Controls using Unix' mincore() to determine whether DB-pages
* are resident in memory. */
#ifndef MDBX_ENABLE_MINCORE
#if MDBX_ENABLE_PREFAULT && \
(defined(MINCORE_INCORE) || !(defined(_WIN32) || defined(_WIN64)))
#if defined(MINCORE_INCORE) || !(defined(_WIN32) || defined(_WIN64))
#define MDBX_ENABLE_MINCORE 1
#else
#define MDBX_ENABLE_MINCORE 0
@@ -118,13 +125,6 @@
#error MDBX_ENABLE_BIGFOOT must be defined as 0 or 1
#endif /* MDBX_ENABLE_BIGFOOT */
/** Controls using of POSIX' madvise() and/or similar hints. */
#ifndef MDBX_ENABLE_MADVISE
#define MDBX_ENABLE_MADVISE 1
#elif !(MDBX_ENABLE_MADVISE == 0 || MDBX_ENABLE_MADVISE == 1)
#error MDBX_ENABLE_MADVISE must be defined as 0 or 1
#endif /* MDBX_ENABLE_MADVISE */
/** Disable some checks to reduce an overhead and detection probability of
* database corruption to a values closer to the LMDB. */
#ifndef MDBX_DISABLE_VALIDATION

View File

@@ -1,7 +1,7 @@
/* https://en.wikipedia.org/wiki/Operating_system_abstraction_layer */
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
@@ -1826,8 +1826,8 @@ MDBX_INTERNAL_FUNC int osal_check_fs_rdonly(mdbx_filehandle_t handle,
#else
struct statvfs info;
if (err != MDBX_ENOFILE) {
if (statvfs(pathname, &info) == 0 && (info.f_flag & ST_RDONLY) == 0)
return err;
if (statvfs(pathname, &info) == 0)
return (info.f_flag & ST_RDONLY) ? MDBX_SUCCESS : err;
if (errno != MDBX_ENOFILE)
return errno;
}
@@ -2571,7 +2571,7 @@ retry_mapview:;
ptr_disp(map->base, size),
((map->current < map->limit) ? map->current : map->limit) - size);
}
map->current = size;
map->current = (size < map->limit) ? size : map->limit;
}
if (limit == map->limit)
@@ -2732,6 +2732,7 @@ retry_mapview:;
map->base = ptr;
}
map->limit = limit;
map->current = size;
#if MDBX_ENABLE_MADVISE
#ifdef MADV_DONTFORK

View File

@@ -1,7 +1,7 @@
/* https://en.wikipedia.org/wiki/Operating_system_abstraction_layer */
/*
* Copyright 2015-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2015-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -72,6 +72,10 @@ if(UNIX AND NOT SUBPROJECT)
target_include_directories(test_extra_upsert_alldups PRIVATE "${PROJECT_SOURCE_DIR}")
target_link_libraries(test_extra_upsert_alldups ${TOOL_MDBX_LIB})
add_executable(test_extra_dupfixed_addodd extra/dupfixed_addodd.c)
target_include_directories(test_extra_dupfixed_addodd PRIVATE "${PROJECT_SOURCE_DIR}")
target_link_libraries(test_extra_dupfixed_addodd ${TOOL_MDBX_LIB})
if(MDBX_BUILD_CXX)
add_executable(test_extra_maindb_ordinal extra/maindb_ordinal.c++)
target_include_directories(test_extra_maindb_ordinal PRIVATE "${PROJECT_SOURCE_DIR}")
@@ -80,6 +84,13 @@ if(UNIX AND NOT SUBPROJECT)
set_target_properties(test_extra_maindb_ordinal PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
endif()
add_executable(test_extra_dupfixed_multiple extra/dupfixed_multiple.c++)
target_include_directories(test_extra_dupfixed_multiple PRIVATE "${PROJECT_SOURCE_DIR}")
target_link_libraries(test_extra_dupfixed_multiple ${TOOL_MDBX_LIB})
if(MDBX_CXX_STANDARD)
set_target_properties(test_extra_dupfixed_multiple PROPERTIES
CXX_STANDARD ${MDBX_CXX_STANDARD} CXX_STANDARD_REQUIRED ON)
endif()
endif()
endif()
@@ -159,4 +170,13 @@ else()
REQUIRED_FILES uniq_nested.db-copy)
endif()
if(UNIX AND NOT SUBPROJECT)
add_test(NAME extra_upsert_alldups COMMAND test_extra_upsert_alldups)
add_test(NAME extra_dupfixed_addodd COMMAND test_extra_dupfixed_addodd)
if(MDBX_BUILD_CXX)
add_test(NAME extra_maindb_ordinal COMMAND test_extra_maindb_ordinal)
add_test(NAME extra_dupfixed_multiple COMMAND test_extra_dupfixed_multiple)
endif()
endif()
endif()

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
@@ -667,7 +667,10 @@ bool actor_config::deserialize(const char *str, actor_config &config) {
}
str = slash + 1;
uint64_t verify = std::stoull(std::string(str));
uint64_t verify = 0;
while (*str >= '0' && *str <= '9')
verify = verify * 10 + *str++ - '0';
if (checksum.value != verify) {
TRACE("<< actor_config::deserialize: checksum mismatch\n");
return false;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -0,0 +1,93 @@
/*
* @Dvirsw (https://t.me/Dvirsw)
* I think there is a bug with DUPFIXED. The following code fails.
*
* https://t.me/libmdbx/5368
*/
#include <sys/stat.h>
#include <sys/time.h>
#include "mdbx.h"
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int rc;
MDBX_env *env = NULL;
MDBX_dbi dbi = 0;
MDBX_val key, data;
MDBX_txn *txn = NULL;
rc = mdbx_env_create(&env);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_env_create: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}
rc = mdbx_env_set_maxdbs(env, 1);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_env_create: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}
rc = mdbx_env_open(env, "./example-db",
MDBX_NOSUBDIR | MDBX_COALESCE | MDBX_LIFORECLAIM, 0664);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_env_open: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}
rc = mdbx_txn_begin(env, NULL, 0, &txn);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_txn_begin: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}
rc = mdbx_dbi_open(txn, "test", MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_CREATE,
&dbi);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_dbi_open: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}
char key_bytes[32] = {0};
key.iov_len = 32;
key.iov_base = key_bytes;
// Another put after this will fail.
unsigned char idx;
for (idx = 0; idx < 129; idx++) {
char data_bytes[15] = {idx};
data.iov_len = 15;
data.iov_base = data_bytes;
rc = mdbx_put(txn, dbi, &key, &data, 0);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_put: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}
}
// This will fail and exit.
char data_bytes[15] = {idx};
data.iov_len = 15;
data.iov_base = data_bytes;
rc = mdbx_put(txn, dbi, &key, &data, 0);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_put: (%d) %s\n", rc, mdbx_strerror(rc));
fprintf(stderr, "expected failure\n");
exit(EXIT_FAILURE);
}
rc = mdbx_txn_commit(txn);
if (rc) {
fprintf(stderr, "mdbx_txn_commit: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}
}

View File

@@ -0,0 +1,210 @@
#include "mdbx.h++"
#include <array>
#include <iostream>
#include <unistd.h>
int main(int argc, const char *argv[]) {
(void)argc;
(void)argv;
unlink("." MDBX_DATANAME);
unlink("." MDBX_LOCKNAME);
mdbx::env_managed env(".", mdbx::env_managed::create_parameters(),
mdbx::env::operate_parameters());
using buffer =
mdbx::buffer<mdbx::default_allocator, mdbx::default_capacity_policy>;
auto txn = env.start_write();
auto map = txn.create_map(nullptr, mdbx::key_mode::ordinal,
mdbx::value_mode::multi_ordinal);
txn.insert(map, buffer::key_from_u64(21), buffer::key_from_u64(18));
txn.insert(map, buffer::key_from_u64(7), buffer::key_from_u64(19));
txn.insert(map, buffer::key_from_u64(22), buffer::key_from_u64(17));
txn.insert(map, buffer::key_from_u64(26), buffer::key_from_u64(13));
txn.insert(map, buffer::key_from_u64(24), buffer::key_from_u64(15));
txn.insert(map, buffer::key_from_u64(23), buffer::key_from_u64(16));
txn.insert(map, buffer::key_from_u64(25), buffer::key_from_u64(14));
txn.insert(map, buffer::key_from_u64(27), buffer::key_from_u64(12));
txn.commit();
txn = env.start_read();
auto cursor = txn.open_cursor(map);
if (cursor.to_first().value.as_uint64() != 19 ||
cursor.to_next().value.as_uint64() != 18 ||
cursor.to_next().value.as_uint64() != 17 ||
cursor.to_next().value.as_uint64() != 16 ||
cursor.to_next().value.as_uint64() != 15 ||
cursor.to_next().value.as_uint64() != 14 ||
cursor.to_next().value.as_uint64() != 13 ||
cursor.to_next().value.as_uint64() != 12 || cursor.to_next(false).done ||
!cursor.eof()) {
std::cerr << "Fail\n";
return EXIT_FAILURE;
}
txn.abort();
const uint64_t array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 42, 17, 99, 0, 33, 333};
txn = env.start_write();
txn.put_multiple(map, buffer::key_from_u64(13), array + 3, 4, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(10), array + 0, 1, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(12), array + 2, 3, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(15), array + 5, 6, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(14), array + 4, 5, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(11), array + 1, 2, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(16), array + 6, 7, mdbx::upsert);
txn.commit();
txn = env.start_read();
cursor = txn.open_cursor(map);
if (/* key = 7 */ cursor.to_first().value.as_uint64() != 19 ||
/* key = 10: 1 элемент */
cursor.to_next().value.as_uint64() != 1 ||
/* key = 11: 2 элемента, пропуск 1 */
cursor.to_next().value.as_uint64() != 2 ||
cursor.to_next().value.as_uint64() != 3 ||
/* key = 12: 3 элемента, пропуск 2 */
cursor.to_next().value.as_uint64() != 3 ||
cursor.to_next().value.as_uint64() != 4 ||
cursor.to_next().value.as_uint64() != 5 ||
/* key = 13: 4 элемента, пропуск 3 */
cursor.to_next().value.as_uint64() != 4 ||
cursor.to_next().value.as_uint64() != 5 ||
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
/* key = 14: 5 элементов, пропуск 4 */
cursor.to_next().value.as_uint64() != 5 ||
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
/* key = 15: 6 элементов, пропуск 5 */
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
cursor.to_next().value.as_uint64() != 17 ||
cursor.to_next().value.as_uint64() != 42 ||
/* key = 16: 7 элементов, пропуск 6 */
cursor.to_next().value.as_uint64() != 0 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
cursor.to_next().value.as_uint64() != 17 ||
cursor.to_next().value.as_uint64() != 42 ||
cursor.to_next().value.as_uint64() != 99 ||
/* key = 21 */ cursor.to_next().value.as_uint64() != 18 ||
/* key = 22 */ cursor.to_next().value.as_uint64() != 17 ||
/* key = 23 */ cursor.to_next().value.as_uint64() != 16 ||
/* key = 24 */ cursor.to_next().value.as_uint64() != 15 ||
/* key = 25 */ cursor.to_next().value.as_uint64() != 14 ||
/* key = 26 */ cursor.to_next().value.as_uint64() != 13 ||
/* key = 27 */ cursor.to_next().value.as_uint64() != 12 ||
cursor.to_next(false).done || !cursor.eof()) {
std::cerr << "Fail\n";
return EXIT_FAILURE;
}
txn.abort();
txn = env.start_write();
txn.put_multiple(map, buffer::key_from_u64(7), array + 3, 4, mdbx::update);
txn.upsert(map, buffer::key_from_u64(10), buffer::key_from_u64(14));
txn.put_multiple(map, buffer::key_from_u64(11), array + 4, 5, mdbx::upsert);
txn.put_multiple(map, buffer::key_from_u64(12), array + 0, 1, mdbx::update);
txn.update(map, buffer::key_from_u64(13), buffer::key_from_u64(18));
txn.put_multiple(map, buffer::key_from_u64(14), array + 2, 3, mdbx::update);
txn.update(map, buffer::key_from_u64(15), buffer::key_from_u64(13));
txn.put_multiple(map, buffer::key_from_u64(16), array + 6, 9, mdbx::update);
txn.update(map, buffer::key_from_u64(21), buffer::key_from_u64(17));
txn.update(map, buffer::key_from_u64(22), buffer::key_from_u64(15));
txn.put_multiple(map, buffer::key_from_u64(23), array + 1, 2, mdbx::update);
txn.update(map, buffer::key_from_u64(24), buffer::key_from_u64(16));
txn.put_multiple(map, buffer::key_from_u64(25), array + 5, 6, mdbx::update);
txn.upsert(map, buffer::key_from_u64(26), buffer::key_from_u64(12));
txn.put_multiple(map, buffer::key_from_u64(27), array + 12, 3, mdbx::update);
txn.commit();
txn = env.start_read();
cursor = txn.open_cursor(map);
if (/* key = 7 */
cursor.to_first().value.as_uint64() != 4 ||
cursor.to_next().value.as_uint64() != 5 ||
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
/* key = 10: 1 элемент */
cursor.to_next().value.as_uint64() != 1 ||
/* +1 upsert */
cursor.to_next().value.as_uint64() != 14 ||
/* key = 11: 2 элемента, пропуск 1 */
cursor.to_next().value.as_uint64() != 2 ||
cursor.to_next().value.as_uint64() != 3 ||
/* +5 элементов, пропуск 4 */
cursor.to_next().value.as_uint64() != 5 ||
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
/* key = 12: 1 элемент */
cursor.to_next().value.as_uint64() != 1 ||
/* key = 13 */ cursor.to_next().value.as_uint64() != 18 ||
/* key = 14: 3 элемента, пропуск 2 */
cursor.to_next().value.as_uint64() != 3 ||
cursor.to_next().value.as_uint64() != 4 ||
cursor.to_next().value.as_uint64() != 5 ||
/* key = 15 */ cursor.to_next().value.as_uint64() != 13 ||
/* key = 16: 9 элементов, пропуск 6 */
cursor.to_next().value.as_uint64() != 0 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
cursor.to_next().value.as_uint64() != 17 ||
cursor.to_next().value.as_uint64() != 33 ||
cursor.to_next().value.as_uint64() != 42 ||
cursor.to_next().value.as_uint64() != 99 ||
cursor.to_next().value.as_uint64() != 333 ||
/* key = 21 */ cursor.to_next().value.as_uint64() != 17 ||
/* key = 22 */ cursor.to_next().value.as_uint64() != 15 ||
/* key = 23: 2 элемента, пропуск 1 */
cursor.to_next().value.as_uint64() != 2 ||
cursor.to_next().value.as_uint64() != 3 ||
/* key = 24 */ cursor.to_next().value.as_uint64() != 16 ||
/* key = 25: 6 элемента, пропуск 5 */
cursor.to_next().value.as_uint64() != 6 ||
cursor.to_next().value.as_uint64() != 7 ||
cursor.to_next().value.as_uint64() != 8 ||
cursor.to_next().value.as_uint64() != 9 ||
cursor.to_next().value.as_uint64() != 17 ||
cursor.to_next().value.as_uint64() != 42 ||
/* key = 26, 1+1 upsert */
cursor.to_next().value.as_uint64() != 12 ||
cursor.to_next().value.as_uint64() != 13 ||
/* key = 27: 3 элемента, пропуск 12 */
cursor.to_next().value.as_uint64() != 0 ||
cursor.to_next().value.as_uint64() != 33 ||
cursor.to_next().value.as_uint64() != 333 ||
cursor.to_next(false).done || !cursor.eof()) {
std::cerr << "Fail\n";
return EXIT_FAILURE;
}
std::cout << "OK\n";
return EXIT_SUCCESS;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2016-2024 Leonid Yuriev <leo@yuriev.ru>.
* Copyright 2015 Vladimir Romanov
* <https://www.linkedin.com/in/vladimirromanov>, Yota Lab.
*

View File

@@ -12,8 +12,8 @@
#include <unistd.h>
static int dump(MDBX_cursor *cur) {
MDBX_val key = {};
MDBX_val data = {};
MDBX_val key = {NULL, 0};
MDBX_val data = {NULL, 0};
int rc = mdbx_cursor_get(cur, &key, &data, MDBX_FIRST);
while (rc == 0) {
@@ -25,15 +25,14 @@ static int dump(MDBX_cursor *cur) {
}
static int clear(MDBX_cursor *cur) {
MDBX_val key = {};
MDBX_val data = {};
MDBX_val key = {NULL, 0};
MDBX_val data = {NULL, 0};
int rc = mdbx_cursor_get(cur, &key, &data, MDBX_FIRST);
while (rc == 0) {
rc = mdbx_cursor_del(cur, MDBX_ALLDUPS);
if (rc) {
if (rc)
return rc;
}
rc = mdbx_cursor_get(cur, &key, &data, MDBX_NEXT);
}
return (rc == MDBX_NOTFOUND) ? 0 : rc;
@@ -183,6 +182,7 @@ int main(int argc, const char *argv[]) {
errmsg = "failed to mdbx_txn_commit: %s\n";
goto Fail;
}
mdbx_cursor_close(cur);
if ((rc = mdbx_env_close(env))) {
errmsg = "failed to mdbx_env_close: %s\n";
goto Fail;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
@@ -99,6 +99,37 @@ bool testcase_jitter::run() {
jitter_delay();
txn_begin(true);
fetch_canary();
if (flipcoin()) {
MDBX_txn_info info;
err = mdbx_txn_reset(txn_guard.get());
if (err)
failure_perror("mdbx_txn_reset()", err);
err = mdbx_txn_info(txn_guard.get(), &info, false);
if (err != MDBX_BAD_TXN)
failure_perror("mdbx_txn_info(MDBX_BAD_TXN)", err);
err = mdbx_txn_reset(txn_guard.get());
if (err)
failure_perror("mdbx_txn_reset(again)", err);
err = mdbx_txn_break(txn_guard.get());
if (err)
failure_perror("mdbx_txn_break()", err);
err = mdbx_txn_abort(txn_guard.get());
if (err)
failure_perror("mdbx_txn_abort()", err);
txn_guard.release();
txn_begin(true);
err = mdbx_txn_reset(txn_guard.get());
if (err)
failure_perror("mdbx_txn_reset()", err);
err = mdbx_txn_renew(txn_guard.get());
if (err)
failure_perror("mdbx_txn_renew()", err);
err = mdbx_txn_info(txn_guard.get(), &info, false);
if (err)
failure_perror("mdbx_txn_info()", err);
}
jitter_delay();
txn_end(flipcoin());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -432,6 +432,12 @@ for nops in 10 33 100 333 1000 3333 10000 33333 100000 333333 1000000 3333333 10
--pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=9 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=11 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
split=24
caption="Probe #$((++count)) int-key,with-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
@@ -446,6 +452,12 @@ for nops in 10 33 100 333 1000 3333 10000 33333 100000 333333 1000000 3333333 10
--pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=10 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=12 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
split=16
caption="Probe #$((++count)) int-key,w/o-dups, split=${split}, case $((++subcase)) of ${cases}" probe \
@@ -468,6 +480,12 @@ for nops in 10 33 100 333 1000 3333 10000 33333 100000 333333 1000000 3333333 10
--pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=9 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=11 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
if [ "$EXTRA" != "no" ]; then
split=10
@@ -491,6 +509,12 @@ for nops in 10 33 100 333 1000 3333 10000 33333 100000 333333 1000000 3333333 10
--pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=max \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=13 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=16 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
fi
split=4
@@ -506,6 +530,12 @@ for nops in 10 33 100 333 1000 3333 10000 33333 100000 333333 1000000 3333333 10
--pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=-data.dups --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen.min=min --datalen.max=1111 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]} \
--keygen.seed=${seed}
caption="Probe #$((++count)) int-key,fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+key.integer,+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=21 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
caption="Probe #$((++count)) fixdups, split=${split}, case $((++subcase)) of ${cases}" probe \
--keygen.seed=${seed} --pagesize=$PAGESIZE --size-upper-upto=${db_size_mb}M --table=+data.fixed --keygen.split=${split} --keylen.min=min --keylen.max=max --datalen=32 \
--nops=$nops --batch.write=$wbatch --mode=$(bits2options $bits)${syncmodes[count%4]}
done # options
loop=$((loop + 1))
if [ -n "$LOOPS" ] && [ $loop -ge "$LOOPS" ]; then break; fi

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
@@ -24,9 +24,9 @@ const char *testcase2str(const actor_testcase testcase) {
case ac_hill:
return "hill";
case ac_deadread:
return "deadread";
return "dead.reader";
case ac_deadwrite:
return "deadwrite";
return "dead.writer";
case ac_jitter:
return "jitter";
case ac_try:
@@ -733,6 +733,18 @@ void testcase::speculum_check_iterator(const char *where, const char *stage,
mdbx_dump_val(&v, dump_value, sizeof(dump_value)));
}
void testcase::failure(const char *fmt, ...) const {
va_list ap;
va_start(ap, fmt);
fflush(nullptr);
logging::output_nocheckloglevel_ap(logging::failure, fmt, ap);
va_end(ap);
fflush(nullptr);
if (txn_guard)
mdbx_txn_commit(const_cast<testcase *>(this)->txn_guard.release());
exit(EXIT_FAILURE);
}
#if SPECULUM_CURSORS
void testcase::speculum_check_cursor(const char *where, const char *stage,
const testcase::SET::const_iterator &it,

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*
@@ -277,6 +277,7 @@ protected:
void signal();
bool should_continue(bool check_timeout_only = false) const;
void failure(const char *fmt, ...) const;
void generate_pair(const keygen::serial_t serial, keygen::buffer &out_key,
keygen::buffer &out_value, keygen::serial_t data_age) {
keyvalue_maker.pair(serial, out_key, out_value, data_age, false);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2023 Leonid Yuriev <leo@yuriev.ru>
* Copyright 2017-2024 Leonid Yuriev <leo@yuriev.ru>
* and other libmdbx authors: please see AUTHORS file.
* All rights reserved.
*