libmdbx/test/keygen.h
2017-04-10 23:20:22 +03:00

124 lines
5.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 &params, result_t &out);
static __inline void make(const serial_t serial, const params_t &params,
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 */