mirror of
https://github.com/isar/libmdbx.git
synced 2024-10-30 23:39:19 +08:00
124 lines
5.0 KiB
C
124 lines
5.0 KiB
C
|
/*
|
|||
|
* Copyright 2017 Leonid Yuriev <leo@yuriev.ru>
|
|||
|
* and other libmdbx authors: please see AUTHORS file.
|
|||
|
* All rights reserved.
|
|||
|
*
|
|||
|
* Redistribution and use in source and binary forms, with or without
|
|||
|
* modification, are permitted only as authorized by the OpenLDAP
|
|||
|
* Public License.
|
|||
|
*
|
|||
|
* A copy of this license is available in the file LICENSE in the
|
|||
|
* top-level directory of the distribution or, alternatively, at
|
|||
|
* <http://www.OpenLDAP.org/license.html>.
|
|||
|
*/
|
|||
|
|
|||
|
#pragma once
|
|||
|
|
|||
|
#include "base.h"
|
|||
|
#include "log.h"
|
|||
|
#include "utils.h"
|
|||
|
|
|||
|
namespace keygen {
|
|||
|
|
|||
|
/* Под "генерацией ключей" здесь понимается генерация обоих значений для
|
|||
|
* пар key-value, т.е. не только ключей, но и ассоциированных с ними данных.
|
|||
|
*/
|
|||
|
|
|||
|
/* Генерацию ключей нельзя отнести к простым задачам, так как требования
|
|||
|
* примерно следующие:
|
|||
|
* - генерация разного количества уникальных ключей различной длины
|
|||
|
* в задаваемом диапазоне;
|
|||
|
* - возможность выбора как псевдо-случайного порядка ключей,
|
|||
|
* так и по некоторым специфическим законам (ограниченными упорядоченными
|
|||
|
* последовательностями, в шахматном порядке по граница диапазона и т.д.);
|
|||
|
* - возможность генерации дубликатов с задаваемым законом распределения;
|
|||
|
* - возможность генерации непересекающимися кластерами для параллельного
|
|||
|
* использования в нескольких потоках;
|
|||
|
* - использовать минимум ресурсов, как CPU, так и RAM, в том числе
|
|||
|
* включая cache pollution и ram bandwidth.
|
|||
|
*
|
|||
|
* При этом заведомо известно, что для MDBX не имеет значения:
|
|||
|
* - используемый алфавит (значения байтов);
|
|||
|
* - частотное распределение по алфавиту;
|
|||
|
* - абсолютное значение ключей или разность между отдельными значениями;
|
|||
|
*
|
|||
|
* Соответственно, схема генерации следующая:
|
|||
|
* - для ключей вводится плоская одномерная "координата" uint64_t;
|
|||
|
* - все преобразования (назначение диапазонов, переупорядочивание,
|
|||
|
* коррекция распределения) выполняются только над "координатой";
|
|||
|
* - итоговая "координата" преобразуется в 8-байтное суррогатное значение
|
|||
|
* ключа, при этом опционально суррогат может усекаться до ненулевых байт;
|
|||
|
* - для получения ключей длиной более 8 байт суррогат дополняется
|
|||
|
* фиксированной последовательностью;
|
|||
|
*/
|
|||
|
|
|||
|
typedef uint64_t serial_t;
|
|||
|
|
|||
|
struct params_t {
|
|||
|
uint8_t minlen;
|
|||
|
uint8_t flags;
|
|||
|
uint16_t maxlen;
|
|||
|
};
|
|||
|
|
|||
|
struct result_t {
|
|||
|
MDB_val value;
|
|||
|
size_t limit;
|
|||
|
union {
|
|||
|
uint8_t bytes[sizeof(uint64_t)];
|
|||
|
uint32_t u32;
|
|||
|
uint64_t u64;
|
|||
|
};
|
|||
|
};
|
|||
|
|
|||
|
void make(const serial_t serial, const params_t ¶ms, result_t &out);
|
|||
|
|
|||
|
static __inline void make(const serial_t serial, const params_t ¶ms,
|
|||
|
result_t &out, size_t limit) {
|
|||
|
out.limit = limit;
|
|||
|
make(serial, params, out);
|
|||
|
}
|
|||
|
|
|||
|
size_t ffs_fallback(serial_t serial);
|
|||
|
|
|||
|
static __inline size_t ffs(serial_t serial) {
|
|||
|
size_t rc;
|
|||
|
#ifdef __GNUC__
|
|||
|
if (sizeof(serial) <= sizeof(int))
|
|||
|
rc = __builtin_ffs((int)serial);
|
|||
|
else if (sizeof(serial) == sizeof(long))
|
|||
|
rc = __builtin_ffsl((long)serial);
|
|||
|
else if (sizeof(serial) == sizeof(long long))
|
|||
|
rc = __builtin_ffsll((long long)serial);
|
|||
|
else
|
|||
|
return ffs_fallback(serial);
|
|||
|
#elif defined(_MSC_VER)
|
|||
|
unsigned long index;
|
|||
|
if (sizeof(serial) <= sizeof(unsigned long))
|
|||
|
rc = _BitScanReverse(&index, (unsigned long)serial) ? index : 0;
|
|||
|
else if (sizeof(serial) <= sizeof(unsigned __int64)) {
|
|||
|
#if defined(_M_ARM64) || defined(_M_X64)
|
|||
|
rc = _BitScanReverse64(&index, (unsigned __int64)serial) ? index : 0;
|
|||
|
#else
|
|||
|
size_t base = 0;
|
|||
|
unsigned long value = (unsigned long)serial;
|
|||
|
if ((unsigned __int64)serial > ULONG_MAX) {
|
|||
|
base = 32;
|
|||
|
value = (unsigned long)(serial >> 32);
|
|||
|
}
|
|||
|
rc = (_BitScanReverse(&index, value) ? index : 0) + base;
|
|||
|
#endif /* _M_ARM64 || _M_X64 */
|
|||
|
} else
|
|||
|
return ffs_fallback(serial);
|
|||
|
#else
|
|||
|
return ffs_fallback(serial);
|
|||
|
#endif
|
|||
|
assert(rc == ffs_fallback(serial));
|
|||
|
return rc;
|
|||
|
}
|
|||
|
|
|||
|
static __inline size_t length(const serial_t serial) {
|
|||
|
return (ffs(serial) + 7) >> 3;
|
|||
|
}
|
|||
|
|
|||
|
} /* namespace keygen */
|