mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
More work on fast CSS transforms
This commit is contained in:
parent
dcb37dd46f
commit
eb1ad62632
@ -117,7 +117,8 @@
|
|||||||
{
|
{
|
||||||
"name": "fast_css_transform",
|
"name": "fast_css_transform",
|
||||||
"sources": "calibre/srv/fast_css_transform.cpp",
|
"sources": "calibre/srv/fast_css_transform.cpp",
|
||||||
"needs_c++11": true
|
"inc_dirs": "perfect-hashing",
|
||||||
|
"needs_c++14": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pictureflow",
|
"name": "pictureflow",
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <frozen/unordered_map.h>
|
||||||
|
#include <frozen/string.h>
|
||||||
|
|
||||||
// character classes {{{
|
// character classes {{{
|
||||||
static inline bool
|
static inline bool
|
||||||
@ -53,6 +55,12 @@ static inline bool
|
|||||||
is_name(char32_t ch) {
|
is_name(char32_t ch) {
|
||||||
return is_name_start(ch) || is_digit(ch) || ch == '-';
|
return is_name_start(ch) || is_digit(ch) || ch == '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
is_printable_ascii(char32_t ch) {
|
||||||
|
return ch >= ' ' && ch <= '~';
|
||||||
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
class python_error : public std::runtime_error {
|
class python_error : public std::runtime_error {
|
||||||
@ -79,6 +87,129 @@ class pyobject_raii {
|
|||||||
PyObject *detach() { PyObject *ans = handle; handle = NULL; return ans; }
|
PyObject *detach() { PyObject *ans = handle; handle = NULL; return ans; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef long long integer_type;
|
||||||
|
|
||||||
|
class ParsedNumber {
|
||||||
|
public:
|
||||||
|
bool is_integer;
|
||||||
|
integer_type integer_value;
|
||||||
|
double float_value;
|
||||||
|
ParsedNumber(integer_type val) : is_integer(true), integer_value(val), float_value(0) {}
|
||||||
|
ParsedNumber(double val) : is_integer(false), integer_value(0), float_value(val) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const double base_font_size = 16.0, dpi = 96.0, pt_to_px = dpi / 72.0, pt_to_rem = pt_to_px / base_font_size;
|
||||||
|
|
||||||
|
static double
|
||||||
|
convert_font_size(double val, double factor) {
|
||||||
|
return (factor == 0.0) ? val / base_font_size : (val * factor * pt_to_rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static integer_type
|
||||||
|
ipow(integer_type base, integer_type exp) {
|
||||||
|
integer_type result = 1;
|
||||||
|
while(true) {
|
||||||
|
if (exp & 1) result *= base;
|
||||||
|
exp >>= 1;
|
||||||
|
if (!exp) break;
|
||||||
|
base *= base;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static integer_type
|
||||||
|
parse_integer(const T &src, const size_t first, size_t last) {
|
||||||
|
integer_type ans = 0, base = 1;
|
||||||
|
while(true) {
|
||||||
|
integer_type digit = src[last] - '0';
|
||||||
|
ans += digit * base;
|
||||||
|
if (last == first) break;
|
||||||
|
last--;
|
||||||
|
base *= 10;
|
||||||
|
}
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static ParsedNumber
|
||||||
|
parse_css_number(const T &src) {
|
||||||
|
int sign = 1, exponent_sign = 1;
|
||||||
|
integer_type integer_part = 0, fractional_part = 0, exponent_part = 0;
|
||||||
|
unsigned num_of_fractional_digits = 0;
|
||||||
|
size_t first_digit = 0, last_digit = 0;
|
||||||
|
const size_t src_sz = src.size();
|
||||||
|
size_t pos = 0;
|
||||||
|
#define read_sign(which) { if (pos < src_sz && (src[pos] == '+' || src[pos] == '-')) { if (src[pos++] == '-') which = -1; }}
|
||||||
|
#define read_integer(which) { \
|
||||||
|
if (pos < src_sz && is_digit(src[pos])) { \
|
||||||
|
first_digit = pos; \
|
||||||
|
while (pos + 1 < src_sz && is_digit(src[pos+1])) pos++; \
|
||||||
|
last_digit = pos++; \
|
||||||
|
which = parse_integer<T>(src, first_digit, last_digit); \
|
||||||
|
}}
|
||||||
|
read_sign(sign);
|
||||||
|
read_integer(integer_part);
|
||||||
|
if (pos < src_sz && src[pos] == '.') {
|
||||||
|
pos++;
|
||||||
|
read_integer(fractional_part);
|
||||||
|
if (fractional_part) num_of_fractional_digits = last_digit - first_digit + 1;
|
||||||
|
}
|
||||||
|
if (pos < src_sz && (src[pos] == 'e' || src[pos] == 'E')) {
|
||||||
|
pos++;
|
||||||
|
read_sign(exponent_sign);
|
||||||
|
read_integer(exponent_part);
|
||||||
|
}
|
||||||
|
if (fractional_part || (exponent_part && exponent_sign == -1)) {
|
||||||
|
double ans = integer_part;
|
||||||
|
if (fractional_part) ans += ((double) fractional_part) / ((double)(ipow(10, num_of_fractional_digits)));
|
||||||
|
if (exponent_part) {
|
||||||
|
if (exponent_sign == -1) ans /= (double)ipow(10, exponent_part);
|
||||||
|
else ans *= ipow(10, exponent_part);
|
||||||
|
}
|
||||||
|
return ParsedNumber(sign * ans);
|
||||||
|
}
|
||||||
|
return ParsedNumber(sign * integer_part * ipow(10, exponent_part));
|
||||||
|
#undef read_sign
|
||||||
|
#undef read_integer
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class PropertyType : unsigned int {
|
||||||
|
font_size, page_break, non_standard_writing_mode
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr auto known_properties = frozen::make_unordered_map<frozen::string, PropertyType>({
|
||||||
|
{"font-size", PropertyType::font_size},
|
||||||
|
{"font", PropertyType::font_size},
|
||||||
|
|
||||||
|
{"page-break-before", PropertyType::page_break},
|
||||||
|
{"page-break-after", PropertyType::page_break},
|
||||||
|
{"page-break-inside", PropertyType::page_break},
|
||||||
|
|
||||||
|
{"-webkit-writing-mode", PropertyType::non_standard_writing_mode},
|
||||||
|
{"-epub-writing-mode", PropertyType::non_standard_writing_mode},
|
||||||
|
});
|
||||||
|
|
||||||
|
constexpr auto font_size_keywords = frozen::make_unordered_map<frozen::string, frozen::string>({
|
||||||
|
{"xx-small", "0.5rem"},
|
||||||
|
{"x-small", "0.625rem"},
|
||||||
|
{"small", "0.8rem"},
|
||||||
|
{"medium", "1rem"},
|
||||||
|
{"large", "1.125rem"},
|
||||||
|
{"x-large", "1.5rem"},
|
||||||
|
{"xx-large", "2rem"},
|
||||||
|
{"xxx-large", "2.55rem"}
|
||||||
|
});
|
||||||
|
|
||||||
|
constexpr auto absolute_length_units = frozen::make_unordered_map<frozen::string, double>({
|
||||||
|
{"mm", 2.8346456693},
|
||||||
|
{"cm", 28.346456693},
|
||||||
|
{"in", 72},
|
||||||
|
{"pc", 12},
|
||||||
|
{"q", 0.708661417325},
|
||||||
|
{"px", 0.0},
|
||||||
|
{"pt", 1.0}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
enum class TokenType : unsigned int {
|
enum class TokenType : unsigned int {
|
||||||
@ -97,7 +228,7 @@ enum class TokenType : unsigned int {
|
|||||||
comment,
|
comment,
|
||||||
cdo,
|
cdo,
|
||||||
cdc
|
cdc
|
||||||
} TokenTypes;
|
};
|
||||||
|
|
||||||
|
|
||||||
class Token {
|
class Token {
|
||||||
@ -143,6 +274,7 @@ class Token {
|
|||||||
text.clear(); unit_at = 0; out_pos = 0; type = TokenType::whitespace;
|
text.clear(); unit_at = 0; out_pos = 0; type = TokenType::whitespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TokenType get_type() const { return type; }
|
||||||
void set_type(const TokenType q) { type = q; }
|
void set_type(const TokenType q) { type = q; }
|
||||||
void set_output_position(const size_t val) { out_pos = val; }
|
void set_output_position(const size_t val) { out_pos = val; }
|
||||||
bool is_type(const TokenType q) const { return type == q; }
|
bool is_type(const TokenType q) const { return type == q; }
|
||||||
@ -163,6 +295,17 @@ class Token {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool text_as_ascii_lowercase(std::string &scratch) {
|
||||||
|
scratch.clear();
|
||||||
|
for (auto ch : text) {
|
||||||
|
if (is_printable_ascii(ch)) {
|
||||||
|
if ('A' <= ch && ch <= 'Z') ch += 'a' - 'A';
|
||||||
|
scratch.push_back(ch);
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool is_keyword_case_insensitive(const char *lowercase_text) const {
|
bool is_keyword_case_insensitive(const char *lowercase_text) const {
|
||||||
return type == TokenType::ident && text_equals_case_insensitive(lowercase_text);
|
return type == TokenType::ident && text_equals_case_insensitive(lowercase_text);
|
||||||
}
|
}
|
||||||
@ -183,18 +326,37 @@ class Token {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject* text_as_python_string() const {
|
PyObject* get_text() const {
|
||||||
PyObject *ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, text.data(), text.size());
|
PyObject *ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, text.data(), text.size());
|
||||||
if (ans == NULL) throw python_error("Failed to convert token value to python unicode object");
|
if (ans == NULL) throw python_error("Failed to convert token value to python unicode object");
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_text_from_python_string(const PyObject* src) {
|
void erase_text_substring(size_t pos, size_t len) {
|
||||||
|
text.replace(pos, len, (size_t)0u, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_text(const PyObject* src) {
|
||||||
if (PyUnicode_READY(src) != 0) throw python_error("Failed to set token value from unicode object as readying the unicode obect failed");
|
if (PyUnicode_READY(src) != 0) throw python_error("Failed to set token value from unicode object as readying the unicode obect failed");
|
||||||
text.clear();
|
text.clear();
|
||||||
int kind = PyUnicode_KIND(src); void *data = PyUnicode_DATA(src);
|
int kind = PyUnicode_KIND(src); void *data = PyUnicode_DATA(src);
|
||||||
for (Py_ssize_t i = 0; i < PyUnicode_GET_LENGTH(src); i++) text.push_back(PyUnicode_READ(kind, data, i));
|
for (Py_ssize_t i = 0; i < PyUnicode_GET_LENGTH(src); i++) text.push_back(PyUnicode_READ(kind, data, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_text(const char* src) {
|
||||||
|
text.clear();
|
||||||
|
while(*src) text.push_back(*(src++));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_text(const frozen::string &src) {
|
||||||
|
text.clear();
|
||||||
|
for (size_t i = 0; i < src.size(); i++) text.push_back(src[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_dimension(std::string &scratch) {
|
||||||
|
if (!text_as_ascii_lowercase(scratch)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TokenQueue {
|
class TokenQueue {
|
||||||
@ -202,6 +364,7 @@ class TokenQueue {
|
|||||||
std::stack<Token> pool;
|
std::stack<Token> pool;
|
||||||
std::vector<Token> queue;
|
std::vector<Token> queue;
|
||||||
std::u32string out;
|
std::u32string out;
|
||||||
|
std::string scratch, scratch2;
|
||||||
pyobject_raii url_callback;
|
pyobject_raii url_callback;
|
||||||
|
|
||||||
void new_token(const TokenType type, const char32_t ch = 0) {
|
void new_token(const TokenType type, const char32_t ch = 0) {
|
||||||
@ -252,12 +415,12 @@ class TokenQueue {
|
|||||||
if (url_callback) {
|
if (url_callback) {
|
||||||
for (auto& tok : queue) {
|
for (auto& tok : queue) {
|
||||||
if (tok.is_type(type)) {
|
if (tok.is_type(type)) {
|
||||||
pyobject_raii url(tok.text_as_python_string());
|
pyobject_raii url(tok.get_text());
|
||||||
pyobject_raii new_url(PyObject_CallFunctionObjArgs(url_callback.ptr(), url.ptr(), NULL));
|
pyobject_raii new_url(PyObject_CallFunctionObjArgs(url_callback.ptr(), url.ptr(), NULL));
|
||||||
if (!new_url) { PyErr_Print(); }
|
if (!new_url) { PyErr_Print(); }
|
||||||
else {
|
else {
|
||||||
if (PyUnicode_Check(new_url.ptr()) && new_url.ptr() != url.ptr()) {
|
if (PyUnicode_Check(new_url.ptr()) && new_url.ptr() != url.ptr()) {
|
||||||
tok.set_text_from_python_string(new_url.ptr());
|
tok.set_text(new_url.ptr());
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,10 +432,10 @@ class TokenQueue {
|
|||||||
|
|
||||||
bool process_declaration() {
|
bool process_declaration() {
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
bool colon_found = false, key_found = false;
|
bool colon_found = false, key_found = false, keep_going = true;
|
||||||
std::function<bool(std::vector<Token>::iterator)> process_values;
|
std::function<bool(std::vector<Token>::iterator)> process_values;
|
||||||
|
|
||||||
for (auto it = queue.begin(); it < queue.end(); it++) {
|
for (auto it = queue.begin(); keep_going && it < queue.end(); it++) {
|
||||||
if (!it->is_significant()) continue;
|
if (!it->is_significant()) continue;
|
||||||
if (key_found) {
|
if (key_found) {
|
||||||
if (colon_found) {
|
if (colon_found) {
|
||||||
@ -285,8 +448,22 @@ class TokenQueue {
|
|||||||
} else {
|
} else {
|
||||||
if (it->is_type(TokenType::ident)) {
|
if (it->is_type(TokenType::ident)) {
|
||||||
key_found = true;
|
key_found = true;
|
||||||
if (it->text_equals_case_insensitive("font") || it->text_equals_case_insensitive("font-size")) {
|
if (!it->text_as_ascii_lowercase(scratch)) break; // not a printable ascii property name
|
||||||
process_values = std::bind(&TokenQueue::process_font_sizes, this, std::placeholders::_1);
|
frozen::string property_name(scratch.data(), scratch.size());
|
||||||
|
auto pit = known_properties.find(property_name);
|
||||||
|
if (pit == known_properties.end()) break; // not a known property
|
||||||
|
switch(pit->second) {
|
||||||
|
case PropertyType::font_size:
|
||||||
|
process_values = std::bind(&TokenQueue::process_font_sizes, this, std::placeholders::_1);
|
||||||
|
break;
|
||||||
|
case PropertyType::page_break:
|
||||||
|
it->erase_text_substring(0, 5);
|
||||||
|
changed = true; keep_going = false;
|
||||||
|
break;
|
||||||
|
case PropertyType::non_standard_writing_mode:
|
||||||
|
it->set_text("writing-mode");
|
||||||
|
changed = true; keep_going = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else break; // no property key found
|
} else break; // no property key found
|
||||||
}
|
}
|
||||||
@ -294,13 +471,34 @@ class TokenQueue {
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool process_font_sizes(std::vector<Token>::iterator) {
|
bool process_font_sizes(std::vector<Token>::iterator it) {
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
for (; it < queue.end(); it++) {
|
||||||
|
switch (it->get_type()) {
|
||||||
|
case TokenType::ident:
|
||||||
|
if (it->text_as_ascii_lowercase(scratch2)) {
|
||||||
|
frozen::string key(scratch2.data(), scratch2.size());
|
||||||
|
auto fsm = font_size_keywords.find(key);
|
||||||
|
if (fsm != font_size_keywords.end()) {
|
||||||
|
it->set_text(fsm->second);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TokenType::dimension:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TokenQueue(const size_t src_sz, PyObject *url_callback=NULL) : pool(), queue(), out(), url_callback(url_callback) { out.reserve(src_sz * 2); }
|
TokenQueue(const size_t src_sz, PyObject *url_callback=NULL) :
|
||||||
|
pool(), queue(), out(), scratch(), scratch2(), url_callback(url_callback) {
|
||||||
|
out.reserve(src_sz * 2); scratch.reserve(16); scratch2.reserve(16);
|
||||||
|
}
|
||||||
|
|
||||||
void rewind_output() { out.pop_back(); }
|
void rewind_output() { out.pop_back(); }
|
||||||
|
|
||||||
@ -820,6 +1018,20 @@ class Parser {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define handle_exceptions(msg) \
|
||||||
|
catch (std::bad_alloc &ex) { \
|
||||||
|
return PyErr_NoMemory(); \
|
||||||
|
} catch (python_error &ex) { \
|
||||||
|
return NULL; \
|
||||||
|
} catch (std::exception &ex) { \
|
||||||
|
PyErr_SetString(PyExc_Exception, ex.what()); \
|
||||||
|
return NULL; \
|
||||||
|
} catch (...) { \
|
||||||
|
PyErr_SetString(PyExc_Exception, msg); \
|
||||||
|
return NULL; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
transform_properties(const char32_t *src, size_t src_sz, bool is_declaration) {
|
transform_properties(const char32_t *src, size_t src_sz, bool is_declaration) {
|
||||||
try {
|
try {
|
||||||
@ -827,21 +1039,30 @@ transform_properties(const char32_t *src, size_t src_sz, bool is_declaration) {
|
|||||||
Parser parser(src, src_sz, is_declaration);
|
Parser parser(src, src_sz, is_declaration);
|
||||||
parser.parse(result);
|
parser.parse(result);
|
||||||
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, result.data(), result.size());
|
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, result.data(), result.size());
|
||||||
} catch (std::bad_alloc &ex) {
|
} handle_exceptions("Unknown error while parsing CSS");
|
||||||
return PyErr_NoMemory();
|
|
||||||
} catch (python_error &ex) {
|
|
||||||
return NULL;
|
|
||||||
} catch (std::exception &ex) {
|
|
||||||
PyErr_SetString(PyExc_Exception, ex.what());
|
|
||||||
return NULL;
|
|
||||||
} catch (...) {
|
|
||||||
PyErr_SetString(PyExc_Exception, "Unknown error while parsing CSS");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
parse_css_number_python(PyObject *self, PyObject *src) {
|
||||||
|
if (!PyUnicode_Check(src)) { PyErr_SetString(PyExc_TypeError, "Unicode string required"); return NULL; }
|
||||||
|
if (PyUnicode_READY(src) != 0) { return NULL; }
|
||||||
|
try {
|
||||||
|
std::u32string text;
|
||||||
|
text.reserve(PyUnicode_GET_LENGTH(src));
|
||||||
|
int kind = PyUnicode_KIND(src); void *data = PyUnicode_DATA(src);
|
||||||
|
for (Py_ssize_t i = 0; i < PyUnicode_GET_LENGTH(src); i++) text.push_back(PyUnicode_READ(kind, data, i));
|
||||||
|
|
||||||
|
ParsedNumber ans = parse_css_number<std::u32string>(text);
|
||||||
|
if (ans.is_integer) return PyLong_FromLongLong(ans.integer_value);
|
||||||
|
return PyFloat_FromDouble(ans.float_value);
|
||||||
|
} handle_exceptions("Unknown error while parsing CSS number");
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef handle_exceptions
|
||||||
static PyMethodDef methods[] = {
|
static PyMethodDef methods[] = {
|
||||||
|
{"parse_css_number", parse_css_number_python, METH_O,
|
||||||
|
"Parse a CSS number form a string"
|
||||||
|
},
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,13 +17,16 @@ from polyglot import http_client
|
|||||||
rmtree = partial(shutil.rmtree, ignore_errors=True)
|
rmtree = partial(shutil.rmtree, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
class BaseTest(unittest.TestCase):
|
class SimpleTest(unittest.TestCase):
|
||||||
|
|
||||||
longMessage = True
|
longMessage = True
|
||||||
maxDiff = None
|
maxDiff = None
|
||||||
|
|
||||||
ae = unittest.TestCase.assertEqual
|
ae = unittest.TestCase.assertEqual
|
||||||
|
|
||||||
|
|
||||||
|
class BaseTest(SimpleTest):
|
||||||
|
|
||||||
def run(self, result=None):
|
def run(self, result=None):
|
||||||
# we retry failing server tests since they are flaky on CI
|
# we retry failing server tests since they are flaky on CI
|
||||||
if result is None:
|
if result is None:
|
||||||
|
19
src/calibre/srv/tests/fast_css_transform.py
Normal file
19
src/calibre/srv/tests/fast_css_transform.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
|
||||||
|
import ast
|
||||||
|
|
||||||
|
from calibre.srv.tests.base import SimpleTest
|
||||||
|
|
||||||
|
|
||||||
|
class TestTransform(SimpleTest):
|
||||||
|
|
||||||
|
def test_number_parsing(self):
|
||||||
|
from calibre_extensions.fast_css_transform import parse_css_number
|
||||||
|
for x in '.314 -.314 0.314 0 2 +2 -1 1e2 -3.14E+2 2e-2'.split():
|
||||||
|
self.ae(parse_css_number(x), ast.literal_eval(x))
|
||||||
|
self.ae(parse_css_number('2em'), 2)
|
||||||
|
self.ae(parse_css_number('.3em'), 0.3)
|
||||||
|
self.ae(parse_css_number('3x3'), 3)
|
12
src/perfect-hashing/frozen/CMakeLists.txt
Normal file
12
src/perfect-hashing/frozen/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
target_sources(frozen-headers INTERFACE
|
||||||
|
"${prefix}/frozen/algorithm.h"
|
||||||
|
"${prefix}/frozen/map.h"
|
||||||
|
"${prefix}/frozen/random.h"
|
||||||
|
"${prefix}/frozen/set.h"
|
||||||
|
"${prefix}/frozen/string.h"
|
||||||
|
"${prefix}/frozen/unordered_map.h"
|
||||||
|
"${prefix}/frozen/unordered_set.h"
|
||||||
|
"${prefix}/frozen/bits/algorithms.h"
|
||||||
|
"${prefix}/frozen/bits/basic_types.h"
|
||||||
|
"${prefix}/frozen/bits/elsa.h"
|
||||||
|
"${prefix}/frozen/bits/pmh.h")
|
197
src/perfect-hashing/frozen/algorithm.h
Normal file
197
src/perfect-hashing/frozen/algorithm.h
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_ALGORITHM_H
|
||||||
|
#define FROZEN_LETITGO_ALGORITHM_H
|
||||||
|
|
||||||
|
#include "frozen/bits/basic_types.h"
|
||||||
|
#include "frozen/bits/version.h"
|
||||||
|
#include "frozen/string.h"
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
// 'search' implementation if C++17 is not available
|
||||||
|
// https://en.cppreference.com/w/cpp/algorithm/search
|
||||||
|
template<class ForwardIterator, class Searcher>
|
||||||
|
ForwardIterator search(ForwardIterator first, ForwardIterator last, const Searcher & searcher)
|
||||||
|
{
|
||||||
|
return searcher(first, last).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// text book implementation from
|
||||||
|
// https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
|
||||||
|
|
||||||
|
template <std::size_t size> class knuth_morris_pratt_searcher {
|
||||||
|
bits::carray<std::ptrdiff_t, size> step_;
|
||||||
|
bits::carray<char, size> needle_;
|
||||||
|
|
||||||
|
static constexpr bits::carray<std::ptrdiff_t, size>
|
||||||
|
build_kmp_cache(char const (&needle)[size + 1]) {
|
||||||
|
std::ptrdiff_t cnd = 0;
|
||||||
|
bits::carray<std::ptrdiff_t, size> cache;
|
||||||
|
|
||||||
|
cache.fill(-1);
|
||||||
|
for (std::size_t pos = 1; pos < size; ++pos) {
|
||||||
|
if (needle[pos] == needle[cnd]) {
|
||||||
|
cache[pos] = cache[cnd];
|
||||||
|
cnd += 1;
|
||||||
|
} else {
|
||||||
|
cache[pos] = cnd;
|
||||||
|
cnd = cache[cnd];
|
||||||
|
while (cnd >= 0 && needle[pos] != needle[cnd])
|
||||||
|
cnd = cache[cnd];
|
||||||
|
cnd += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr knuth_morris_pratt_searcher(char const (&needle)[size + 1])
|
||||||
|
: step_{build_kmp_cache(needle)}, needle_(needle) {}
|
||||||
|
|
||||||
|
template <class ForwardIterator>
|
||||||
|
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(ForwardIterator first, ForwardIterator last) const {
|
||||||
|
std::size_t i = 0;
|
||||||
|
ForwardIterator iter = first;
|
||||||
|
while (iter != last) {
|
||||||
|
if (needle_[i] == *iter) {
|
||||||
|
if (i == (size - 1))
|
||||||
|
return { iter - i, iter - i + size };
|
||||||
|
++i;
|
||||||
|
++iter;
|
||||||
|
} else {
|
||||||
|
if (step_[i] > -1) {
|
||||||
|
i = step_[i];
|
||||||
|
} else {
|
||||||
|
++iter;
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { last, last };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
constexpr knuth_morris_pratt_searcher<N - 1> make_knuth_morris_pratt_searcher(char const (&needle)[N]) {
|
||||||
|
return {needle};
|
||||||
|
}
|
||||||
|
|
||||||
|
// text book implementation from
|
||||||
|
// https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
|
||||||
|
|
||||||
|
template <std::size_t size> class boyer_moore_searcher {
|
||||||
|
using skip_table_type = bits::carray<std::ptrdiff_t, sizeof(char) << 8>;
|
||||||
|
using suffix_table_type = bits::carray<std::ptrdiff_t, size>;
|
||||||
|
|
||||||
|
skip_table_type skip_table_;
|
||||||
|
suffix_table_type suffix_table_;
|
||||||
|
bits::carray<char, size> needle_;
|
||||||
|
|
||||||
|
constexpr auto build_skip_table(char const (&needle)[size + 1]) {
|
||||||
|
skip_table_type skip_table;
|
||||||
|
|
||||||
|
skip_table.fill(size);
|
||||||
|
for (std::size_t i = 0; i < size - 1; ++i)
|
||||||
|
skip_table[needle[i]] -= i + 1;
|
||||||
|
return skip_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool is_prefix(char const (&needle)[size + 1], std::size_t pos) {
|
||||||
|
std::size_t suffixlen = size - pos;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < suffixlen; i++) {
|
||||||
|
if (needle[i] != needle[pos + i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::size_t suffix_length(char const (&needle)[size + 1],
|
||||||
|
std::size_t pos) {
|
||||||
|
// increment suffix length slen to the first mismatch or beginning
|
||||||
|
// of the word
|
||||||
|
for (std::size_t slen = 0; slen < pos ; slen++)
|
||||||
|
if (needle[pos - slen] != needle[size - 1 - slen])
|
||||||
|
return slen;
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto build_suffix_table(char const (&needle)[size + 1]) {
|
||||||
|
suffix_table_type suffix;
|
||||||
|
std::ptrdiff_t last_prefix_index = size - 1;
|
||||||
|
|
||||||
|
// first loop
|
||||||
|
for (std::ptrdiff_t p = size - 1; p >= 0; p--) {
|
||||||
|
if (is_prefix(needle, p + 1))
|
||||||
|
last_prefix_index = p + 1;
|
||||||
|
|
||||||
|
suffix[p] = last_prefix_index + (size - 1 - p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// second loop
|
||||||
|
for (std::size_t p = 0; p < size - 1; p++) {
|
||||||
|
auto slen = suffix_length(needle, p);
|
||||||
|
if (needle[p - slen] != needle[size - 1 - slen])
|
||||||
|
suffix[size - 1 - slen] = size - 1 - p + slen;
|
||||||
|
|
||||||
|
}
|
||||||
|
return suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr boyer_moore_searcher(char const (&needle)[size + 1])
|
||||||
|
: skip_table_{build_skip_table(needle)},
|
||||||
|
suffix_table_{build_suffix_table(needle)},
|
||||||
|
needle_(needle) {}
|
||||||
|
|
||||||
|
template <class ForwardIterator>
|
||||||
|
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(ForwardIterator first, ForwardIterator last) const {
|
||||||
|
if (size == 0)
|
||||||
|
return { first, first + size };
|
||||||
|
|
||||||
|
ForwardIterator iter = first + size - 1;
|
||||||
|
while (iter < last) {
|
||||||
|
std::ptrdiff_t j = size - 1;
|
||||||
|
while (j > 0 && (*iter == needle_[j])) {
|
||||||
|
--iter;
|
||||||
|
--j;
|
||||||
|
}
|
||||||
|
if (*iter == needle_[0])
|
||||||
|
return { iter, iter + size};
|
||||||
|
|
||||||
|
iter += std::max(skip_table_[*iter], suffix_table_[j]);
|
||||||
|
}
|
||||||
|
return { last, last + size};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
constexpr boyer_moore_searcher<N - 1> make_boyer_moore_searcher(char const (&needle)[N]) {
|
||||||
|
return {needle};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
229
src/perfect-hashing/frozen/bits/algorithms.h
Normal file
229
src/perfect-hashing/frozen/bits/algorithms.h
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_BITS_ALGORITHMS_H
|
||||||
|
#define FROZEN_LETITGO_BITS_ALGORITHMS_H
|
||||||
|
|
||||||
|
#include "frozen/bits/basic_types.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
namespace bits {
|
||||||
|
|
||||||
|
auto constexpr next_highest_power_of_two(std::size_t v) {
|
||||||
|
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||||
|
constexpr auto trip_count = std::numeric_limits<decltype(v)>::digits;
|
||||||
|
v--;
|
||||||
|
for(std::size_t i = 1; i < trip_count; i <<= 1)
|
||||||
|
v |= v >> i;
|
||||||
|
v++;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
auto constexpr log(T v) {
|
||||||
|
std::size_t n = 0;
|
||||||
|
while (v > 1) {
|
||||||
|
n += 1;
|
||||||
|
v >>= 1;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::size_t bit_weight(std::size_t n) {
|
||||||
|
return (n <= 8*sizeof(unsigned int))
|
||||||
|
+ (n <= 8*sizeof(unsigned long))
|
||||||
|
+ (n <= 8*sizeof(unsigned long long))
|
||||||
|
+ (n <= 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int select_uint_least(std::integral_constant<std::size_t, 4>);
|
||||||
|
unsigned long select_uint_least(std::integral_constant<std::size_t, 3>);
|
||||||
|
unsigned long long select_uint_least(std::integral_constant<std::size_t, 2>);
|
||||||
|
template<std::size_t N>
|
||||||
|
unsigned long long select_uint_least(std::integral_constant<std::size_t, N>) {
|
||||||
|
static_assert(N < 2, "unsupported type size");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
using select_uint_least_t = decltype(select_uint_least(std::integral_constant<std::size_t, bit_weight(N)>()));
|
||||||
|
|
||||||
|
template <typename Iter, typename Compare>
|
||||||
|
constexpr auto min_element(Iter begin, const Iter end,
|
||||||
|
Compare const &compare) {
|
||||||
|
auto result = begin;
|
||||||
|
while (begin != end) {
|
||||||
|
if (compare(*begin, *result)) {
|
||||||
|
result = begin;
|
||||||
|
}
|
||||||
|
++begin;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
constexpr void cswap(T &a, T &b) {
|
||||||
|
auto tmp = a;
|
||||||
|
a = b;
|
||||||
|
b = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class U>
|
||||||
|
constexpr void cswap(std::pair<T, U> & a, std::pair<T, U> & b) {
|
||||||
|
cswap(a.first, b.first);
|
||||||
|
cswap(a.second, b.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Tys, std::size_t... Is>
|
||||||
|
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b, std::index_sequence<Is...>) {
|
||||||
|
using swallow = int[];
|
||||||
|
(void) swallow{(cswap(std::get<Is>(a), std::get<Is>(b)), 0)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Tys>
|
||||||
|
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b) {
|
||||||
|
cswap(a, b, std::make_index_sequence<sizeof...(Tys)>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator, class Compare>
|
||||||
|
constexpr Iterator partition(Iterator left, Iterator right, Compare const &compare) {
|
||||||
|
auto pivot = left + (right - left) / 2;
|
||||||
|
auto value = *pivot;
|
||||||
|
cswap(*right, *pivot);
|
||||||
|
for (auto it = left; 0 < right - it; ++it) {
|
||||||
|
if (compare(*it, value)) {
|
||||||
|
cswap(*it, *left);
|
||||||
|
left++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cswap(*right, *left);
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator, class Compare>
|
||||||
|
constexpr void quicksort(Iterator left, Iterator right, Compare const &compare) {
|
||||||
|
while (0 < right - left) {
|
||||||
|
auto new_pivot = bits::partition(left, right, compare);
|
||||||
|
quicksort(left, new_pivot, compare);
|
||||||
|
left = new_pivot + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t N, class Compare>
|
||||||
|
constexpr bits::carray<T, N> quicksort(bits::carray<T, N> const &array,
|
||||||
|
Compare const &compare) {
|
||||||
|
bits::carray<T, N> res = array;
|
||||||
|
quicksort(res.begin(), res.end() - 1, compare);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class Compare> struct LowerBound {
|
||||||
|
T const &value_;
|
||||||
|
Compare const &compare_;
|
||||||
|
constexpr LowerBound(T const &value, Compare const &compare)
|
||||||
|
: value_(value), compare_(compare) {}
|
||||||
|
|
||||||
|
template <class ForwardIt>
|
||||||
|
inline constexpr ForwardIt doit_fast(ForwardIt first,
|
||||||
|
std::integral_constant<std::size_t, 0>) {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class ForwardIt, std::size_t N>
|
||||||
|
inline constexpr ForwardIt doit_fast(ForwardIt first,
|
||||||
|
std::integral_constant<std::size_t, N>) {
|
||||||
|
auto constexpr step = N / 2;
|
||||||
|
static_assert(N/2 == N - N / 2 - 1, "power of two minus 1");
|
||||||
|
auto it = first + step;
|
||||||
|
auto next_it = compare_(*it, value_) ? it + 1 : first;
|
||||||
|
return doit_fast(next_it, std::integral_constant<std::size_t, N / 2>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class ForwardIt, std::size_t N>
|
||||||
|
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, N>, std::integral_constant<bool, true>) {
|
||||||
|
return doit_fast(first, std::integral_constant<std::size_t, N>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class ForwardIt, std::size_t N>
|
||||||
|
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, N>, std::integral_constant<bool, false>) {
|
||||||
|
auto constexpr next_power = next_highest_power_of_two(N);
|
||||||
|
auto constexpr next_start = next_power / 2 - 1;
|
||||||
|
auto it = first + next_start;
|
||||||
|
if (compare_(*it, value_)) {
|
||||||
|
auto constexpr next = N - next_start - 1;
|
||||||
|
return doitfirst(it + 1, std::integral_constant<std::size_t, next>{}, std::integral_constant<bool, next_highest_power_of_two(next) - 1 == next>{});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return doit_fast(first, std::integral_constant<std::size_t, next_start>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class ForwardIt>
|
||||||
|
inline constexpr ForwardIt doitfirst(ForwardIt first, std::integral_constant<std::size_t, 1>, std::integral_constant<bool, false>) {
|
||||||
|
return doit_fast(first, std::integral_constant<std::size_t, 1>{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N, class ForwardIt, class T, class Compare>
|
||||||
|
constexpr ForwardIt lower_bound(ForwardIt first, const T &value, Compare const &compare) {
|
||||||
|
return LowerBound<T, Compare>{value, compare}.doitfirst(first, std::integral_constant<std::size_t, N>{}, std::integral_constant<bool, next_highest_power_of_two(N) - 1 == N>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t N, class Compare, class ForwardIt, class T>
|
||||||
|
constexpr bool binary_search(ForwardIt first, const T &value,
|
||||||
|
Compare const &compare) {
|
||||||
|
ForwardIt where = lower_bound<N>(first, value, compare);
|
||||||
|
return (!(where == first + N) && !(compare(value, *where)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class InputIt1, class InputIt2>
|
||||||
|
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2)
|
||||||
|
{
|
||||||
|
for (; first1 != last1; ++first1, ++first2) {
|
||||||
|
if (!(*first1 == *first2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class InputIt1, class InputIt2>
|
||||||
|
constexpr bool lexicographical_compare(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
|
||||||
|
{
|
||||||
|
for (; (first1 != last1) && (first2 != last2); ++first1, ++first2) {
|
||||||
|
if (*first1 < *first2)
|
||||||
|
return true;
|
||||||
|
if (*first2 < *first1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (first1 == last1) && (first2 != last2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bits
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
207
src/perfect-hashing/frozen/bits/basic_types.h
Normal file
207
src/perfect-hashing/frozen/bits/basic_types.h
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_BASIC_TYPES_H
|
||||||
|
#define FROZEN_LETITGO_BASIC_TYPES_H
|
||||||
|
|
||||||
|
#include "frozen/bits/exceptions.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <utility>
|
||||||
|
#include <iterator>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
namespace bits {
|
||||||
|
|
||||||
|
// used as a fake argument for frozen::make_set and frozen::make_map in the case of N=0
|
||||||
|
struct ignored_arg {};
|
||||||
|
|
||||||
|
template <class T, std::size_t N>
|
||||||
|
class cvector {
|
||||||
|
T data [N] = {}; // zero-initialization for scalar type T, default-initialized otherwise
|
||||||
|
std::size_t dsize = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Container typdefs
|
||||||
|
using value_type = T;
|
||||||
|
using reference = value_type &;
|
||||||
|
using const_reference = const value_type &;
|
||||||
|
using pointer = value_type *;
|
||||||
|
using const_pointer = const value_type *;
|
||||||
|
using iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
constexpr cvector(void) = default;
|
||||||
|
constexpr cvector(size_type count, const T& value) : dsize(count) {
|
||||||
|
for (std::size_t i = 0; i < N; ++i)
|
||||||
|
data[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterators
|
||||||
|
constexpr iterator begin() noexcept { return data; }
|
||||||
|
constexpr iterator end() noexcept { return data + dsize; }
|
||||||
|
|
||||||
|
// Capacity
|
||||||
|
constexpr size_type size() const { return dsize; }
|
||||||
|
|
||||||
|
// Element access
|
||||||
|
constexpr reference operator[](std::size_t index) { return data[index]; }
|
||||||
|
constexpr const_reference operator[](std::size_t index) const { return data[index]; }
|
||||||
|
|
||||||
|
constexpr reference back() { return data[dsize - 1]; }
|
||||||
|
constexpr const_reference back() const { return data[dsize - 1]; }
|
||||||
|
|
||||||
|
// Modifiers
|
||||||
|
constexpr void push_back(const T & a) { data[dsize++] = a; }
|
||||||
|
constexpr void push_back(T && a) { data[dsize++] = std::move(a); }
|
||||||
|
constexpr void pop_back() { --dsize; }
|
||||||
|
|
||||||
|
constexpr void clear() { dsize = 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, std::size_t N>
|
||||||
|
class carray {
|
||||||
|
T data_ [N] = {}; // zero-initialization for scalar type T, default-initialized otherwise
|
||||||
|
|
||||||
|
template <std::size_t M, std::size_t... I>
|
||||||
|
constexpr carray(T const (&init)[M], std::index_sequence<I...>)
|
||||||
|
: data_{init[I]...} {}
|
||||||
|
template <class Iter, std::size_t... I>
|
||||||
|
constexpr carray(Iter iter, std::index_sequence<I...>)
|
||||||
|
: data_{((void)I, *iter++)...} {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Container typdefs
|
||||||
|
using value_type = T;
|
||||||
|
using reference = value_type &;
|
||||||
|
using const_reference = const value_type &;
|
||||||
|
using pointer = value_type *;
|
||||||
|
using const_pointer = const value_type *;
|
||||||
|
using iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
constexpr carray(void) = default;
|
||||||
|
template <std::size_t M>
|
||||||
|
constexpr carray(T const (&init)[M])
|
||||||
|
: carray(init, std::make_index_sequence<N>())
|
||||||
|
{
|
||||||
|
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
|
||||||
|
}
|
||||||
|
template <std::size_t M>
|
||||||
|
constexpr carray(std::array<T, M> const &init)
|
||||||
|
: carray(&init[0], std::make_index_sequence<N>())
|
||||||
|
{
|
||||||
|
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
|
||||||
|
}
|
||||||
|
constexpr carray(std::initializer_list<T> init)
|
||||||
|
: carray(init.begin(), std::make_index_sequence<N>())
|
||||||
|
{
|
||||||
|
// clang & gcc doesn't recognize init.size() as a constexpr
|
||||||
|
// static_assert(init.size() >= N, "Cannot initialize a carray with an smaller initializer list");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterators
|
||||||
|
constexpr iterator begin() noexcept { return data_; }
|
||||||
|
constexpr const_iterator begin() const noexcept { return data_; }
|
||||||
|
constexpr const_iterator cbegin() const noexcept { return data_; }
|
||||||
|
constexpr iterator end() noexcept { return data_ + N; }
|
||||||
|
constexpr const_iterator end() const noexcept { return data_ + N; }
|
||||||
|
constexpr const_iterator cend() const noexcept { return data_ + N; }
|
||||||
|
|
||||||
|
constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
|
||||||
|
constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
|
||||||
|
constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); }
|
||||||
|
constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
|
||||||
|
constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
|
||||||
|
constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); }
|
||||||
|
|
||||||
|
// Capacity
|
||||||
|
constexpr size_type size() const { return N; }
|
||||||
|
constexpr size_type max_size() const { return N; }
|
||||||
|
|
||||||
|
// Element access
|
||||||
|
constexpr reference operator[](std::size_t index) { return data_[index]; }
|
||||||
|
constexpr const_reference operator[](std::size_t index) const { return data_[index]; }
|
||||||
|
|
||||||
|
constexpr reference at(std::size_t index) {
|
||||||
|
if (index > N)
|
||||||
|
FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (" + std::to_string(N) + ')'));
|
||||||
|
return data_[index];
|
||||||
|
}
|
||||||
|
constexpr const_reference at(std::size_t index) const {
|
||||||
|
if (index > N)
|
||||||
|
FROZEN_THROW_OR_ABORT(std::out_of_range("Index (" + std::to_string(index) + ") out of bound (" + std::to_string(N) + ')'));
|
||||||
|
return data_[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr reference front() { return data_[0]; }
|
||||||
|
constexpr const_reference front() const { return data_[0]; }
|
||||||
|
|
||||||
|
constexpr reference back() { return data_[N - 1]; }
|
||||||
|
constexpr const_reference back() const { return data_[N - 1]; }
|
||||||
|
|
||||||
|
constexpr value_type* data() noexcept { return data_; }
|
||||||
|
constexpr const value_type* data() const noexcept { return data_; }
|
||||||
|
|
||||||
|
// Modifiers
|
||||||
|
constexpr void fill(const value_type& val) {
|
||||||
|
for (std::size_t i = 0; i < N; ++i)
|
||||||
|
data_[i] = val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <class T>
|
||||||
|
class carray<T, 0> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Container typdefs
|
||||||
|
using value_type = T;
|
||||||
|
using reference = value_type &;
|
||||||
|
using const_reference = const value_type &;
|
||||||
|
using pointer = value_type *;
|
||||||
|
using const_pointer = const value_type *;
|
||||||
|
using iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
constexpr carray(void) = default;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bits
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
40
src/perfect-hashing/frozen/bits/constexpr_assert.h
Normal file
40
src/perfect-hashing/frozen/bits/constexpr_assert.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_CONSTEXPR_ASSERT_H
|
||||||
|
#define FROZEN_LETITGO_CONSTEXPR_ASSERT_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
// FIXME: find a way to implement that correctly for msvc
|
||||||
|
#define constexpr_assert(cond, msg)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define constexpr_assert(cond, msg)\
|
||||||
|
assert(cond && msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
58
src/perfect-hashing/frozen/bits/defines.h
Normal file
58
src/perfect-hashing/frozen/bits/defines.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_DEFINES_H
|
||||||
|
#define FROZEN_LETITGO_DEFINES_H
|
||||||
|
|
||||||
|
#if defined(_MSVC_LANG) && !(defined(__EDG__) && defined(__clang__)) // TRANSITION, VSO#273681
|
||||||
|
#define FROZEN_LETITGO_IS_MSVC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Code taken from https://stackoverflow.com/questions/43639122/which-values-can-msvc-lang-have
|
||||||
|
#if defined(FROZEN_LETITGO_IS_MSVC)
|
||||||
|
#if _MSVC_LANG > 201402
|
||||||
|
#define FROZEN_LETITGO_HAS_CXX17 1
|
||||||
|
#else /* _MSVC_LANG > 201402 */
|
||||||
|
#define FROZEN_LETITGO_HAS_CXX17 0
|
||||||
|
#endif /* _MSVC_LANG > 201402 */
|
||||||
|
#else /* _MSVC_LANG etc. */
|
||||||
|
#if __cplusplus > 201402
|
||||||
|
#define FROZEN_LETITGO_HAS_CXX17 1
|
||||||
|
#else /* __cplusplus > 201402 */
|
||||||
|
#define FROZEN_LETITGO_HAS_CXX17 0
|
||||||
|
#endif /* __cplusplus > 201402 */
|
||||||
|
#endif /* _MSVC_LANG etc. */
|
||||||
|
// End if taken code
|
||||||
|
|
||||||
|
#if FROZEN_LETITGO_HAS_CXX17 == 1 && defined(FROZEN_LETITGO_IS_MSVC)
|
||||||
|
#define FROZEN_LETITGO_HAS_STRING_VIEW // We assume Visual Studio always has string_view in C++17
|
||||||
|
#else
|
||||||
|
#if FROZEN_LETITGO_HAS_CXX17 == 1 && __has_include(<string_view>)
|
||||||
|
#define FROZEN_LETITGO_HAS_STRING_VIEW
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cpp_char8_t
|
||||||
|
#define FROZEN_LETITGO_HAS_CHAR8T
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // FROZEN_LETITGO_DEFINES_H
|
50
src/perfect-hashing/frozen/bits/elsa.h
Normal file
50
src/perfect-hashing/frozen/bits/elsa.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_ELSA_H
|
||||||
|
#define FROZEN_LETITGO_ELSA_H
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
template <class T> struct elsa {
|
||||||
|
static_assert(std::is_integral<T>::value || std::is_enum<T>::value,
|
||||||
|
"only supports integral types, specialize for other types");
|
||||||
|
|
||||||
|
constexpr std::size_t operator()(T const &value, std::size_t seed) const {
|
||||||
|
std::size_t key = seed ^ static_cast<std::size_t>(value);
|
||||||
|
key = (~key) + (key << 21); // key = (key << 21) - key - 1;
|
||||||
|
key = key ^ (key >> 24);
|
||||||
|
key = (key + (key << 3)) + (key << 8); // key * 265
|
||||||
|
key = key ^ (key >> 14);
|
||||||
|
key = (key + (key << 2)) + (key << 4); // key * 21
|
||||||
|
key = key ^ (key >> 28);
|
||||||
|
key = key + (key << 31);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T> using anna = elsa<T>;
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
39
src/perfect-hashing/frozen/bits/exceptions.h
Normal file
39
src/perfect-hashing/frozen/bits/exceptions.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_EXCEPTIONS_H
|
||||||
|
#define FROZEN_LETITGO_EXCEPTIONS_H
|
||||||
|
|
||||||
|
#if defined(FROZEN_NO_EXCEPTIONS) || (defined(_MSC_VER) && !defined(_CPPUNWIND)) || (!defined(_MSC_VER) && !defined(__cpp_exceptions))
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#define FROZEN_THROW_OR_ABORT(_) std::abort()
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#define FROZEN_THROW_OR_ABORT(err) throw err
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
240
src/perfect-hashing/frozen/bits/pmh.h
Normal file
240
src/perfect-hashing/frozen/bits/pmh.h
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// inspired from http://stevehanov.ca/blog/index.php?id=119
|
||||||
|
#ifndef FROZEN_LETITGO_PMH_H
|
||||||
|
#define FROZEN_LETITGO_PMH_H
|
||||||
|
|
||||||
|
#include "frozen/bits/algorithms.h"
|
||||||
|
#include "frozen/bits/basic_types.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
namespace bits {
|
||||||
|
|
||||||
|
// Function object for sorting buckets in decreasing order of size
|
||||||
|
struct bucket_size_compare {
|
||||||
|
template <typename B>
|
||||||
|
bool constexpr operator()(B const &b0,
|
||||||
|
B const &b1) const {
|
||||||
|
return b0.size() > b1.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step One in pmh routine is to take all items and hash them into buckets,
|
||||||
|
// with some collisions. Then process those buckets further to build a perfect
|
||||||
|
// hash function.
|
||||||
|
// pmh_buckets represents the initial placement into buckets.
|
||||||
|
|
||||||
|
template <size_t M>
|
||||||
|
struct pmh_buckets {
|
||||||
|
// Step 0: Bucket max is 2 * sqrt M
|
||||||
|
// TODO: Come up with justification for this, should it not be O(log M)?
|
||||||
|
static constexpr auto bucket_max = 2 * (1u << (log(M) / 2));
|
||||||
|
|
||||||
|
using bucket_t = cvector<std::size_t, bucket_max>;
|
||||||
|
carray<bucket_t, M> buckets;
|
||||||
|
uint64_t seed;
|
||||||
|
|
||||||
|
// Represents a reference to a bucket. This is used because the buckets
|
||||||
|
// have to be sorted, but buckets are big, making it slower than sorting refs
|
||||||
|
struct bucket_ref {
|
||||||
|
unsigned hash;
|
||||||
|
const bucket_t * ptr;
|
||||||
|
|
||||||
|
// Forward some interface of bucket
|
||||||
|
using value_type = typename bucket_t::value_type;
|
||||||
|
using const_iterator = typename bucket_t::const_iterator;
|
||||||
|
|
||||||
|
constexpr auto size() const { return ptr->size(); }
|
||||||
|
constexpr const auto & operator[](std::size_t idx) const { return (*ptr)[idx]; }
|
||||||
|
constexpr auto begin() const { return ptr->begin(); }
|
||||||
|
constexpr auto end() const { return ptr->end(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make a bucket_ref for each bucket
|
||||||
|
template <std::size_t... Is>
|
||||||
|
carray<bucket_ref, M> constexpr make_bucket_refs(std::index_sequence<Is...>) const {
|
||||||
|
return {{ bucket_ref{Is, &buckets[Is]}... }};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes a bucket_ref for each bucket and sorts them by size
|
||||||
|
carray<bucket_ref, M> constexpr get_sorted_buckets() const {
|
||||||
|
carray<bucket_ref, M> result{this->make_bucket_refs(std::make_index_sequence<M>())};
|
||||||
|
bits::quicksort(result.begin(), result.end() - 1, bucket_size_compare{});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t M, class Item, size_t N, class Hash, class Key, class PRG>
|
||||||
|
pmh_buckets<M> constexpr make_pmh_buckets(const carray<Item, N> & items,
|
||||||
|
Hash const & hash,
|
||||||
|
Key const & key,
|
||||||
|
PRG & prg) {
|
||||||
|
using result_t = pmh_buckets<M>;
|
||||||
|
result_t result{};
|
||||||
|
bool rejected = false;
|
||||||
|
// Continue until all items are placed without exceeding bucket_max
|
||||||
|
while (1) {
|
||||||
|
for (auto & b : result.buckets) {
|
||||||
|
b.clear();
|
||||||
|
}
|
||||||
|
result.seed = prg();
|
||||||
|
rejected = false;
|
||||||
|
for (std::size_t i = 0; i < N; ++i) {
|
||||||
|
auto & bucket = result.buckets[hash(key(items[i]), static_cast<size_t>(result.seed)) % M];
|
||||||
|
if (bucket.size() >= result_t::bucket_max) {
|
||||||
|
rejected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bucket.push_back(i);
|
||||||
|
}
|
||||||
|
if (!rejected) { return result; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if an item appears in a cvector
|
||||||
|
template<class T, size_t N>
|
||||||
|
constexpr bool all_different_from(cvector<T, N> & data, T & a) {
|
||||||
|
for (std::size_t i = 0; i < data.size(); ++i)
|
||||||
|
if (data[i] == a)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represents either an index to a data item array, or a seed to be used with
|
||||||
|
// a hasher. Seed must have high bit of 1, value has high bit of zero.
|
||||||
|
struct seed_or_index {
|
||||||
|
using value_type = uint64_t;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr value_type MINUS_ONE = std::numeric_limits<value_type>::max();
|
||||||
|
static constexpr value_type HIGH_BIT = ~(MINUS_ONE >> 1);
|
||||||
|
|
||||||
|
value_type value_ = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr value_type value() const { return value_; }
|
||||||
|
constexpr bool is_seed() const { return value_ & HIGH_BIT; }
|
||||||
|
|
||||||
|
constexpr seed_or_index(bool is_seed, value_type value)
|
||||||
|
: value_(is_seed ? (value | HIGH_BIT) : (value & ~HIGH_BIT)) {}
|
||||||
|
|
||||||
|
constexpr seed_or_index() = default;
|
||||||
|
constexpr seed_or_index(const seed_or_index &) = default;
|
||||||
|
constexpr seed_or_index & operator =(const seed_or_index &) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Represents the perfect hash function created by pmh algorithm
|
||||||
|
template <std::size_t M, class Hasher>
|
||||||
|
struct pmh_tables {
|
||||||
|
uint64_t first_seed_;
|
||||||
|
carray<seed_or_index, M> first_table_;
|
||||||
|
carray<std::size_t, M> second_table_;
|
||||||
|
Hasher hash_;
|
||||||
|
|
||||||
|
// Looks up a given key, to find its expected index in carray<Item, N>
|
||||||
|
// Always returns a valid index, must use KeyEqual test after to confirm.
|
||||||
|
template <typename KeyType>
|
||||||
|
constexpr std::size_t lookup(const KeyType & key) const {
|
||||||
|
auto const d = first_table_[hash_(key, static_cast<size_t>(first_seed_)) % M];
|
||||||
|
if (!d.is_seed()) { return static_cast<std::size_t>(d.value()); } // this is narrowing uint64 -> size_t but should be fine
|
||||||
|
else { return second_table_[hash_(key, static_cast<std::size_t>(d.value())) % M]; }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make pmh tables for given items, hash function, prg, etc.
|
||||||
|
template <std::size_t M, class Item, std::size_t N, class Hash, class Key, class PRG>
|
||||||
|
pmh_tables<M, Hash> constexpr make_pmh_tables(const carray<Item, N> &
|
||||||
|
items,
|
||||||
|
Hash const &hash,
|
||||||
|
Key const &key,
|
||||||
|
PRG prg) {
|
||||||
|
// Step 1: Place all of the keys into buckets
|
||||||
|
auto step_one = make_pmh_buckets<M>(items, hash, key, prg);
|
||||||
|
|
||||||
|
// Step 2: Sort the buckets to process the ones with the most items first.
|
||||||
|
auto buckets = step_one.get_sorted_buckets();
|
||||||
|
|
||||||
|
// G becomes the first hash table in the resulting pmh function
|
||||||
|
carray<seed_or_index, M> G; // Default constructed to "index 0"
|
||||||
|
|
||||||
|
// H becomes the second hash table in the resulting pmh function
|
||||||
|
constexpr std::size_t UNUSED = std::numeric_limits<std::size_t>::max();
|
||||||
|
carray<std::size_t, M> H;
|
||||||
|
H.fill(UNUSED);
|
||||||
|
|
||||||
|
// Step 3: Map the items in buckets into hash tables.
|
||||||
|
for (const auto & bucket : buckets) {
|
||||||
|
auto const bsize = bucket.size();
|
||||||
|
|
||||||
|
if (bsize == 1) {
|
||||||
|
// Store index to the (single) item in G
|
||||||
|
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
|
||||||
|
G[bucket.hash] = {false, static_cast<uint64_t>(bucket[0])};
|
||||||
|
} else if (bsize > 1) {
|
||||||
|
|
||||||
|
// Repeatedly try different H of d until we find a hash function
|
||||||
|
// that places all items in the bucket into free slots
|
||||||
|
seed_or_index d{true, prg()};
|
||||||
|
cvector<std::size_t, decltype(step_one)::bucket_max> bucket_slots;
|
||||||
|
|
||||||
|
while (bucket_slots.size() < bsize) {
|
||||||
|
auto slot = hash(key(items[bucket[bucket_slots.size()]]), static_cast<size_t>(d.value())) % M;
|
||||||
|
|
||||||
|
if (H[slot] != UNUSED || !all_different_from(bucket_slots, slot)) {
|
||||||
|
bucket_slots.clear();
|
||||||
|
d = {true, prg()};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket_slots.push_back(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put successful seed in G, and put indices to items in their slots
|
||||||
|
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
|
||||||
|
G[bucket.hash] = d;
|
||||||
|
for (std::size_t i = 0; i < bsize; ++i)
|
||||||
|
H[bucket_slots[i]] = bucket[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any unused entries in the H table have to get changed to zero.
|
||||||
|
// This is because hashing should not fail or return an out-of-bounds entry.
|
||||||
|
// A lookup fails after we apply user-supplied KeyEqual to the query and the
|
||||||
|
// key found by hashing. Sending such queries to zero cannot hurt.
|
||||||
|
for (std::size_t i = 0; i < M; ++i)
|
||||||
|
if (H[i] == UNUSED)
|
||||||
|
H[i] = 0;
|
||||||
|
|
||||||
|
return {step_one.seed, G, H, hash};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bits
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
30
src/perfect-hashing/frozen/bits/version.h
Normal file
30
src/perfect-hashing/frozen/bits/version.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_VERSION_H
|
||||||
|
#define FROZEN_LETITGO_VERSION_H
|
||||||
|
|
||||||
|
#define FROZEN_MAJOR_VERSION 1
|
||||||
|
#define FROZEN_MINOR_VERSION 0
|
||||||
|
#define FROZEN_PATCH_VERSION 1
|
||||||
|
|
||||||
|
#endif
|
328
src/perfect-hashing/frozen/map.h
Normal file
328
src/perfect-hashing/frozen/map.h
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_MAP_H
|
||||||
|
#define FROZEN_LETITGO_MAP_H
|
||||||
|
|
||||||
|
#include "frozen/bits/algorithms.h"
|
||||||
|
#include "frozen/bits/basic_types.h"
|
||||||
|
#include "frozen/bits/constexpr_assert.h"
|
||||||
|
#include "frozen/bits/exceptions.h"
|
||||||
|
#include "frozen/bits/version.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
template <class Comparator> class CompareKey {
|
||||||
|
|
||||||
|
Comparator const comparator_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr CompareKey(Comparator const &comparator)
|
||||||
|
: comparator_(comparator) {}
|
||||||
|
|
||||||
|
template <class Key, class Value>
|
||||||
|
constexpr int operator()(std::pair<Key, Value> const &self,
|
||||||
|
std::pair<Key, Value> const &other) const {
|
||||||
|
return comparator_(std::get<0>(self), std::get<0>(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key, class Value>
|
||||||
|
constexpr int operator()(Key const &self_key,
|
||||||
|
std::pair<Key, Value> const &other) const {
|
||||||
|
return comparator_(self_key, std::get<0>(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key, class Value>
|
||||||
|
constexpr int operator()(std::pair<Key, Value> const &self,
|
||||||
|
Key const &other_key) const {
|
||||||
|
return comparator_(std::get<0>(self), other_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Key>
|
||||||
|
constexpr int operator()(Key const &self_key, Key const &other_key) const {
|
||||||
|
return comparator_(self_key, other_key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
template <class Key, class Value, std::size_t N, class Compare = std::less<Key>>
|
||||||
|
class map {
|
||||||
|
using container_type = bits::carray<std::pair<Key, Value>, N>;
|
||||||
|
impl::CompareKey<Compare> less_than_;
|
||||||
|
container_type items_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using mapped_type = Value;
|
||||||
|
using value_type = typename container_type::value_type;
|
||||||
|
using size_type = typename container_type::size_type;
|
||||||
|
using difference_type = typename container_type::difference_type;
|
||||||
|
using key_compare = decltype(less_than_);
|
||||||
|
using reference = typename container_type::reference;
|
||||||
|
using const_reference = typename container_type::const_reference;
|
||||||
|
using pointer = typename container_type::pointer;
|
||||||
|
using const_pointer = typename container_type::const_pointer;
|
||||||
|
using iterator = typename container_type::iterator;
|
||||||
|
using const_iterator = typename container_type::const_iterator;
|
||||||
|
using reverse_iterator = typename container_type::reverse_iterator;
|
||||||
|
using const_reverse_iterator =
|
||||||
|
typename container_type::const_reverse_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* constructors */
|
||||||
|
constexpr map(container_type items, Compare const &compare)
|
||||||
|
: less_than_{compare}
|
||||||
|
, items_{bits::quicksort(items, less_than_)} {}
|
||||||
|
|
||||||
|
explicit constexpr map(container_type items)
|
||||||
|
: map{items, Compare{}} {}
|
||||||
|
|
||||||
|
constexpr map(std::initializer_list<value_type> items, Compare const &compare)
|
||||||
|
: map{container_type {items}, compare} {
|
||||||
|
constexpr_assert(items.size() == N, "Inconsistent initializer_list size and type size argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr map(std::initializer_list<value_type> items)
|
||||||
|
: map{items, Compare{}} {}
|
||||||
|
|
||||||
|
/* element access */
|
||||||
|
constexpr Value const& at(Key const &key) const {
|
||||||
|
return at_impl(*this, key);
|
||||||
|
}
|
||||||
|
constexpr Value& at(Key const &key) {
|
||||||
|
return at_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterators */
|
||||||
|
constexpr iterator begin() { return items_.begin(); }
|
||||||
|
constexpr const_iterator begin() const { return items_.begin(); }
|
||||||
|
constexpr const_iterator cbegin() const { return items_.cbegin(); }
|
||||||
|
constexpr iterator end() { return items_.end(); }
|
||||||
|
constexpr const_iterator end() const { return items_.end(); }
|
||||||
|
constexpr const_iterator cend() const { return items_.cend(); }
|
||||||
|
|
||||||
|
constexpr reverse_iterator rbegin() { return items_.rbegin(); }
|
||||||
|
constexpr const_reverse_iterator rbegin() const { return items_.rbegin(); }
|
||||||
|
constexpr const_reverse_iterator crbegin() const { return items_.crbegin(); }
|
||||||
|
constexpr reverse_iterator rend() { return items_.rend(); }
|
||||||
|
constexpr const_reverse_iterator rend() const { return items_.rend(); }
|
||||||
|
constexpr const_reverse_iterator crend() const { return items_.crend(); }
|
||||||
|
|
||||||
|
/* capacity */
|
||||||
|
constexpr bool empty() const { return !N; }
|
||||||
|
constexpr size_type size() const { return N; }
|
||||||
|
constexpr size_type max_size() const { return N; }
|
||||||
|
|
||||||
|
/* lookup */
|
||||||
|
|
||||||
|
constexpr std::size_t count(Key const &key) const {
|
||||||
|
return bits::binary_search<N>(items_.begin(), key, less_than_);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator find(Key const &key) const {
|
||||||
|
return find_impl(*this, key);
|
||||||
|
}
|
||||||
|
constexpr iterator find(Key const &key) {
|
||||||
|
return find_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::pair<const_iterator, const_iterator>
|
||||||
|
equal_range(Key const &key) const {
|
||||||
|
return equal_range_impl(*this, key);
|
||||||
|
}
|
||||||
|
constexpr std::pair<iterator, iterator> equal_range(Key const &key) {
|
||||||
|
return equal_range_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator lower_bound(Key const &key) const {
|
||||||
|
return lower_bound_impl(*this, key);
|
||||||
|
}
|
||||||
|
constexpr iterator lower_bound(Key const &key) {
|
||||||
|
return lower_bound_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator upper_bound(Key const &key) const {
|
||||||
|
return upper_bound_impl(*this, key);
|
||||||
|
}
|
||||||
|
constexpr iterator upper_bound(Key const &key) {
|
||||||
|
return upper_bound_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* observers */
|
||||||
|
constexpr key_compare key_comp() const { return less_than_; }
|
||||||
|
constexpr key_compare value_comp() const { return less_than_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class This>
|
||||||
|
static inline constexpr auto& at_impl(This&& self, Key const &key) {
|
||||||
|
auto where = self.lower_bound(key);
|
||||||
|
if (where != self.end())
|
||||||
|
return where->second;
|
||||||
|
else
|
||||||
|
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class This>
|
||||||
|
static inline constexpr auto find_impl(This&& self, Key const &key) {
|
||||||
|
auto where = self.lower_bound(key);
|
||||||
|
if ((where != self.end()) && !self.less_than_(key, *where))
|
||||||
|
return where;
|
||||||
|
else
|
||||||
|
return self.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class This>
|
||||||
|
static inline constexpr auto equal_range_impl(This&& self, Key const &key) {
|
||||||
|
auto lower = self.lower_bound(key);
|
||||||
|
using lower_t = decltype(lower);
|
||||||
|
if (lower == self.end())
|
||||||
|
return std::pair<lower_t, lower_t>{lower, lower};
|
||||||
|
else
|
||||||
|
return std::pair<lower_t, lower_t>{lower, lower + 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class This>
|
||||||
|
static inline constexpr auto lower_bound_impl(This&& self, Key const &key) -> decltype(self.end()) {
|
||||||
|
auto where = bits::lower_bound<N>(self.items_.begin(), key, self.less_than_);
|
||||||
|
if ((where != self.end()) && !self.less_than_(key, *where))
|
||||||
|
return where;
|
||||||
|
else
|
||||||
|
return self.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class This>
|
||||||
|
static inline constexpr auto upper_bound_impl(This&& self, Key const &key) -> decltype(self.end()) {
|
||||||
|
auto where = bits::lower_bound<N>(self.items_.begin(), key, self.less_than_);
|
||||||
|
if ((where != self.end()) && !self.less_than_(key, *where))
|
||||||
|
return where + 1;
|
||||||
|
else
|
||||||
|
return self.end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Key, class Value, class Compare>
|
||||||
|
class map<Key, Value, 0, Compare> {
|
||||||
|
using container_type = bits::carray<std::pair<Key, Value>, 0>;
|
||||||
|
impl::CompareKey<Compare> less_than_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using mapped_type = Value;
|
||||||
|
using value_type = typename container_type::value_type;
|
||||||
|
using size_type = typename container_type::size_type;
|
||||||
|
using difference_type = typename container_type::difference_type;
|
||||||
|
using key_compare = decltype(less_than_);
|
||||||
|
using reference = typename container_type::reference;
|
||||||
|
using const_reference = typename container_type::const_reference;
|
||||||
|
using pointer = typename container_type::pointer;
|
||||||
|
using const_pointer = typename container_type::const_pointer;
|
||||||
|
using iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using reverse_iterator = pointer;
|
||||||
|
using const_reverse_iterator = const_pointer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* constructors */
|
||||||
|
constexpr map(const map &other) = default;
|
||||||
|
constexpr map(std::initializer_list<value_type>, Compare const &compare)
|
||||||
|
: less_than_{compare} {}
|
||||||
|
constexpr map(std::initializer_list<value_type> items)
|
||||||
|
: map{items, Compare{}} {}
|
||||||
|
|
||||||
|
/* element access */
|
||||||
|
constexpr mapped_type at(Key const &) const {
|
||||||
|
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
|
||||||
|
}
|
||||||
|
constexpr mapped_type at(Key const &) {
|
||||||
|
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterators */
|
||||||
|
constexpr iterator begin() { return nullptr; }
|
||||||
|
constexpr const_iterator begin() const { return nullptr; }
|
||||||
|
constexpr const_iterator cbegin() const { return nullptr; }
|
||||||
|
constexpr iterator end() { return nullptr; }
|
||||||
|
constexpr const_iterator end() const { return nullptr; }
|
||||||
|
constexpr const_iterator cend() const { return nullptr; }
|
||||||
|
|
||||||
|
constexpr reverse_iterator rbegin() { return nullptr; }
|
||||||
|
constexpr const_reverse_iterator rbegin() const { return nullptr; }
|
||||||
|
constexpr const_reverse_iterator crbegin() const { return nullptr; }
|
||||||
|
constexpr reverse_iterator rend() { return nullptr; }
|
||||||
|
constexpr const_reverse_iterator rend() const { return nullptr; }
|
||||||
|
constexpr const_reverse_iterator crend() const { return nullptr; }
|
||||||
|
|
||||||
|
/* capacity */
|
||||||
|
constexpr bool empty() const { return true; }
|
||||||
|
constexpr size_type size() const { return 0; }
|
||||||
|
constexpr size_type max_size() const { return 0; }
|
||||||
|
|
||||||
|
/* lookup */
|
||||||
|
|
||||||
|
constexpr std::size_t count(Key const &) const { return 0; }
|
||||||
|
|
||||||
|
constexpr const_iterator find(Key const &) const { return end(); }
|
||||||
|
constexpr iterator find(Key const &) { return end(); }
|
||||||
|
|
||||||
|
constexpr std::pair<const_iterator, const_iterator>
|
||||||
|
equal_range(Key const &) const {
|
||||||
|
return {end(), end()};
|
||||||
|
}
|
||||||
|
constexpr std::pair<iterator, iterator>
|
||||||
|
equal_range(Key const &) {
|
||||||
|
return {end(), end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator lower_bound(Key const &) const { return end(); }
|
||||||
|
constexpr iterator lower_bound(Key const &) { return end(); }
|
||||||
|
|
||||||
|
constexpr const_iterator upper_bound(Key const &) const { return end(); }
|
||||||
|
constexpr iterator upper_bound(Key const &) { return end(); }
|
||||||
|
|
||||||
|
/* observers */
|
||||||
|
constexpr key_compare key_comp() const { return less_than_; }
|
||||||
|
constexpr key_compare value_comp() const { return less_than_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
constexpr auto make_map(bits::ignored_arg = {}/* for consistency with the initializer below for N = 0*/) {
|
||||||
|
return map<T, U, 0>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, std::size_t N>
|
||||||
|
constexpr auto make_map(std::pair<T, U> const (&items)[N]) {
|
||||||
|
return map<T, U, N>{items};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, std::size_t N>
|
||||||
|
constexpr auto make_map(std::array<std::pair<T, U>, N> const &items) {
|
||||||
|
return map<T, U, N>{items};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
90
src/perfect-hashing/frozen/random.h
Normal file
90
src/perfect-hashing/frozen/random.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_RANDOM_H
|
||||||
|
#define FROZEN_LETITGO_RANDOM_H
|
||||||
|
|
||||||
|
#include "frozen/bits/algorithms.h"
|
||||||
|
#include "frozen/bits/version.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
template <class UIntType, UIntType a, UIntType c, UIntType m>
|
||||||
|
class linear_congruential_engine {
|
||||||
|
|
||||||
|
static_assert(std::is_unsigned<UIntType>::value,
|
||||||
|
"UIntType must be an unsigned integral type");
|
||||||
|
|
||||||
|
public:
|
||||||
|
using result_type = UIntType;
|
||||||
|
static constexpr result_type multiplier = a;
|
||||||
|
static constexpr result_type increment = c;
|
||||||
|
static constexpr result_type modulus = m;
|
||||||
|
static constexpr result_type default_seed = 1u;
|
||||||
|
|
||||||
|
linear_congruential_engine() = default;
|
||||||
|
constexpr linear_congruential_engine(result_type s) { seed(s); }
|
||||||
|
|
||||||
|
void seed(result_type s = default_seed) { state_ = s; }
|
||||||
|
constexpr result_type operator()() {
|
||||||
|
using uint_least_t = bits::select_uint_least_t<bits::log(a) + bits::log(m) + 4>;
|
||||||
|
uint_least_t tmp = static_cast<uint_least_t>(multiplier) * state_ + increment;
|
||||||
|
|
||||||
|
// the static cast below may end up doing a truncation
|
||||||
|
if(modulus != 0)
|
||||||
|
state_ = static_cast<result_type>(tmp % modulus);
|
||||||
|
else
|
||||||
|
state_ = static_cast<result_type>(tmp);
|
||||||
|
return state_;
|
||||||
|
}
|
||||||
|
constexpr void discard(unsigned long long n) {
|
||||||
|
while (n--)
|
||||||
|
operator()();
|
||||||
|
}
|
||||||
|
static constexpr result_type min() { return increment == 0u ? 1u : 0u; }
|
||||||
|
static constexpr result_type max() { return modulus - 1u; }
|
||||||
|
friend constexpr bool operator==(linear_congruential_engine const &self,
|
||||||
|
linear_congruential_engine const &other) {
|
||||||
|
return self.state_ == other.state_;
|
||||||
|
}
|
||||||
|
friend constexpr bool operator!=(linear_congruential_engine const &self,
|
||||||
|
linear_congruential_engine const &other) {
|
||||||
|
return !(self == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
result_type state_ = default_seed;
|
||||||
|
};
|
||||||
|
|
||||||
|
using minstd_rand0 =
|
||||||
|
linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>;
|
||||||
|
using minstd_rand =
|
||||||
|
linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;
|
||||||
|
|
||||||
|
// This generator is used by default in unordered frozen containers
|
||||||
|
using default_prg_t = minstd_rand;
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
225
src/perfect-hashing/frozen/set.h
Normal file
225
src/perfect-hashing/frozen/set.h
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_SET_H
|
||||||
|
#define FROZEN_SET_H
|
||||||
|
|
||||||
|
#include "frozen/bits/algorithms.h"
|
||||||
|
#include "frozen/bits/basic_types.h"
|
||||||
|
#include "frozen/bits/constexpr_assert.h"
|
||||||
|
#include "frozen/bits/version.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
template <class Key, std::size_t N, class Compare = std::less<Key>> class set {
|
||||||
|
using container_type = bits::carray<Key, N>;
|
||||||
|
Compare less_than_;
|
||||||
|
container_type keys_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* container typedefs*/
|
||||||
|
using key_type = Key;
|
||||||
|
using value_type = Key;
|
||||||
|
using size_type = typename container_type::size_type;
|
||||||
|
using difference_type = typename container_type::size_type;
|
||||||
|
using key_compare = Compare;
|
||||||
|
using value_compare = Compare;
|
||||||
|
using reference = typename container_type::const_reference;
|
||||||
|
using const_reference = reference;
|
||||||
|
using pointer = typename container_type::const_pointer;
|
||||||
|
using const_pointer = pointer;
|
||||||
|
using iterator = typename container_type::const_iterator;
|
||||||
|
using reverse_iterator = typename container_type::const_reverse_iterator;
|
||||||
|
using const_iterator = iterator;
|
||||||
|
using const_reverse_iterator = reverse_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* constructors */
|
||||||
|
constexpr set(const set &other) = default;
|
||||||
|
|
||||||
|
constexpr set(container_type keys, Compare const & comp)
|
||||||
|
: less_than_{comp}
|
||||||
|
, keys_(bits::quicksort(keys, less_than_)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit constexpr set(container_type keys)
|
||||||
|
: set{keys, Compare{}} {}
|
||||||
|
|
||||||
|
constexpr set(std::initializer_list<Key> keys, Compare const & comp)
|
||||||
|
: set{container_type{keys}, comp} {
|
||||||
|
constexpr_assert(keys.size() == N, "Inconsistent initializer_list size and type size argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr set(std::initializer_list<Key> keys)
|
||||||
|
: set{keys, Compare{}} {}
|
||||||
|
|
||||||
|
/* capacity */
|
||||||
|
constexpr bool empty() const { return !N; }
|
||||||
|
constexpr size_type size() const { return N; }
|
||||||
|
constexpr size_type max_size() const { return N; }
|
||||||
|
|
||||||
|
/* lookup */
|
||||||
|
constexpr std::size_t count(Key const &key) const {
|
||||||
|
return bits::binary_search<N>(keys_.begin(), key, less_than_);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator find(Key const &key) const {
|
||||||
|
const_iterator where = lower_bound(key);
|
||||||
|
if ((where != end()) && !less_than_(key, *where))
|
||||||
|
return where;
|
||||||
|
else
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::pair<const_iterator, const_iterator> equal_range(Key const &key) const {
|
||||||
|
auto const lower = lower_bound(key);
|
||||||
|
if (lower == end())
|
||||||
|
return {lower, lower};
|
||||||
|
else
|
||||||
|
return {lower, lower + 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator lower_bound(Key const &key) const {
|
||||||
|
auto const where = bits::lower_bound<N>(keys_.begin(), key, less_than_);
|
||||||
|
if ((where != end()) && !less_than_(key, *where))
|
||||||
|
return where;
|
||||||
|
else
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator upper_bound(Key const &key) const {
|
||||||
|
auto const where = bits::lower_bound<N>(keys_.begin(), key, less_than_);
|
||||||
|
if ((where != end()) && !less_than_(key, *where))
|
||||||
|
return where + 1;
|
||||||
|
else
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* observers */
|
||||||
|
constexpr key_compare key_comp() const { return less_than_; }
|
||||||
|
constexpr key_compare value_comp() const { return less_than_; }
|
||||||
|
|
||||||
|
/* iterators */
|
||||||
|
constexpr const_iterator begin() const { return keys_.begin(); }
|
||||||
|
constexpr const_iterator cbegin() const { return keys_.cbegin(); }
|
||||||
|
constexpr const_iterator end() const { return keys_.end(); }
|
||||||
|
constexpr const_iterator cend() const { return keys_.cend(); }
|
||||||
|
|
||||||
|
constexpr const_reverse_iterator rbegin() const { return keys_.rbegin(); }
|
||||||
|
constexpr const_reverse_iterator crbegin() const { return keys_.crbegin(); }
|
||||||
|
constexpr const_reverse_iterator rend() const { return keys_.rend(); }
|
||||||
|
constexpr const_reverse_iterator crend() const { return keys_.crend(); }
|
||||||
|
|
||||||
|
/* comparison */
|
||||||
|
constexpr bool operator==(set const& rhs) const { return bits::equal(begin(), end(), rhs.begin()); }
|
||||||
|
constexpr bool operator!=(set const& rhs) const { return !(*this == rhs); }
|
||||||
|
constexpr bool operator<(set const& rhs) const { return bits::lexicographical_compare(begin(), end(), rhs.begin(), rhs.end()); }
|
||||||
|
constexpr bool operator<=(set const& rhs) const { return (*this < rhs) || (*this == rhs); }
|
||||||
|
constexpr bool operator>(set const& rhs) const { return bits::lexicographical_compare(rhs.begin(), rhs.end(), begin(), end()); }
|
||||||
|
constexpr bool operator>=(set const& rhs) const { return (*this > rhs) || (*this == rhs); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Key, class Compare> class set<Key, 0, Compare> {
|
||||||
|
using container_type = bits::carray<Key, 0>; // just for the type definitions
|
||||||
|
Compare less_than_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* container typedefs*/
|
||||||
|
using key_type = Key;
|
||||||
|
using value_type = Key;
|
||||||
|
using size_type = typename container_type::size_type;
|
||||||
|
using difference_type = typename container_type::size_type;
|
||||||
|
using key_compare = Compare;
|
||||||
|
using value_compare = Compare;
|
||||||
|
using reference = typename container_type::const_reference;
|
||||||
|
using const_reference = reference;
|
||||||
|
using pointer = typename container_type::const_pointer;
|
||||||
|
using const_pointer = pointer;
|
||||||
|
using iterator = pointer;
|
||||||
|
using reverse_iterator = pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using const_reverse_iterator = const_pointer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* constructors */
|
||||||
|
constexpr set(const set &other) = default;
|
||||||
|
constexpr set(bits::carray<Key, 0>, Compare const &) {}
|
||||||
|
explicit constexpr set(bits::carray<Key, 0>) {}
|
||||||
|
|
||||||
|
constexpr set(std::initializer_list<Key>, Compare const &comp)
|
||||||
|
: less_than_{comp} {}
|
||||||
|
constexpr set(std::initializer_list<Key> keys) : set{keys, Compare{}} {}
|
||||||
|
|
||||||
|
/* capacity */
|
||||||
|
constexpr bool empty() const { return true; }
|
||||||
|
constexpr size_type size() const { return 0; }
|
||||||
|
constexpr size_type max_size() const { return 0; }
|
||||||
|
|
||||||
|
/* lookup */
|
||||||
|
constexpr std::size_t count(Key const &) const { return 0; }
|
||||||
|
|
||||||
|
constexpr const_iterator find(Key const &) const { return end(); }
|
||||||
|
|
||||||
|
constexpr std::pair<const_iterator, const_iterator>
|
||||||
|
equal_range(Key const &) const { return {end(), end()}; }
|
||||||
|
|
||||||
|
constexpr const_iterator lower_bound(Key const &) const { return end(); }
|
||||||
|
|
||||||
|
constexpr const_iterator upper_bound(Key const &) const { return end(); }
|
||||||
|
|
||||||
|
/* observers */
|
||||||
|
constexpr key_compare key_comp() const { return less_than_; }
|
||||||
|
constexpr key_compare value_comp() const { return less_than_; }
|
||||||
|
|
||||||
|
/* iterators */
|
||||||
|
constexpr const_iterator begin() const { return nullptr; }
|
||||||
|
constexpr const_iterator cbegin() const { return nullptr; }
|
||||||
|
constexpr const_iterator end() const { return nullptr; }
|
||||||
|
constexpr const_iterator cend() const { return nullptr; }
|
||||||
|
|
||||||
|
constexpr const_reverse_iterator rbegin() const { return nullptr; }
|
||||||
|
constexpr const_reverse_iterator crbegin() const { return nullptr; }
|
||||||
|
constexpr const_reverse_iterator rend() const { return nullptr; }
|
||||||
|
constexpr const_reverse_iterator crend() const { return nullptr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr auto make_set(bits::ignored_arg = {}/* for consistency with the initializer below for N = 0*/) {
|
||||||
|
return set<T, 0>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
constexpr auto make_set(const T (&args)[N]) {
|
||||||
|
return set<T, N>(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
constexpr auto make_set(std::array<T, N> const &args) {
|
||||||
|
return set<T, N>(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
152
src/perfect-hashing/frozen/string.h
Normal file
152
src/perfect-hashing/frozen/string.h
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_STRING_H
|
||||||
|
#define FROZEN_LETITGO_STRING_H
|
||||||
|
|
||||||
|
#include "frozen/bits/elsa.h"
|
||||||
|
#include "frozen/bits/version.h"
|
||||||
|
#include "frozen/bits/defines.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
|
||||||
|
#include <string_view>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
template <typename _CharT>
|
||||||
|
class basic_string {
|
||||||
|
using chr_t = _CharT;
|
||||||
|
|
||||||
|
chr_t const *data_;
|
||||||
|
std::size_t size_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <std::size_t N>
|
||||||
|
constexpr basic_string(chr_t const (&data)[N])
|
||||||
|
: data_(data), size_(N - 1) {}
|
||||||
|
constexpr basic_string(chr_t const *data, std::size_t size)
|
||||||
|
: data_(data), size_(size) {}
|
||||||
|
|
||||||
|
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
|
||||||
|
constexpr basic_string(std::basic_string_view<chr_t> data)
|
||||||
|
: data_(data.data()), size_(data.size()) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr basic_string(const basic_string &) noexcept = default;
|
||||||
|
constexpr basic_string &operator=(const basic_string &) noexcept = default;
|
||||||
|
|
||||||
|
constexpr std::size_t size() const { return size_; }
|
||||||
|
|
||||||
|
constexpr chr_t operator[](std::size_t i) const { return data_[i]; }
|
||||||
|
|
||||||
|
constexpr bool operator==(basic_string other) const {
|
||||||
|
if (size_ != other.size_)
|
||||||
|
return false;
|
||||||
|
for (std::size_t i = 0; i < size_; ++i)
|
||||||
|
if (data_[i] != other.data_[i])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator<(const basic_string &other) const {
|
||||||
|
unsigned i = 0;
|
||||||
|
while (i < size() && i < other.size()) {
|
||||||
|
if ((*this)[i] < other[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ((*this)[i] > other[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return size() < other.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const chr_t *data() const { return data_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename _CharT> struct elsa<basic_string<_CharT>> {
|
||||||
|
constexpr std::size_t operator()(basic_string<_CharT> value) const {
|
||||||
|
std::size_t d = 5381;
|
||||||
|
for (std::size_t i = 0; i < value.size(); ++i)
|
||||||
|
d = d * 33 + static_cast<size_t>(value[i]);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||||
|
// With the lowest bits removed, based on experimental setup.
|
||||||
|
constexpr std::size_t operator()(basic_string<_CharT> value, std::size_t seed) const {
|
||||||
|
std::size_t d = (0x811c9dc5 ^ seed) * static_cast<size_t>(0x01000193);
|
||||||
|
for (std::size_t i = 0; i < value.size(); ++i)
|
||||||
|
d = (d ^ static_cast<size_t>(value[i])) * static_cast<size_t>(0x01000193);
|
||||||
|
return d >> 8 ;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using string = basic_string<char>;
|
||||||
|
using wstring = basic_string<wchar_t>;
|
||||||
|
using u16string = basic_string<char16_t>;
|
||||||
|
using u32string = basic_string<char32_t>;
|
||||||
|
|
||||||
|
#ifdef FROZEN_LETITGO_HAS_CHAR8T
|
||||||
|
using u8string = basic_string<char8_t>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace string_literals {
|
||||||
|
|
||||||
|
constexpr string operator"" _s(const char *data, std::size_t size) {
|
||||||
|
return {data, size};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr wstring operator"" _s(const wchar_t *data, std::size_t size) {
|
||||||
|
return {data, size};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u16string operator"" _s(const char16_t *data, std::size_t size) {
|
||||||
|
return {data, size};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32string operator"" _s(const char32_t *data, std::size_t size) {
|
||||||
|
return {data, size};
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef FROZEN_LETITGO_HAS_CHAR8T
|
||||||
|
constexpr u8string operator"" _s(const char8_t *data, std::size_t size) {
|
||||||
|
return {data, size};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace string_literals
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <typename _CharT> struct hash<frozen::basic_string<_CharT>> {
|
||||||
|
size_t operator()(frozen::basic_string<_CharT> s) const {
|
||||||
|
return frozen::elsa<frozen::basic_string<_CharT>>{}(s);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
#endif
|
202
src/perfect-hashing/frozen/unordered_map.h
Normal file
202
src/perfect-hashing/frozen/unordered_map.h
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_UNORDERED_MAP_H
|
||||||
|
#define FROZEN_LETITGO_UNORDERED_MAP_H
|
||||||
|
|
||||||
|
#include "frozen/bits/basic_types.h"
|
||||||
|
#include "frozen/bits/constexpr_assert.h"
|
||||||
|
#include "frozen/bits/elsa.h"
|
||||||
|
#include "frozen/bits/exceptions.h"
|
||||||
|
#include "frozen/bits/pmh.h"
|
||||||
|
#include "frozen/bits/version.h"
|
||||||
|
#include "frozen/random.h"
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
namespace bits {
|
||||||
|
|
||||||
|
struct GetKey {
|
||||||
|
template <class KV> constexpr auto const &operator()(KV const &kv) const {
|
||||||
|
return kv.first;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bits
|
||||||
|
|
||||||
|
template <class Key, class Value, std::size_t N, typename Hash = anna<Key>,
|
||||||
|
class KeyEqual = std::equal_to<Key>>
|
||||||
|
class unordered_map {
|
||||||
|
static constexpr std::size_t storage_size =
|
||||||
|
bits::next_highest_power_of_two(N) * (N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for small sets
|
||||||
|
using container_type = bits::carray<std::pair<Key, Value>, N>;
|
||||||
|
using tables_type = bits::pmh_tables<storage_size, Hash>;
|
||||||
|
|
||||||
|
KeyEqual const equal_;
|
||||||
|
container_type items_;
|
||||||
|
tables_type tables_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* typedefs */
|
||||||
|
using Self = unordered_map<Key, Value, N, Hash, KeyEqual>;
|
||||||
|
using key_type = Key;
|
||||||
|
using mapped_type = Value;
|
||||||
|
using value_type = typename container_type::value_type;
|
||||||
|
using size_type = typename container_type::size_type;
|
||||||
|
using difference_type = typename container_type::difference_type;
|
||||||
|
using hasher = Hash;
|
||||||
|
using key_equal = KeyEqual;
|
||||||
|
using reference = typename container_type::reference;
|
||||||
|
using const_reference = typename container_type::const_reference;
|
||||||
|
using pointer = typename container_type::pointer;
|
||||||
|
using const_pointer = typename container_type::const_pointer;
|
||||||
|
using iterator = typename container_type::iterator;
|
||||||
|
using const_iterator = typename container_type::const_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* constructors */
|
||||||
|
unordered_map(unordered_map const &) = default;
|
||||||
|
constexpr unordered_map(container_type items,
|
||||||
|
Hash const &hash, KeyEqual const &equal)
|
||||||
|
: equal_{equal}
|
||||||
|
, items_{items}
|
||||||
|
, tables_{
|
||||||
|
bits::make_pmh_tables<storage_size>(
|
||||||
|
items_, hash, bits::GetKey{}, default_prg_t{})} {}
|
||||||
|
explicit constexpr unordered_map(container_type items)
|
||||||
|
: unordered_map{items, Hash{}, KeyEqual{}} {}
|
||||||
|
|
||||||
|
constexpr unordered_map(std::initializer_list<value_type> items,
|
||||||
|
Hash const & hash, KeyEqual const & equal)
|
||||||
|
: unordered_map{container_type{items}, hash, equal} {
|
||||||
|
constexpr_assert(items.size() == N, "Inconsistent initializer_list size and type size argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr unordered_map(std::initializer_list<value_type> items)
|
||||||
|
: unordered_map{items, Hash{}, KeyEqual{}} {}
|
||||||
|
|
||||||
|
/* iterators */
|
||||||
|
constexpr iterator begin() { return items_.begin(); }
|
||||||
|
constexpr iterator end() { return items_.end(); }
|
||||||
|
constexpr const_iterator begin() const { return items_.begin(); }
|
||||||
|
constexpr const_iterator end() const { return items_.end(); }
|
||||||
|
constexpr const_iterator cbegin() const { return items_.cbegin(); }
|
||||||
|
constexpr const_iterator cend() const { return items_.cend(); }
|
||||||
|
|
||||||
|
/* capacity */
|
||||||
|
constexpr bool empty() const { return !N; }
|
||||||
|
constexpr size_type size() const { return N; }
|
||||||
|
constexpr size_type max_size() const { return N; }
|
||||||
|
|
||||||
|
/* lookup */
|
||||||
|
constexpr std::size_t count(Key const &key) const {
|
||||||
|
auto const &kv = lookup(key);
|
||||||
|
return equal_(kv.first, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Value const &at(Key const &key) const {
|
||||||
|
return at_impl(*this, key);
|
||||||
|
}
|
||||||
|
constexpr Value &at(Key const &key) {
|
||||||
|
return at_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator find(Key const &key) const {
|
||||||
|
return find_impl(*this, key);
|
||||||
|
}
|
||||||
|
constexpr iterator find(Key const &key) {
|
||||||
|
return find_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::pair<const_iterator, const_iterator> equal_range(Key const &key) const {
|
||||||
|
return equal_range_impl(*this, key);
|
||||||
|
}
|
||||||
|
constexpr std::pair<iterator, iterator> equal_range(Key const &key) {
|
||||||
|
return equal_range_impl(*this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bucket interface */
|
||||||
|
constexpr std::size_t bucket_count() const { return storage_size; }
|
||||||
|
constexpr std::size_t max_bucket_count() const { return storage_size; }
|
||||||
|
|
||||||
|
/* observers*/
|
||||||
|
constexpr hasher hash_function() const { return tables_.hash_; }
|
||||||
|
constexpr key_equal key_eq() const { return equal_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class This>
|
||||||
|
static inline constexpr auto& at_impl(This&& self, Key const &key) {
|
||||||
|
auto& kv = self.lookup(key);
|
||||||
|
if (self.equal_(kv.first, key))
|
||||||
|
return kv.second;
|
||||||
|
else
|
||||||
|
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class This>
|
||||||
|
static inline constexpr auto find_impl(This&& self, Key const &key) {
|
||||||
|
auto& kv = self.lookup(key);
|
||||||
|
if (self.equal_(kv.first, key))
|
||||||
|
return &kv;
|
||||||
|
else
|
||||||
|
return self.items_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class This>
|
||||||
|
static inline constexpr auto equal_range_impl(This&& self, Key const &key) {
|
||||||
|
auto& kv = self.lookup(key);
|
||||||
|
using kv_ptr = decltype(&kv);
|
||||||
|
if (self.equal_(kv.first, key))
|
||||||
|
return std::pair<kv_ptr, kv_ptr>{&kv, &kv + 1};
|
||||||
|
else
|
||||||
|
return std::pair<kv_ptr, kv_ptr>{self.items_.end(), self.items_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class This>
|
||||||
|
static inline constexpr auto& lookup_impl(This&& self, Key const &key) {
|
||||||
|
return self.items_[self.tables_.lookup(key)];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto const& lookup(Key const &key) const {
|
||||||
|
return lookup_impl(*this, key);
|
||||||
|
}
|
||||||
|
constexpr auto& lookup(Key const &key) {
|
||||||
|
return lookup_impl(*this, key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, std::size_t N>
|
||||||
|
constexpr auto make_unordered_map(std::pair<T, U> const (&items)[N]) {
|
||||||
|
return unordered_map<T, U, N>{items};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U, std::size_t N>
|
||||||
|
constexpr auto make_unordered_map(std::array<std::pair<T, U>, N> const &items) {
|
||||||
|
return unordered_map<T, U, N>{items};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
152
src/perfect-hashing/frozen/unordered_set.h
Normal file
152
src/perfect-hashing/frozen/unordered_set.h
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Frozen
|
||||||
|
* Copyright 2016 QuarksLab
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FROZEN_LETITGO_UNORDERED_SET_H
|
||||||
|
#define FROZEN_LETITGO_UNORDERED_SET_H
|
||||||
|
|
||||||
|
#include "frozen/bits/basic_types.h"
|
||||||
|
#include "frozen/bits/constexpr_assert.h"
|
||||||
|
#include "frozen/bits/elsa.h"
|
||||||
|
#include "frozen/bits/pmh.h"
|
||||||
|
#include "frozen/bits/version.h"
|
||||||
|
#include "frozen/random.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace frozen {
|
||||||
|
|
||||||
|
namespace bits {
|
||||||
|
|
||||||
|
struct Get {
|
||||||
|
template <class T> constexpr T const &operator()(T const &key) const {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bits
|
||||||
|
|
||||||
|
template <class Key, std::size_t N, typename Hash = elsa<Key>,
|
||||||
|
class KeyEqual = std::equal_to<Key>>
|
||||||
|
class unordered_set {
|
||||||
|
static constexpr std::size_t storage_size =
|
||||||
|
bits::next_highest_power_of_two(N) * (N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for small sets
|
||||||
|
using container_type = bits::carray<Key, N>;
|
||||||
|
using tables_type = bits::pmh_tables<storage_size, Hash>;
|
||||||
|
|
||||||
|
KeyEqual const equal_;
|
||||||
|
container_type keys_;
|
||||||
|
tables_type tables_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* typedefs */
|
||||||
|
using key_type = Key;
|
||||||
|
using value_type = Key;
|
||||||
|
using size_type = typename container_type::size_type;
|
||||||
|
using difference_type = typename container_type::difference_type;
|
||||||
|
using hasher = Hash;
|
||||||
|
using key_equal = KeyEqual;
|
||||||
|
using const_reference = typename container_type::const_reference;
|
||||||
|
using reference = const_reference;
|
||||||
|
using const_pointer = typename container_type::const_pointer;
|
||||||
|
using pointer = const_pointer;
|
||||||
|
using const_iterator = const_pointer;
|
||||||
|
using iterator = const_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/* constructors */
|
||||||
|
unordered_set(unordered_set const &) = default;
|
||||||
|
constexpr unordered_set(container_type keys, Hash const &hash,
|
||||||
|
KeyEqual const &equal)
|
||||||
|
: equal_{equal}
|
||||||
|
, keys_{keys}
|
||||||
|
, tables_{bits::make_pmh_tables<storage_size>(
|
||||||
|
keys_, hash, bits::Get{}, default_prg_t{})} {}
|
||||||
|
explicit constexpr unordered_set(container_type keys)
|
||||||
|
: unordered_set{keys, Hash{}, KeyEqual{}} {}
|
||||||
|
|
||||||
|
constexpr unordered_set(std::initializer_list<Key> keys)
|
||||||
|
: unordered_set{keys, Hash{}, KeyEqual{}} {}
|
||||||
|
|
||||||
|
constexpr unordered_set(std::initializer_list<Key> keys, Hash const & hash, KeyEqual const & equal)
|
||||||
|
: unordered_set{container_type{keys}, hash, equal} {
|
||||||
|
constexpr_assert(keys.size() == N, "Inconsistent initializer_list size and type size argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterators */
|
||||||
|
constexpr const_iterator begin() const { return keys_.begin(); }
|
||||||
|
constexpr const_iterator end() const { return keys_.end(); }
|
||||||
|
constexpr const_iterator cbegin() const { return keys_.cbegin(); }
|
||||||
|
constexpr const_iterator cend() const { return keys_.cend(); }
|
||||||
|
|
||||||
|
/* capacity */
|
||||||
|
constexpr bool empty() const { return !N; }
|
||||||
|
constexpr size_type size() const { return N; }
|
||||||
|
constexpr size_type max_size() const { return N; }
|
||||||
|
|
||||||
|
/* lookup */
|
||||||
|
constexpr std::size_t count(Key const &key) const {
|
||||||
|
auto const k = lookup(key);
|
||||||
|
return equal_(k, key);
|
||||||
|
}
|
||||||
|
constexpr const_iterator find(Key const &key) const {
|
||||||
|
auto const &k = lookup(key);
|
||||||
|
if (equal_(k, key))
|
||||||
|
return &k;
|
||||||
|
else
|
||||||
|
return keys_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::pair<const_iterator, const_iterator> equal_range(Key const &key) const {
|
||||||
|
auto const &k = lookup(key);
|
||||||
|
if (equal_(k, key))
|
||||||
|
return {&k, &k + 1};
|
||||||
|
else
|
||||||
|
return {keys_.end(), keys_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bucket interface */
|
||||||
|
constexpr std::size_t bucket_count() const { return storage_size; }
|
||||||
|
constexpr std::size_t max_bucket_count() const { return storage_size; }
|
||||||
|
|
||||||
|
/* observers*/
|
||||||
|
constexpr hasher hash_function() const { return tables_.hash_; }
|
||||||
|
constexpr key_equal key_eq() const { return equal_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr auto const &lookup(Key const &key) const {
|
||||||
|
return keys_[tables_.lookup(key)];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
constexpr auto make_unordered_set(T const (&keys)[N]) {
|
||||||
|
return unordered_set<T, N>{keys};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
constexpr auto make_unordered_set(std::array<T, N> const &keys) {
|
||||||
|
return unordered_set<T, N>{keys};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace frozen
|
||||||
|
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user