Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
22
libs/CMakeLists.txt
Normal file
22
libs/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
add_subdirectory(base)
|
||||
add_subdirectory(coding)
|
||||
add_subdirectory(descriptions)
|
||||
add_subdirectory(drape)
|
||||
add_subdirectory(drape_frontend)
|
||||
add_subdirectory(editor)
|
||||
add_subdirectory(ge0)
|
||||
add_subdirectory(mwm_diff)
|
||||
add_subdirectory(geometry)
|
||||
add_subdirectory(indexer)
|
||||
add_subdirectory(kml)
|
||||
add_subdirectory(map)
|
||||
add_subdirectory(cppjansson)
|
||||
add_subdirectory(platform)
|
||||
add_subdirectory(routing)
|
||||
add_subdirectory(routing_common)
|
||||
add_subdirectory(search)
|
||||
add_subdirectory(shaders)
|
||||
add_subdirectory(storage)
|
||||
add_subdirectory(tracking)
|
||||
add_subdirectory(traffic)
|
||||
add_subdirectory(transit)
|
||||
124
libs/base/CMakeLists.txt
Normal file
124
libs/base/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
project(base)
|
||||
|
||||
set(SRC
|
||||
# Our code will be recompiled on modification of these headers.
|
||||
../std/boost_container_hash.hpp
|
||||
../std/boost_geometry.hpp
|
||||
../std/glm_gtx_rotate_vector.hpp
|
||||
../std/target_os.hpp
|
||||
../std/windows.hpp
|
||||
array_adapters.hpp
|
||||
assert.hpp
|
||||
atomic_shared_ptr.hpp
|
||||
base.cpp
|
||||
base.hpp
|
||||
beam.hpp
|
||||
bidirectional_map.hpp
|
||||
bits.hpp
|
||||
buffer_vector.hpp
|
||||
cache.hpp
|
||||
cancellable.cpp
|
||||
cancellable.hpp
|
||||
checked_cast.hpp
|
||||
clustering_map.hpp
|
||||
collection_cast.hpp
|
||||
control_flow.hpp
|
||||
deferred_task.cpp
|
||||
deferred_task.hpp
|
||||
dfa_helpers.hpp
|
||||
exception.cpp
|
||||
exception.hpp
|
||||
fast_math.cpp
|
||||
fifo_cache.hpp
|
||||
file_name_utils.cpp
|
||||
file_name_utils.hpp
|
||||
geo_object_id.cpp
|
||||
geo_object_id.hpp
|
||||
gmtime.cpp
|
||||
gmtime.hpp
|
||||
internal/message.cpp
|
||||
internal/message.hpp
|
||||
levenshtein_dfa.cpp
|
||||
levenshtein_dfa.hpp
|
||||
limited_priority_queue.hpp
|
||||
linked_map.hpp
|
||||
logging.cpp
|
||||
logging.hpp
|
||||
lower_case.cpp
|
||||
lru_cache.hpp
|
||||
macros.hpp
|
||||
math.cpp
|
||||
math.hpp
|
||||
matrix.hpp
|
||||
mem_trie.hpp
|
||||
newtype.hpp
|
||||
normalize_unicode.cpp
|
||||
non_intersecting_intervals.hpp
|
||||
observer_list.hpp
|
||||
pprof.cpp
|
||||
pprof.hpp
|
||||
random.hpp
|
||||
range_iterator.hpp
|
||||
ref_counted.hpp
|
||||
rolling_hash.hpp
|
||||
scope_guard.hpp
|
||||
set_operations.hpp
|
||||
shared_buffer_manager.cpp
|
||||
shared_buffer_manager.hpp
|
||||
small_map.hpp
|
||||
small_set.hpp
|
||||
src_point.cpp
|
||||
src_point.hpp
|
||||
stats.hpp
|
||||
stl_helpers.hpp
|
||||
stl_iterator.hpp
|
||||
string_utils.cpp
|
||||
string_utils.hpp
|
||||
strings_bundle.cpp
|
||||
strings_bundle.hpp
|
||||
suffix_array.cpp
|
||||
suffix_array.hpp
|
||||
sunrise_sunset.cpp
|
||||
sunrise_sunset.hpp
|
||||
task_loop.hpp
|
||||
thread.cpp
|
||||
thread.hpp
|
||||
thread_checker.cpp
|
||||
thread_checker.hpp
|
||||
thread_pool.cpp
|
||||
thread_pool.hpp
|
||||
thread_pool_computational.hpp
|
||||
thread_pool_delayed.cpp
|
||||
thread_pool_delayed.hpp
|
||||
thread_safe_queue.hpp
|
||||
thread_utils.hpp
|
||||
threaded_container.cpp
|
||||
threaded_container.hpp
|
||||
threaded_list.hpp
|
||||
timegm.cpp
|
||||
timegm.hpp
|
||||
timer.cpp
|
||||
timer.hpp
|
||||
uni_string_dfa.cpp
|
||||
uni_string_dfa.hpp
|
||||
visitor.hpp
|
||||
)
|
||||
|
||||
omim_add_library(${PROJECT_NAME} ${SRC})
|
||||
|
||||
# Exception for some of our math to work reliably.
|
||||
# Ubuntu x86_64 gcc 14.2.0 ignores -fno-finite-math-only in debug builds without -O1
|
||||
if (NOT MSVC)
|
||||
set_source_files_properties(fast_math.cpp PROPERTIES COMPILE_OPTIONS "-fno-finite-math-only;$<$<CONFIG:DEBUG>:-O1>")
|
||||
endif()
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE Threads::Threads)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC utf8cpp::utf8cpp)
|
||||
|
||||
if (NOT WITH_SYSTEM_PROVIDED_3PARTY)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE "${OMIM_ROOT}/3party/fast_double_parser/include")
|
||||
endif()
|
||||
|
||||
omim_add_test_subdirectory(base_tests)
|
||||
71
libs/base/array_adapters.hpp
Normal file
71
libs/base/array_adapters.hpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <class T, class TPtr>
|
||||
class array_impl
|
||||
{
|
||||
protected:
|
||||
TPtr m_p;
|
||||
size_t m_size;
|
||||
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef T const & const_reference;
|
||||
typedef T & reference;
|
||||
|
||||
array_impl(TPtr p, size_t sz) : m_p(p), m_size(sz) {}
|
||||
|
||||
T const & operator[](size_t i) const
|
||||
{
|
||||
ASSERT_LESS(i, m_size, ());
|
||||
return m_p[i];
|
||||
}
|
||||
|
||||
T const & back() const
|
||||
{
|
||||
ASSERT_GREATER(m_size, 0, ());
|
||||
return m_p[m_size - 1];
|
||||
}
|
||||
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
bool empty() const { return (m_size == 0); }
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class T>
|
||||
class array_read : public detail::array_impl<T, T const *>
|
||||
{
|
||||
public:
|
||||
array_read(T const * p, size_t sz) : detail::array_impl<T, T const *>(p, sz) {}
|
||||
};
|
||||
|
||||
template <class TCont>
|
||||
array_read<typename TCont::value_type> make_read_adapter(TCont const & cont)
|
||||
{
|
||||
return array_read<typename TCont::value_type>(cont.empty() ? 0 : &cont[0], cont.size());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
class array_write : public detail::array_impl<T, T *>
|
||||
{
|
||||
size_t m_capacity;
|
||||
|
||||
typedef detail::array_impl<T, T *> base_t;
|
||||
|
||||
public:
|
||||
template <class TCont>
|
||||
explicit array_write(TCont & cont)
|
||||
: detail::array_impl<T, T *>(cont.empty() ? 0 : &cont[0], 0)
|
||||
, m_capacity(cont.size())
|
||||
{}
|
||||
|
||||
void push_back(T const & t)
|
||||
{
|
||||
ASSERT_LESS(base_t::m_size, m_capacity, ());
|
||||
base_t::m_p[base_t::m_size++] = t;
|
||||
}
|
||||
};
|
||||
168
libs/base/assert.hpp
Normal file
168
libs/base/assert.hpp
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/base.hpp"
|
||||
#include "base/internal/message.hpp"
|
||||
#include "base/src_point.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
// NOLINTBEGIN(misc-static-assert)
|
||||
|
||||
namespace base
|
||||
{
|
||||
// Called when ASSERT, CHECK or VERIFY failed.
|
||||
// If returns true then crash application.
|
||||
using AssertFailedFn = bool (*)(SrcPoint const &, std::string const &);
|
||||
extern AssertFailedFn OnAssertFailed;
|
||||
|
||||
/// @return Pointer to previous message function.
|
||||
AssertFailedFn SetAssertFunction(AssertFailedFn fn);
|
||||
} // namespace base
|
||||
|
||||
#ifdef DEBUG
|
||||
#define ASSERT_CRASH() assert(false)
|
||||
#else
|
||||
#define ASSERT_CRASH() std::abort()
|
||||
#endif
|
||||
|
||||
#define ASSERT_FAIL(msg) \
|
||||
if (::base::OnAssertFailed(SRC(), msg)) \
|
||||
ASSERT_CRASH();
|
||||
|
||||
// TODO: Evaluate X only once in CHECK().
|
||||
#define CHECK(X, msg) \
|
||||
do \
|
||||
{ \
|
||||
if (X) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
ASSERT_FAIL(::base::Message("CHECK(" #X ")", ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
#define CHECK_EQUAL(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) == (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
ASSERT_FAIL(::base::Message("CHECK(" #X " == " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
#define CHECK_NOT_EQUAL(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) != (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
ASSERT_FAIL(::base::Message("CHECK(" #X " != " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
#define CHECK_LESS(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) < (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
ASSERT_FAIL(::base::Message("CHECK(" #X " < " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
#define CHECK_LESS_OR_EQUAL(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) <= (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
ASSERT_FAIL(::base::Message("CHECK(" #X " <= " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
#define CHECK_GREATER(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) > (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
ASSERT_FAIL(::base::Message("CHECK(" #X " > " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
#define CHECK_GREATER_OR_EQUAL(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) >= (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
ASSERT_FAIL(::base::Message("CHECK(" #X " >= " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
#define CHECK_OR_CALL(fail, call, X, msg) \
|
||||
do \
|
||||
{ \
|
||||
if (X) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
if (fail) \
|
||||
{ \
|
||||
ASSERT_FAIL(::base::Message(::base::Message("CHECK(" #X ")"), ::base::Message msg)); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
call(); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
#ifdef DEBUG
|
||||
#define ASSERT(X, msg) CHECK(X, msg)
|
||||
#define VERIFY(X, msg) CHECK(X, msg)
|
||||
#define ASSERT_EQUAL(X, Y, msg) CHECK_EQUAL(X, Y, msg)
|
||||
#define ASSERT_NOT_EQUAL(X, Y, msg) CHECK_NOT_EQUAL(X, Y, msg)
|
||||
#define ASSERT_LESS(X, Y, msg) CHECK_LESS(X, Y, msg)
|
||||
#define ASSERT_LESS_OR_EQUAL(X, Y, msg) CHECK_LESS_OR_EQUAL(X, Y, msg)
|
||||
#define ASSERT_GREATER(X, Y, msg) CHECK_GREATER(X, Y, msg)
|
||||
#define ASSERT_GREATER_OR_EQUAL(X, Y, msg) CHECK_GREATER_OR_EQUAL(X, Y, msg)
|
||||
#else
|
||||
#define ASSERT(X, msg)
|
||||
#define VERIFY(X, msg) (void)(X)
|
||||
#define ASSERT_EQUAL(X, Y, msg)
|
||||
#define ASSERT_NOT_EQUAL(X, Y, msg)
|
||||
#define ASSERT_LESS(X, Y, msg)
|
||||
#define ASSERT_LESS_OR_EQUAL(X, Y, msg)
|
||||
#define ASSERT_GREATER(X, Y, msg)
|
||||
#define ASSERT_GREATER_OR_EQUAL(X, Y, msg)
|
||||
#endif
|
||||
|
||||
// The macro that causes this warning to be ignored:
|
||||
// "control reaches end of non-void function".
|
||||
#define UNREACHABLE() \
|
||||
do \
|
||||
{ \
|
||||
CHECK(false, ("Unreachable statement.")); \
|
||||
std::abort(); \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
// NOLINTEND(misc-static-assert)
|
||||
27
libs/base/atomic_shared_ptr.hpp
Normal file
27
libs/base/atomic_shared_ptr.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace base
|
||||
{
|
||||
// Template which provides methods for concurrently using shared pointers.
|
||||
template <typename T>
|
||||
class AtomicSharedPtr final
|
||||
{
|
||||
public:
|
||||
using ContentType = T const;
|
||||
using ValueType = std::shared_ptr<ContentType>;
|
||||
|
||||
AtomicSharedPtr() = default;
|
||||
|
||||
void Set(ValueType value) noexcept { atomic_store(&m_wrapped, value); }
|
||||
ValueType Get() const noexcept { return atomic_load(&m_wrapped); }
|
||||
|
||||
private:
|
||||
ValueType m_wrapped = std::make_shared<ContentType>();
|
||||
|
||||
DISALLOW_COPY_AND_MOVE(AtomicSharedPtr);
|
||||
};
|
||||
} // namespace base
|
||||
29
libs/base/base.cpp
Normal file
29
libs/base/base.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#include "base/base.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/src_point.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace base
|
||||
{
|
||||
bool OnAssertFailedDefault(SrcPoint const & srcPoint, std::string const & msg)
|
||||
{
|
||||
auto & logger = LogHelper::Instance();
|
||||
|
||||
std::cerr << '(' << logger.GetThreadID() << ") ASSERT FAILED" << '\n'
|
||||
<< srcPoint.FileName() << ':' << srcPoint.Line() << '\n'
|
||||
<< msg << std::endl
|
||||
<< std::flush;
|
||||
return true;
|
||||
}
|
||||
|
||||
AssertFailedFn OnAssertFailed = &OnAssertFailedDefault;
|
||||
|
||||
AssertFailedFn SetAssertFunction(AssertFailedFn fn)
|
||||
{
|
||||
std::swap(OnAssertFailed, fn);
|
||||
return fn;
|
||||
}
|
||||
} // namespace base
|
||||
35
libs/base/base.hpp
Normal file
35
libs/base/base.hpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#if defined(DEBUG) || defined(_DEBUG) || defined(NRELEASE)
|
||||
#define MY_DEBUG_DEFINED 1
|
||||
#else
|
||||
#define MY_DEBUG_DEFINED 0
|
||||
#endif
|
||||
|
||||
#if defined(RELEASE) || defined(_RELEASE) || defined(NDEBUG) || defined(_NDEBUG)
|
||||
#define MY_RELEASE_DEFINED 1
|
||||
#else
|
||||
#define MY_RELEASE_DEFINED 0
|
||||
#endif
|
||||
|
||||
static_assert(MY_DEBUG_DEFINED ^ MY_RELEASE_DEFINED, "Either Debug or Release should be defined, but not both.");
|
||||
|
||||
// #define DEBUG macro, which should be used with #ifdef.
|
||||
#if !MY_RELEASE_DEFINED
|
||||
#ifndef DEBUG
|
||||
#define DEBUG 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
// #include "internal/debug_new.hpp"
|
||||
// TODO: STL debug mode.
|
||||
#define IF_DEBUG_ELSE(a, b) (a)
|
||||
#else
|
||||
#define IF_DEBUG_ELSE(a, b) (b)
|
||||
#endif
|
||||
|
||||
// platform macroses
|
||||
#include "std/target_os.hpp"
|
||||
59
libs/base/base_tests/CMakeLists.txt
Normal file
59
libs/base/base_tests/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
project(base_tests)
|
||||
|
||||
set(SRC
|
||||
assert_test.cpp
|
||||
beam_tests.cpp
|
||||
bidirectional_map_tests.cpp
|
||||
bits_test.cpp
|
||||
buffer_vector_test.cpp
|
||||
cache_test.cpp
|
||||
cancellable_tests.cpp
|
||||
checked_cast_tests.cpp
|
||||
clustering_map_tests.cpp
|
||||
collection_cast_test.cpp
|
||||
containers_test.cpp
|
||||
control_flow_tests.cpp
|
||||
exception_tests.cpp
|
||||
fifo_cache_test.cpp
|
||||
file_name_utils_tests.cpp
|
||||
geo_object_id_tests.cpp
|
||||
levenshtein_dfa_test.cpp
|
||||
linked_map_tests.cpp
|
||||
logging_test.cpp
|
||||
lru_cache_tests.cpp
|
||||
math_test.cpp
|
||||
matrix_test.cpp
|
||||
mem_trie_test.cpp
|
||||
message_test.cpp
|
||||
newtype_test.cpp
|
||||
non_intersecting_intervals_tests.cpp
|
||||
observer_list_test.cpp
|
||||
range_iterator_test.cpp
|
||||
ref_counted_tests.cpp
|
||||
regexp_test.cpp
|
||||
rolling_hash_test.cpp
|
||||
scope_guard_test.cpp
|
||||
small_set_test.cpp
|
||||
stl_helpers_tests.cpp
|
||||
string_utils_test.cpp
|
||||
suffix_array_tests.cpp
|
||||
sunrise_sunset_test.cpp
|
||||
thread_pool_computational_tests.cpp
|
||||
thread_pool_delayed_tests.cpp
|
||||
thread_pool_tests.cpp
|
||||
thread_safe_queue_tests.cpp
|
||||
threaded_list_test.cpp
|
||||
threads_test.cpp
|
||||
timer_test.cpp
|
||||
uni_string_dfa_test.cpp
|
||||
visitor_tests.cpp
|
||||
)
|
||||
|
||||
# Windows does not have timegm function
|
||||
if(NOT WIN32)
|
||||
list(APPEND SRC "timegm_test.cpp")
|
||||
endif()
|
||||
|
||||
omim_add_test(${PROJECT_NAME} ${SRC} NO_PLATFORM_INIT)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} base)
|
||||
37
libs/base/base_tests/assert_test.cpp
Normal file
37
libs/base/base_tests/assert_test.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/base.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include "base/logging.hpp"
|
||||
|
||||
UNIT_TEST(Assert_Smoke)
|
||||
{
|
||||
int x = 5;
|
||||
// to avoid warning in release
|
||||
#ifdef RELEASE
|
||||
UNUSED_VALUE(x);
|
||||
#endif
|
||||
ASSERT_EQUAL(x, 5, ());
|
||||
ASSERT_NOT_EQUAL(x, 6, ());
|
||||
// ASSERT_EQUAL ( x, 666, ("Skip this to continue test") );
|
||||
}
|
||||
|
||||
UNIT_TEST(Check_Smoke)
|
||||
{
|
||||
int x = 5;
|
||||
CHECK_EQUAL(x, 5, ());
|
||||
CHECK_NOT_EQUAL(x, 6, ());
|
||||
// CHECK_EQUAL ( x, 666, ("Skip this to continue test") );
|
||||
}
|
||||
|
||||
UNIT_TEST(Exception_Formatting)
|
||||
{
|
||||
try
|
||||
{
|
||||
MYTHROW(RootException, ("String1", "String2", "String3"));
|
||||
}
|
||||
catch (RootException const & e)
|
||||
{
|
||||
LOG(LINFO, ("Exception string: ", e.what()));
|
||||
}
|
||||
}
|
||||
85
libs/base/base_tests/beam_tests.cpp
Normal file
85
libs/base/base_tests/beam_tests.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/beam.hpp"
|
||||
|
||||
#include "base/scope_guard.hpp"
|
||||
#include "base/timer.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace beam_tests
|
||||
{
|
||||
using namespace base;
|
||||
using namespace std;
|
||||
|
||||
template <template <typename, typename> class Beam>
|
||||
void Smoke()
|
||||
{
|
||||
size_t const kCapacity = 10;
|
||||
size_t const kTotal = 100;
|
||||
|
||||
CHECK_LESS_OR_EQUAL(kCapacity, kTotal, ());
|
||||
|
||||
Beam<uint32_t, double> beam(kCapacity);
|
||||
|
||||
for (uint32_t i = 0; i < kTotal; ++i)
|
||||
beam.Add(i, static_cast<double>(i));
|
||||
|
||||
vector<double> expected;
|
||||
for (size_t i = 0; i < kCapacity; ++i)
|
||||
expected.emplace_back(kTotal - 1 - i);
|
||||
|
||||
vector<double> actual;
|
||||
actual.reserve(kCapacity);
|
||||
for (auto const & e : beam.GetEntries())
|
||||
actual.emplace_back(e.m_value);
|
||||
|
||||
sort(actual.rbegin(), actual.rend());
|
||||
CHECK_EQUAL(expected, actual, ());
|
||||
}
|
||||
|
||||
template <template <typename, typename> class Beam>
|
||||
void Benchmark(string const & beamType, uint64_t const numResets, size_t const capacity, uint64_t const numEvents)
|
||||
{
|
||||
base::Timer timer;
|
||||
SCOPE_GUARD(timerGuard, [&] { LOG(LINFO, ("type:", beamType, "\ttime passed:", timer.ElapsedSeconds())); });
|
||||
|
||||
CHECK_LESS_OR_EQUAL(capacity, numEvents, ());
|
||||
|
||||
mt19937 rng(0);
|
||||
uniform_real_distribution<double> dis(0.0, 1.0);
|
||||
for (uint64_t wave = 0; wave <= numResets; ++wave)
|
||||
{
|
||||
Beam<uint64_t, double> beam(capacity);
|
||||
|
||||
uint64_t const begin = wave * numEvents / (numResets + 1);
|
||||
uint64_t const end = (wave + 1) * numEvents / (numResets + 1);
|
||||
for (uint64_t i = begin; i < end; ++i)
|
||||
beam.Add(i, dis(rng));
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(Beam_Smoke)
|
||||
{
|
||||
Smoke<Beam>();
|
||||
Smoke<HeapBeam>();
|
||||
}
|
||||
|
||||
UNIT_TEST(Beam_Benchmark)
|
||||
{
|
||||
size_t const kCapacity = 100;
|
||||
uint64_t const kNumEvents = 1000000;
|
||||
|
||||
for (uint64_t numResets = 0; numResets < 1000; numResets += 200)
|
||||
{
|
||||
LOG(LINFO, ("Resets =", numResets, "Capacity =", kCapacity, "Total events =", kNumEvents));
|
||||
Benchmark<Beam>("Vector-based", numResets, kCapacity, kNumEvents);
|
||||
Benchmark<HeapBeam>("Heap-based", numResets, kCapacity, kNumEvents);
|
||||
}
|
||||
}
|
||||
} // namespace beam_tests
|
||||
96
libs/base/base_tests/bidirectional_map_tests.cpp
Normal file
96
libs/base/base_tests/bidirectional_map_tests.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/bidirectional_map.hpp"
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace base;
|
||||
using namespace std;
|
||||
|
||||
UNIT_TEST(BidirectionalMap_Smoke)
|
||||
{
|
||||
BidirectionalMap<int, string> m;
|
||||
m.Add(1, "a");
|
||||
|
||||
{
|
||||
string value;
|
||||
TEST(m.GetValue(1, value), ());
|
||||
TEST_EQUAL(value, "a", ());
|
||||
}
|
||||
{
|
||||
int key;
|
||||
TEST(m.GetKey("a", key), ());
|
||||
TEST_EQUAL(key, 1, ());
|
||||
}
|
||||
|
||||
TEST(!m.Add(1, "b"), ());
|
||||
TEST(!m.Add(2, "a"), ());
|
||||
TEST(m.Add(2, "b"), ());
|
||||
TEST(!m.Add(2, "b"), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(BidirectionalMap_Remove)
|
||||
{
|
||||
{
|
||||
BidirectionalMap<int, string> m;
|
||||
TEST(m.Add(1, "a"), ());
|
||||
TEST(m.Add(2, "b"), ());
|
||||
|
||||
TEST_EQUAL(m.Size(), 2, ());
|
||||
|
||||
TEST(!m.RemoveKey(3), ());
|
||||
TEST_EQUAL(m.Size(), 2, ());
|
||||
|
||||
TEST(m.RemoveKey(1), ());
|
||||
TEST_EQUAL(m.Size(), 1, ());
|
||||
|
||||
TEST(!m.RemoveValue("a"), ());
|
||||
TEST_EQUAL(m.Size(), 1, ());
|
||||
|
||||
TEST(m.RemoveValue("b"), ());
|
||||
TEST(m.IsEmpty(), ());
|
||||
}
|
||||
|
||||
{
|
||||
BidirectionalMap<int, int> m;
|
||||
TEST(m.Add(1, 1), ());
|
||||
TEST(m.Add(2, 2), ());
|
||||
|
||||
TEST_EQUAL(m.Size(), 2, ());
|
||||
|
||||
TEST(!m.RemoveKey(3), ());
|
||||
TEST_EQUAL(m.Size(), 2, ());
|
||||
|
||||
TEST(m.RemoveKey(1), ());
|
||||
TEST_EQUAL(m.Size(), 1, ());
|
||||
|
||||
TEST(!m.RemoveValue(1), ());
|
||||
TEST_EQUAL(m.Size(), 1, ());
|
||||
|
||||
TEST(m.RemoveValue(2), ());
|
||||
TEST(m.IsEmpty(), ());
|
||||
}
|
||||
|
||||
{
|
||||
BidirectionalMap<int, int> m;
|
||||
TEST(m.Add(1, 2), ());
|
||||
TEST(m.Add(2, 1), ());
|
||||
|
||||
TEST_EQUAL(m.Size(), 2, ());
|
||||
|
||||
TEST(!m.RemoveKey(3), ());
|
||||
TEST_EQUAL(m.Size(), 2, ());
|
||||
|
||||
TEST(m.RemoveKey(1), ());
|
||||
TEST_EQUAL(m.Size(), 1, ());
|
||||
|
||||
TEST(!m.RemoveValue(2), ());
|
||||
TEST_EQUAL(m.Size(), 1, ());
|
||||
|
||||
TEST(m.RemoveValue(1), ());
|
||||
TEST(m.IsEmpty(), ());
|
||||
}
|
||||
}
|
||||
114
libs/base/base_tests/bits_test.cpp
Normal file
114
libs/base/base_tests/bits_test.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/bits.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
UNIT_TEST(Select1Test)
|
||||
{
|
||||
TEST_EQUAL(0U, bits::select1(1, 1), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(PerfectShuffle)
|
||||
{
|
||||
// 0010 0001 0100 0000
|
||||
// 0010 0001 1000 1110
|
||||
TEST_EQUAL(bits::PerfectShuffle(557851022), 288529443381657684ULL, ());
|
||||
TEST_EQUAL(bits::PerfectUnshuffle(288529443381657684ULL), 557851022, ());
|
||||
|
||||
TEST_EQUAL(bits::PerfectShuffle(0b0), 0b0, ());
|
||||
TEST_EQUAL(bits::PerfectShuffle(0b1), 0b1, ());
|
||||
|
||||
TEST_EQUAL(bits::PerfectShuffle(0b1111111111111111ULL), 0b01010101010101010101010101010101ULL, ());
|
||||
TEST_EQUAL(bits::PerfectUnshuffle(0b01010101010101010101010101010101ULL), 0b1111111111111111ULL, ());
|
||||
TEST_EQUAL(bits::PerfectShuffle(0b00000000000000001111111100000000ULL), 0b01010101010101010000000000000000ULL, ());
|
||||
TEST_EQUAL(bits::PerfectUnshuffle(0b01010101010101010000000000000000ULL), 0b00000000000000001111111100000000ULL, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(BitwiseMerge)
|
||||
{
|
||||
TEST_EQUAL(bits::BitwiseMerge(1, 1), 3, ());
|
||||
TEST_EQUAL(bits::BitwiseMerge(3, 1), 7, ());
|
||||
TEST_EQUAL(bits::BitwiseMerge(1, 3), 11, ());
|
||||
TEST_EQUAL(bits::BitwiseMerge(uint32_t{1} << 31, uint32_t{1} << 31), uint64_t{3} << 62, ());
|
||||
|
||||
auto bitwiseMergeSlow = [](uint32_t x, uint32_t y) -> uint64_t
|
||||
{
|
||||
uint64_t result = 0;
|
||||
for (uint32_t i = 0; i < 32; ++i)
|
||||
{
|
||||
uint64_t const bitX = (static_cast<uint64_t>(x) >> i) & 1;
|
||||
uint64_t const bitY = (static_cast<uint64_t>(y) >> i) & 1;
|
||||
result |= bitX << (2 * i);
|
||||
result |= bitY << (2 * i + 1);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
for (uint32_t x = 0; x < 16; ++x)
|
||||
for (uint32_t y = 0; y < 16; ++y)
|
||||
TEST_EQUAL(bits::BitwiseMerge(x, y), bitwiseMergeSlow(x, y), (x, y));
|
||||
}
|
||||
|
||||
UNIT_TEST(ZigZagEncode)
|
||||
{
|
||||
TEST_EQUAL(bits::ZigZagEncode(0), 0, ());
|
||||
TEST_EQUAL(bits::ZigZagEncode(-1), 1, ());
|
||||
TEST_EQUAL(bits::ZigZagEncode(1), 2, ());
|
||||
TEST_EQUAL(bits::ZigZagEncode(-2), 3, ());
|
||||
TEST_EQUAL(bits::ZigZagEncode(2), 4, ());
|
||||
TEST_EQUAL(bits::ZigZagEncode(127), 254, ());
|
||||
TEST_EQUAL(bits::ZigZagEncode(-128), 255, ());
|
||||
TEST_EQUAL(bits::ZigZagEncode(128), 256, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ZigZagDecode)
|
||||
{
|
||||
TEST_EQUAL(bits::ZigZagDecode(0U), 0, ());
|
||||
TEST_EQUAL(bits::ZigZagDecode(1U), -1, ());
|
||||
TEST_EQUAL(bits::ZigZagDecode(2U), 1, ());
|
||||
TEST_EQUAL(bits::ZigZagDecode(3U), -2, ());
|
||||
TEST_EQUAL(bits::ZigZagDecode(4U), 2, ());
|
||||
TEST_EQUAL(bits::ZigZagDecode(254U), 127, ());
|
||||
TEST_EQUAL(bits::ZigZagDecode(255U), -128, ());
|
||||
TEST_EQUAL(bits::ZigZagDecode(256U), 128, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NumHiZeroBits32)
|
||||
{
|
||||
TEST_EQUAL(bits::NumHiZeroBits32(0), 32, ());
|
||||
TEST_EQUAL(bits::NumHiZeroBits32(0xFFFFFFFF), 0, ());
|
||||
TEST_EQUAL(bits::NumHiZeroBits32(0x0FABCDEF), 4, ());
|
||||
TEST_EQUAL(bits::NumHiZeroBits32(0x000000FF), 24, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NumHiZeroBits64)
|
||||
{
|
||||
TEST_EQUAL(bits::NumHiZeroBits64(0), 64, ());
|
||||
TEST_EQUAL(bits::NumHiZeroBits64(0xFFFFFFFFFFFFFFFFULL), 0, ());
|
||||
TEST_EQUAL(bits::NumHiZeroBits64(0x0FABCDEF0FABCDEFULL), 4, ());
|
||||
TEST_EQUAL(bits::NumHiZeroBits64(0x000000000000FDEFULL), 48, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NumUsedBits)
|
||||
{
|
||||
TEST_EQUAL(bits::NumUsedBits(0), 0, ());
|
||||
TEST_EQUAL(bits::NumUsedBits(0xFFFFFFFFFFFFFFFFULL), 64, ());
|
||||
TEST_EQUAL(bits::NumUsedBits(0x0FABCDEF0FABCDEFULL), 60, ());
|
||||
TEST_EQUAL(bits::NumUsedBits(0x000000000000FDEFULL), 16, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(CeilLog)
|
||||
{
|
||||
TEST_EQUAL(0, bits::FloorLog(0x0), ());
|
||||
TEST_EQUAL(0, bits::FloorLog(0x1), ());
|
||||
TEST_EQUAL(1, bits::FloorLog(0x2), ());
|
||||
TEST_EQUAL(1, bits::FloorLog(0x3), ());
|
||||
TEST_EQUAL(2, bits::FloorLog(0x4), ());
|
||||
|
||||
TEST_EQUAL(6, bits::FloorLog(0x7f), ());
|
||||
TEST_EQUAL(7, bits::FloorLog(0x80), ());
|
||||
TEST_EQUAL(31, bits::FloorLog(0xFFFFFFFF), ());
|
||||
TEST_EQUAL(63, bits::FloorLog(0xFFFFFFFFFFFFFFFF), ());
|
||||
}
|
||||
412
libs/base/base_tests/buffer_vector_test.cpp
Normal file
412
libs/base/base_tests/buffer_vector_test.cpp
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/buffer_vector.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
|
||||
namespace buffer_vector_test
|
||||
{
|
||||
template <class TCont>
|
||||
void CheckVector(TCont & cont, size_t count)
|
||||
{
|
||||
TEST_EQUAL(cont.size(), count, ());
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
TEST_EQUAL(cont[i], i, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_PushBackAndRealloc)
|
||||
{
|
||||
using ElementT = std::vector<int>;
|
||||
ElementT element({1, 2, 3});
|
||||
|
||||
size_t constexpr kFixedSize = 2;
|
||||
{
|
||||
buffer_vector<ElementT, kFixedSize> v;
|
||||
v.append(kFixedSize, element);
|
||||
|
||||
v.push_back(v[0]);
|
||||
TEST_EQUAL(v.size(), kFixedSize + 1, ());
|
||||
|
||||
for (auto const & e : v)
|
||||
TEST_EQUAL(e, element, ());
|
||||
}
|
||||
|
||||
{
|
||||
buffer_vector<ElementT, kFixedSize> v;
|
||||
v.append(kFixedSize, element);
|
||||
|
||||
v.emplace_back(3, v[0][1]);
|
||||
TEST_EQUAL(v.size(), kFixedSize + 1, ());
|
||||
|
||||
for (size_t i = 0; i < kFixedSize; ++i)
|
||||
TEST_EQUAL(v[i], element, ());
|
||||
TEST_EQUAL(v[kFixedSize], ElementT({2, 2, 2}), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_Bounds)
|
||||
{
|
||||
buffer_vector<size_t, 2> v;
|
||||
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
{
|
||||
v.push_back(i);
|
||||
CheckVector(v, i + 1);
|
||||
}
|
||||
|
||||
v.resize(2);
|
||||
CheckVector(v, 2);
|
||||
|
||||
v.resize(3);
|
||||
v[2] = 2;
|
||||
CheckVector(v, 3);
|
||||
|
||||
v.resize(4);
|
||||
v[3] = 3;
|
||||
CheckVector(v, 4);
|
||||
|
||||
v.resize(1);
|
||||
CheckVector(v, 1);
|
||||
|
||||
v.resize(0);
|
||||
CheckVector(v, 0);
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_Swap)
|
||||
{
|
||||
typedef buffer_vector<size_t, 2> value_t;
|
||||
buffer_vector<value_t, 2> v1, v2;
|
||||
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
{
|
||||
v1.push_back(value_t());
|
||||
v2.push_back(value_t());
|
||||
}
|
||||
|
||||
// check swap of vectors
|
||||
value_t const * d1 = v1.data();
|
||||
value_t const * d2 = v2.data();
|
||||
|
||||
swap(v1, v2);
|
||||
TEST_EQUAL(d1, v2.data(), ());
|
||||
TEST_EQUAL(d2, v1.data(), ());
|
||||
|
||||
// check swap in resized data
|
||||
{
|
||||
v1[0].push_back(666);
|
||||
|
||||
auto const * dd1 = v1[0].data();
|
||||
|
||||
// resize from 5 to 1 => v[0] will stay at the same place
|
||||
v1.resize(1);
|
||||
TEST_EQUAL(v1[0].size(), 1, ());
|
||||
TEST_EQUAL(v1[0][0], 666, ());
|
||||
TEST_EQUAL(dd1, v1[0].data(), ());
|
||||
|
||||
v1.resize(7);
|
||||
TEST_EQUAL(v1[0].size(), 1, ());
|
||||
TEST_EQUAL(v1[0][0], 666, ());
|
||||
}
|
||||
|
||||
{
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
v2[0].push_back(i);
|
||||
|
||||
// inner dynamic buffer should be swapped during resizing
|
||||
// (??? but it's not specified by standart of std::vector ???)
|
||||
auto const * dd2 = v2[0].data();
|
||||
|
||||
v2.resize(1);
|
||||
TEST_EQUAL(v2[0].size(), 5, ());
|
||||
TEST_EQUAL(dd2, v2[0].data(), ());
|
||||
|
||||
v1.resize(7);
|
||||
TEST_EQUAL(v2[0].size(), 5, ());
|
||||
TEST_EQUAL(dd2, v2[0].data(), ());
|
||||
}
|
||||
|
||||
// check resize from static to dynamic buffer
|
||||
buffer_vector<value_t, 2> v3;
|
||||
v3.push_back(value_t());
|
||||
|
||||
{
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
v3[0].push_back(i);
|
||||
|
||||
auto const * dd3 = v3[0].data();
|
||||
|
||||
// resize from static to dynamic buffer => v3[0] will stay at the same place
|
||||
v1.resize(7);
|
||||
TEST_EQUAL(v3[0].size(), 5, ());
|
||||
TEST_EQUAL(dd3, v3[0].data(), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_Resize)
|
||||
{
|
||||
for (size_t size = 0; size < 20; ++size)
|
||||
{
|
||||
buffer_vector<int, 5> v;
|
||||
v.resize(size, 3);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
TEST_EQUAL(v[i], 3, ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_Insert)
|
||||
{
|
||||
for (size_t initialLength = 0; initialLength < 20; ++initialLength)
|
||||
{
|
||||
for (size_t insertLength = 0; insertLength < 20; ++insertLength)
|
||||
{
|
||||
for (size_t insertPos = 0; insertPos <= initialLength; ++insertPos)
|
||||
{
|
||||
buffer_vector<char, 5> b;
|
||||
std::vector<char> v;
|
||||
for (size_t i = 0; i < initialLength; ++i)
|
||||
{
|
||||
b.push_back('A' + i);
|
||||
v.push_back('A' + i);
|
||||
}
|
||||
|
||||
std::vector<int> dataToInsert(insertLength);
|
||||
std::iota(dataToInsert.begin(), dataToInsert.end(), 'a');
|
||||
|
||||
b.insert(b.begin() + insertPos, dataToInsert.begin(), dataToInsert.end());
|
||||
v.insert(v.begin() + insertPos, dataToInsert.begin(), dataToInsert.end());
|
||||
|
||||
std::vector<char> result(b.begin(), b.end());
|
||||
TEST_EQUAL(v, result, (initialLength, insertLength, insertPos));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_InsertSingleValue)
|
||||
{
|
||||
buffer_vector<char, 3> v;
|
||||
v.insert(v.end(), 'x');
|
||||
TEST_EQUAL(v.size(), 1, ());
|
||||
TEST_EQUAL(v[0], 'x', ());
|
||||
|
||||
v.insert(v.begin(), 'y');
|
||||
TEST_EQUAL(v.size(), 2, ());
|
||||
TEST_EQUAL(v[0], 'y', ());
|
||||
TEST_EQUAL(v[1], 'x', ());
|
||||
|
||||
v.insert(v.begin() + 1, 'z');
|
||||
TEST_EQUAL(v.size(), 3, ());
|
||||
TEST_EQUAL(v[0], 'y', ());
|
||||
TEST_EQUAL(v[1], 'z', ());
|
||||
TEST_EQUAL(v[2], 'x', ());
|
||||
// Switch to dynamic.
|
||||
v.insert(v.begin() + 1, 'q');
|
||||
TEST_EQUAL(v.size(), 4, ());
|
||||
TEST_EQUAL(v[0], 'y', ());
|
||||
TEST_EQUAL(v[1], 'q', ());
|
||||
TEST_EQUAL(v[2], 'z', ());
|
||||
TEST_EQUAL(v[3], 'x', ());
|
||||
|
||||
v.insert(v.end() - 1, 'c');
|
||||
TEST_EQUAL(v[3], 'c', ());
|
||||
TEST_EQUAL(v[4], 'x', ());
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_Append)
|
||||
{
|
||||
for (size_t initialLength = 0; initialLength < 20; ++initialLength)
|
||||
{
|
||||
for (size_t insertLength = 0; insertLength < 20; ++insertLength)
|
||||
{
|
||||
buffer_vector<char, 5> b;
|
||||
std::vector<char> v;
|
||||
for (size_t i = 0; i < initialLength; ++i)
|
||||
{
|
||||
b.push_back('A' + i);
|
||||
v.push_back('A' + i);
|
||||
}
|
||||
|
||||
std::vector<int> dataToInsert(insertLength);
|
||||
std::iota(dataToInsert.begin(), dataToInsert.end(), 'a');
|
||||
|
||||
b.append(dataToInsert.begin(), dataToInsert.end());
|
||||
v.insert(v.end(), dataToInsert.begin(), dataToInsert.end());
|
||||
|
||||
std::vector<char> result(b.begin(), b.end());
|
||||
TEST_EQUAL(v, result, (initialLength, insertLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_PopBack)
|
||||
{
|
||||
for (size_t len = 1; len < 6; ++len)
|
||||
{
|
||||
buffer_vector<size_t, 3> v;
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
v.push_back(i);
|
||||
for (size_t i = len; i > 0; --i)
|
||||
{
|
||||
TEST(!v.empty(), (len, i));
|
||||
TEST_EQUAL(v.size(), i, ());
|
||||
TEST_EQUAL(v.front(), 0, ());
|
||||
TEST_EQUAL(v.back(), i - 1, ());
|
||||
v.pop_back();
|
||||
TEST_EQUAL(v.size(), i - 1, ());
|
||||
}
|
||||
TEST(v.empty(), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_Assign)
|
||||
{
|
||||
int const arr5[] = {1, 2, 3, 4, 5};
|
||||
buffer_vector<int, 5> v(&arr5[0], &arr5[0] + ARRAY_SIZE(arr5));
|
||||
for (size_t i = 0; i < ARRAY_SIZE(arr5); ++i)
|
||||
TEST_EQUAL(arr5[i], v[i], ());
|
||||
|
||||
int const arr10[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
v.assign(&arr10[0], &arr10[0] + ARRAY_SIZE(arr10));
|
||||
for (size_t i = 0; i < ARRAY_SIZE(arr10); ++i)
|
||||
TEST_EQUAL(arr10[i], v[i], ());
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_Equality)
|
||||
{
|
||||
int const arr5[] = {1, 2, 3, 4, 5};
|
||||
buffer_vector<int, 5> v1(&arr5[0], &arr5[0] + ARRAY_SIZE(arr5));
|
||||
buffer_vector<int, 10> v2(&arr5[0], &arr5[0] + ARRAY_SIZE(arr5));
|
||||
buffer_vector<int, 3> v3(&arr5[0], &arr5[0] + ARRAY_SIZE(arr5));
|
||||
TEST_EQUAL(v1, v2, ());
|
||||
TEST_EQUAL(v1, v3, ());
|
||||
TEST_EQUAL(v2, v3, ());
|
||||
v1.push_back(999);
|
||||
TEST_NOT_EQUAL(v1, v2, ());
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct CopyCtorChecker
|
||||
{
|
||||
std::string m_s;
|
||||
|
||||
CopyCtorChecker() = default;
|
||||
explicit CopyCtorChecker(char const * s) : m_s(s) {}
|
||||
|
||||
CopyCtorChecker(CopyCtorChecker const & rhs)
|
||||
{
|
||||
TEST(rhs.m_s.empty(), ("Copy ctor is called only in resize with default element"));
|
||||
}
|
||||
CopyCtorChecker(CopyCtorChecker && rhs) = default;
|
||||
|
||||
CopyCtorChecker & operator=(CopyCtorChecker &&) = default;
|
||||
CopyCtorChecker & operator=(CopyCtorChecker const &)
|
||||
{
|
||||
TEST(false, ("Assigment operator should not be called"));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// void swap(CopyCtorChecker & r1, CopyCtorChecker & r2)
|
||||
// {
|
||||
// r1.m_s.swap(r2.m_s);
|
||||
// }
|
||||
|
||||
typedef buffer_vector<CopyCtorChecker, 2> VectorT;
|
||||
|
||||
VectorT GetVector()
|
||||
{
|
||||
VectorT v;
|
||||
v.emplace_back("0");
|
||||
v.emplace_back("1");
|
||||
return v;
|
||||
}
|
||||
|
||||
void TestVector(VectorT const & v, size_t sz)
|
||||
{
|
||||
TEST_EQUAL(v.size(), sz, ());
|
||||
for (size_t i = 0; i < sz; ++i)
|
||||
TEST_EQUAL(v[i].m_s, strings::to_string(i), ());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(BufferVector_Move)
|
||||
{
|
||||
VectorT v1 = GetVector();
|
||||
TestVector(v1, 2);
|
||||
|
||||
// Make intermediate array to avoid warning (moving to itself).
|
||||
std::array<VectorT *, 2> arr = {&v1, &v1};
|
||||
*arr[0] = std::move(*arr[1]);
|
||||
TestVector(v1, 2);
|
||||
|
||||
v1.emplace_back("2");
|
||||
TestVector(v1, 3);
|
||||
|
||||
VectorT v2(std::move(v1));
|
||||
TestVector(v2, 3);
|
||||
TestVector(v1, 0);
|
||||
|
||||
v1 = std::move(v2);
|
||||
TestVector(v1, 3);
|
||||
TestVector(v2, 0);
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_EraseIf)
|
||||
{
|
||||
buffer_vector<int, 2> v;
|
||||
v.push_back(1);
|
||||
v.push_back(2);
|
||||
v.erase_if([](int x) { return x == 1; });
|
||||
TEST_EQUAL(v.size(), 1, ());
|
||||
TEST_EQUAL(v[0], 2, ());
|
||||
|
||||
v.push_back(3);
|
||||
v.push_back(4);
|
||||
v.erase_if([](int x) { return x == 3; });
|
||||
TEST_EQUAL(v.size(), 2, ());
|
||||
TEST_EQUAL(v[0], 2, ());
|
||||
TEST_EQUAL(v[1], 4, ());
|
||||
|
||||
v.erase_if([](int) { return true; });
|
||||
TEST_EQUAL(v.size(), 0, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_OnlyMoveableItems)
|
||||
{
|
||||
buffer_vector<std::unique_ptr<size_t>, 4> v;
|
||||
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
v.emplace_back(std::make_unique<size_t>(i));
|
||||
|
||||
TEST_EQUAL(v.size(), 10, ());
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
TEST_EQUAL(*v[i], i, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(BufferVector_Erase)
|
||||
{
|
||||
buffer_vector<int, 32> v1;
|
||||
std::vector<int> v2;
|
||||
for (int i = 1; i < 100; ++i)
|
||||
{
|
||||
v1.push_back(i);
|
||||
v2.push_back(i);
|
||||
}
|
||||
|
||||
while (v1.size() > 1)
|
||||
{
|
||||
v1.erase(v1.begin() + v1.size() / 3, v1.begin() + 2 * v1.size() / 3);
|
||||
v2.erase(v2.begin() + v2.size() / 3, v2.begin() + 2 * v2.size() / 3);
|
||||
|
||||
TEST_EQUAL(v1.size(), v2.size(), ());
|
||||
for (size_t i = 0; i < v1.size(); ++i)
|
||||
TEST_EQUAL(v1[i], v2[i], ());
|
||||
}
|
||||
}
|
||||
} // namespace buffer_vector_test
|
||||
178
libs/base/base_tests/cache_test.cpp
Normal file
178
libs/base/base_tests/cache_test.cpp
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/cache.hpp"
|
||||
#include "base/macros.hpp"
|
||||
#include "base/math.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace
|
||||
{
|
||||
// This functor will be passed in Cache::ForEachValue by reference
|
||||
class SimpleFunctor
|
||||
{
|
||||
public:
|
||||
SimpleFunctor() {}
|
||||
|
||||
void operator()(char c) { m_v.push_back(c); }
|
||||
|
||||
std::vector<char> m_v;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY(SimpleFunctor);
|
||||
};
|
||||
|
||||
// This functor will be passed in Cache::ForEachValue by move ctor
|
||||
class SimpleMovableFunctor
|
||||
{
|
||||
public:
|
||||
explicit SimpleMovableFunctor(std::vector<char> * v) : m_v(v) {}
|
||||
|
||||
SimpleMovableFunctor(SimpleMovableFunctor && other)
|
||||
{
|
||||
m_v = other.m_v;
|
||||
other.m_v = nullptr;
|
||||
}
|
||||
|
||||
SimpleMovableFunctor & operator=(SimpleMovableFunctor && other)
|
||||
{
|
||||
m_v = other.m_v;
|
||||
other.m_v = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator()(char c) { m_v->push_back(c); }
|
||||
|
||||
private:
|
||||
std::vector<char> * m_v;
|
||||
|
||||
DISALLOW_COPY(SimpleMovableFunctor);
|
||||
};
|
||||
|
||||
double constexpr kEpsilon = 1e-6;
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(CacheSmoke)
|
||||
{
|
||||
char const s[] = "1123212434445";
|
||||
char const isNew[] = "1011???1??001";
|
||||
size_t const n = ARRAY_SIZE(s) - 1;
|
||||
for (int logCacheSize = 2; logCacheSize < 6; ++logCacheSize)
|
||||
{
|
||||
base::Cache<uint32_t, char> cache(logCacheSize);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
bool found = false;
|
||||
char & c = cache.Find(s[i], found);
|
||||
if (isNew[i] != '?')
|
||||
TEST_EQUAL(found, isNew[i] == '0', (i, s[i]));
|
||||
if (found)
|
||||
TEST_EQUAL(c, s[i], (i));
|
||||
else
|
||||
c = s[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(CacheSmoke_0)
|
||||
{
|
||||
base::Cache<uint32_t, char> cache(3); // it contains 2^3=8 elements
|
||||
bool found = true;
|
||||
cache.Find(0, found);
|
||||
TEST(!found, ());
|
||||
std::vector<char> v;
|
||||
cache.ForEachValue(base::MakeBackInsertFunctor(v));
|
||||
TEST_EQUAL(v, std::vector<char>(8, 0), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(CacheSmoke_1)
|
||||
{
|
||||
base::Cache<uint32_t, char> cache(3); // it contains 2^3=8 elements
|
||||
SimpleFunctor f;
|
||||
cache.ForEachValue(f); // f passed by reference
|
||||
TEST_EQUAL(f.m_v, std::vector<char>(8, 0), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(CacheSmoke_2)
|
||||
{
|
||||
base::Cache<uint32_t, char> cache(3); // it contains 2^3=8 elements
|
||||
SimpleFunctor f;
|
||||
cache.ForEachValue(std::ref(f)); // f passed by reference
|
||||
TEST_EQUAL(f.m_v, std::vector<char>(8, 0), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(CacheSmoke_3)
|
||||
{
|
||||
base::CacheWithStat<uint32_t, char> cache(3); // it contains 2^3=8 elements
|
||||
|
||||
// 0 access, cache miss is 0
|
||||
TEST(AlmostEqualAbs(0.0, cache.GetCacheMiss(), kEpsilon), ());
|
||||
|
||||
bool found = true;
|
||||
cache.Find(1, found);
|
||||
TEST(!found, ());
|
||||
// 1 access, 1 miss, cache miss = 1/1 = 1
|
||||
TEST(AlmostEqualAbs(1.0, cache.GetCacheMiss(), kEpsilon), ());
|
||||
|
||||
found = false;
|
||||
cache.Find(1, found);
|
||||
TEST(found, ());
|
||||
// 2 access, 1 miss, cache miss = 1/2 = 0.5
|
||||
TEST(AlmostEqualAbs(0.5, cache.GetCacheMiss(), kEpsilon), ());
|
||||
|
||||
found = false;
|
||||
cache.Find(2, found);
|
||||
TEST(!found, ());
|
||||
// 3 access, 2 miss, cache miss = 2/3 = 0.6(6)
|
||||
TEST(AlmostEqualAbs(2.0 / 3.0, cache.GetCacheMiss(), kEpsilon), ());
|
||||
|
||||
cache.Reset();
|
||||
|
||||
// 0 access, cache miss is 0
|
||||
TEST(AlmostEqualAbs(0.0, cache.GetCacheMiss(), kEpsilon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(CacheSmoke_4)
|
||||
{
|
||||
base::CacheWithStat<uint32_t, char> cache(3); // it contains 2^3=8 elements
|
||||
SimpleFunctor f;
|
||||
cache.ForEachValue(f); // f passed by reference
|
||||
TEST_EQUAL(f.m_v, std::vector<char>(8, 0), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(CacheSmoke_5)
|
||||
{
|
||||
base::CacheWithStat<uint32_t, char> cache(3); // it contains 2^3=8 elements
|
||||
SimpleFunctor f;
|
||||
cache.ForEachValue(std::ref(f)); // f passed by reference
|
||||
TEST_EQUAL(f.m_v, std::vector<char>(8, 0), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(CacheSmoke_6)
|
||||
{
|
||||
base::CacheWithStat<uint32_t, char> cache(3); // it contains 2^3=8 elements
|
||||
std::vector<char> v;
|
||||
cache.ForEachValue(SimpleMovableFunctor(&v));
|
||||
TEST_EQUAL(v, std::vector<char>(8, 0), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(Cache_Init)
|
||||
{
|
||||
base::Cache<uint32_t, char> cache;
|
||||
cache.Init(3 /* logCacheSize */);
|
||||
|
||||
bool found = true;
|
||||
cache.Find(5, found) = 'a';
|
||||
TEST(!found, ());
|
||||
|
||||
TEST_EQUAL(cache.Find(5, found), 'a', ());
|
||||
TEST(found, ());
|
||||
|
||||
cache.Init(1 /* logCacheSize */);
|
||||
cache.Find(5, found) = 'b';
|
||||
TEST(!found, ());
|
||||
|
||||
TEST_EQUAL(cache.Find(5, found), 'b', ());
|
||||
TEST(found, ());
|
||||
}
|
||||
83
libs/base/base_tests/cancellable_tests.cpp
Normal file
83
libs/base/base_tests/cancellable_tests.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/cancellable.hpp"
|
||||
#include "base/math.hpp"
|
||||
#include "base/thread.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <future>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace base
|
||||
{
|
||||
UNIT_TEST(Cancellable_Smoke)
|
||||
{
|
||||
Cancellable cancellable;
|
||||
|
||||
promise<void> syncPromise;
|
||||
auto syncFuture = syncPromise.get_future();
|
||||
|
||||
double x = 0.123;
|
||||
|
||||
auto const fn = [&]
|
||||
{
|
||||
for (size_t it = 0;; it++)
|
||||
{
|
||||
if (it > 100 && cancellable.IsCancelled())
|
||||
break;
|
||||
|
||||
x = cos(x);
|
||||
}
|
||||
|
||||
syncPromise.set_value();
|
||||
};
|
||||
|
||||
threads::SimpleThread thread(fn);
|
||||
cancellable.Cancel();
|
||||
syncFuture.wait();
|
||||
thread.join();
|
||||
|
||||
TEST(cancellable.IsCancelled(), ());
|
||||
TEST_EQUAL(cancellable.CancellationStatus(), Cancellable::Status::CancelCalled, ());
|
||||
TEST(AlmostEqualAbs(x, 0.739, 1e-3), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(Cancellable_Deadline)
|
||||
{
|
||||
Cancellable cancellable;
|
||||
chrono::steady_clock::duration kTimeout = chrono::milliseconds(20);
|
||||
cancellable.SetDeadline(chrono::steady_clock::now() + kTimeout);
|
||||
|
||||
promise<void> syncPromise;
|
||||
auto syncFuture = syncPromise.get_future();
|
||||
|
||||
double x = 0.123;
|
||||
|
||||
auto const fn = [&]
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (cancellable.IsCancelled())
|
||||
break;
|
||||
|
||||
x = cos(x);
|
||||
}
|
||||
|
||||
syncPromise.set_value();
|
||||
};
|
||||
|
||||
threads::SimpleThread thread(fn);
|
||||
syncFuture.wait();
|
||||
thread.join();
|
||||
|
||||
TEST(cancellable.IsCancelled(), ());
|
||||
TEST_EQUAL(cancellable.CancellationStatus(), Cancellable::Status::DeadlineExceeded, ());
|
||||
TEST(AlmostEqualAbs(x, 0.739, 1e-3), ());
|
||||
|
||||
cancellable.Cancel();
|
||||
TEST(cancellable.IsCancelled(), ());
|
||||
TEST_EQUAL(cancellable.CancellationStatus(), Cancellable::Status::CancelCalled, ());
|
||||
}
|
||||
} // namespace base
|
||||
74
libs/base/base_tests/checked_cast_tests.cpp
Normal file
74
libs/base/base_tests/checked_cast_tests.cpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/checked_cast.hpp"
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wimplicitly-unsigned-literal"
|
||||
#endif // #ifdef __clang__
|
||||
UNIT_TEST(IsCastValid)
|
||||
{
|
||||
{
|
||||
int8_t value = -1;
|
||||
TEST(base::IsCastValid<int8_t>(value), ());
|
||||
TEST(base::IsCastValid<int16_t>(value), ());
|
||||
TEST(base::IsCastValid<int32_t>(value), ());
|
||||
TEST(base::IsCastValid<int64_t>(value), ());
|
||||
|
||||
TEST(!base::IsCastValid<uint8_t>(value), ());
|
||||
TEST(!base::IsCastValid<uint16_t>(value), ());
|
||||
TEST(!base::IsCastValid<uint32_t>(value), ());
|
||||
TEST(!base::IsCastValid<uint64_t>(value), ());
|
||||
}
|
||||
{
|
||||
int64_t value = -1;
|
||||
TEST(base::IsCastValid<int8_t>(value), ());
|
||||
TEST(base::IsCastValid<int16_t>(value), ());
|
||||
TEST(base::IsCastValid<int32_t>(value), ());
|
||||
TEST(base::IsCastValid<int64_t>(value), ());
|
||||
|
||||
TEST(!base::IsCastValid<uint8_t>(value), ());
|
||||
TEST(!base::IsCastValid<uint16_t>(value), ());
|
||||
TEST(!base::IsCastValid<uint32_t>(value), ());
|
||||
TEST(!base::IsCastValid<uint64_t>(value), ());
|
||||
}
|
||||
{
|
||||
uint8_t value = 128;
|
||||
TEST(!base::IsCastValid<int8_t>(value), ());
|
||||
TEST(base::IsCastValid<int16_t>(value), ());
|
||||
TEST(base::IsCastValid<int32_t>(value), ());
|
||||
TEST(base::IsCastValid<int64_t>(value), ());
|
||||
|
||||
TEST(base::IsCastValid<uint8_t>(value), ());
|
||||
TEST(base::IsCastValid<uint16_t>(value), ());
|
||||
TEST(base::IsCastValid<uint32_t>(value), ());
|
||||
TEST(base::IsCastValid<uint64_t>(value), ());
|
||||
}
|
||||
{
|
||||
uint64_t value = 9223372036854775808ULL;
|
||||
TEST(!base::IsCastValid<int8_t>(value), ());
|
||||
TEST(!base::IsCastValid<int16_t>(value), ());
|
||||
TEST(!base::IsCastValid<int32_t>(value), ());
|
||||
TEST(!base::IsCastValid<int64_t>(value), ());
|
||||
|
||||
TEST(!base::IsCastValid<uint8_t>(value), ());
|
||||
TEST(!base::IsCastValid<uint16_t>(value), ());
|
||||
TEST(!base::IsCastValid<uint32_t>(value), ());
|
||||
TEST(base::IsCastValid<uint64_t>(value), ());
|
||||
}
|
||||
{
|
||||
int64_t value = -9223372036854775807LL;
|
||||
TEST(!base::IsCastValid<int8_t>(value), ());
|
||||
TEST(!base::IsCastValid<int16_t>(value), ());
|
||||
TEST(!base::IsCastValid<int32_t>(value), ());
|
||||
TEST(base::IsCastValid<int64_t>(value), ());
|
||||
|
||||
TEST(!base::IsCastValid<uint8_t>(value), ());
|
||||
TEST(!base::IsCastValid<uint16_t>(value), ());
|
||||
TEST(!base::IsCastValid<uint32_t>(value), ());
|
||||
TEST(!base::IsCastValid<uint64_t>(value), ());
|
||||
}
|
||||
}
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif // #ifdef __clang__
|
||||
177
libs/base/base_tests/clustering_map_tests.cpp
Normal file
177
libs/base/base_tests/clustering_map_tests.cpp
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/clustering_map.hpp"
|
||||
#include "base/internal/message.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace base;
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
vector<T> Sort(vector<T> vs)
|
||||
{
|
||||
sort(vs.begin(), vs.end());
|
||||
return vs;
|
||||
}
|
||||
|
||||
template <typename Key, typename Value, typename Hash = hash<Key>>
|
||||
class ClusteringMapAdapter
|
||||
{
|
||||
public:
|
||||
struct Cluster
|
||||
{
|
||||
Cluster(Key const & key, Value const & value) : m_keys({key}), m_values({value}) {}
|
||||
|
||||
Cluster(vector<Key> const & keys, vector<Value> const & values) : m_keys(keys), m_values(values)
|
||||
{
|
||||
sort(m_keys.begin(), m_keys.end());
|
||||
sort(m_values.begin(), m_values.end());
|
||||
}
|
||||
|
||||
bool operator<(Cluster const & rhs) const
|
||||
{
|
||||
if (m_keys != rhs.m_keys)
|
||||
return m_keys < rhs.m_keys;
|
||||
return m_values < rhs.m_values;
|
||||
}
|
||||
|
||||
bool operator==(Cluster const & rhs) const { return m_keys == rhs.m_keys && m_values == rhs.m_values; }
|
||||
|
||||
friend string DebugPrint(Cluster const & cluster)
|
||||
{
|
||||
ostringstream os;
|
||||
os << "Cluster [";
|
||||
os << "keys: " << ::DebugPrint(cluster.m_keys) << ", ";
|
||||
os << "values: " << ::DebugPrint(cluster.m_values);
|
||||
os << "]";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
vector<Key> m_keys;
|
||||
vector<Value> m_values;
|
||||
};
|
||||
|
||||
template <typename V>
|
||||
void Append(Key const & key, V && value)
|
||||
{
|
||||
m_m.Append(key, std::forward<V>(value));
|
||||
}
|
||||
|
||||
void Union(Key const & u, Key const & v) { m_m.Union(u, v); }
|
||||
|
||||
vector<Value> Get(Key const & key) { return Sort(m_m.Get(key)); }
|
||||
|
||||
vector<Cluster> Clusters()
|
||||
{
|
||||
vector<Cluster> clusters;
|
||||
|
||||
m_m.ForEachCluster([&](vector<Key> const & keys, vector<Value> const & values)
|
||||
{ clusters.emplace_back(keys, values); });
|
||||
sort(clusters.begin(), clusters.end());
|
||||
return clusters;
|
||||
}
|
||||
|
||||
private:
|
||||
ClusteringMap<Key, Value, Hash> m_m;
|
||||
};
|
||||
|
||||
UNIT_TEST(ClusteringMap_Smoke)
|
||||
{
|
||||
{
|
||||
ClusteringMapAdapter<int, string> m;
|
||||
TEST(m.Get(0).empty(), ());
|
||||
TEST(m.Get(1).empty(), ());
|
||||
|
||||
m.Union(0, 1);
|
||||
TEST(m.Get(0).empty(), ());
|
||||
TEST(m.Get(1).empty(), ());
|
||||
}
|
||||
|
||||
{
|
||||
ClusteringMapAdapter<int, string> m;
|
||||
m.Append(0, "Hello");
|
||||
m.Append(1, "World!");
|
||||
|
||||
TEST_EQUAL(m.Get(0), vector<string>({"Hello"}), ());
|
||||
TEST_EQUAL(m.Get(1), vector<string>({"World!"}), ());
|
||||
|
||||
m.Union(0, 1);
|
||||
TEST_EQUAL(m.Get(0), vector<string>({"Hello", "World!"}), ());
|
||||
TEST_EQUAL(m.Get(1), vector<string>({"Hello", "World!"}), ());
|
||||
|
||||
m.Append(2, "alpha");
|
||||
m.Append(3, "beta");
|
||||
m.Append(4, "gamma");
|
||||
|
||||
TEST_EQUAL(m.Get(2), vector<string>({"alpha"}), ());
|
||||
TEST_EQUAL(m.Get(3), vector<string>({"beta"}), ());
|
||||
TEST_EQUAL(m.Get(4), vector<string>({"gamma"}), ());
|
||||
|
||||
m.Union(2, 3);
|
||||
m.Union(3, 4);
|
||||
|
||||
TEST_EQUAL(m.Get(2), vector<string>({"alpha", "beta", "gamma"}), ());
|
||||
TEST_EQUAL(m.Get(3), vector<string>({"alpha", "beta", "gamma"}), ());
|
||||
TEST_EQUAL(m.Get(4), vector<string>({"alpha", "beta", "gamma"}), ());
|
||||
|
||||
TEST_EQUAL(m.Get(5), vector<string>(), ());
|
||||
m.Union(2, 5);
|
||||
TEST_EQUAL(m.Get(5), vector<string>({"alpha", "beta", "gamma"}), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(ClusteringMap_ForEach)
|
||||
{
|
||||
using Map = ClusteringMapAdapter<int, string>;
|
||||
using Cluster = Map::Cluster;
|
||||
|
||||
{
|
||||
Map m;
|
||||
auto const clusters = m.Clusters();
|
||||
TEST(clusters.empty(), (clusters));
|
||||
}
|
||||
|
||||
{
|
||||
Map m;
|
||||
m.Append(0, "Hello");
|
||||
m.Append(1, "World!");
|
||||
m.Append(2, "alpha");
|
||||
m.Append(3, "beta");
|
||||
m.Append(4, "gamma");
|
||||
|
||||
{
|
||||
vector<Cluster> const expected = {
|
||||
{Cluster{0, "Hello"}, Cluster{1, "World!"}, Cluster{2, "alpha"}, Cluster{3, "beta"}, Cluster{4, "gamma"}}};
|
||||
TEST_EQUAL(expected, m.Clusters(), ());
|
||||
}
|
||||
|
||||
m.Union(0, 1);
|
||||
{
|
||||
vector<Cluster> const expected = {
|
||||
{Cluster{{0, 1}, {"Hello", "World!"}}, Cluster{2, "alpha"}, Cluster{3, "beta"}, Cluster{4, "gamma"}}};
|
||||
TEST_EQUAL(expected, m.Clusters(), ());
|
||||
}
|
||||
|
||||
m.Union(2, 3);
|
||||
m.Union(3, 4);
|
||||
{
|
||||
vector<Cluster> const expected = {
|
||||
{Cluster{{0, 1}, {"Hello", "World!"}}, Cluster{{2, 3, 4}, {"alpha", "beta", "gamma"}}}};
|
||||
TEST_EQUAL(expected, m.Clusters(), ());
|
||||
}
|
||||
|
||||
m.Union(0, 3);
|
||||
{
|
||||
vector<Cluster> const expected = {{Cluster{{0, 1, 2, 3, 4}, {"Hello", "World!", "alpha", "beta", "gamma"}}}};
|
||||
TEST_EQUAL(expected, m.Clusters(), ());
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
17
libs/base/base_tests/collection_cast_test.cpp
Normal file
17
libs/base/base_tests/collection_cast_test.cpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/collection_cast.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
UNIT_TEST(collection_cast)
|
||||
{
|
||||
TEST_EQUAL((std::list<int>{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
}),
|
||||
base::collection_cast<std::list>(std::vector<int>{1, 2, 3, 4}), ());
|
||||
}
|
||||
35
libs/base/base_tests/containers_test.cpp
Normal file
35
libs/base/base_tests/containers_test.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/limited_priority_queue.hpp"
|
||||
|
||||
using namespace base;
|
||||
|
||||
UNIT_TEST(LPQueue_Smoke)
|
||||
{
|
||||
limited_priority_queue<int> q(3);
|
||||
TEST(q.empty(), ());
|
||||
|
||||
q.push(5);
|
||||
q.push(-1);
|
||||
q.push(3);
|
||||
TEST_EQUAL(q.top(), 5, ());
|
||||
|
||||
q.push(2);
|
||||
TEST_EQUAL(q.top(), 3, ());
|
||||
TEST_EQUAL(q.size(), 3, ());
|
||||
|
||||
q.push(0);
|
||||
q.push(0);
|
||||
q.push(0);
|
||||
|
||||
TEST_EQUAL(q.top(), 0, ());
|
||||
q.pop();
|
||||
|
||||
TEST_EQUAL(q.top(), 0, ());
|
||||
q.pop();
|
||||
|
||||
TEST_EQUAL(q.top(), -1, ());
|
||||
q.pop();
|
||||
|
||||
TEST(q.empty(), ());
|
||||
}
|
||||
58
libs/base/base_tests/control_flow_tests.cpp
Normal file
58
libs/base/base_tests/control_flow_tests.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/control_flow.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
using namespace base;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Repeater
|
||||
{
|
||||
explicit Repeater(uint32_t repetitions) : m_repetitions(repetitions) {}
|
||||
|
||||
template <typename Fn>
|
||||
void ForEach(Fn && fn)
|
||||
{
|
||||
ControlFlowWrapper<Fn> wrapper(std::forward<Fn>(fn));
|
||||
for (uint32_t i = 0; i < m_repetitions; ++i)
|
||||
{
|
||||
++m_calls;
|
||||
if (wrapper() == ControlFlow::Break)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t m_repetitions = 0;
|
||||
uint32_t m_calls = 0;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(ControlFlow_Smoke)
|
||||
{
|
||||
{
|
||||
Repeater repeater(10);
|
||||
uint32_t c = 0;
|
||||
repeater.ForEach([&c] { ++c; });
|
||||
|
||||
TEST_EQUAL(c, 10, ());
|
||||
TEST_EQUAL(c, repeater.m_repetitions, ());
|
||||
TEST_EQUAL(c, repeater.m_calls, ());
|
||||
}
|
||||
|
||||
{
|
||||
Repeater repeater(10);
|
||||
uint32_t c = 0;
|
||||
repeater.ForEach([&c]
|
||||
{
|
||||
++c;
|
||||
if (c == 5)
|
||||
return ControlFlow::Break;
|
||||
return ControlFlow::Continue;
|
||||
});
|
||||
|
||||
TEST_EQUAL(c, 5, ());
|
||||
TEST_EQUAL(c, repeater.m_calls, ());
|
||||
}
|
||||
}
|
||||
185
libs/base/base_tests/exception_tests.cpp
Normal file
185
libs/base/base_tests/exception_tests.cpp
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/exception.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
int FuncDoesNotThrow() noexcept
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
int FuncThrowsRootException()
|
||||
{
|
||||
throw RootException("RootException", "RootException");
|
||||
}
|
||||
int FuncThrowsException()
|
||||
{
|
||||
throw std::exception();
|
||||
}
|
||||
int FuncThrowsRuntimeError()
|
||||
{
|
||||
throw std::runtime_error("runtime_error");
|
||||
}
|
||||
int FuncThrowsNumber()
|
||||
{
|
||||
throw 1;
|
||||
}
|
||||
|
||||
int FuncDoesNotThrowArg(int) noexcept
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
int FuncThrowsRootExceptionArg(int)
|
||||
{
|
||||
throw RootException("RootException", "RootException");
|
||||
}
|
||||
int FuncThrowsExceptionArg(int)
|
||||
{
|
||||
throw std::exception();
|
||||
}
|
||||
int FuncThrowsNumberArg(int)
|
||||
{
|
||||
throw 1;
|
||||
}
|
||||
|
||||
void FuncDoesNotThrowVoid(int) noexcept
|
||||
{
|
||||
return;
|
||||
}
|
||||
void FuncThrowsRootExceptionVoid(int)
|
||||
{
|
||||
throw RootException("RootException", "RootException");
|
||||
}
|
||||
void FuncThrowsExceptionVoid()
|
||||
{
|
||||
throw std::exception();
|
||||
}
|
||||
void FuncThrowsNumberVoid()
|
||||
{
|
||||
throw 1;
|
||||
}
|
||||
|
||||
std::string const & ReturnsByRef(std::string const & str)
|
||||
{
|
||||
bool exception = true;
|
||||
return ExceptionCatcher("ReturnsByRef().", exception,
|
||||
[](std::string const & str) -> std::string const & { return str; }, str);
|
||||
}
|
||||
|
||||
UNIT_TEST(ExceptionCatcher_FunctionsWithoutArgs)
|
||||
{
|
||||
bool exception = false;
|
||||
ExceptionCatcher("Function without arg.", exception, FuncDoesNotThrow);
|
||||
TEST(!exception, ());
|
||||
ExceptionCatcher("Function without arg.", exception, FuncThrowsRootException);
|
||||
TEST(exception, ());
|
||||
ExceptionCatcher("Function without arg.", exception, FuncThrowsException);
|
||||
TEST(exception, ());
|
||||
ExceptionCatcher("Function without arg.", exception, FuncThrowsRuntimeError);
|
||||
TEST(exception, ());
|
||||
ExceptionCatcher("Function without arg.", exception, FuncThrowsNumber);
|
||||
TEST(exception, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ExceptionCatcher_Functions)
|
||||
{
|
||||
bool exception = false;
|
||||
ExceptionCatcher("Function with arg.", exception, FuncDoesNotThrowArg, 7);
|
||||
TEST(!exception, ());
|
||||
ExceptionCatcher("Function with arg.", exception, FuncThrowsRootExceptionArg, 7);
|
||||
TEST(exception, ());
|
||||
ExceptionCatcher("Function with arg.", exception, FuncThrowsExceptionArg, 7);
|
||||
TEST(exception, ());
|
||||
ExceptionCatcher("Function with arg.", exception, FuncThrowsNumberArg, 7);
|
||||
TEST(exception, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ExceptionCatcher_LambdasReturnInt)
|
||||
{
|
||||
bool exception = false;
|
||||
size_t callCount = 0;
|
||||
ExceptionCatcher("Lambda", exception, [&callCount](int)
|
||||
{
|
||||
++callCount;
|
||||
return 1;
|
||||
}, 7);
|
||||
TEST(!exception, ());
|
||||
TEST_EQUAL(callCount, 1, ());
|
||||
|
||||
ExceptionCatcher("Lambda", exception, [&callCount](int) -> int
|
||||
{
|
||||
++callCount;
|
||||
throw RootException("RootException", "RootException");
|
||||
}, 7);
|
||||
TEST(exception, ());
|
||||
TEST_EQUAL(callCount, 2, ());
|
||||
|
||||
ExceptionCatcher("Lambda", exception, [&callCount](int) -> int
|
||||
{
|
||||
++callCount;
|
||||
throw std::exception();
|
||||
}, 7);
|
||||
TEST(exception, ());
|
||||
TEST_EQUAL(callCount, 3, ());
|
||||
|
||||
ExceptionCatcher("Lambda", exception, [&callCount](int) -> int
|
||||
{
|
||||
++callCount;
|
||||
throw 1;
|
||||
}, 7);
|
||||
TEST(exception, ());
|
||||
TEST_EQUAL(callCount, 4, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ExceptionCatcher_LambdasReturnVoid)
|
||||
{
|
||||
bool exception = false;
|
||||
size_t callCount = 0;
|
||||
ExceptionCatcher("Lambda", exception, [&callCount](int) { ++callCount; }, 7);
|
||||
TEST(!exception, ());
|
||||
TEST_EQUAL(callCount, 1, ());
|
||||
|
||||
ExceptionCatcher("Lambda", exception, [&callCount](int)
|
||||
{
|
||||
++callCount;
|
||||
throw RootException("RootException", "RootException");
|
||||
}, 7);
|
||||
TEST(exception, ());
|
||||
TEST_EQUAL(callCount, 2, ());
|
||||
|
||||
ExceptionCatcher("Lambda", exception, [&callCount](int)
|
||||
{
|
||||
++callCount;
|
||||
throw std::exception();
|
||||
}, 7);
|
||||
TEST(exception, ());
|
||||
TEST_EQUAL(callCount, 3, ());
|
||||
|
||||
ExceptionCatcher("Lambda", exception, [&callCount](int)
|
||||
{
|
||||
++callCount;
|
||||
throw 1;
|
||||
}, 7);
|
||||
TEST(exception, ());
|
||||
TEST_EQUAL(callCount, 4, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ExceptionCatcher_FunctionsReturnVoid)
|
||||
{
|
||||
bool exception = false;
|
||||
ExceptionCatcher("Function returns void.", exception, FuncDoesNotThrowVoid, 7);
|
||||
ExceptionCatcher("Function returns void.", exception, FuncThrowsRootExceptionVoid, 7);
|
||||
ExceptionCatcher("Function returns void.", exception, FuncThrowsExceptionVoid);
|
||||
ExceptionCatcher("Function returns void.", exception, FuncThrowsNumberVoid);
|
||||
}
|
||||
|
||||
UNIT_TEST(ExceptionCatcher_PreventReturningRefOnLocalTemporaryObj)
|
||||
{
|
||||
std::string const str = "A string";
|
||||
auto const returnedStr = ReturnsByRef(str);
|
||||
TEST_EQUAL(str, returnedStr, ());
|
||||
}
|
||||
} // namespace
|
||||
114
libs/base/base_tests/fifo_cache_test.cpp
Normal file
114
libs/base/base_tests/fifo_cache_test.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/fifo_cache.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-copy"
|
||||
#endif
|
||||
#include <boost/circular_buffer.hpp>
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class FifoCacheTest
|
||||
{
|
||||
public:
|
||||
FifoCacheTest(size_t capacity, typename FifoCache<Key, Value>::Loader const & loader) : m_cache(capacity, loader) {}
|
||||
|
||||
Value const & GetValue(Key const & key) { return m_cache.GetValue(key); }
|
||||
unordered_map<Key, Value> const & GetMap() const { return m_cache.m_map; }
|
||||
boost::circular_buffer<Key> const & GetFifo() const { return m_cache.m_fifo; }
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
set<Key> listKeys(m_cache.m_fifo.begin(), m_cache.m_fifo.end());
|
||||
set<Key> mapKeys;
|
||||
|
||||
for (auto const & kv : m_cache.m_map)
|
||||
mapKeys.insert(kv.first);
|
||||
|
||||
return listKeys == mapKeys;
|
||||
}
|
||||
|
||||
private:
|
||||
FifoCache<Key, Value> m_cache;
|
||||
};
|
||||
|
||||
UNIT_TEST(FifoCache_Smoke)
|
||||
{
|
||||
using Key = int;
|
||||
using Value = int;
|
||||
FifoCacheTest<Key, Value> cache(3 /* capacity */, [](Key k, Value & v) { v = k; } /* loader */);
|
||||
|
||||
TEST_EQUAL(cache.GetValue(1), 1, ());
|
||||
TEST_EQUAL(cache.GetValue(2), 2, ());
|
||||
TEST_EQUAL(cache.GetValue(3), 3, ());
|
||||
TEST_EQUAL(cache.GetValue(4), 4, ());
|
||||
TEST_EQUAL(cache.GetValue(1), 1, ());
|
||||
TEST(cache.IsValid(), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(FifoCache)
|
||||
{
|
||||
using Key = int;
|
||||
using Value = int;
|
||||
FifoCacheTest<Key, Value> cache(3 /* capacity */, [](Key k, Value & v) { v = k; } /* loader */);
|
||||
|
||||
TEST_EQUAL(cache.GetValue(1), 1, ());
|
||||
TEST_EQUAL(cache.GetValue(3), 3, ());
|
||||
TEST_EQUAL(cache.GetValue(2), 2, ());
|
||||
TEST(cache.IsValid(), ());
|
||||
{
|
||||
unordered_map<Key, Value> expectedMap({{1 /* key */, 1 /* value */}, {2, 2}, {3, 3}});
|
||||
TEST_EQUAL(cache.GetMap(), expectedMap, ());
|
||||
list<Key> expectedList({2, 3, 1});
|
||||
boost::circular_buffer<Key> expectedCB(expectedList.cbegin(), expectedList.cend());
|
||||
TEST(cache.GetFifo() == expectedCB, ());
|
||||
}
|
||||
|
||||
TEST_EQUAL(cache.GetValue(7), 7, ());
|
||||
TEST(cache.IsValid(), ());
|
||||
{
|
||||
unordered_map<Key, Value> expectedMap({{7 /* key */, 7 /* value */}, {2, 2}, {3, 3}});
|
||||
TEST_EQUAL(cache.GetMap(), expectedMap, ());
|
||||
list<Key> expectedList({7, 2, 3});
|
||||
boost::circular_buffer<Key> expectedCB(expectedList.cbegin(), expectedList.cend());
|
||||
TEST(cache.GetFifo() == expectedCB, ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(FifoCache_LoaderCalls)
|
||||
{
|
||||
using Key = int;
|
||||
using Value = int;
|
||||
bool shouldLoadBeCalled = true;
|
||||
auto loader = [&shouldLoadBeCalled](Key k, Value & v)
|
||||
{
|
||||
TEST(shouldLoadBeCalled, ());
|
||||
v = k;
|
||||
};
|
||||
|
||||
FifoCacheTest<Key, Value> cache(3 /* capacity */, loader);
|
||||
TEST(cache.IsValid(), ());
|
||||
cache.GetValue(1);
|
||||
cache.GetValue(2);
|
||||
cache.GetValue(3);
|
||||
TEST(cache.IsValid(), ());
|
||||
shouldLoadBeCalled = false;
|
||||
cache.GetValue(3);
|
||||
cache.GetValue(2);
|
||||
cache.GetValue(1);
|
||||
TEST(cache.IsValid(), ());
|
||||
shouldLoadBeCalled = true;
|
||||
cache.GetValue(4);
|
||||
cache.GetValue(1);
|
||||
TEST(cache.IsValid(), ());
|
||||
}
|
||||
86
libs/base/base_tests/file_name_utils_tests.cpp
Normal file
86
libs/base/base_tests/file_name_utils_tests.cpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/file_name_utils.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
UNIT_TEST(FileName_Smoke)
|
||||
{
|
||||
std::string name = "/Users/xxx/Documents/test.test";
|
||||
TEST_EQUAL(base::GetFileExtension(name), ".test", ());
|
||||
base::GetNameFromFullPath(name);
|
||||
TEST_EQUAL(name, "test.test", ());
|
||||
base::GetNameFromFullPath(name);
|
||||
TEST_EQUAL(name, "test.test", ());
|
||||
base::GetNameWithoutExt(name);
|
||||
TEST_EQUAL(name, "test", ());
|
||||
|
||||
name = "C:\\My Documents\\test.test";
|
||||
TEST_EQUAL(base::GetFileExtension(name), ".test", ());
|
||||
base::GetNameFromFullPath(name);
|
||||
TEST_EQUAL(name, "test.test", ());
|
||||
base::GetNameWithoutExt(name);
|
||||
TEST_EQUAL(name, "test", ());
|
||||
|
||||
name = "/";
|
||||
TEST_EQUAL(base::GetFileExtension(name), std::string(), ());
|
||||
base::GetNameFromFullPath(name);
|
||||
TEST(name.empty(), ());
|
||||
|
||||
name = "C:\\";
|
||||
TEST_EQUAL(base::GetFileExtension(name), std::string(), ());
|
||||
base::GetNameFromFullPath(name);
|
||||
TEST(name.empty(), ());
|
||||
|
||||
name = "../test";
|
||||
TEST_EQUAL(base::GetFileExtension(name), std::string(), ());
|
||||
base::GetNameFromFullPath(name);
|
||||
TEST_EQUAL(name, "test", ());
|
||||
base::GetNameWithoutExt(name);
|
||||
TEST_EQUAL(name, "test", ());
|
||||
}
|
||||
|
||||
// TODO (@gorshenin): implement a Clean() method to clean file path
|
||||
// (remove redundant separators, correctly collapse dots, dot-dots, etc.).
|
||||
#ifndef OMIM_OS_WINDOWS
|
||||
|
||||
UNIT_TEST(FileName_GetDirectory)
|
||||
{
|
||||
TEST_EQUAL("/tmp", base::GetDirectory("/tmp/evil\\file"), ());
|
||||
TEST_EQUAL(".", base::GetDirectory("evil\\file"), ());
|
||||
|
||||
TEST_EQUAL("/", base::GetDirectory("/somefile.txt"), ());
|
||||
|
||||
TEST_EQUAL("/", base::GetDirectory("////somefile"), ());
|
||||
TEST_EQUAL("a/b", base::GetDirectory("a/b///somefile.txt"), ());
|
||||
|
||||
TEST_EQUAL("/a/b", base::GetDirectory("/a/b/c"), ());
|
||||
TEST_EQUAL("/a/b", base::GetDirectory("/a/b/c.txt"), ());
|
||||
|
||||
TEST_EQUAL(".", base::GetDirectory("somefile.txt"), ());
|
||||
TEST_EQUAL(".", base::GetDirectory("somefile"), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(FilePath_Slash)
|
||||
{
|
||||
TEST_EQUAL("/", base::AddSlashIfNeeded(""), ());
|
||||
TEST_EQUAL("/", base::AddSlashIfNeeded("/"), ());
|
||||
TEST_EQUAL("./", base::AddSlashIfNeeded("."), ());
|
||||
TEST_EQUAL("data/", base::AddSlashIfNeeded("data"), ());
|
||||
TEST_EQUAL("data/", base::AddSlashIfNeeded("data/"), ());
|
||||
TEST_EQUAL("/data/", base::AddSlashIfNeeded("/data"), ());
|
||||
TEST_EQUAL("/data/", base::AddSlashIfNeeded("/data/"), ());
|
||||
TEST_EQUAL("../../data/", base::AddSlashIfNeeded("../../data"), ());
|
||||
TEST_EQUAL("../../data/", base::AddSlashIfNeeded("../../data/"), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(FilePath_Join)
|
||||
{
|
||||
TEST_EQUAL("/root//path//file", base::JoinPath("/root/", "/path/", "/file"), ());
|
||||
TEST_EQUAL("omim/strings.txt", base::JoinPath("omim", "strings.txt"), ());
|
||||
TEST_EQUAL("omim/strings.txt", base::JoinPath("omim/", "strings.txt"), ());
|
||||
TEST_EQUAL("../../omim/strings.txt", base::JoinPath("..", "..", "omim", "strings.txt"), ());
|
||||
TEST_EQUAL("../../omim/strings.txt", base::JoinPath("../", "..", "omim/", "strings.txt"), ());
|
||||
}
|
||||
|
||||
#endif // OMIM_OS_WINDOWS
|
||||
67
libs/base/base_tests/geo_object_id_tests.cpp
Normal file
67
libs/base/base_tests/geo_object_id_tests.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/geo_object_id.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace base;
|
||||
|
||||
UNIT_TEST(GeoObjectId_Smoke)
|
||||
{
|
||||
GeoObjectId const invalid(GeoObjectId::kInvalid);
|
||||
TEST_EQUAL(invalid.GetType(), GeoObjectId::Type::Invalid, ());
|
||||
TEST_EQUAL(DebugPrint(invalid), "Invalid 0", ());
|
||||
|
||||
GeoObjectId const node(GeoObjectId::Type::ObsoleteOsmNode, 12345);
|
||||
TEST_EQUAL(node.GetSerialId(), 12345ULL, ());
|
||||
TEST_EQUAL(node.GetType(), GeoObjectId::Type::ObsoleteOsmNode, ());
|
||||
TEST_EQUAL(DebugPrint(node), "Osm Node 12345", ());
|
||||
|
||||
GeoObjectId const way(GeoObjectId::Type::ObsoleteOsmWay, 93245123456332ULL);
|
||||
TEST_EQUAL(way.GetSerialId(), 93245123456332ULL, ());
|
||||
TEST_EQUAL(way.GetType(), GeoObjectId::Type::ObsoleteOsmWay, ());
|
||||
TEST_EQUAL(DebugPrint(way), "Osm Way 93245123456332", ());
|
||||
|
||||
GeoObjectId const relation(GeoObjectId::Type::ObsoleteOsmRelation, 5);
|
||||
TEST_EQUAL(relation.GetSerialId(), 5ULL, ());
|
||||
TEST_EQUAL(relation.GetType(), GeoObjectId::Type::ObsoleteOsmRelation, ());
|
||||
TEST_EQUAL(DebugPrint(relation), "Osm Relation 5", ());
|
||||
|
||||
// 2^48 - 1, maximal possible serial id.
|
||||
GeoObjectId const surrogate(GeoObjectId::Type::OsmSurrogate, 281474976710655ULL);
|
||||
TEST_EQUAL(surrogate.GetSerialId(), 281474976710655ULL, ());
|
||||
TEST_EQUAL(surrogate.GetType(), GeoObjectId::Type::OsmSurrogate, ());
|
||||
TEST_EQUAL(DebugPrint(surrogate), "Osm Surrogate 281474976710655", ());
|
||||
|
||||
// 0 is not prohibited by the encoding even though OSM ids start from 1.
|
||||
GeoObjectId const booking(GeoObjectId::Type::BookingComNode, 0);
|
||||
TEST_EQUAL(booking.GetSerialId(), 0, ());
|
||||
TEST_EQUAL(booking.GetType(), GeoObjectId::Type::BookingComNode, ());
|
||||
TEST_EQUAL(DebugPrint(booking), "Booking.com 0", ());
|
||||
|
||||
GeoObjectId const fias(GeoObjectId::Type::Fias, 0xf1a5);
|
||||
TEST_EQUAL(fias.GetSerialId(), 0xf1a5, ());
|
||||
TEST_EQUAL(fias.GetType(), GeoObjectId::Type::Fias, ());
|
||||
TEST_EQUAL(DebugPrint(fias), "FIAS 61861", ());
|
||||
}
|
||||
|
||||
UNIT_TEST(GeoObjectId_Print)
|
||||
{
|
||||
// https://www.openstreetmap.org/#map=19/54.54438/25.69932
|
||||
|
||||
// Belarus -> Lithuania B L
|
||||
LOG(LINFO, (MakeOsmWay(533044131).GetEncodedId())); // 1 3 0
|
||||
LOG(LINFO, (MakeOsmWay(489294139).GetEncodedId())); // 1 0 1
|
||||
|
||||
// Lithuania -> Belarus
|
||||
LOG(LINFO, (MakeOsmWay(614091318).GetEncodedId())); // 1 5 1 1 5 0
|
||||
LOG(LINFO, (MakeOsmWay(148247387).GetEncodedId()));
|
||||
|
||||
// https://www.openstreetmap.org/#map=19/55.30215/10.86668
|
||||
|
||||
// Denmark_Zealand -> Denmark_Southern Z S
|
||||
LOG(LINFO, (MakeOsmWay(2032610).GetEncodedId())); // 1 6 0 1 6 1
|
||||
|
||||
// Denmark_Southern -> Denmark_Zealand
|
||||
LOG(LINFO, (MakeOsmWay(4360600).GetEncodedId())); // 1 18 1 1 18 0
|
||||
}
|
||||
293
libs/base/base_tests/levenshtein_dfa_test.cpp
Normal file
293
libs/base/base_tests/levenshtein_dfa_test.cpp
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/dfa_helpers.hpp"
|
||||
#include "base/levenshtein_dfa.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace levenshtein_dfa_test
|
||||
{
|
||||
using namespace std;
|
||||
using namespace strings;
|
||||
|
||||
enum class Status
|
||||
{
|
||||
Accepts,
|
||||
Rejects,
|
||||
Intermediate
|
||||
};
|
||||
|
||||
struct Result
|
||||
{
|
||||
Result() = default;
|
||||
Result(Status status, size_t errorsMade = 0, size_t prefixErrorsMade = 0)
|
||||
: m_status(status)
|
||||
, m_errorsMade(errorsMade)
|
||||
, m_prefixErrorsMade(prefixErrorsMade)
|
||||
{}
|
||||
|
||||
bool operator==(Result const & rhs) const
|
||||
{
|
||||
return m_status == rhs.m_status && (m_errorsMade == rhs.m_errorsMade || m_status == Status::Rejects) &&
|
||||
(m_prefixErrorsMade == rhs.m_prefixErrorsMade || m_status == Status::Rejects);
|
||||
}
|
||||
|
||||
Status m_status = Status::Accepts;
|
||||
size_t m_errorsMade = 0;
|
||||
size_t m_prefixErrorsMade = 0;
|
||||
};
|
||||
|
||||
string DebugPrint(Status status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case Status::Accepts: return "Accepts";
|
||||
case Status::Rejects: return "Rejects";
|
||||
case Status::Intermediate: return "Intermediate";
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
string DebugPrint(Result const & result)
|
||||
{
|
||||
ostringstream os;
|
||||
os << "Result [ ";
|
||||
os << "status: " << DebugPrint(result.m_status) << ", ";
|
||||
os << "errorsMade: " << result.m_errorsMade << ", ";
|
||||
os << "prefixErrorsMade: " << result.m_prefixErrorsMade << " ]";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
Result GetResult(LevenshteinDFA const & dfa, std::string const & s)
|
||||
{
|
||||
auto it = dfa.Begin();
|
||||
DFAMove(it, s);
|
||||
if (it.Accepts())
|
||||
return Result(Status::Accepts, it.ErrorsMade(), it.PrefixErrorsMade());
|
||||
if (it.Rejects())
|
||||
return Result(Status::Rejects, it.ErrorsMade(), it.PrefixErrorsMade());
|
||||
return Result(Status::Intermediate, it.ErrorsMade(), it.PrefixErrorsMade());
|
||||
}
|
||||
|
||||
bool Accepts(LevenshteinDFA const & dfa, std::string const & s)
|
||||
{
|
||||
return GetResult(dfa, s).m_status == Status::Accepts;
|
||||
}
|
||||
|
||||
bool Rejects(LevenshteinDFA const & dfa, std::string const & s)
|
||||
{
|
||||
return GetResult(dfa, s).m_status == Status::Rejects;
|
||||
}
|
||||
|
||||
bool Intermediate(LevenshteinDFA const & dfa, std::string const & s)
|
||||
{
|
||||
return GetResult(dfa, s).m_status == Status::Intermediate;
|
||||
}
|
||||
|
||||
UNIT_TEST(LevenshteinDFA_Smoke)
|
||||
{
|
||||
{
|
||||
LevenshteinDFA dfa("", 0 /* maxErrors */);
|
||||
|
||||
auto it = dfa.Begin();
|
||||
TEST(it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
|
||||
it.Move('a');
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(it.Rejects(), ());
|
||||
|
||||
it.Move('b');
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(it.Rejects(), ());
|
||||
}
|
||||
|
||||
{
|
||||
LevenshteinDFA dfa("abc", 1 /* maxErrors */);
|
||||
|
||||
TEST(Accepts(dfa, "ab"), ());
|
||||
TEST(Accepts(dfa, "abd"), ());
|
||||
TEST(Accepts(dfa, "abcd"), ());
|
||||
TEST(Accepts(dfa, "bbc"), ());
|
||||
|
||||
TEST(Rejects(dfa, "cba"), ());
|
||||
TEST(Rejects(dfa, "abcde"), ());
|
||||
|
||||
TEST(Accepts(dfa, "ac"), ());
|
||||
TEST(Accepts(dfa, "acb"), ());
|
||||
TEST(Accepts(dfa, "acbc"), ());
|
||||
TEST(Rejects(dfa, "acbd"), ());
|
||||
|
||||
TEST(Intermediate(dfa, "a"), ());
|
||||
}
|
||||
|
||||
{
|
||||
LevenshteinDFA dfa("ленинградский", 2 /* maxErrors */);
|
||||
|
||||
TEST(Accepts(dfa, "ленинградский"), ());
|
||||
TEST(Accepts(dfa, "ленингадский"), ());
|
||||
TEST(Accepts(dfa, "ленигнрадский"), ());
|
||||
TEST(Rejects(dfa, "ленинский"), ());
|
||||
}
|
||||
|
||||
{
|
||||
LevenshteinDFA dfa("atm", 1 /* maxErrors */);
|
||||
TEST(Rejects(dfa, "san"), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(LevenshteinDFA_Prefix)
|
||||
{
|
||||
{
|
||||
LevenshteinDFA dfa("москва", 1 /* prefixSize */, 1 /* maxErrors */);
|
||||
TEST(Accepts(dfa, "москва"), ());
|
||||
TEST(Accepts(dfa, "масква"), ());
|
||||
TEST(Accepts(dfa, "моска"), ());
|
||||
TEST(Rejects(dfa, "иосква"), ());
|
||||
}
|
||||
{
|
||||
LevenshteinDFA dfa("москва", 0 /* prefixSize */, 1 /* maxErrors */);
|
||||
TEST(Accepts(dfa, "москва"), ());
|
||||
TEST(Accepts(dfa, "иосква"), ());
|
||||
TEST(Accepts(dfa, "моксва"), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(LevenshteinDFA_ErrorsMade)
|
||||
{
|
||||
{
|
||||
LevenshteinDFA dfa("москва", 1 /* prefixSize */, 2 /* maxErrors */);
|
||||
|
||||
TEST_EQUAL(GetResult(dfa, "москва"), Result(Status::Accepts, 0 /* errorsMade */, 0 /* prefixErrorsMade */), ());
|
||||
TEST_EQUAL(GetResult(dfa, "москв"), Result(Status::Accepts, 1 /* errorsMade */, 0 /* prefixErrorsMade */), ());
|
||||
TEST_EQUAL(GetResult(dfa, "моск"), Result(Status::Accepts, 2 /* errorsMade */, 0 /* prefixErrorsMade */), ());
|
||||
TEST_EQUAL(GetResult(dfa, "мос").m_status, Status::Intermediate, ());
|
||||
TEST_EQUAL(GetResult(dfa, "мос").m_prefixErrorsMade, 0, ());
|
||||
|
||||
TEST_EQUAL(GetResult(dfa, "моксав"), Result(Status::Accepts, 2 /* errorsMade */, 2 /* prefixErrorsMade */), ());
|
||||
TEST_EQUAL(GetResult(dfa, "максав").m_status, Status::Rejects, ());
|
||||
|
||||
TEST_EQUAL(GetResult(dfa, "мсовк").m_status, Status::Intermediate, ());
|
||||
TEST_EQUAL(GetResult(dfa, "мсовк").m_prefixErrorsMade, 2, ());
|
||||
TEST_EQUAL(GetResult(dfa, "мсовка"), Result(Status::Accepts, 2 /* errorsMade */, 2 /* prefixErrorsMade */), ());
|
||||
TEST_EQUAL(GetResult(dfa, "мсовкб").m_status, Status::Rejects, ());
|
||||
}
|
||||
|
||||
{
|
||||
LevenshteinDFA dfa("aa", 0 /* prefixSize */, 2 /* maxErrors */);
|
||||
TEST_EQUAL(GetResult(dfa, "abab"), Result(Status::Accepts, 2 /* errorsMade */, 2 /* prefixErrorsMade */), ());
|
||||
}
|
||||
|
||||
{
|
||||
LevenshteinDFA dfa("mississippi", 0 /* prefixSize */, 0 /* maxErrors */);
|
||||
TEST_EQUAL(GetResult(dfa, "misisipi").m_status, Status::Rejects, ());
|
||||
TEST_EQUAL(GetResult(dfa, "mississipp").m_status, Status::Intermediate, ());
|
||||
TEST_EQUAL(GetResult(dfa, "mississipp").m_prefixErrorsMade, 0, ());
|
||||
TEST_EQUAL(GetResult(dfa, "mississippi"), Result(Status::Accepts, 0 /* errorsMade */, 0 /* prefixErrorsMade */),
|
||||
());
|
||||
}
|
||||
|
||||
{
|
||||
array<UniString, 11> const allowedMisprints = {MakeUniString("yj")};
|
||||
size_t const prefixSize = 1;
|
||||
size_t const maxErrors = 1;
|
||||
string const str = "yekaterinburg";
|
||||
vector<pair<string, Result>> const queries = {
|
||||
{"yekaterinburg", Result(Status::Accepts, 0 /* errorsMade */, 0 /* prefixErrorsMade */)},
|
||||
{"ekaterinburg", Result(Status::Accepts, 1 /* errorsMade */, 1 /* prefixErrorsMade */)},
|
||||
{"jekaterinburg", Result(Status::Accepts, 1 /* errorsMade */, 1 /* prefixErrorsMade */)},
|
||||
{"iekaterinburg", Result(Status::Rejects)}};
|
||||
|
||||
for (auto const & q : queries)
|
||||
{
|
||||
LevenshteinDFA dfa(MakeUniString(q.first), prefixSize, allowedMisprints, maxErrors);
|
||||
TEST_EQUAL(GetResult(dfa, str), q.second, ("Query:", q.first, "string:", str));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
LevenshteinDFA dfa("кафе", 1 /* prefixSize */, 1 /* maxErrors */);
|
||||
TEST_EQUAL(GetResult(dfa, "кафе"), Result(Status::Accepts, 0 /* errorsMade */, 0 /* prefixErrorsMade */), ());
|
||||
TEST_EQUAL(GetResult(dfa, "кафер"), Result(Status::Accepts, 1 /* errorsMade */, 1 /* prefixErrorsMade */), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(LevenshteinDFA_PrefixDFAModifier)
|
||||
{
|
||||
{
|
||||
PrefixDFAModifier<LevenshteinDFA> dfa(LevenshteinDFA("abcde", 2 /* maxErrors */));
|
||||
|
||||
auto it = dfa.Begin();
|
||||
DFAMove(it, "ab");
|
||||
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
TEST_EQUAL(it.PrefixErrorsMade(), 0, ());
|
||||
// |maxErrors| for all non-accepting states.
|
||||
TEST_EQUAL(it.ErrorsMade(), 2, ());
|
||||
|
||||
DFAMove(it, "c");
|
||||
TEST(it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
TEST_EQUAL(it.ErrorsMade(), 2, ());
|
||||
TEST_EQUAL(it.PrefixErrorsMade(), 0, ());
|
||||
|
||||
DFAMove(it, "d");
|
||||
TEST(it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
TEST_EQUAL(it.ErrorsMade(), 1, ());
|
||||
TEST_EQUAL(it.PrefixErrorsMade(), 0, ());
|
||||
|
||||
DFAMove(it, "e");
|
||||
TEST(it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
TEST_EQUAL(it.ErrorsMade(), 0, ());
|
||||
TEST_EQUAL(it.PrefixErrorsMade(), 0, ());
|
||||
|
||||
DFAMove(it, "fghijklmn");
|
||||
TEST(it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
TEST_EQUAL(it.ErrorsMade(), 0, ());
|
||||
TEST_EQUAL(it.PrefixErrorsMade(), 0, ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(LevenshteinDFA_PrefixDFASmoke)
|
||||
{
|
||||
vector<char> const kAlphabet = {'a', 'b', 'c'};
|
||||
vector<string> sources;
|
||||
vector<string> queries;
|
||||
auto generate = [](vector<char> const & alphabet, size_t size, vector<string> & result)
|
||||
{
|
||||
result.clear();
|
||||
result.resize(math::PowUint(alphabet.size(), size));
|
||||
for (size_t letterNumber = 0; letterNumber < size; ++letterNumber)
|
||||
{
|
||||
for (size_t i = 0; i < result.size(); ++i)
|
||||
{
|
||||
auto const letterIndex = (i / math::PowUint(alphabet.size(), size - letterNumber - 1)) % alphabet.size();
|
||||
result[i].push_back(alphabet[letterIndex]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// @todo What do we check here?
|
||||
{
|
||||
generate(kAlphabet, 4, sources);
|
||||
generate(kAlphabet, 2, queries);
|
||||
|
||||
for (auto const & source : sources)
|
||||
{
|
||||
for (auto const & query : queries)
|
||||
{
|
||||
PrefixDFAModifier<LevenshteinDFA> dfa(LevenshteinDFA(source, 2 /* maxErrors */));
|
||||
auto it = dfa.Begin();
|
||||
for (auto const c : query)
|
||||
DFAMove(it, std::string({c}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace levenshtein_dfa_test
|
||||
52
libs/base/base_tests/linked_map_tests.cpp
Normal file
52
libs/base/base_tests/linked_map_tests.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/linked_map.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
UNIT_TEST(LinkedMap_Smoke)
|
||||
{
|
||||
base::LinkedMap<int, std::string, std::map> container;
|
||||
|
||||
TEST(container.IsEmpty(), ());
|
||||
|
||||
TEST(container.Emplace(1, "hello"), ());
|
||||
TEST(container.Emplace(2, "world"), ());
|
||||
TEST(container.Emplace(3, "!"), ());
|
||||
|
||||
TEST(!container.IsEmpty(), ());
|
||||
TEST_EQUAL(container.Size(), 3, ());
|
||||
|
||||
TEST(!container.Emplace(2, "again"), ());
|
||||
|
||||
TEST_EQUAL(container.Size(), 3, ());
|
||||
|
||||
TEST(container.Contains(2), ());
|
||||
auto const getResult = container.Get(2);
|
||||
TEST_EQUAL(getResult, "world", ());
|
||||
|
||||
TEST_EQUAL(container.Front(), "hello", ());
|
||||
container.PopFront();
|
||||
TEST_EQUAL(container.Front(), "world", ());
|
||||
|
||||
TEST_EQUAL(container.Size(), 2, ());
|
||||
|
||||
TEST(!container.Contains(10), ());
|
||||
TEST(!container.Erase(10), ());
|
||||
TEST_EQUAL(container.Size(), 2, ());
|
||||
|
||||
TEST(container.Contains(3), ());
|
||||
TEST(container.Erase(3), ());
|
||||
TEST_EQUAL(container.Size(), 1, ());
|
||||
TEST_EQUAL(container.Front(), "world", ());
|
||||
|
||||
decltype(container) otherContainer;
|
||||
otherContainer.Swap(container);
|
||||
|
||||
TEST(container.IsEmpty(), ());
|
||||
TEST_EQUAL(container.Size(), 0, ());
|
||||
TEST(!otherContainer.IsEmpty(), ());
|
||||
TEST_EQUAL(otherContainer.Size(), 1, ());
|
||||
TEST_EQUAL(otherContainer.Front(), "world", ());
|
||||
}
|
||||
59
libs/base/base_tests/logging_test.cpp
Normal file
59
libs/base/base_tests/logging_test.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
void TestLogMessage(base::LogLevel, base::SrcPoint const &, std::string const &) {}
|
||||
|
||||
bool g_SomeFunctionCalled;
|
||||
int SomeFunction()
|
||||
{
|
||||
g_SomeFunctionCalled = true;
|
||||
return 3;
|
||||
}
|
||||
|
||||
bool BoolFunction(bool result, bool & called)
|
||||
{
|
||||
called = true;
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(Logging_Level)
|
||||
{
|
||||
base::LogLevel const logLevelSaved = base::g_LogLevel;
|
||||
base::g_LogLevel = LWARNING;
|
||||
|
||||
g_SomeFunctionCalled = false;
|
||||
base::LogMessageFn logMessageSaved = base::SetLogMessageFn(&TestLogMessage);
|
||||
|
||||
LOG(LINFO, ("This should not pass", SomeFunction()));
|
||||
TEST(!g_SomeFunctionCalled, ());
|
||||
|
||||
LOG(LWARNING, ("This should pass", SomeFunction()));
|
||||
TEST(g_SomeFunctionCalled, ());
|
||||
|
||||
base::SetLogMessageFn(logMessageSaved);
|
||||
base::g_LogLevel = logLevelSaved;
|
||||
}
|
||||
|
||||
UNIT_TEST(NullMessage)
|
||||
{
|
||||
char const * ptr = 0;
|
||||
LOG(LINFO, ("Null message test", ptr));
|
||||
}
|
||||
|
||||
UNIT_TEST(Logging_ConditionalLog)
|
||||
{
|
||||
bool isCalled = false;
|
||||
CLOG(LINFO, BoolFunction(true, isCalled), ("This should not be displayed"));
|
||||
TEST(isCalled, ());
|
||||
|
||||
isCalled = false;
|
||||
CLOG(LWARNING, BoolFunction(false, isCalled), ("This should be displayed"));
|
||||
TEST(isCalled, ());
|
||||
}
|
||||
174
libs/base/base_tests/lru_cache_tests.cpp
Normal file
174
libs/base/base_tests/lru_cache_tests.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/lru_cache.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class LruCacheTest
|
||||
{
|
||||
public:
|
||||
using Loader = std::function<void(Key const & key, Value & value)>;
|
||||
|
||||
LruCacheTest(size_t maxCacheSize, Loader const & loader) : m_cache(maxCacheSize), m_loader(loader) {}
|
||||
|
||||
Value const & GetValue(Key const & key)
|
||||
{
|
||||
bool found;
|
||||
auto & value = m_cache.Find(key, found);
|
||||
if (!found)
|
||||
m_loader(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool IsValid() const { return m_cache.IsValidForTesting(); }
|
||||
|
||||
private:
|
||||
LruCache<Key, Value> m_cache;
|
||||
Loader m_loader;
|
||||
};
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class LruCacheKeyAgeTest
|
||||
{
|
||||
public:
|
||||
void InsertKey(Key const & key) { m_keyAge.InsertKey(key); }
|
||||
void UpdateAge(Key const & key) { m_keyAge.UpdateAge(key); }
|
||||
Key const & GetLruKey() const { return m_keyAge.GetLruKey(); }
|
||||
void RemoveLru() { m_keyAge.RemoveLru(); }
|
||||
bool IsValid() const { return m_keyAge.IsValidForTesting(); }
|
||||
|
||||
size_t GetAge() const { return m_keyAge.m_age; }
|
||||
std::map<size_t, Key> const & GetAgeToKey() const { return m_keyAge.m_ageToKey; }
|
||||
std::unordered_map<Key, size_t> const & GetKeyToAge() const { return m_keyAge.m_keyToAge; }
|
||||
|
||||
private:
|
||||
typename LruCache<Key, Value>::KeyAge m_keyAge;
|
||||
};
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void TestAge(LruCacheKeyAgeTest<Key, Value> const & keyAge, size_t expectedAge,
|
||||
std::map<size_t, Key> const & expectedAgeToKey, std::unordered_map<Key, size_t> const & expectedKeyToAge)
|
||||
{
|
||||
TEST(keyAge.IsValid(), ());
|
||||
TEST_EQUAL(keyAge.GetAge(), expectedAge, ());
|
||||
TEST_EQUAL(keyAge.GetAgeToKey(), expectedAgeToKey, ());
|
||||
TEST_EQUAL(keyAge.GetKeyToAge(), expectedKeyToAge, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(LruCacheAgeTest)
|
||||
{
|
||||
using Key = int;
|
||||
using Value = double;
|
||||
LruCacheKeyAgeTest<Key, Value> age;
|
||||
|
||||
TEST_EQUAL(age.GetAge(), 0, ());
|
||||
TEST(age.GetAgeToKey().empty(), ());
|
||||
TEST(age.GetKeyToAge().empty(), ());
|
||||
|
||||
age.InsertKey(10);
|
||||
{
|
||||
std::map<size_t, Key> const expectedAgeToKey({{1 /* age */, 10 /* key */}});
|
||||
std::unordered_map<Key, size_t> const expectedKeyToAge({{10 /* key */, 1 /* age */}});
|
||||
TestAge(age, 1 /* cache age */, expectedAgeToKey, expectedKeyToAge);
|
||||
}
|
||||
|
||||
age.InsertKey(9);
|
||||
{
|
||||
std::map<size_t, Key> const expectedAgeToKey({{1, 10}, {2, 9}});
|
||||
std::unordered_map<Key, size_t> const expectedKeyToAge({{10, 1}, {9, 2}});
|
||||
TestAge(age, 2 /* cache age */, expectedAgeToKey, expectedKeyToAge);
|
||||
}
|
||||
|
||||
age.RemoveLru();
|
||||
{
|
||||
std::map<size_t, Key> const expectedAgeToKey({{2, 9}});
|
||||
std::unordered_map<Key, size_t> const expectedKeyToAge({{9, 2}});
|
||||
TestAge(age, 2 /* cache age */, expectedAgeToKey, expectedKeyToAge);
|
||||
}
|
||||
|
||||
age.InsertKey(11);
|
||||
{
|
||||
std::map<size_t, Key> const expectedAgeToKey({{2, 9}, {3, 11}});
|
||||
std::unordered_map<Key, size_t> const expectedKeyToAge({{9, 2}, {11, 3}});
|
||||
TestAge(age, 3 /* cache age */, expectedAgeToKey, expectedKeyToAge);
|
||||
}
|
||||
|
||||
age.UpdateAge(9);
|
||||
{
|
||||
std::map<size_t, Key> const expectedAgeToKey({{4, 9}, {3, 11}});
|
||||
std::unordered_map<Key, size_t> const expectedKeyToAge({{9, 4}, {11, 3}});
|
||||
TestAge(age, 4 /* cache age */, expectedAgeToKey, expectedKeyToAge);
|
||||
}
|
||||
|
||||
age.RemoveLru();
|
||||
{
|
||||
std::map<size_t, Key> const expectedAgeToKey({{4, 9}});
|
||||
std::unordered_map<Key, size_t> const expectedKeyToAge({{9, 4}});
|
||||
TestAge(age, 4 /* cache age */, expectedAgeToKey, expectedKeyToAge);
|
||||
}
|
||||
|
||||
age.InsertKey(12);
|
||||
{
|
||||
std::map<size_t, Key> const expectedAgeToKey({{4, 9}, {5, 12}});
|
||||
std::unordered_map<Key, size_t> const expectedKeyToAge({{9, 4}, {12, 5}});
|
||||
TestAge(age, 5 /* cache age */, expectedAgeToKey, expectedKeyToAge);
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(LruCacheSmokeTest)
|
||||
{
|
||||
using Key = int;
|
||||
using Value = int;
|
||||
|
||||
{
|
||||
LruCacheTest<Key, Value> cache(1 /* maxCacheSize */, [](Key k, Value & v) { v = 1; } /* loader */);
|
||||
TEST_EQUAL(cache.GetValue(1), 1, ());
|
||||
TEST_EQUAL(cache.GetValue(2), 1, ());
|
||||
TEST_EQUAL(cache.GetValue(3), 1, ());
|
||||
TEST_EQUAL(cache.GetValue(4), 1, ());
|
||||
TEST_EQUAL(cache.GetValue(1), 1, ());
|
||||
TEST(cache.IsValid(), ());
|
||||
}
|
||||
|
||||
{
|
||||
LruCacheTest<Key, Value> cache(3 /* maxCacheSize */, [](Key k, Value & v) { v = k; } /* loader */);
|
||||
TEST_EQUAL(cache.GetValue(1), 1, ());
|
||||
TEST_EQUAL(cache.GetValue(2), 2, ());
|
||||
TEST_EQUAL(cache.GetValue(2), 2, ());
|
||||
TEST_EQUAL(cache.GetValue(5), 5, ());
|
||||
TEST_EQUAL(cache.GetValue(1), 1, ());
|
||||
TEST(cache.IsValid(), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(LruCacheLoaderCallsTest)
|
||||
{
|
||||
using Key = int;
|
||||
using Value = int;
|
||||
bool shouldLoadBeCalled = true;
|
||||
auto loader = [&shouldLoadBeCalled](Key k, Value & v)
|
||||
{
|
||||
TEST(shouldLoadBeCalled, ());
|
||||
v = k;
|
||||
};
|
||||
|
||||
LruCacheTest<Key, Value> cache(3 /* maxCacheSize */, loader);
|
||||
TEST(cache.IsValid(), ());
|
||||
cache.GetValue(1);
|
||||
cache.GetValue(2);
|
||||
cache.GetValue(3);
|
||||
TEST(cache.IsValid(), ());
|
||||
shouldLoadBeCalled = false;
|
||||
cache.GetValue(3);
|
||||
cache.GetValue(2);
|
||||
cache.GetValue(1);
|
||||
TEST(cache.IsValid(), ());
|
||||
shouldLoadBeCalled = true;
|
||||
cache.GetValue(4);
|
||||
TEST(cache.IsValid(), ());
|
||||
shouldLoadBeCalled = false;
|
||||
cache.GetValue(1);
|
||||
TEST(cache.IsValid(), ());
|
||||
}
|
||||
228
libs/base/base_tests/math_test.cpp
Normal file
228
libs/base/base_tests/math_test.cpp
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/math.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <boost/math/special_functions/next.hpp>
|
||||
|
||||
namespace math_test
|
||||
{
|
||||
using namespace math;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Returns the next representable floating point value without using conversion to integer.
|
||||
template <typename Float>
|
||||
Float NextFloat(Float const x, int dir = 1)
|
||||
{
|
||||
return boost::math::float_advance(x, dir);
|
||||
}
|
||||
|
||||
template <typename Float>
|
||||
void TestMaxULPs()
|
||||
{
|
||||
for (unsigned int logMaxULPs = 0; logMaxULPs <= 8; ++logMaxULPs)
|
||||
{
|
||||
unsigned int const maxULPs = (1 << logMaxULPs) - 1;
|
||||
for (int base = -1; base <= 1; ++base)
|
||||
{
|
||||
for (int dir = -1; dir <= 1; dir += 2)
|
||||
{
|
||||
Float const x = base;
|
||||
Float y = x;
|
||||
for (unsigned int i = 0; i <= maxULPs; ++i)
|
||||
{
|
||||
TEST(AlmostEqualULPs(x, y, maxULPs), (x, y, maxULPs, x - y, dir));
|
||||
Float const nextY = NextFloat(y, dir);
|
||||
TEST_NOT_EQUAL(y, nextY, (i, base, dir));
|
||||
y = nextY;
|
||||
}
|
||||
TEST(!AlmostEqualULPs(x, y, maxULPs), (x, y, maxULPs, x - y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(round_lround_smoke)
|
||||
{
|
||||
TEST_EQUAL(std::round(0.4), 0.0, ());
|
||||
TEST_EQUAL(std::round(0.6), 1.0, ());
|
||||
TEST_EQUAL(std::round(-0.4), 0.0, ());
|
||||
TEST_EQUAL(std::round(-0.6), -1.0, ());
|
||||
|
||||
TEST_EQUAL(std::lround(0.4), 0l, ());
|
||||
TEST_EQUAL(std::lround(0.6), 1l, ());
|
||||
TEST_EQUAL(std::lround(-0.4), 0l, ());
|
||||
TEST_EQUAL(std::lround(-0.6), -1l, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(PowUInt)
|
||||
{
|
||||
TEST_EQUAL(PowUint(3, 10), 59049, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(AlmostEqualULPs_double)
|
||||
{
|
||||
TEST_ALMOST_EQUAL_ULPS(3.0, 3.0, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(+0.0, -0.0, ());
|
||||
|
||||
double constexpr eps = std::numeric_limits<double>::epsilon();
|
||||
double constexpr dmax = std::numeric_limits<double>::max();
|
||||
|
||||
TEST_ALMOST_EQUAL_ULPS(1.0 + eps, 1.0, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(1.0 - eps, 1.0, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(1.0 - eps, 1.0 + eps, ());
|
||||
|
||||
TEST_ALMOST_EQUAL_ULPS(dmax, dmax, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(-dmax, -dmax, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(dmax / 2.0, dmax / 2.0, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(1.0 / dmax, 1.0 / dmax, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(-1.0 / dmax, -1.0 / dmax, ());
|
||||
|
||||
TEST(!AlmostEqualULPs(1.0, -1.0), ());
|
||||
TEST(!AlmostEqualULPs(2.0, -2.0), ());
|
||||
TEST(!AlmostEqualULPs(dmax, -dmax), ());
|
||||
|
||||
// That's why AlmostEqualULPs is a strange function, IMHO.
|
||||
TEST(!AlmostEqualULPs(0.0, eps), ());
|
||||
TEST(!AlmostEqualULPs(-eps, 0.0), ());
|
||||
TEST(!AlmostEqualULPs(eps, 2.0 * eps), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(AlmostEqualULPs_float)
|
||||
{
|
||||
TEST_ALMOST_EQUAL_ULPS(3.0f, 3.0f, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(+0.0f, -0.0f, ());
|
||||
|
||||
float const eps = std::numeric_limits<float>::epsilon();
|
||||
float const dmax = std::numeric_limits<float>::max();
|
||||
|
||||
TEST_ALMOST_EQUAL_ULPS(1.0f + eps, 1.0f, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(1.0f - eps, 1.0f, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(1.0f - eps, 1.0f + eps, ());
|
||||
|
||||
TEST_ALMOST_EQUAL_ULPS(dmax, dmax, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(-dmax, -dmax, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(dmax / 2.0f, dmax / 2.0f, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(1.0f / dmax, 1.0f / dmax, ());
|
||||
TEST_ALMOST_EQUAL_ULPS(-1.0f / dmax, -1.0f / dmax, ());
|
||||
|
||||
TEST(!AlmostEqualULPs(1.0f, -1.0f), ());
|
||||
TEST(!AlmostEqualULPs(2.0f, -2.0f), ());
|
||||
TEST(!AlmostEqualULPs(dmax, -dmax), ());
|
||||
|
||||
// That's why AlmostEqualULPs is a strange function, IMHO.
|
||||
TEST(!AlmostEqualULPs(0.0f, eps), ());
|
||||
TEST(!AlmostEqualULPs(-eps, 0.0f), ());
|
||||
TEST(!AlmostEqualULPs(eps, 2.0f * eps), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(AlmostEqual_Smoke)
|
||||
{
|
||||
double constexpr small = 1e-18;
|
||||
double constexpr eps = 1e-10;
|
||||
|
||||
TEST(AlmostEqualAbs(0.0, 0.0 + small, eps), ());
|
||||
TEST(!AlmostEqualRel(0.0, 0.0 + small, eps), ());
|
||||
TEST(!AlmostEqualULPs(0.0, 0.0 + small), ());
|
||||
|
||||
TEST(AlmostEqualAbs(1.0, 1.0 + small, eps), ());
|
||||
TEST(AlmostEqualRel(1.0, 1.0 + small, eps), ());
|
||||
TEST(AlmostEqualULPs(1.0, 1.0 + small), ());
|
||||
|
||||
TEST(AlmostEqualRel(123456789.0, 123456780.0, 1e-7), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(AlmostEqualULPs_MaxULPs_double)
|
||||
{
|
||||
TestMaxULPs<double>();
|
||||
}
|
||||
|
||||
UNIT_TEST(AlmostEqualULPs_MaxULPs_float)
|
||||
{
|
||||
TestMaxULPs<float>();
|
||||
}
|
||||
|
||||
UNIT_TEST(TEST_FLOAT_DOUBLE_EQUAL_macros)
|
||||
{
|
||||
float const fx = 3;
|
||||
float const fy = NextFloat(NextFloat(NextFloat(fx)));
|
||||
TEST_ALMOST_EQUAL_ULPS(fx, fy, ());
|
||||
TEST_NOT_ALMOST_EQUAL_ULPS(fx, 2.0f, ());
|
||||
|
||||
double const dx = 3;
|
||||
double const dy = NextFloat(NextFloat(NextFloat(dx)));
|
||||
TEST_ALMOST_EQUAL_ULPS(dx, dy, ());
|
||||
TEST_NOT_ALMOST_EQUAL_ULPS(dx, 2.0, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(GCD)
|
||||
{
|
||||
TEST_EQUAL(GCD(6, 3), 3, ());
|
||||
TEST_EQUAL(GCD(14, 7), 7, ());
|
||||
TEST_EQUAL(GCD(100, 100), 100, ());
|
||||
TEST_EQUAL(GCD(7, 3), 1, ());
|
||||
TEST_EQUAL(GCD(8, 3), 1, ());
|
||||
TEST_EQUAL(GCD(9, 3), 3, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(LCM)
|
||||
{
|
||||
TEST_EQUAL(LCM(6, 3), 6, ());
|
||||
TEST_EQUAL(LCM(14, 7), 14, ());
|
||||
TEST_EQUAL(LCM(100, 100), 100, ());
|
||||
TEST_EQUAL(LCM(7, 3), 21, ());
|
||||
TEST_EQUAL(LCM(8, 3), 24, ());
|
||||
TEST_EQUAL(LCM(9, 3), 9, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(Sign)
|
||||
{
|
||||
TEST_EQUAL(1, Sign(1), ());
|
||||
TEST_EQUAL(1, Sign(10.4), ());
|
||||
|
||||
TEST_EQUAL(0, Sign(0), ());
|
||||
TEST_EQUAL(0, Sign(0.0), ());
|
||||
|
||||
TEST_EQUAL(-1, Sign(-11), ());
|
||||
TEST_EQUAL(-1, Sign(-10.4), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(is_finite)
|
||||
{
|
||||
static_assert(std::numeric_limits<double>::has_infinity);
|
||||
static_assert(std::numeric_limits<double>::has_quiet_NaN);
|
||||
|
||||
using math::is_finite, math::Nan, math::Infinity;
|
||||
|
||||
TEST(!is_finite(Nan()), ());
|
||||
TEST(!is_finite(Infinity()), ());
|
||||
TEST(!is_finite(DBL_MAX * 2.0), ());
|
||||
|
||||
TEST(is_finite(0.0), ());
|
||||
TEST(is_finite(1.0), ());
|
||||
TEST(is_finite(-2.0), ());
|
||||
TEST(is_finite(DBL_MIN), ());
|
||||
TEST(is_finite(DBL_MAX), ());
|
||||
TEST(is_finite(DBL_MIN / 2.0), ("As in cppreference example"));
|
||||
}
|
||||
|
||||
UNIT_TEST(iround)
|
||||
{
|
||||
TEST_EQUAL(iround(0.0), 0, ());
|
||||
TEST_EQUAL(iround(1.0), 1, ());
|
||||
TEST_EQUAL(iround(1.5), 2, ());
|
||||
TEST_EQUAL(iround(-1.5), -2, ());
|
||||
TEST_EQUAL(iround(2.5), 3, ());
|
||||
TEST_EQUAL(iround(-2.5), -3, ());
|
||||
|
||||
TEST_EQUAL(iround(2.5f), 3, ());
|
||||
TEST_EQUAL(iround(-2.5f), -3, ());
|
||||
|
||||
TEST_EQUAL(iround(double(std::numeric_limits<int>::max()) - 0.5), std::numeric_limits<int>::max(), ());
|
||||
TEST_EQUAL(iround(double(std::numeric_limits<int>::min()) + 0.5), std::numeric_limits<int>::min(), ());
|
||||
}
|
||||
|
||||
} // namespace math_test
|
||||
34
libs/base/base_tests/matrix_test.cpp
Normal file
34
libs/base/base_tests/matrix_test.cpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/matrix.hpp"
|
||||
|
||||
UNIT_TEST(Matrix_Inverse_Simple)
|
||||
{
|
||||
math::Matrix<double, 3, 3> m;
|
||||
|
||||
m = math::Identity<double, 3>();
|
||||
|
||||
m(2, 0) = 2;
|
||||
|
||||
math::Matrix<double, 3, 3> m1 = math::Inverse(m);
|
||||
|
||||
TEST_EQUAL((m1 * m).Equal(math::Identity<double, 3>()), true, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(Matrix_Inverse_AllElementsNonZero)
|
||||
{
|
||||
math::Matrix<double, 3, 3> m;
|
||||
m(0, 0) = 5;
|
||||
m(0, 1) = 3;
|
||||
m(0, 2) = 6;
|
||||
m(1, 0) = 1;
|
||||
m(1, 1) = 7;
|
||||
m(1, 2) = 9;
|
||||
m(2, 0) = 2;
|
||||
m(2, 1) = 13;
|
||||
m(2, 2) = 4;
|
||||
|
||||
math::Matrix<double, 3, 3> m1 = math::Inverse(m);
|
||||
|
||||
TEST_EQUAL((m1 * m).Equal(math::Identity<double, 3>()), true, ());
|
||||
}
|
||||
190
libs/base/base_tests/mem_trie_test.cpp
Normal file
190
libs/base/base_tests/mem_trie_test.cpp
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/mem_trie.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace base;
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
using Key = string;
|
||||
using Value = int;
|
||||
using Trie = MemTrie<Key, VectorValues<Value>>;
|
||||
using Data = vector<pair<Key, Value>>;
|
||||
|
||||
Data GetTrieContents(Trie const & trie)
|
||||
{
|
||||
Data data;
|
||||
trie.ForEachInTrie([&data](string const & k, int v) { data.emplace_back(k, v); });
|
||||
sort(data.begin(), data.end());
|
||||
return data;
|
||||
}
|
||||
|
||||
class MemTrieTest
|
||||
{
|
||||
public:
|
||||
Data GetActualContents() const
|
||||
{
|
||||
Data data;
|
||||
m_trie.ForEachInTrie([&data](Key const & k, Value const & v) { data.emplace_back(k, v); });
|
||||
sort(data.begin(), data.end());
|
||||
return data;
|
||||
}
|
||||
|
||||
Data GetExpectedContents() const { return {m_data.cbegin(), m_data.cend()}; }
|
||||
|
||||
vector<Value> GetValuesByKey(Key const & key) const
|
||||
{
|
||||
vector<Value> values;
|
||||
m_trie.ForEachInNode(key, [&](Value const & value) { values.push_back(value); });
|
||||
sort(values.begin(), values.end());
|
||||
return values;
|
||||
}
|
||||
|
||||
Data GetContentsByPrefix(Key const & prefix) const
|
||||
{
|
||||
Data data;
|
||||
m_trie.ForEachInSubtree(prefix, [&data](Key const & k, Value const & v) { data.emplace_back(k, v); });
|
||||
sort(data.begin(), data.end());
|
||||
return data;
|
||||
}
|
||||
|
||||
bool HasKey(Key const & key) const { return m_trie.HasKey(key); }
|
||||
bool HasPrefix(Key const & prefix) const { return m_trie.HasPrefix(prefix); }
|
||||
|
||||
size_t GetNumNodes() const { return m_trie.GetNumNodes(); }
|
||||
|
||||
void Add(Key const & key, Value const & value)
|
||||
{
|
||||
m_trie.Add(key, value);
|
||||
m_data.insert(make_pair(key, value));
|
||||
}
|
||||
|
||||
void Erase(Key const & key, Value const & value)
|
||||
{
|
||||
m_trie.Erase(key, value);
|
||||
m_data.erase(make_pair(key, value));
|
||||
}
|
||||
|
||||
protected:
|
||||
Trie m_trie;
|
||||
multiset<pair<Key, Value>> m_data;
|
||||
};
|
||||
|
||||
UNIT_CLASS_TEST(MemTrieTest, Basic)
|
||||
{
|
||||
TEST_EQUAL(GetNumNodes(), 1, ());
|
||||
|
||||
TEST(!HasKey(""), ());
|
||||
TEST(!HasKey("a"), ());
|
||||
TEST(!HasPrefix(""), ());
|
||||
TEST(!HasPrefix("a"), ());
|
||||
|
||||
Data const data = {{"roger", 3}, {"amy", 1}, {"emma", 1}, {"ann", 1},
|
||||
{"rob", 1}, {"roger", 2}, {"", 0}, {"roger", 1}};
|
||||
for (auto const & kv : data)
|
||||
Add(kv.first, kv.second);
|
||||
|
||||
TEST_EQUAL(GetNumNodes(), 8, ());
|
||||
TEST(HasKey(""), ());
|
||||
TEST(!HasKey("a"), ());
|
||||
TEST(HasPrefix(""), ());
|
||||
TEST(HasPrefix("a"), ());
|
||||
|
||||
TEST_EQUAL(GetExpectedContents(), GetActualContents(), ());
|
||||
TEST_EQUAL(GetNumNodes(), 8, ());
|
||||
|
||||
Trie newTrie(std::move(m_trie));
|
||||
|
||||
TEST_EQUAL(m_trie.GetNumNodes(), 1, ());
|
||||
TEST(GetTrieContents(m_trie).empty(), ());
|
||||
|
||||
TEST_EQUAL(newTrie.GetNumNodes(), 8, ());
|
||||
TEST_EQUAL(GetTrieContents(newTrie), GetExpectedContents(), ());
|
||||
}
|
||||
|
||||
UNIT_CLASS_TEST(MemTrieTest, KeysRemoval)
|
||||
{
|
||||
TEST_EQUAL(GetNumNodes(), 1, ());
|
||||
|
||||
TEST(!HasKey("r"), ());
|
||||
TEST(!HasPrefix("r"), ());
|
||||
TEST(!HasKey("ro"), ());
|
||||
TEST(!HasPrefix("ro"), ());
|
||||
|
||||
Data const data = {{"bobby", 1}, {"robby", 2}, {"rob", 3}, {"r", 4}, {"robert", 5}, {"bob", 6}};
|
||||
|
||||
for (auto const & kv : data)
|
||||
Add(kv.first, kv.second);
|
||||
|
||||
TEST(HasKey("r"), ());
|
||||
TEST(HasPrefix("r"), ());
|
||||
TEST(!HasKey("ro"), ());
|
||||
TEST(HasPrefix("ro"), ());
|
||||
|
||||
TEST_EQUAL(GetNumNodes(), 7, ());
|
||||
TEST_EQUAL(GetExpectedContents(), GetActualContents(), ());
|
||||
|
||||
Erase("r", 3);
|
||||
TEST_EQUAL(GetNumNodes(), 7, ());
|
||||
TEST_EQUAL(GetExpectedContents(), GetActualContents(), ());
|
||||
|
||||
Erase("r", 4);
|
||||
TEST_EQUAL(GetNumNodes(), 6, ());
|
||||
TEST_EQUAL(GetExpectedContents(), GetActualContents(), ());
|
||||
|
||||
Erase("robert", 5);
|
||||
TEST_EQUAL(GetNumNodes(), 5, ());
|
||||
TEST_EQUAL(GetExpectedContents(), GetActualContents(), ());
|
||||
|
||||
Erase("rob", 3);
|
||||
TEST_EQUAL(GetNumNodes(), 4, ());
|
||||
TEST_EQUAL(GetExpectedContents(), GetActualContents(), ());
|
||||
|
||||
Erase("robby", 2);
|
||||
TEST_EQUAL(GetNumNodes(), 3, ());
|
||||
TEST_EQUAL(GetExpectedContents(), GetActualContents(), ());
|
||||
}
|
||||
|
||||
UNIT_CLASS_TEST(MemTrieTest, ForEachInNode)
|
||||
{
|
||||
Add("abracadabra", 0);
|
||||
Add("abra", 1);
|
||||
Add("abra", 2);
|
||||
Add("abrau", 3);
|
||||
|
||||
TEST_EQUAL(GetValuesByKey("a"), vector<Value>{}, ());
|
||||
TEST_EQUAL(GetValuesByKey("abrac"), vector<Value>{}, ());
|
||||
TEST_EQUAL(GetValuesByKey("abracadabr"), vector<Value>{}, ());
|
||||
TEST_EQUAL(GetValuesByKey("void"), vector<Value>{}, ());
|
||||
|
||||
TEST_EQUAL(GetValuesByKey("abra"), vector<Value>({1, 2}), ());
|
||||
TEST_EQUAL(GetValuesByKey("abracadabra"), vector<Value>({0}), ());
|
||||
TEST_EQUAL(GetValuesByKey("abrau"), vector<Value>({3}), ());
|
||||
}
|
||||
|
||||
UNIT_CLASS_TEST(MemTrieTest, ForEachInSubtree)
|
||||
{
|
||||
Add("abracadabra", 0);
|
||||
Add("abra", 1);
|
||||
Add("abra", 2);
|
||||
Add("abrau", 3);
|
||||
|
||||
Data const all = {{"abra", 1}, {"abra", 2}, {"abracadabra", 0}, {"abrau", 3}};
|
||||
|
||||
TEST_EQUAL(GetContentsByPrefix(""), all, ());
|
||||
TEST_EQUAL(GetContentsByPrefix("a"), all, ());
|
||||
TEST_EQUAL(GetContentsByPrefix("abra"), all, ());
|
||||
TEST_EQUAL(GetContentsByPrefix("abracadabr"), Data({{"abracadabra", 0}}), ());
|
||||
TEST_EQUAL(GetContentsByPrefix("abracadabra"), Data({{"abracadabra", 0}}), ());
|
||||
TEST_EQUAL(GetContentsByPrefix("void"), Data{}, ());
|
||||
TEST_EQUAL(GetContentsByPrefix("abra"), all, ());
|
||||
TEST_EQUAL(GetContentsByPrefix("abrau"), Data({{"abrau", 3}}), ());
|
||||
}
|
||||
} // namespace
|
||||
20
libs/base/base_tests/message_test.cpp
Normal file
20
libs/base/base_tests/message_test.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
UNIT_TEST(DebugPrint_Char)
|
||||
{
|
||||
char const * s = "Тест";
|
||||
|
||||
char const arr1[] = "Тест";
|
||||
char constexpr arr2[] = "Тест";
|
||||
char arr3[] = "Тест";
|
||||
|
||||
std::string str(s);
|
||||
char * s2 = str.data();
|
||||
|
||||
TEST_EQUAL(s, DebugPrint(s), ());
|
||||
TEST_EQUAL(s, DebugPrint(arr1), ());
|
||||
TEST_EQUAL(s, DebugPrint(arr2), ());
|
||||
TEST_EQUAL(s, DebugPrint(arr3), ());
|
||||
TEST_EQUAL(s, DebugPrint(s2), ());
|
||||
TEST_EQUAL(s, DebugPrint(str), ());
|
||||
}
|
||||
115
libs/base/base_tests/newtype_test.cpp
Normal file
115
libs/base/base_tests/newtype_test.cpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/newtype.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace newtype_test
|
||||
{
|
||||
NEWTYPE(int, Int);
|
||||
|
||||
std::string DebugPrint(Int const & i)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << "Int(" << i.Get() << ')';
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
UNIT_TEST(NewType_TypeChecks)
|
||||
{
|
||||
TEST((std::is_constructible<Int, int>::value), ());
|
||||
TEST((std::is_constructible<Int, char>::value), ());
|
||||
TEST(!(std::is_convertible<int, Int>::value), ());
|
||||
TEST(!(std::is_convertible<Int, int>::value), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NewType_Base)
|
||||
{
|
||||
Int a{10};
|
||||
TEST_EQUAL(a.Get(), 10, ());
|
||||
|
||||
a.Set(100);
|
||||
TEST_EQUAL(a.Get(), 100, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NewType_Operations)
|
||||
{
|
||||
TEST(Int(10) == Int(10), ());
|
||||
TEST(Int(20) != Int(30), ());
|
||||
TEST(Int(10) < Int(20), ());
|
||||
TEST(Int(10) <= Int(20), ());
|
||||
TEST(Int(20) > Int(10), ());
|
||||
TEST(Int(20) >= Int(10), ());
|
||||
|
||||
TEST_EQUAL(Int(10) + Int(20), Int(30), ());
|
||||
TEST_EQUAL(Int(10) - Int(20), Int(-10), ());
|
||||
TEST_EQUAL(Int(10) / Int(2), Int(5), ());
|
||||
TEST_EQUAL(Int(10) * Int(2), Int(20), ());
|
||||
TEST_EQUAL(Int(10) % Int(3), Int(1), ());
|
||||
|
||||
TEST_EQUAL(Int(10) | Int(7), Int(10 | 7), ());
|
||||
TEST_EQUAL(Int(10) & Int(7), Int(10 & 7), ());
|
||||
TEST_EQUAL(Int(10) ^ Int(7), Int(10 ^ 7), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NewTypeMember_Operations)
|
||||
{
|
||||
Int a(10);
|
||||
auto b = a--;
|
||||
TEST_EQUAL(a, Int(9), ());
|
||||
TEST_EQUAL(b, Int(10), ());
|
||||
|
||||
b = --a;
|
||||
TEST_EQUAL(a, b, ());
|
||||
TEST_EQUAL(a, Int(8), ());
|
||||
|
||||
b = ++a;
|
||||
TEST_EQUAL(a, b, ());
|
||||
TEST_EQUAL(a, Int(9), ());
|
||||
|
||||
b = a++;
|
||||
TEST_EQUAL(a, Int(10), ());
|
||||
TEST_EQUAL(b, Int(9), ());
|
||||
|
||||
a.Set(100);
|
||||
b = Int(2);
|
||||
a *= b;
|
||||
TEST_EQUAL(a, Int(200), ());
|
||||
|
||||
a /= b;
|
||||
TEST_EQUAL(a, Int(100), ());
|
||||
|
||||
b.Set(3);
|
||||
a %= b;
|
||||
TEST_EQUAL(a, Int(1), ());
|
||||
|
||||
a.Set(10);
|
||||
a |= Int(7);
|
||||
TEST_EQUAL(a, Int(10 | 7), ());
|
||||
|
||||
a.Set(10);
|
||||
a &= Int(7);
|
||||
TEST_EQUAL(a, Int(10 & 7), ());
|
||||
|
||||
a.Set(10);
|
||||
a ^= Int(7);
|
||||
TEST_EQUAL(a, Int(10 ^ 7), ());
|
||||
}
|
||||
|
||||
namespace test_output
|
||||
{
|
||||
NEWTYPE_SIMPLE_OUTPUT(Int);
|
||||
} // namespace test_output
|
||||
|
||||
UNIT_TEST(NewType_SimpleOutPut)
|
||||
{
|
||||
using namespace test_output;
|
||||
TEST_EQUAL(test_output::DebugPrint(Int(10)), "10", ());
|
||||
|
||||
std::ostringstream sstr;
|
||||
sstr << Int(20);
|
||||
TEST_EQUAL(sstr.str(), "20", ());
|
||||
}
|
||||
} // namespace newtype_test
|
||||
79
libs/base/base_tests/non_intersecting_intervals_tests.cpp
Normal file
79
libs/base/base_tests/non_intersecting_intervals_tests.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/non_intersecting_intervals.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace base;
|
||||
|
||||
UNIT_TEST(NonIntersectingIntervals_1)
|
||||
{
|
||||
NonIntersectingIntervals<int> intervals;
|
||||
|
||||
TEST(intervals.AddInterval(1, 10), ());
|
||||
TEST(intervals.AddInterval(11, 15), ());
|
||||
// Overlap with [1, 10].
|
||||
TEST(!intervals.AddInterval(1, 20), ());
|
||||
|
||||
// Overlap with [11, 15].
|
||||
TEST(!intervals.AddInterval(13, 20), ());
|
||||
|
||||
// Overlap with [1, 10] and [11, 15].
|
||||
TEST(!intervals.AddInterval(0, 100), ());
|
||||
|
||||
TEST(intervals.AddInterval(100, 150), ());
|
||||
|
||||
// Overlap with [100, 150].
|
||||
TEST(!intervals.AddInterval(90, 200), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NonIntersectingIntervals_2)
|
||||
{
|
||||
NonIntersectingIntervals<int> intervals;
|
||||
|
||||
TEST(intervals.AddInterval(1, 10), ());
|
||||
// Overlap with [1, 10].
|
||||
TEST(!intervals.AddInterval(2, 9), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NonIntersectingIntervals_3)
|
||||
{
|
||||
NonIntersectingIntervals<int> intervals;
|
||||
|
||||
TEST(intervals.AddInterval(1, 10), ());
|
||||
// Overlap with [1, 10].
|
||||
TEST(!intervals.AddInterval(0, 20), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NonIntersectingIntervals_4)
|
||||
{
|
||||
NonIntersectingIntervals<int> intervals;
|
||||
|
||||
TEST(intervals.AddInterval(1, 10), ());
|
||||
// Overlap with [1, 10].
|
||||
TEST(!intervals.AddInterval(10, 20), ());
|
||||
TEST(!intervals.AddInterval(0, 1), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NonIntersectingIntervals_5)
|
||||
{
|
||||
NonIntersectingIntervals<int> intervals;
|
||||
|
||||
TEST(intervals.AddInterval(0, 1), ());
|
||||
|
||||
// Overlap with [0, 1].
|
||||
TEST(!intervals.AddInterval(1, 2), ());
|
||||
|
||||
TEST(intervals.AddInterval(2, 3), ());
|
||||
TEST(intervals.AddInterval(4, 5), ());
|
||||
|
||||
// Overlap with [0, 1] and [2, 3].
|
||||
TEST(!intervals.AddInterval(1, 3), ());
|
||||
|
||||
// Overlap with [2, 3].
|
||||
TEST(!intervals.AddInterval(2, 2), ());
|
||||
|
||||
// Overlap with [2, 3] and [4, 5].
|
||||
TEST(!intervals.AddInterval(2, 5), ());
|
||||
}
|
||||
} // namespace
|
||||
68
libs/base/base_tests/observer_list_test.cpp
Normal file
68
libs/base/base_tests/observer_list_test.cpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/observer_list.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Observer
|
||||
{
|
||||
Observer() : status(0) {}
|
||||
|
||||
void OnOperationCompleted(std::string const & message, int status)
|
||||
{
|
||||
this->message = message;
|
||||
this->status = status;
|
||||
}
|
||||
|
||||
std::string message;
|
||||
int status;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(ObserverList_Basic)
|
||||
{
|
||||
Observer observer0;
|
||||
Observer observer1;
|
||||
Observer observer2;
|
||||
|
||||
base::ObserverListSafe<Observer> observers;
|
||||
|
||||
// Register all observers in a list. Also check that it's not
|
||||
// possible to add the same observer twice.
|
||||
TEST(observers.Add(observer0), ());
|
||||
TEST(!observers.Add(observer0), ());
|
||||
|
||||
TEST(observers.Add(observer1), ());
|
||||
TEST(!observers.Add(observer1), ());
|
||||
|
||||
TEST(observers.Add(observer2), ());
|
||||
TEST(!observers.Add(observer2), ());
|
||||
|
||||
TEST(!observers.Add(observer1), ());
|
||||
TEST(!observers.Add(observer2), ());
|
||||
|
||||
std::string const message = "HTTP OK";
|
||||
observers.ForEach(&Observer::OnOperationCompleted, message, 204);
|
||||
|
||||
// Check observers internal state.
|
||||
TEST_EQUAL(message, observer0.message, ());
|
||||
TEST_EQUAL(204, observer0.status, ());
|
||||
|
||||
TEST_EQUAL(message, observer1.message, ());
|
||||
TEST_EQUAL(204, observer1.status, ());
|
||||
|
||||
TEST_EQUAL(message, observer2.message, ());
|
||||
TEST_EQUAL(204, observer2.status, ());
|
||||
|
||||
// Remove all observers from a list.
|
||||
TEST(observers.Remove(observer0), ());
|
||||
TEST(!observers.Remove(observer0), ());
|
||||
|
||||
TEST(observers.Remove(observer1), ());
|
||||
TEST(!observers.Remove(observer1), ());
|
||||
|
||||
TEST(observers.Remove(observer2), ());
|
||||
TEST(!observers.Remove(observer2), ());
|
||||
}
|
||||
42
libs/base/base_tests/range_iterator_test.cpp
Normal file
42
libs/base/base_tests/range_iterator_test.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/range_iterator.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
UNIT_TEST(RangeIterator)
|
||||
{
|
||||
using namespace base;
|
||||
|
||||
{
|
||||
std::vector<int> result;
|
||||
for (auto const i : UpTo(5))
|
||||
result.push_back(i);
|
||||
TEST_EQUAL(result, (std::vector<int>{0, 1, 2, 3, 4}), ());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<int> result;
|
||||
for (auto const i : UpTo(2, 5))
|
||||
result.push_back(i);
|
||||
TEST_EQUAL(result, (std::vector<int>{2, 3, 4}), ());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<int> result;
|
||||
for (auto const i : DownTo(5))
|
||||
result.push_back(i);
|
||||
TEST_EQUAL(result, (std::vector<int>{4, 3, 2, 1, 0}), ());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<int> result;
|
||||
for (auto const i : DownTo(2, 5))
|
||||
result.push_back(i);
|
||||
TEST_EQUAL(result, (std::vector<int>{4, 3, 2}), ());
|
||||
}
|
||||
|
||||
{
|
||||
TEST_EQUAL(std::vector<int>(MakeRangeIterator(0), MakeRangeIterator(5)), (std::vector<int>{0, 1, 2, 3, 4}), ());
|
||||
}
|
||||
}
|
||||
83
libs/base/base_tests/ref_counted_tests.cpp
Normal file
83
libs/base/base_tests/ref_counted_tests.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/ref_counted.hpp"
|
||||
|
||||
using namespace base;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Resource : public RefCounted
|
||||
{
|
||||
explicit Resource(bool & destroyed) : m_destroyed(destroyed) { m_destroyed = false; }
|
||||
|
||||
~Resource() override { m_destroyed = true; }
|
||||
|
||||
bool & m_destroyed;
|
||||
};
|
||||
|
||||
UNIT_TEST(RefCounted_Smoke)
|
||||
{
|
||||
{
|
||||
RefCountPtr<Resource> p;
|
||||
}
|
||||
|
||||
{
|
||||
bool destroyed;
|
||||
{
|
||||
RefCountPtr<Resource> p(new Resource(destroyed));
|
||||
TEST_EQUAL(1, p->NumRefs(), ());
|
||||
TEST(!destroyed, ());
|
||||
}
|
||||
TEST(destroyed, ());
|
||||
}
|
||||
|
||||
{
|
||||
bool destroyed;
|
||||
{
|
||||
RefCountPtr<Resource> a(new Resource(destroyed));
|
||||
TEST_EQUAL(1, a->NumRefs(), ());
|
||||
TEST(!destroyed, ());
|
||||
|
||||
RefCountPtr<Resource> b(a);
|
||||
TEST(a.Get() == b.Get(), ());
|
||||
TEST_EQUAL(2, a->NumRefs(), ());
|
||||
TEST(!destroyed, ());
|
||||
|
||||
{
|
||||
RefCountPtr<Resource> c;
|
||||
TEST(c.Get() == nullptr, ());
|
||||
|
||||
c = b;
|
||||
TEST(a.Get() == b.Get(), ());
|
||||
TEST(b.Get() == c.Get(), ());
|
||||
TEST_EQUAL(3, a->NumRefs(), ());
|
||||
TEST(!destroyed, ());
|
||||
}
|
||||
|
||||
TEST(a.Get() == b.Get(), ());
|
||||
TEST_EQUAL(2, a->NumRefs(), ());
|
||||
TEST(!destroyed, ());
|
||||
|
||||
RefCountPtr<Resource> d(std::move(b));
|
||||
TEST(b.Get() == nullptr, ());
|
||||
TEST(a.Get() == d.Get(), ());
|
||||
TEST_EQUAL(2, a->NumRefs(), ());
|
||||
TEST(!destroyed, ());
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
|
||||
#endif // #ifdef __clang__
|
||||
a = a;
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif // #ifdef __clang__
|
||||
|
||||
TEST_EQUAL(a.Get(), d.Get(), ());
|
||||
TEST_EQUAL(2, a->NumRefs(), ());
|
||||
TEST(!destroyed, ());
|
||||
}
|
||||
TEST(destroyed, ());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
86
libs/base/base_tests/regexp_test.cpp
Normal file
86
libs/base/base_tests/regexp_test.cpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
namespace regexp_test
|
||||
{
|
||||
template <typename Fn>
|
||||
void ForEachMatched(std::string const & s, boost::regex const & regex, Fn && fn)
|
||||
{
|
||||
for (std::sregex_token_iterator cur(s.begin(), s.end(), regex), end; cur != end; ++cur)
|
||||
fn(*cur);
|
||||
}
|
||||
|
||||
UNIT_TEST(RegExp_Or)
|
||||
{
|
||||
boost::regex exp("\\.mwm\\.(downloading2?$|resume2?$)");
|
||||
|
||||
TEST(boost::regex_search("Aruba.mwm.downloading", exp), ());
|
||||
TEST(!regex_search("Aruba.mwm.downloading1", exp), ());
|
||||
TEST(boost::regex_search("Aruba.mwm.downloading2", exp), ());
|
||||
TEST(!regex_search("Aruba.mwm.downloading3", exp), ());
|
||||
TEST(!regex_search("Aruba.mwm.downloading.tmp", exp), ());
|
||||
|
||||
TEST(boost::regex_search("Aruba.mwm.resume", exp), ());
|
||||
TEST(!regex_search("Aruba.mwm.resume1", exp), ());
|
||||
TEST(boost::regex_search("Aruba.mwm.resume2", exp), ());
|
||||
TEST(!regex_search("Aruba.mwm.resume3", exp), ());
|
||||
TEST(!regex_search("Aruba.mwm.resume.tmp", exp), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(RegExp_ForEachMatched)
|
||||
{
|
||||
boost::regex exp("-?\\d+\\.?\\d*, *-?\\d+\\.?\\d*");
|
||||
|
||||
{
|
||||
std::string const s = "6.66, 9.99";
|
||||
std::vector<std::string> v;
|
||||
ForEachMatched(s, exp, base::MakeBackInsertFunctor(v));
|
||||
TEST_EQUAL(v.size(), 1, ());
|
||||
TEST_EQUAL(v[0], s, ());
|
||||
}
|
||||
|
||||
{
|
||||
std::string const s1 = "6.66, 9.99";
|
||||
std::string const s2 = "-5.55, -7.77";
|
||||
std::vector<std::string> v;
|
||||
ForEachMatched(s1 + " 180 , bfuewib 365@" + s2, exp, base::MakeBackInsertFunctor(v));
|
||||
TEST_EQUAL(v.size(), 2, ());
|
||||
TEST_EQUAL(v[0], s1, ());
|
||||
TEST_EQUAL(v[1], s2, ());
|
||||
}
|
||||
|
||||
{
|
||||
std::string const s = "X6.66, 9.99";
|
||||
std::vector<std::string> v;
|
||||
ForEachMatched(s, exp, base::MakeBackInsertFunctor(v));
|
||||
TEST_EQUAL(v.size(), 1, ());
|
||||
TEST_EQUAL(v[0], std::string(s.begin() + 1, s.end()), ());
|
||||
}
|
||||
|
||||
{
|
||||
std::string const s = "6.66, 9.99X";
|
||||
std::vector<std::string> v;
|
||||
ForEachMatched(s, exp, base::MakeBackInsertFunctor(v));
|
||||
TEST_EQUAL(v.size(), 1, ());
|
||||
TEST_EQUAL(v[0], std::string(s.begin(), s.end() - 1), ());
|
||||
}
|
||||
|
||||
{
|
||||
std::string const s = "6.66X, 9.99";
|
||||
std::vector<std::string> v;
|
||||
ForEachMatched(s, exp, base::MakeBackInsertFunctor(v));
|
||||
TEST_EQUAL(v.size(), 0, ());
|
||||
}
|
||||
|
||||
{
|
||||
std::string const s = "6.66, X9.99";
|
||||
std::vector<std::string> v;
|
||||
ForEachMatched(s, exp, base::MakeBackInsertFunctor(v));
|
||||
TEST_EQUAL(v.size(), 0, ());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace regexp_test
|
||||
125
libs/base/base_tests/rolling_hash_test.cpp
Normal file
125
libs/base/base_tests/rolling_hash_test.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
#include "base/rolling_hash.hpp"
|
||||
#include "testing/benchmark.hpp"
|
||||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/base.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/macros.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <class RollingHasherT>
|
||||
void SmokeTest1RollingHasher()
|
||||
{
|
||||
typedef typename RollingHasherT::hash_type hash_type;
|
||||
RollingHasherT hash;
|
||||
hash_type const h0 = hash.Init("a", 1);
|
||||
TEST_EQUAL(h0, hash.Scroll('a', 'a'), (sizeof(hash_type)));
|
||||
}
|
||||
|
||||
template <class RollingHasherT>
|
||||
void SmokeTest2RollingHasher()
|
||||
{
|
||||
typedef typename RollingHasherT::hash_type hash_type;
|
||||
RollingHasherT hash;
|
||||
hash_type const hAB = hash.Init("ab", 2);
|
||||
hash_type const hBA = hash.Scroll('a', 'a');
|
||||
hash_type const hAB1 = hash.Scroll('b', 'b');
|
||||
TEST_EQUAL(hAB, hAB1, (sizeof(hash_type)));
|
||||
hash_type const hBA1 = hash.Scroll('a', 'a');
|
||||
TEST_EQUAL(hBA, hBA1, (sizeof(hash_type)));
|
||||
}
|
||||
|
||||
template <class RollingHasherT>
|
||||
void TestRollingHasher()
|
||||
{
|
||||
SmokeTest1RollingHasher<RollingHasherT>();
|
||||
SmokeTest2RollingHasher<RollingHasherT>();
|
||||
// 01234567890123
|
||||
char const s[] = "abcdefghaabcde";
|
||||
size_t const len = ARRAY_SIZE(s) - 1;
|
||||
for (uint32_t size = 1; size <= 6; ++size)
|
||||
{
|
||||
typedef typename RollingHasherT::hash_type hash_type;
|
||||
RollingHasherT hash;
|
||||
std::vector<hash_type> hashes;
|
||||
hashes.push_back(hash.Init(static_cast<char const *>(s), size));
|
||||
for (uint32_t i = size; i < len; ++i)
|
||||
hashes.push_back(hash.Scroll(s[i - size], s[i]));
|
||||
TEST_EQUAL(hashes.size(), len - size + 1, (size, len, sizeof(hash_type)));
|
||||
switch (size)
|
||||
{
|
||||
case 6:
|
||||
{
|
||||
// Test that there are no collisions.
|
||||
sort(hashes.begin(), hashes.end());
|
||||
TEST(hashes.end() == unique(hashes.begin(), hashes.end()), (size, hashes));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
TEST_EQUAL(hashes[0], hashes[8], (size, len, sizeof(hash_type)));
|
||||
TEST_EQUAL(hashes[0], hashes[9], (size, len, sizeof(hash_type)));
|
||||
TEST_EQUAL(hashes[1], hashes[10], (size, len, sizeof(hash_type)));
|
||||
TEST_EQUAL(hashes[2], hashes[11], (size, len, sizeof(hash_type)));
|
||||
TEST_EQUAL(hashes[3], hashes[12], (size, len, sizeof(hash_type)));
|
||||
TEST_EQUAL(hashes[4], hashes[13], (size, len, sizeof(hash_type)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
for (unsigned int i = 0; i < 6 - size; ++i)
|
||||
TEST_EQUAL(hashes[i], hashes[i + 9], (i, size, len, sizeof(hash_type)));
|
||||
sort(hashes.begin(), hashes.end());
|
||||
TEST((hashes.end() - (6 - size)) == unique(hashes.begin(), hashes.end()), (size, hashes));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(RabinKarpRollingHasher32)
|
||||
{
|
||||
TestRollingHasher<RabinKarpRollingHasher32>();
|
||||
}
|
||||
|
||||
UNIT_TEST(RabinKarpRollingHasher64)
|
||||
{
|
||||
TestRollingHasher<RabinKarpRollingHasher64>();
|
||||
}
|
||||
|
||||
UNIT_TEST(RollingHasher32)
|
||||
{
|
||||
TestRollingHasher<RollingHasher32>();
|
||||
}
|
||||
|
||||
UNIT_TEST(RollingHasher64)
|
||||
{
|
||||
TestRollingHasher<RollingHasher64>();
|
||||
}
|
||||
|
||||
#ifndef DEBUG
|
||||
BENCHMARK_TEST(RollingHasher32)
|
||||
{
|
||||
RollingHasher32 hash;
|
||||
hash.Init("abcdefghijklmnopq", 17);
|
||||
BENCHMARK_N_TIMES(IF_DEBUG_ELSE(500000, 20000000), 0.2)
|
||||
{
|
||||
FORCE_USE_VALUE(hash.Scroll(static_cast<uint32_t>('a') + benchmark.Iteration(),
|
||||
static_cast<uint32_t>('r') + benchmark.Iteration()));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_TEST(RollingHasher64)
|
||||
{
|
||||
RollingHasher64 hash;
|
||||
hash.Init("abcdefghijklmnopq", 17);
|
||||
BENCHMARK_N_TIMES(IF_DEBUG_ELSE(500000, 10000000), 0.3)
|
||||
{
|
||||
FORCE_USE_VALUE(hash.Scroll(static_cast<uint64_t>('a') + benchmark.Iteration(),
|
||||
static_cast<uint64_t>('r') + benchmark.Iteration()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
31
libs/base/base_tests/scope_guard_test.cpp
Normal file
31
libs/base/base_tests/scope_guard_test.cpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/scope_guard.hpp"
|
||||
|
||||
static bool b = false;
|
||||
|
||||
void SetB()
|
||||
{
|
||||
b = true;
|
||||
}
|
||||
|
||||
UNIT_TEST(ScopeGuard)
|
||||
{
|
||||
{
|
||||
b = false;
|
||||
SCOPE_GUARD(guard, &SetB);
|
||||
TEST_EQUAL(b, false, ("Start test condition"));
|
||||
}
|
||||
TEST_EQUAL(b, true, ("scope_guard works in destructor"));
|
||||
}
|
||||
|
||||
UNIT_TEST(ScopeGuardRelease)
|
||||
{
|
||||
{
|
||||
b = false;
|
||||
SCOPE_GUARD(guard, &SetB);
|
||||
TEST_EQUAL(b, false, ("Start test condition"));
|
||||
guard.release();
|
||||
}
|
||||
TEST_EQUAL(b, false, ("If relese() was called then scope_guard shouldn't work"));
|
||||
}
|
||||
269
libs/base/base_tests/small_set_test.cpp
Normal file
269
libs/base/base_tests/small_set_test.cpp
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/small_map.hpp"
|
||||
#include "base/small_set.hpp"
|
||||
#include "base/timer.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace small_set_test
|
||||
{
|
||||
using namespace base;
|
||||
|
||||
UNIT_TEST(SmallSet_Smoke)
|
||||
{
|
||||
SmallSet<300> set;
|
||||
TEST_EQUAL(set.Size(), 0, ());
|
||||
for (uint64_t i = 0; i < 300; ++i)
|
||||
TEST(!set.Contains(i), ());
|
||||
|
||||
set.Insert(0);
|
||||
TEST_EQUAL(set.Size(), 1, ());
|
||||
TEST(set.Contains(0), ());
|
||||
|
||||
set.Insert(0);
|
||||
TEST_EQUAL(set.Size(), 1, ());
|
||||
TEST(set.Contains(0), ());
|
||||
|
||||
set.Insert(5);
|
||||
TEST_EQUAL(set.Size(), 2, ());
|
||||
TEST(set.Contains(0), ());
|
||||
TEST(set.Contains(5), ());
|
||||
|
||||
set.Insert(64);
|
||||
TEST_EQUAL(set.Size(), 3, ());
|
||||
TEST(set.Contains(0), ());
|
||||
TEST(set.Contains(5), ());
|
||||
TEST(set.Contains(64), ());
|
||||
|
||||
{
|
||||
auto cur = set.begin();
|
||||
auto end = set.end();
|
||||
for (uint64_t i : {0, 5, 64})
|
||||
{
|
||||
TEST(cur != end, ());
|
||||
TEST_EQUAL(*cur, i, ());
|
||||
++cur;
|
||||
}
|
||||
TEST(cur == end, ());
|
||||
}
|
||||
|
||||
set.Remove(5);
|
||||
TEST_EQUAL(set.Size(), 2, ());
|
||||
TEST(set.Contains(0), ());
|
||||
TEST(!set.Contains(5), ());
|
||||
TEST(set.Contains(64), ());
|
||||
|
||||
set.Insert(297);
|
||||
set.Insert(298);
|
||||
set.Insert(299);
|
||||
TEST_EQUAL(set.Size(), 5, ());
|
||||
|
||||
{
|
||||
std::vector<uint64_t> const actual(set.begin(), set.end());
|
||||
std::vector<uint64_t> const expected = {0, 64, 297, 298, 299};
|
||||
TEST_EQUAL(actual, expected, ());
|
||||
}
|
||||
|
||||
TEST_EQUAL(set.Size(), std::distance(set.begin(), set.end()), ());
|
||||
}
|
||||
|
||||
bool BenchmarkTimeLessOrNear(uint64_t l, uint64_t r, double relativeTolerance)
|
||||
{
|
||||
return (l < r) || ((l - r) / static_cast<double>(l) < relativeTolerance);
|
||||
}
|
||||
|
||||
#ifndef DEBUG
|
||||
std::vector<uint32_t> GenerateIndices(uint32_t min, uint32_t max)
|
||||
{
|
||||
std::vector<uint32_t> res;
|
||||
|
||||
std::uniform_int_distribution<uint64_t> randDist(min, max);
|
||||
std::random_device randDevice;
|
||||
std::mt19937 randEngine(randDevice());
|
||||
|
||||
for (size_t i = 0; i < 10000000; ++i)
|
||||
res.push_back(randDist(randEngine));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
UNIT_TEST(SmallMap_Benchmark1)
|
||||
{
|
||||
// 1. Init maps.
|
||||
// Dataset is similar to routing::VehicleModel.
|
||||
std::unordered_map<uint32_t, bool> uMap = {
|
||||
{1, true}, {2, false}, {4, false}, {6, true}, {7, true}, {8, true}, {12, false},
|
||||
{15, false}, {26, true}, {30, false}, {36, false}, {43, false}, {54, false}, {57, true},
|
||||
{58, true}, {65, true}, {69, true}, {90, true}, {95, false}, {119, false}, {167, true},
|
||||
{176, false}, {259, true}, {272, false}, {994, true}, {1054, false}};
|
||||
|
||||
base::SmallMap<uint32_t, bool> sMap(uMap.begin(), uMap.end());
|
||||
|
||||
// 2. Generate indices.
|
||||
std::vector<uint32_t> indices = GenerateIndices(1, 1054);
|
||||
|
||||
uint64_t t1, t2;
|
||||
uint32_t sum1 = 0, sum2 = 0;
|
||||
|
||||
// 3. Run unordered_map.
|
||||
{
|
||||
base::HighResTimer timer;
|
||||
for (auto i : indices)
|
||||
sum1 += (uMap.find(i) != uMap.end() ? 1 : 0);
|
||||
t1 = timer.ElapsedMilliseconds();
|
||||
}
|
||||
|
||||
// 4. Run SmallMap.
|
||||
{
|
||||
base::HighResTimer timer;
|
||||
for (auto i : indices)
|
||||
sum2 += (sMap.Find(i) ? 1 : 0);
|
||||
t2 = timer.ElapsedMilliseconds();
|
||||
}
|
||||
|
||||
TEST_EQUAL(sum1, sum2, ());
|
||||
// At this moment, we have rare t2 > t1 on Linux CI.
|
||||
TEST(BenchmarkTimeLessOrNear(t2, t1, 0.3), (t2, t1));
|
||||
LOG(LINFO, ("unordered_map time =", t1, "SmallMap time =", t2));
|
||||
}
|
||||
|
||||
UNIT_TEST(SmallMap_Benchmark2)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
uint32_t i = 0;
|
||||
// Dataset is similar to routing::VehicleModelFactory.
|
||||
unordered_map<string, shared_ptr<int>> uMap = {
|
||||
{"", make_shared<int>(i++)},
|
||||
{"Australia", make_shared<int>(i++)},
|
||||
{"Austria", make_shared<int>(i++)},
|
||||
{"Belarus", make_shared<int>(i++)},
|
||||
{"Belgium", make_shared<int>(i++)},
|
||||
{"Brazil", make_shared<int>(i++)},
|
||||
{"Denmark", make_shared<int>(i++)},
|
||||
{"France", make_shared<int>(i++)},
|
||||
{"Finland", make_shared<int>(i++)},
|
||||
{"Germany", make_shared<int>(i++)},
|
||||
{"Hungary", make_shared<int>(i++)},
|
||||
{"Iceland", make_shared<int>(i++)},
|
||||
{"Netherlands", make_shared<int>(i++)},
|
||||
{"Norway", make_shared<int>(i++)},
|
||||
{"Oman", make_shared<int>(i++)},
|
||||
{"Poland", make_shared<int>(i++)},
|
||||
{"Romania", make_shared<int>(i++)},
|
||||
{"Russian Federation", make_shared<int>(i++)},
|
||||
{"Slovakia", make_shared<int>(i++)},
|
||||
{"Spain", make_shared<int>(i++)},
|
||||
{"Switzerland", make_shared<int>(i++)},
|
||||
{"Turkey", make_shared<int>(i++)},
|
||||
{"Ukraine", make_shared<int>(i++)},
|
||||
{"United Kingdom", make_shared<int>(i++)},
|
||||
{"United States of America", make_shared<int>(i++)},
|
||||
};
|
||||
|
||||
base::SmallMap<std::string, std::shared_ptr<int>> sMap(uMap.begin(), uMap.end());
|
||||
|
||||
// 2. Generate indices.
|
||||
std::vector<std::string> keys;
|
||||
for (auto const & e : uMap)
|
||||
{
|
||||
keys.push_back(e.first);
|
||||
keys.push_back(e.first + "_Foo");
|
||||
keys.push_back(e.first + "_Bar");
|
||||
keys.push_back(e.first + "_Bazz");
|
||||
}
|
||||
std::vector<uint32_t> indices = GenerateIndices(0, keys.size() - 1);
|
||||
|
||||
uint64_t t1, t2;
|
||||
uint32_t sum1 = 0, sum2 = 0;
|
||||
|
||||
// 3. Run unordered_map.
|
||||
{
|
||||
base::HighResTimer timer;
|
||||
for (auto i : indices)
|
||||
{
|
||||
auto const it = uMap.find(keys[i]);
|
||||
if (it != uMap.end())
|
||||
sum1 += *it->second;
|
||||
}
|
||||
t1 = timer.ElapsedMilliseconds();
|
||||
}
|
||||
|
||||
// 4. Run SmallMap.
|
||||
{
|
||||
base::HighResTimer timer;
|
||||
for (auto i : indices)
|
||||
{
|
||||
auto const * p = sMap.Find(keys[i]);
|
||||
if (p)
|
||||
sum2 += **p;
|
||||
}
|
||||
t2 = timer.ElapsedMilliseconds();
|
||||
}
|
||||
|
||||
TEST_EQUAL(sum1, sum2, ());
|
||||
// std::hash(std::string) is better than std::less(std::string)
|
||||
TEST_LESS(t1, t2, ());
|
||||
LOG(LINFO, ("unordered_map time =", t1, "SmallMap time =", t2));
|
||||
}
|
||||
|
||||
// Small 4 elements sample doesn't work for new (gcc11+, clang14+) toolchain.
|
||||
/*
|
||||
UNIT_TEST(SmallMap_Benchmark3)
|
||||
{
|
||||
// Dataset is similar to routing::VehicleModel.m_surfaceFactors.
|
||||
std::unordered_map<int, int> uMap = {
|
||||
{1, 0}, {10, 1}, {100, 2}, {1000, 3},
|
||||
};
|
||||
|
||||
base::SmallMap<int, int> sMap(uMap.begin(), uMap.end());
|
||||
base::SmallMapBase<int, int> sbMap(uMap.begin(), uMap.end());
|
||||
|
||||
std::vector<uint32_t> indices = GenerateIndices(0, 3);
|
||||
// Missing key queries are even worse for the std map.
|
||||
std::vector<int> keys;
|
||||
for (auto const & e : uMap)
|
||||
keys.push_back(e.first);
|
||||
|
||||
uint64_t t1, t2, t3;
|
||||
uint32_t sum1 = 0, sum2 = 0, sum3 = 0;
|
||||
|
||||
// 3. Run unordered_map.
|
||||
{
|
||||
base::HighResTimer timer;
|
||||
for (auto i : indices)
|
||||
sum1 += uMap.find(keys[i])->second;
|
||||
t1 = timer.ElapsedMilliseconds();
|
||||
}
|
||||
|
||||
// 4. Run SmallMap.
|
||||
{
|
||||
base::HighResTimer timer;
|
||||
for (auto i : indices)
|
||||
sum2 += *sMap.Find(keys[i]);
|
||||
t2 = timer.ElapsedMilliseconds();
|
||||
}
|
||||
|
||||
// 5. Run SmallMapBase.
|
||||
{
|
||||
base::HighResTimer timer;
|
||||
for (auto i : indices)
|
||||
sum3 += *sbMap.Find(keys[i]);
|
||||
t3 = timer.ElapsedMilliseconds();
|
||||
}
|
||||
|
||||
TEST_EQUAL(sum1, sum2, ());
|
||||
TEST_EQUAL(sum1, sum3, ());
|
||||
TEST_LESS(t2, t1, ());
|
||||
TEST(BenchmarkTimeLessOrNear(t3, t2, 0.05), (t3, t2));
|
||||
LOG(LINFO, ("unordered_map time =", t1, "SmallMap time =", t2, "SmallMapBase time =", t3));
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
} // namespace small_set_test
|
||||
364
libs/base/base_tests/stl_helpers_tests.cpp
Normal file
364
libs/base/base_tests/stl_helpers_tests.cpp
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace stl_helpers_tests
|
||||
{
|
||||
using namespace base;
|
||||
|
||||
class Int
|
||||
{
|
||||
public:
|
||||
explicit Int(int v) : m_v(v) {}
|
||||
|
||||
inline int Get() const { return m_v; }
|
||||
|
||||
private:
|
||||
int m_v;
|
||||
};
|
||||
|
||||
template <template <typename...> class Cont>
|
||||
void TestSortUnique()
|
||||
{
|
||||
{
|
||||
Cont<int> actual = {1, 2, 1, 4, 3, 5, 2, 7, 1};
|
||||
SortUnique(actual);
|
||||
Cont<int> const expected = {1, 2, 3, 4, 5, 7};
|
||||
TEST_EQUAL(actual, expected, ());
|
||||
}
|
||||
{
|
||||
using Value = int;
|
||||
using Pair = std::pair<Value, int>;
|
||||
Cont<Pair> d = {{1, 22}, {2, 33}, {1, 23}, {4, 54}, {3, 34}, {5, 23}, {2, 23}, {7, 32}, {1, 12}};
|
||||
|
||||
SortUnique(d, LessBy(&Pair::first), EqualsBy(&Pair::first));
|
||||
|
||||
Cont<Value> const expected = {1, 2, 3, 4, 5, 7};
|
||||
TEST_EQUAL(d.size(), expected.size(), ());
|
||||
for (size_t i = 0; i < d.size(); ++i)
|
||||
TEST_EQUAL(d[i].first, expected[i], (i));
|
||||
}
|
||||
{
|
||||
using Value = double;
|
||||
using Pair = std::pair<Value, int>;
|
||||
Cont<Pair> d = {{0.5, 11}, {1000.99, 234}, {0.5, 23}, {1234.56789, 54}, {1000.99, 34}};
|
||||
|
||||
SortUnique(d, LessBy(&Pair::first), EqualsBy(&Pair::first));
|
||||
|
||||
Cont<Value> const expected = {0.5, 1000.99, 1234.56789};
|
||||
TEST_EQUAL(d.size(), expected.size(), ());
|
||||
for (size_t i = 0; i < d.size(); ++i)
|
||||
TEST_EQUAL(d[i].first, expected[i], (i));
|
||||
}
|
||||
}
|
||||
|
||||
template <template <typename...> class Cont>
|
||||
void TestEqualsBy()
|
||||
{
|
||||
{
|
||||
using Value = std::pair<int, int>;
|
||||
Cont<Value> actual = {{1, 2}, {1, 3}, {2, 100}, {3, 7}, {3, 8}, {2, 500}};
|
||||
actual.erase(std::unique(actual.begin(), actual.end(), EqualsBy(&Value::first)), actual.end());
|
||||
|
||||
Cont<int> const expected = {{1, 2, 3, 2}};
|
||||
TEST_EQUAL(expected.size(), actual.size(), ());
|
||||
for (size_t i = 0; i < actual.size(); ++i)
|
||||
TEST_EQUAL(expected[i], actual[i].first, ());
|
||||
}
|
||||
|
||||
{
|
||||
Cont<Int> actual;
|
||||
for (auto const v : {0, 0, 1, 2, 2, 0})
|
||||
actual.emplace_back(v);
|
||||
actual.erase(std::unique(actual.begin(), actual.end(), EqualsBy(&Int::Get)), actual.end());
|
||||
|
||||
Cont<int> const expected = {{0, 1, 2, 0}};
|
||||
TEST_EQUAL(expected.size(), actual.size(), ());
|
||||
for (size_t i = 0; i < actual.size(); ++i)
|
||||
TEST_EQUAL(expected[i], actual[i].Get(), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(LessBy)
|
||||
{
|
||||
{
|
||||
using Value = std::pair<int, int>;
|
||||
|
||||
std::vector<Value> v = {{2, 2}, {0, 4}, {3, 1}, {4, 0}, {1, 3}};
|
||||
std::sort(v.begin(), v.end(), LessBy(&Value::first));
|
||||
for (size_t i = 0; i < v.size(); ++i)
|
||||
TEST_EQUAL(i, static_cast<size_t>(v[i].first), ());
|
||||
|
||||
std::vector<Value const *> pv;
|
||||
for (auto const & p : v)
|
||||
pv.push_back(&p);
|
||||
|
||||
std::sort(pv.begin(), pv.end(), LessBy(&Value::second));
|
||||
for (size_t i = 0; i < pv.size(); ++i)
|
||||
TEST_EQUAL(i, static_cast<size_t>(pv[i]->second), ());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<Int> v;
|
||||
for (int i = 9; i >= 0; --i)
|
||||
v.emplace_back(i);
|
||||
|
||||
std::sort(v.begin(), v.end(), LessBy(&Int::Get));
|
||||
for (size_t i = 0; i < v.size(); ++i)
|
||||
TEST_EQUAL(v[i].Get(), static_cast<int>(i), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(EqualsBy_VectorTest)
|
||||
{
|
||||
TestEqualsBy<std::vector>();
|
||||
TestEqualsBy<std::deque>();
|
||||
}
|
||||
|
||||
UNIT_TEST(SortUnique_VectorTest)
|
||||
{
|
||||
TestSortUnique<std::vector>();
|
||||
TestSortUnique<std::deque>();
|
||||
}
|
||||
|
||||
UNIT_TEST(IgnoreFirstArgument)
|
||||
{
|
||||
{
|
||||
int s = 0;
|
||||
auto f1 = [&](int a, int b) { s += a + b; };
|
||||
auto f2 = [&](int a, int b) { s -= a + b; };
|
||||
auto f3 = MakeIgnoreFirstArgument(f2);
|
||||
|
||||
f1(2, 3);
|
||||
TEST_EQUAL(s, 5, ());
|
||||
f3(1, 2, 3);
|
||||
TEST_EQUAL(s, 0, ());
|
||||
}
|
||||
|
||||
{
|
||||
auto f1 = [](int a, int b) -> int { return a + b; };
|
||||
auto f2 = MakeIgnoreFirstArgument(f1);
|
||||
|
||||
auto const x = f1(2, 3);
|
||||
auto const y = f2("ignored", 2, 3);
|
||||
TEST_EQUAL(x, y, ());
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct EqualZero
|
||||
{
|
||||
bool operator()(int x) { return (x == 0); }
|
||||
};
|
||||
|
||||
template <class ContT>
|
||||
void CheckNoZero(ContT & c, typename ContT::iterator i)
|
||||
{
|
||||
c.erase(i, c.end());
|
||||
TEST(find_if(c.begin(), c.end(), EqualZero()) == c.end(), ());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(RemoveIfKeepValid)
|
||||
{
|
||||
{
|
||||
std::vector<int> v;
|
||||
TEST(RemoveIfKeepValid(v.begin(), v.end(), EqualZero()) == v.end(), ());
|
||||
|
||||
v.push_back(1);
|
||||
TEST(RemoveIfKeepValid(v.begin(), v.end(), EqualZero()) == v.end(), ());
|
||||
|
||||
v.push_back(1);
|
||||
TEST(RemoveIfKeepValid(v.begin(), v.end(), EqualZero()) == v.end(), ());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<int> v;
|
||||
v.push_back(0);
|
||||
TEST(RemoveIfKeepValid(v.begin(), v.end(), EqualZero()) == v.begin(), ());
|
||||
|
||||
v.push_back(0);
|
||||
TEST(RemoveIfKeepValid(v.begin(), v.end(), EqualZero()) == v.begin(), ());
|
||||
|
||||
v.push_back(1);
|
||||
CheckNoZero(v, RemoveIfKeepValid(v.begin(), v.end(), EqualZero()));
|
||||
TEST_EQUAL(v.size(), 1, ());
|
||||
|
||||
v.push_back(1);
|
||||
v.push_back(0);
|
||||
v.push_back(0);
|
||||
CheckNoZero(v, RemoveIfKeepValid(v.begin(), v.end(), EqualZero()));
|
||||
TEST_EQUAL(v.size(), 2, ());
|
||||
|
||||
v.push_back(0);
|
||||
v.push_back(0);
|
||||
v.push_back(1);
|
||||
v.push_back(1);
|
||||
CheckNoZero(v, RemoveIfKeepValid(v.begin(), v.end(), EqualZero()));
|
||||
TEST_EQUAL(v.size(), 4, ());
|
||||
}
|
||||
|
||||
{
|
||||
std::deque<int> v;
|
||||
v.push_back(1);
|
||||
v.push_back(0);
|
||||
v.push_back(1);
|
||||
v.push_back(0);
|
||||
v.push_back(1);
|
||||
v.push_back(0);
|
||||
v.push_back(1);
|
||||
CheckNoZero(v, RemoveIfKeepValid(v.begin(), v.end(), EqualZero()));
|
||||
TEST_EQUAL(v.size(), 4, ());
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
template <class T, size_t N1, size_t N2, size_t N3>
|
||||
void CheckAccumulateIntervals(size_t & idTest, std::pair<T, T> (&arr1)[N1], std::pair<T, T> (&arr2)[N2],
|
||||
std::pair<T, T> (&arr3)[N3])
|
||||
{
|
||||
std::vector<std::pair<T, T>> res;
|
||||
AccumulateIntervals1With2(arr1, arr1 + N1, arr2, arr2 + N2, back_inserter(res));
|
||||
|
||||
++idTest;
|
||||
TEST_EQUAL(N3, res.size(), ("Test", idTest, res));
|
||||
TEST(equal(res.begin(), res.end(), arr3), ("Test", idTest, res));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(AccumulateIntervals)
|
||||
{
|
||||
typedef std::pair<int, int> T;
|
||||
size_t idTest = 0;
|
||||
|
||||
// bound cases
|
||||
{
|
||||
std::vector<T> res;
|
||||
T arr[] = {T(10, 20)};
|
||||
|
||||
res.clear();
|
||||
AccumulateIntervals1With2(arr, arr + 1, arr, arr, back_inserter(res));
|
||||
TEST_EQUAL(res.size(), 1, ());
|
||||
|
||||
res.clear();
|
||||
AccumulateIntervals1With2(arr, arr, arr, arr + 1, back_inserter(res));
|
||||
TEST_EQUAL(res.size(), 0, ());
|
||||
}
|
||||
|
||||
// check splice overlapped
|
||||
{
|
||||
T arr1[] = {T(10, 20), T(30, 40)};
|
||||
T arr2[] = {T(19, 31)};
|
||||
T res[] = {T(10, 40)};
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
|
||||
// check skip not overlapped
|
||||
{
|
||||
T arr1[] = {T(10, 20), T(30, 40)};
|
||||
T arr2[] = {T(0, 9), T(21, 29), T(41, 50)};
|
||||
T res[2] = {T(10, 20), T(30, 40)};
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
{
|
||||
T arr1[] = {T(10, 20), T(30, 40)};
|
||||
T arr2[] = {T(1, 2), T(3, 4), T(5, 6), T(21, 22), T(23, 24), T(25, 26), T(41, 42), T(43, 44), T(45, 46)};
|
||||
T res[] = {T(10, 20), T(30, 40)};
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
|
||||
// check equal bounds
|
||||
{
|
||||
T arr1[] = {T(20, 30)};
|
||||
T arr2[] = {T(10, 20), T(30, 40)};
|
||||
T res[] = {T(20, 30)};
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
{
|
||||
T arr1[] = {T(10, 20), T(30, 40)};
|
||||
T arr2[] = {T(20, 30)};
|
||||
T res[] = {T(10, 20), T(30, 40)};
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
|
||||
// check large overlap interval
|
||||
{
|
||||
T arr1[] = {T(10, 20), T(30, 40), T(50, 60)};
|
||||
T arr2[] = {T(0, 100)};
|
||||
T res[] = {T(0, 100)};
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
{
|
||||
T arr1[] = {T(0, 100)};
|
||||
T arr2[] = {T(10, 20), T(30, 40), T(50, 60)};
|
||||
T res[] = {T(0, 100)};
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
|
||||
// check splice overlapped
|
||||
{
|
||||
T arr1[] = {T(10, 20), T(30, 40)};
|
||||
T arr2[] = {T(5, 15), T(35, 45)};
|
||||
T res[] = {T(5, 20), T(30, 45)};
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
{
|
||||
T arr1[] = {T(10, 20), T(30, 40)};
|
||||
T arr2[] = {T(1, 2), T(3, 4), T(5, 15), T(17, 25), T(26, 27), T(28, 32), T(38, 45), T(46, 50)};
|
||||
T res[] = {T(5, 25), T(28, 45)};
|
||||
CheckAccumulateIntervals(idTest, arr1, arr2, res);
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(Map_EmplaceOrAssign)
|
||||
{
|
||||
{
|
||||
std::map<std::string, std::string, std::less<>> theMap;
|
||||
|
||||
std::string_view key = "key";
|
||||
std::string_view val1 = "value";
|
||||
TEST(EmplaceOrAssign(theMap, key, val1).second, ());
|
||||
TEST_EQUAL(theMap.find(key)->second, val1, ());
|
||||
|
||||
std::string_view val2 = "some_long_value";
|
||||
TEST(!EmplaceOrAssign(theMap, key, val2).second, ());
|
||||
TEST_EQUAL(theMap.find(key)->second, val2, ());
|
||||
|
||||
std::string_view val3 = "some_other_long_value";
|
||||
TEST(!EmplaceOrAssign(theMap, key, std::string(val3)).second, ());
|
||||
TEST_EQUAL(theMap.find(key)->second, val3, ());
|
||||
}
|
||||
|
||||
{
|
||||
class Obj
|
||||
{
|
||||
int m_v;
|
||||
|
||||
Obj(Obj const &) = delete;
|
||||
Obj & operator=(Obj const &) = delete;
|
||||
|
||||
public:
|
||||
Obj(int v) : m_v(v) {}
|
||||
Obj(Obj &&) = default;
|
||||
Obj & operator=(Obj &&) = default;
|
||||
|
||||
bool operator==(Obj const & o) const { return m_v == o.m_v; }
|
||||
bool operator<(Obj const & o) const { return m_v < o.m_v; }
|
||||
};
|
||||
|
||||
std::map<Obj, Obj> theMap;
|
||||
|
||||
TEST(EmplaceOrAssign(theMap, Obj(1), Obj(2)).second, ());
|
||||
TEST(!EmplaceOrAssign(theMap, Obj(1), Obj(3)).second, ());
|
||||
TEST(theMap.find(Obj(1))->second == Obj(3), ());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace stl_helpers_tests
|
||||
1280
libs/base/base_tests/string_utils_test.cpp
Normal file
1280
libs/base/base_tests/string_utils_test.cpp
Normal file
File diff suppressed because it is too large
Load diff
91
libs/base/base_tests/suffix_array_tests.cpp
Normal file
91
libs/base/base_tests/suffix_array_tests.cpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/suffix_array.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace base;
|
||||
using namespace std;
|
||||
|
||||
#define TEST_STR_EQUAL(X, Y, msg) TEST_EQUAL(string(X), string(Y), msg)
|
||||
|
||||
namespace
|
||||
{
|
||||
UNIT_TEST(Skew_Smoke)
|
||||
{
|
||||
Skew(0, nullptr /* s */, nullptr /* sa */);
|
||||
}
|
||||
|
||||
UNIT_TEST(Skew_Simple)
|
||||
{
|
||||
{
|
||||
string const s;
|
||||
vector<size_t> pos;
|
||||
Skew(s, pos);
|
||||
TEST_EQUAL(pos.size(), s.size(), ());
|
||||
}
|
||||
|
||||
{
|
||||
string const s = "a";
|
||||
vector<size_t> pos;
|
||||
Skew(s, pos);
|
||||
TEST_EQUAL(pos.size(), s.size(), ());
|
||||
TEST_EQUAL(pos[0], 0, ());
|
||||
}
|
||||
|
||||
{
|
||||
string const s = "aaaa";
|
||||
vector<size_t> pos;
|
||||
Skew(s, pos);
|
||||
TEST_EQUAL(pos.size(), s.size(), ());
|
||||
TEST_EQUAL(pos[0], 3, ());
|
||||
TEST_EQUAL(pos[1], 2, ());
|
||||
TEST_EQUAL(pos[2], 1, ());
|
||||
TEST_EQUAL(pos[3], 0, ());
|
||||
}
|
||||
|
||||
for (size_t length = 0; length < 100; ++length)
|
||||
{
|
||||
string const s(length, 'a');
|
||||
vector<size_t> pos;
|
||||
Skew(s, pos);
|
||||
TEST_EQUAL(pos.size(), s.size(), ());
|
||||
for (size_t i = 0; i < pos.size(); ++i)
|
||||
TEST_EQUAL(pos[i], pos.size() - i - 1, ());
|
||||
}
|
||||
|
||||
for (size_t length = 0; length < 100; ++length)
|
||||
{
|
||||
string const s(length, '\0');
|
||||
vector<size_t> pos;
|
||||
Skew(s, pos);
|
||||
TEST_EQUAL(pos.size(), s.size(), ());
|
||||
for (size_t i = 0; i < pos.size(); ++i)
|
||||
TEST_EQUAL(pos[i], pos.size() - i - 1, ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(Skew_Classic)
|
||||
{
|
||||
char const * s = "mississippi";
|
||||
size_t const n = strlen(s);
|
||||
TEST_EQUAL(n, 11, ());
|
||||
|
||||
vector<size_t> pos(n);
|
||||
Skew(n, reinterpret_cast<uint8_t const *>(s), pos.data());
|
||||
|
||||
TEST_STR_EQUAL("i", s + pos[0], ());
|
||||
TEST_STR_EQUAL("ippi", s + pos[1], ());
|
||||
TEST_STR_EQUAL("issippi", s + pos[2], ());
|
||||
TEST_STR_EQUAL("ississippi", s + pos[3], ());
|
||||
TEST_STR_EQUAL("mississippi", s + pos[4], ());
|
||||
TEST_STR_EQUAL("pi", s + pos[5], ());
|
||||
TEST_STR_EQUAL("ppi", s + pos[6], ());
|
||||
TEST_STR_EQUAL("sippi", s + pos[7], ());
|
||||
TEST_STR_EQUAL("sissippi", s + pos[8], ());
|
||||
TEST_STR_EQUAL("ssippi", s + pos[9], ());
|
||||
TEST_STR_EQUAL("ssissippi", s + pos[10], ());
|
||||
}
|
||||
} // namespace
|
||||
488
libs/base/base_tests/sunrise_sunset_test.cpp
Normal file
488
libs/base/base_tests/sunrise_sunset_test.cpp
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/sunrise_sunset.hpp"
|
||||
|
||||
#include "base/timegm.hpp"
|
||||
|
||||
// Test site for sunrise and sunset is
|
||||
// http://voshod-solnca.ru/
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Moscow_April)
|
||||
{
|
||||
// Moscow (utc +3), date 2015/4/12:
|
||||
// Sunrise utc time: 2015/4/12,2:34
|
||||
// Sunset utc time: 2015/4/12,16:29
|
||||
double const lat = 55.7522222;
|
||||
double const lon = 37.6155556;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 4, 12, 2, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 4, 12, 2, 45, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 4, 12, 16, 15, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 4, 12, 16, 45, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Moscow_July)
|
||||
{
|
||||
// Moscow (utc +3), date 2015/7/13:
|
||||
// Sunrise utc time: 2015/7/13,1:04
|
||||
// Sunset utc time: 2015/7/13,18:09
|
||||
double const lat = 55.7522222;
|
||||
double const lon = 37.6155556;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 13, 0, 50, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 13, 1, 45, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 13, 18, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 13, 18, 30, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Moscow_September)
|
||||
{
|
||||
// Moscow (utc +3), date 2015/9/17:
|
||||
// Sunrise utc time: 2015/9/17,3:05
|
||||
// Sunset utc time: 2015/9/17,15:46
|
||||
double const lat = 55.7522222;
|
||||
double const lon = 37.6155556;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 9, 17, 2, 55, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 9, 17, 3, 15, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 9, 17, 15, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 9, 17, 16, 0, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Moscow_December)
|
||||
{
|
||||
// Moscow (utc +3), date 2015/12/25:
|
||||
// Sunrise utc time: 2015/12/25,06:00
|
||||
// Sunset utc time: 2015/12/25,13:01
|
||||
double const lat = 55.7522222;
|
||||
double const lon = 37.6155556;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 12, 25, 5, 55, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 12, 25, 6, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 12, 25, 12, 55, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 12, 25, 13, 10, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Moscow_NewYear)
|
||||
{
|
||||
// Moscow (utc +3), date 2016/1/1:
|
||||
// Sunrise utc time: 2016/1/1,6:1
|
||||
// Sunset utc time: 2016/1/1,13:7
|
||||
double const lat = 55.7522222;
|
||||
double const lon = 37.6155556;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2016, 1, 1, 5, 55, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2016, 1, 1, 6, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2016, 1, 1, 13, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2016, 1, 1, 13, 15, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Paris_NewYear)
|
||||
{
|
||||
// Paris (utc +1)
|
||||
// Sunrise utc time: 2016/1/1,7:45
|
||||
// Sunset utc time: 2016/1/1,16:04
|
||||
double const lat = 48.875649;
|
||||
double const lon = 2.344428;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2016, 1, 1, 7, 35, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2016, 1, 1, 7, 55, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2016, 1, 1, 16, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2016, 1, 1, 16, 10, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Honolulu_February)
|
||||
{
|
||||
// Honolulu (utc -10), date 2015/2/12:
|
||||
// Sunrise utc time: 2015/2/12,17:05
|
||||
// Sunset utc time: 2015/2/13,4:29
|
||||
double const lat = 21.307431;
|
||||
double const lon = -157.848568;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 2, 12, 17, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 2, 12, 17, 15, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 2, 13, 4, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 2, 13, 4, 35, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Honolulu_July)
|
||||
{
|
||||
// Honolulu (utc -10). For date 2015/7/13:
|
||||
// Sunrise utc time: 2015/7/13,15:58
|
||||
// Sunset utc time: 2015/7/14,5:18
|
||||
double const lat = 21.307431;
|
||||
double const lon = -157.848568;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 13, 15, 40, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 13, 16, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 14, 5, 5, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 14, 5, 25, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Honolulu_June)
|
||||
{
|
||||
// Honolulu (utc -10). For date 2015/6/22:
|
||||
// Sunrise utc time: 2015/6/22,15:51
|
||||
// Sunset utc time: 2015/6/23,5:17
|
||||
double const lat = 21.307431;
|
||||
double const lon = -157.848568;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 6, 22, 15, 40, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 6, 22, 16, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 6, 23, 5, 5, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 6, 23, 5, 25, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Honolulu_December)
|
||||
{
|
||||
// Honolulu (utc -10). For date 2015/12/23:
|
||||
// Sunrise utc time: 2015/12/23,17:06
|
||||
// Sunset utc time: 2015/12/24,3:56
|
||||
double const lat = 21.307431;
|
||||
double const lon = -157.848568;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 12, 23, 16, 40, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 12, 23, 17, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 12, 23, 3, 50, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 12, 23, 4, 5, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Melbourne_Ferbuary)
|
||||
{
|
||||
// Melbourne (utc +11). For date 2015/2/12:
|
||||
// Sunrise utc time: 2015/2/11,19:46
|
||||
// Sunset utc time: 2015/2/12,9:24
|
||||
double const lat = -37.829188;
|
||||
double const lon = 144.957976;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 2, 11, 19, 30, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 2, 11, 19, 50, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 2, 12, 9, 15, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 2, 12, 9, 30, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Melbourne_NewYear)
|
||||
{
|
||||
// Melbourne (utc +11). For date 2016/1/1:
|
||||
// Sunrise utc time: 2015/12/31,19:02
|
||||
// Sunset utc time: 2016/1/1,9:46
|
||||
double const lat = -37.829188;
|
||||
double const lon = 144.957976;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 12, 31, 18, 55, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 12, 31, 19, 5, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2016, 1, 1, 9, 40, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2016, 1, 1, 9, 55, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_GetDayTime_Melbourne_August)
|
||||
{
|
||||
// Melbourne (utc +11), 2015/8/12
|
||||
// prev sunset utc 2015/8/11,7:41
|
||||
// sunrise utc 2015/8/11,21:10
|
||||
// sunset utc 2015/8/12,7:42
|
||||
// next sunrise utc 2015/8/12,21:09
|
||||
double const lat = -37.829188;
|
||||
double const lon = 144.957976;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 8, 11, 7, 30, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 8, 11, 7, 50, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 8, 11, 21, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 8, 11, 21, 20, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 8, 12, 7, 35, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 8, 12, 7, 55, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 8, 12, 21, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 8, 12, 21, 15, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Wellington_October)
|
||||
{
|
||||
// Melbourne (utc +13). For date 2015/10/20:
|
||||
// Sunrise utc time: 2015/10/19,17:26
|
||||
// Sunset utc time: 2015/10/20,6:47
|
||||
double const lat = -41.287481;
|
||||
double const lon = 174.774189;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 10, 19, 17, 15, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 10, 19, 17, 35, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 10, 20, 6, 40, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 10, 20, 6, 55, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_BuenosAires_March)
|
||||
{
|
||||
// Buenos Aires (utc -3). For date 2015/3/8:
|
||||
// Sunrise utc time: 2015/3/8,9:48
|
||||
// Sunset utc time: 2015/3/8,22:23
|
||||
double const lat = -34.607639;
|
||||
double const lon = -58.438095;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 3, 8, 9, 40, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 3, 8, 10, 5, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 3, 8, 22, 20, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 3, 8, 22, 28, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Seattle_May)
|
||||
{
|
||||
// Seattle (utc -8). For date 2015/5/9:
|
||||
// Sunrise utc time: 2015/5/9,12:41
|
||||
// Sunset utc time: 2015/5/10,3:32
|
||||
double const lat = 47.597482;
|
||||
double const lon = -122.334590;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 5, 9, 12, 35, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 5, 9, 12, 45, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 5, 10, 3, 20, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 5, 10, 3, 40, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Reykjavik_May)
|
||||
{
|
||||
// Reykjavik (utc 0). For date 2015/5/9:
|
||||
// Sunrise utc time: 2015/5/9,4:34
|
||||
// Sunset utc time: 2015/5/9,22:15
|
||||
double const lat = 64.120467;
|
||||
double const lon = -21.809448;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 5, 9, 4, 30, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 5, 9, 4, 40, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 5, 9, 22, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 5, 9, 22, 20, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Reykjavik_June)
|
||||
{
|
||||
// Reykjavik (utc 0). For date 2015/6/22:
|
||||
// Sunrise utc time: 2015/6/22,2:56
|
||||
// Sunset utc time: 2015/6/23,0:04
|
||||
double const lat = 64.120467;
|
||||
double const lon = -21.809448;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 6, 22, 2, 45, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 6, 22, 3, 5, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 6, 23, 0, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 6, 23, 0, 7, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_CapeTown_November)
|
||||
{
|
||||
// Cape Town (utc +2). For date 2015/11/11:
|
||||
// Sunrise utc time: 2015/11/11,3:38
|
||||
// Sunset utc time: 2015/11/11,17:24
|
||||
double const lat = -33.929573;
|
||||
double const lon = 18.428439;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 11, 11, 3, 30, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 11, 11, 3, 45, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 11, 11, 17, 20, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 11, 11, 17, 30, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_CapeTown_March)
|
||||
{
|
||||
// Cape Town (utc +2). For date 2015/3/1:
|
||||
// Sunrise utc time: 2015/3/1,4:34
|
||||
// Sunset utc time: 2015/3/1,17:24
|
||||
double const lat = -33.929573;
|
||||
double const lon = 18.428439;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 3, 1, 4, 30, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 3, 1, 4, 40, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 3, 1, 17, 20, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 3, 1, 17, 30, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Tiksi_March)
|
||||
{
|
||||
// Russia, Siberia, Tiksi. For date 2015/3/1:
|
||||
// Sunrise utc time: 2015/2/28,23:04
|
||||
// Sunset utc time: 2015/3/1,8:12
|
||||
double const lat = 71.635604;
|
||||
double const lon = 128.882922;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 2, 28, 23, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 2, 28, 23, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 3, 1, 8, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 3, 1, 8, 15, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Tiksi_July)
|
||||
{
|
||||
// Russia, Siberia, Tiksi. For date 2015/7/1:
|
||||
// Polar day
|
||||
double const lat = 71.635604;
|
||||
double const lon = 128.882922;
|
||||
|
||||
TEST_EQUAL(DayTimeType::PolarDay, GetDayTime(base::TimeGM(2015, 7, 1, 0, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::PolarDay, GetDayTime(base::TimeGM(2015, 7, 1, 12, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::PolarDay, GetDayTime(base::TimeGM(2015, 7, 1, 23, 0, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Tiksi_December)
|
||||
{
|
||||
// Russia, Siberia, Tiksi. For date 2015/12/1:
|
||||
// Polar night
|
||||
double const lat = 71.635604;
|
||||
double const lon = 128.882922;
|
||||
|
||||
TEST_EQUAL(DayTimeType::PolarNight, GetDayTime(base::TimeGM(2015, 12, 1, 0, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::PolarNight, GetDayTime(base::TimeGM(2015, 12, 1, 12, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::PolarNight, GetDayTime(base::TimeGM(2015, 12, 1, 23, 0, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Norilsk_NewYear)
|
||||
{
|
||||
// Norilsk. For date 2016/1/1:
|
||||
// Polar night
|
||||
double const lat = 69.350000;
|
||||
double const lon = 88.180000;
|
||||
|
||||
TEST_EQUAL(DayTimeType::PolarNight, GetDayTime(base::TimeGM(2016, 1, 1, 0, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::PolarNight, GetDayTime(base::TimeGM(2016, 1, 1, 12, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::PolarNight, GetDayTime(base::TimeGM(2016, 1, 1, 23, 0, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Norilsk_August)
|
||||
{
|
||||
// Norilsk. For date 2015/6/22:
|
||||
// Polar day
|
||||
double const lat = 69.350000;
|
||||
double const lon = 88.180000;
|
||||
|
||||
TEST_EQUAL(DayTimeType::PolarDay, GetDayTime(base::TimeGM(2015, 6, 22, 0, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::PolarDay, GetDayTime(base::TimeGM(2015, 6, 22, 12, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::PolarDay, GetDayTime(base::TimeGM(2015, 6, 22, 23, 0, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Tokio_September)
|
||||
{
|
||||
// Tokio. For date 2015/9/12:
|
||||
// Sunrise utc time: 2015/9/11,20:22
|
||||
// Sunset utc time: 2015/9/12,8:56
|
||||
double const lat = 35.715791;
|
||||
double const lon = 139.743945;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 9, 12, 20, 20, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 9, 12, 20, 25, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 9, 12, 8, 50, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 9, 12, 9, 0, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Kabul_March)
|
||||
{
|
||||
// Kabul. For date 2015/3/20:
|
||||
// Sunrise utc time: 2015/3/20,01:29
|
||||
// Sunset utc time: 2015/3/20,13:35
|
||||
double const lat = 34.552312;
|
||||
double const lon = 69.170520;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 3, 20, 1, 25, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 3, 20, 1, 35, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 3, 20, 13, 30, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 3, 20, 13, 40, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Areora_January)
|
||||
{
|
||||
// Areora. For date 2016/1/1:
|
||||
// Sunrise utc time: 2016/1/1,15:57
|
||||
// Sunset utc time: 2016/1/2,5:16
|
||||
double const lat = -20.003751;
|
||||
double const lon = -158.114640;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2016, 1, 1, 15, 50, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2016, 1, 1, 16, 5, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2016, 1, 2, 5, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2016, 1, 2, 5, 20, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Lorino_February)
|
||||
{
|
||||
// Lorino (utc +12). For date 2016/2/2:
|
||||
// Sunrise utc time: 2016/2/2,20:17
|
||||
// Sunset utc time: 2016/2/3,3:10
|
||||
double const lat = 65.499550;
|
||||
double const lon = -171.715726;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2016, 2, 2, 20, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2016, 2, 2, 20, 20, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2016, 2, 3, 3, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2016, 2, 3, 3, 20, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Anadyr_December)
|
||||
{
|
||||
// Anadyr. For date 2015/12/25:
|
||||
// Sunrise utc time: 2015/12/24,22:17
|
||||
// Sunset utc time: 2015/12/25,02:03
|
||||
double const lat = 64.722245;
|
||||
double const lon = 177.499123;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 12, 24, 22, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 12, 24, 22, 20, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 12, 25, 2, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 12, 25, 2, 5, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Nikolski_December)
|
||||
{
|
||||
// Nikolski. For date 2015/12/25:
|
||||
// Sunrise utc time: 2015/12/25,19:29
|
||||
// Sunset utc time: 2015/12/26,3:04
|
||||
double const lat = 52.933280;
|
||||
double const lon = -168.864102;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 12, 25, 19, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 12, 25, 19, 35, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 12, 26, 3, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 12, 26, 3, 10, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Kiribati_July)
|
||||
{
|
||||
// Kiribati. For date 2015/7/1:
|
||||
// Sunrise utc time: 2015/7/1,16:28
|
||||
// Sunset utc time: 2015/7/2,4:41
|
||||
double const lat = 1.928797;
|
||||
double const lon = -157.494678;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 1, 16, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 1, 16, 35, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 2, 4, 0, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 2, 4, 50, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_Kiribati_July_2)
|
||||
{
|
||||
// Kiribati. For date 2015/7/1:
|
||||
// Sunrise utc time: 2015/7/1,16:28
|
||||
// Sunset utc time: 2015/7/2,4:41
|
||||
// Next sunrise utc time: 2015/7/2,16:28
|
||||
// Next sunset utc time: 2015/7/3,4:42
|
||||
double const lat = 1.928797;
|
||||
double const lon = -157.494678;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 1, 16, 20, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 1, 16, 35, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 2, 4, 35, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 2, 4, 50, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 2, 16, 20, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 2, 16, 35, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 3, 4, 35, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 3, 4, 50, 0), lat, lon), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(SunriseSunsetAlgorithm_London_July)
|
||||
{
|
||||
// London. For date 2015/7/1:
|
||||
// Sunrise utc time: 2015/7/1,3:47
|
||||
// Sunset utc time: 2015/7/1,20:21
|
||||
double const lat = 51.500000;
|
||||
double const lon = 0.120000;
|
||||
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 1, 2, 50, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 1, 16, 20, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Day, GetDayTime(base::TimeGM(2015, 7, 1, 20, 10, 0), lat, lon), ());
|
||||
TEST_EQUAL(DayTimeType::Night, GetDayTime(base::TimeGM(2015, 7, 1, 21, 15, 0), lat, lon), ());
|
||||
}
|
||||
127
libs/base/base_tests/thread_pool_computational_tests.cpp
Normal file
127
libs/base/base_tests/thread_pool_computational_tests.cpp
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/thread.hpp"
|
||||
#include "base/thread_pool_computational.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
size_t const kTimes = 500;
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(ThreadPoolComputational_SomeThreads)
|
||||
{
|
||||
for (size_t t = 0; t < kTimes; ++t)
|
||||
{
|
||||
size_t const threadCount = 4;
|
||||
std::atomic<size_t> counter{0};
|
||||
{
|
||||
base::ComputationalThreadPool threadPool(threadCount);
|
||||
for (size_t i = 0; i < threadCount; ++i)
|
||||
{
|
||||
threadPool.Submit([&]()
|
||||
{
|
||||
threads::Sleep(1);
|
||||
++counter;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_EQUAL(threadCount, counter, ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadPoolComputational_OneThread)
|
||||
{
|
||||
for (size_t t = 0; t < kTimes; ++t)
|
||||
{
|
||||
size_t const threadCount = 1;
|
||||
std::atomic<size_t> counter{0};
|
||||
{
|
||||
base::ComputationalThreadPool threadPool(threadCount);
|
||||
for (size_t i = 0; i < threadCount; ++i)
|
||||
{
|
||||
threadPool.Submit([&]()
|
||||
{
|
||||
threads::Sleep(1);
|
||||
++counter;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_EQUAL(threadCount, counter, ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadPoolComputational_ManyThread)
|
||||
{
|
||||
for (size_t t = 0; t < kTimes; ++t)
|
||||
{
|
||||
size_t threadCount = std::thread::hardware_concurrency();
|
||||
CHECK_NOT_EQUAL(threadCount, 0, ());
|
||||
threadCount *= 2;
|
||||
std::atomic<size_t> counter{0};
|
||||
{
|
||||
base::ComputationalThreadPool threadPool(threadCount);
|
||||
for (size_t i = 0; i < threadCount; ++i)
|
||||
{
|
||||
threadPool.Submit([&]()
|
||||
{
|
||||
threads::Sleep(1);
|
||||
++counter;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_EQUAL(threadCount, counter, ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadPoolComputational_ReturnValue)
|
||||
{
|
||||
for (size_t t = 0; t < kTimes; ++t)
|
||||
{
|
||||
size_t const threadCount = 4;
|
||||
base::ComputationalThreadPool threadPool(threadCount);
|
||||
std::vector<std::future<size_t>> futures;
|
||||
for (size_t i = 0; i < threadCount; ++i)
|
||||
{
|
||||
auto f = threadPool.Submit([=]()
|
||||
{
|
||||
threads::Sleep(1);
|
||||
return i;
|
||||
});
|
||||
|
||||
futures.push_back(std::move(f));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < threadCount; ++i)
|
||||
TEST_EQUAL(futures[i].get(), i, ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadPoolComputational_ManyTasks)
|
||||
{
|
||||
for (size_t t = 0; t < kTimes; ++t)
|
||||
{
|
||||
size_t const taskCount = 11;
|
||||
std::atomic<size_t> counter{0};
|
||||
{
|
||||
base::ComputationalThreadPool threadPool(4);
|
||||
for (size_t i = 0; i < taskCount; ++i)
|
||||
{
|
||||
threadPool.Submit([&]()
|
||||
{
|
||||
threads::Sleep(1);
|
||||
++counter;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_EQUAL(taskCount, counter, ());
|
||||
}
|
||||
}
|
||||
249
libs/base/base_tests/thread_pool_delayed_tests.cpp
Normal file
249
libs/base/base_tests/thread_pool_delayed_tests.cpp
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/thread_pool_delayed.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
|
||||
namespace thread_pool_delayed_tests
|
||||
{
|
||||
using namespace base;
|
||||
using namespace std::chrono;
|
||||
using namespace std;
|
||||
|
||||
UNIT_TEST(ThreadPoolDelayed_Smoke)
|
||||
{
|
||||
{
|
||||
DelayedThreadPool thread;
|
||||
}
|
||||
|
||||
{
|
||||
DelayedThreadPool thread;
|
||||
TEST(thread.Shutdown(DelayedThreadPool::Exit::SkipPending), ());
|
||||
}
|
||||
|
||||
{
|
||||
DelayedThreadPool thread;
|
||||
TEST(thread.Shutdown(DelayedThreadPool::Exit::ExecPending), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadPoolDelayed_SimpleSync)
|
||||
{
|
||||
int value = 0;
|
||||
|
||||
mutex mu;
|
||||
condition_variable cv;
|
||||
bool done = false;
|
||||
|
||||
DelayedThreadPool thread;
|
||||
auto pushResult = thread.Push([&value]() { ++value; });
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_NOT_EQUAL(pushResult.m_id, DelayedThreadPool::kNoId, ());
|
||||
|
||||
pushResult = thread.Push([&value]() { value *= 2; });
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_NOT_EQUAL(pushResult.m_id, DelayedThreadPool::kNoId, ());
|
||||
|
||||
pushResult = thread.Push([&value]() { value = value * value * value; });
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_NOT_EQUAL(pushResult.m_id, DelayedThreadPool::kNoId, ());
|
||||
|
||||
pushResult = thread.Push([&]()
|
||||
{
|
||||
lock_guard<mutex> lk(mu);
|
||||
done = true;
|
||||
cv.notify_one();
|
||||
});
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_NOT_EQUAL(pushResult.m_id, DelayedThreadPool::kNoId, ());
|
||||
|
||||
{
|
||||
unique_lock<mutex> lk(mu);
|
||||
cv.wait(lk, [&done]() { return done; });
|
||||
}
|
||||
|
||||
TEST_EQUAL(value, 8, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadPoolDelayed_SimpleFlush)
|
||||
{
|
||||
int value = 0;
|
||||
{
|
||||
DelayedThreadPool thread;
|
||||
auto pushResult = thread.Push([&value]() { ++value; });
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_NOT_EQUAL(pushResult.m_id, DelayedThreadPool::kNoId, ());
|
||||
|
||||
pushResult = thread.Push([&value]()
|
||||
{
|
||||
for (int i = 0; i < 10; ++i)
|
||||
value *= 2;
|
||||
});
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_NOT_EQUAL(pushResult.m_id, DelayedThreadPool::kNoId, ());
|
||||
|
||||
TEST(thread.Shutdown(DelayedThreadPool::Exit::ExecPending), ());
|
||||
}
|
||||
TEST_EQUAL(value, 1024, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadPoolDelayed_PushFromPendingTask)
|
||||
{
|
||||
// promise - future pair is used as a socketpair here to pass a
|
||||
// signal from the main thread to the worker thread.
|
||||
promise<void> p;
|
||||
auto f = p.get_future();
|
||||
|
||||
DelayedThreadPool thread;
|
||||
auto const pushResult = thread.Push([&f, &thread]()
|
||||
{
|
||||
f.get();
|
||||
auto const innerPushResult = thread.Push([]() { TEST(false, ("This task should not be executed")); });
|
||||
TEST(!innerPushResult.m_isSuccess, ());
|
||||
TEST_EQUAL(innerPushResult.m_id, DelayedThreadPool::kNoId, ());
|
||||
});
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_NOT_EQUAL(pushResult.m_id, DelayedThreadPool::kNoId, ());
|
||||
|
||||
thread.Shutdown(DelayedThreadPool::Exit::ExecPending);
|
||||
p.set_value();
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadPoolDelayed_DelayedAndImmediateTasks)
|
||||
{
|
||||
int const kNumTasks = 100;
|
||||
|
||||
struct DelayedTaskEntry
|
||||
{
|
||||
DelayedThreadPool::TimePoint m_start = {};
|
||||
DelayedThreadPool::Duration m_delay = {};
|
||||
DelayedThreadPool::TimePoint m_end = {};
|
||||
};
|
||||
|
||||
vector<DelayedTaskEntry> delayedEntries(kNumTasks);
|
||||
vector<DelayedThreadPool::TimePoint> immediateEntries(kNumTasks);
|
||||
|
||||
{
|
||||
DelayedThreadPool thread;
|
||||
|
||||
for (int i = kNumTasks - 1; i >= 0; --i)
|
||||
{
|
||||
auto & entry = delayedEntries[i];
|
||||
entry.m_start = thread.Now();
|
||||
entry.m_delay = milliseconds(i + 1);
|
||||
|
||||
auto const pushResult = thread.PushDelayed(entry.m_delay, [&]() { entry.m_end = thread.Now(); });
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_NOT_EQUAL(pushResult.m_id, DelayedThreadPool::kNoId, ());
|
||||
}
|
||||
|
||||
for (int i = 0; i < kNumTasks; ++i)
|
||||
{
|
||||
auto & entry = immediateEntries[i];
|
||||
auto const pushResult = thread.Push([&]() { entry = thread.Now(); });
|
||||
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_NOT_EQUAL(pushResult.m_id, DelayedThreadPool::kNoId, ());
|
||||
}
|
||||
|
||||
thread.Shutdown(DelayedThreadPool::Exit::ExecPending);
|
||||
}
|
||||
|
||||
for (int i = 0; i < kNumTasks; ++i)
|
||||
{
|
||||
auto const & entry = delayedEntries[i];
|
||||
TEST(entry.m_end >= entry.m_start + entry.m_delay, ("Failed delay for the delayed task", i));
|
||||
}
|
||||
|
||||
for (int i = 1; i < kNumTasks; ++i)
|
||||
TEST(immediateEntries[i] >= immediateEntries[i - 1], ("Failed delay for the immediate task", i));
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadPoolDelayed_CancelImmediate)
|
||||
{
|
||||
int value = 0;
|
||||
|
||||
{
|
||||
TaskLoop::TaskId cancelTaskId;
|
||||
DelayedThreadPool thread;
|
||||
{
|
||||
auto const pushResult = thread.Push([&value]()
|
||||
{
|
||||
++value;
|
||||
testing::Wait();
|
||||
});
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_EQUAL(pushResult.m_id, DelayedThreadPool::kImmediateMinId, ());
|
||||
}
|
||||
|
||||
{
|
||||
auto const pushResult = thread.Push([&]() { value += 1023; });
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_EQUAL(pushResult.m_id, DelayedThreadPool::kImmediateMinId + 1, ());
|
||||
|
||||
cancelTaskId = pushResult.m_id;
|
||||
}
|
||||
|
||||
{
|
||||
auto const pushResult = thread.Push([&]() { ++value; });
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_EQUAL(pushResult.m_id, DelayedThreadPool::kImmediateMinId + 2, ());
|
||||
}
|
||||
|
||||
TEST(thread.Cancel(cancelTaskId), ());
|
||||
|
||||
testing::Notify();
|
||||
|
||||
thread.Shutdown(DelayedThreadPool::Exit::ExecPending);
|
||||
}
|
||||
|
||||
TEST_EQUAL(value, 2, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadPoolDelayed_CancelDelayed)
|
||||
{
|
||||
int value = 0;
|
||||
|
||||
{
|
||||
TaskLoop::TaskId cancelTaskId;
|
||||
DelayedThreadPool thread;
|
||||
{
|
||||
auto const pushResult = thread.Push([]() { testing::Wait(); });
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_EQUAL(pushResult.m_id, DelayedThreadPool::kImmediateMinId, ());
|
||||
}
|
||||
|
||||
{
|
||||
auto const pushResult = thread.PushDelayed(milliseconds(1), [&value]() { ++value; });
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_EQUAL(pushResult.m_id, DelayedThreadPool::kDelayedMinId, ());
|
||||
}
|
||||
|
||||
{
|
||||
auto const pushResult = thread.PushDelayed(milliseconds(2), [&]() { value += 1023; });
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_EQUAL(pushResult.m_id, DelayedThreadPool::kDelayedMinId + 1, ());
|
||||
|
||||
cancelTaskId = pushResult.m_id;
|
||||
}
|
||||
|
||||
{
|
||||
auto const pushResult = thread.PushDelayed(milliseconds(3), [&value]() { ++value; });
|
||||
TEST(pushResult.m_isSuccess, ());
|
||||
TEST_EQUAL(pushResult.m_id, DelayedThreadPool::kDelayedMinId + 2, ());
|
||||
}
|
||||
|
||||
TEST(thread.Cancel(cancelTaskId), ());
|
||||
|
||||
testing::Notify();
|
||||
|
||||
thread.Shutdown(DelayedThreadPool::Exit::ExecPending);
|
||||
}
|
||||
|
||||
TEST_EQUAL(value, 2, ());
|
||||
}
|
||||
|
||||
} // namespace thread_pool_delayed_tests
|
||||
106
libs/base/base_tests/thread_pool_tests.cpp
Normal file
106
libs/base/base_tests/thread_pool_tests.cpp
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/thread.hpp"
|
||||
#include "base/thread_pool.hpp"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
|
||||
namespace
|
||||
{
|
||||
int const TASK_COUNT = 10;
|
||||
class CanceledTask : public threads::IRoutine
|
||||
{
|
||||
public:
|
||||
CanceledTask() { Cancel(); }
|
||||
|
||||
virtual void Do() { TEST_EQUAL(true, false, ()); }
|
||||
};
|
||||
struct Condition
|
||||
{
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
void JoinFinishFunction(threads::IRoutine * routine, int & finishCounter, Condition & cond)
|
||||
{
|
||||
cond.m.lock();
|
||||
finishCounter++;
|
||||
cond.m.unlock();
|
||||
|
||||
delete routine;
|
||||
cond.cv.notify_one();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(ThreadPool_CanceledTaskTest)
|
||||
{
|
||||
int finishCounter = 0;
|
||||
Condition cond;
|
||||
base::ThreadPool pool(4,
|
||||
std::bind(&JoinFinishFunction, std::placeholders::_1, std::ref(finishCounter), std::ref(cond)));
|
||||
|
||||
for (int i = 0; i < TASK_COUNT; ++i)
|
||||
pool.PushBack(new CanceledTask());
|
||||
|
||||
pool.Stop();
|
||||
|
||||
TEST_EQUAL(finishCounter, TASK_COUNT, ());
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class CancelTestTask : public threads::IRoutine
|
||||
{
|
||||
public:
|
||||
explicit CancelTestTask(bool isWaitDoCall) : m_waitDoCall(isWaitDoCall), m_doCalled(false) {}
|
||||
|
||||
~CancelTestTask() { TEST_EQUAL(m_waitDoCall, m_doCalled, ()); }
|
||||
|
||||
virtual void Do()
|
||||
{
|
||||
TEST_EQUAL(m_waitDoCall, true, ());
|
||||
m_doCalled = true;
|
||||
threads::Sleep(100);
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_waitDoCall;
|
||||
bool m_doCalled;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(ThreadPool_ExecutionTaskTest)
|
||||
{
|
||||
int finishCounter = 0;
|
||||
Condition cond;
|
||||
base::ThreadPool pool(4,
|
||||
std::bind(&JoinFinishFunction, std::placeholders::_1, std::ref(finishCounter), std::ref(cond)));
|
||||
|
||||
for (int i = 0; i < TASK_COUNT - 1; ++i)
|
||||
pool.PushBack(new CancelTestTask(true));
|
||||
|
||||
// CancelTastTask::Do method should not be called for last task
|
||||
auto * p = new CancelTestTask(false);
|
||||
pool.PushBack(p);
|
||||
p->Cancel();
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::unique_lock lock(cond.m);
|
||||
if (finishCounter == TASK_COUNT)
|
||||
break;
|
||||
cond.cv.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadPool_EmptyTest)
|
||||
{
|
||||
int finishCouter = 0;
|
||||
Condition cond;
|
||||
base::ThreadPool pool(4,
|
||||
std::bind(&JoinFinishFunction, std::placeholders::_1, std::ref(finishCouter), std::ref(cond)));
|
||||
|
||||
threads::Sleep(100);
|
||||
pool.Stop();
|
||||
}
|
||||
92
libs/base/base_tests/thread_safe_queue_tests.cpp
Normal file
92
libs/base/base_tests/thread_safe_queue_tests.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/thread_pool_delayed.hpp"
|
||||
#include "base/thread_safe_queue.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
UNIT_TEST(ThreadSafeQueue_ThreadSafeQueue)
|
||||
{
|
||||
threads::ThreadSafeQueue<size_t> queue;
|
||||
|
||||
TEST(queue.Empty(), ());
|
||||
TEST_EQUAL(queue.Size(), 0, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadSafeQueue_Push)
|
||||
{
|
||||
size_t const kSize = 100;
|
||||
threads::ThreadSafeQueue<size_t> queue;
|
||||
base::DelayedThreadPool pool(2, base::DelayedThreadPool::Exit::ExecPending);
|
||||
for (size_t i = 0; i < kSize; ++i)
|
||||
pool.Push([&, i]() { queue.Push(i); });
|
||||
|
||||
pool.ShutdownAndJoin();
|
||||
|
||||
TEST_EQUAL(queue.Size(), kSize, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadSafeQueue_WaitAndPop)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
threads::ThreadSafeQueue<size_t> queue;
|
||||
size_t const value = 101;
|
||||
size_t result;
|
||||
auto thread = std::thread([&]()
|
||||
{
|
||||
std::this_thread::sleep_for(10ms);
|
||||
queue.Push(value);
|
||||
});
|
||||
|
||||
queue.WaitAndPop(result);
|
||||
|
||||
TEST_EQUAL(result, value, ());
|
||||
|
||||
thread.join();
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadSafeQueue_TryPop)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
threads::ThreadSafeQueue<size_t> queue;
|
||||
size_t const value = 101;
|
||||
size_t result;
|
||||
auto thread = std::thread([&]()
|
||||
{
|
||||
std::this_thread::sleep_for(10ms);
|
||||
queue.Push(value);
|
||||
});
|
||||
|
||||
TEST(!queue.TryPop(result), ());
|
||||
thread.join();
|
||||
|
||||
TEST(queue.TryPop(result), ());
|
||||
TEST_EQUAL(result, value, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(ThreadSafeQueue_ExampleWithDataWrapper)
|
||||
{
|
||||
size_t const kSize = 100000;
|
||||
threads::ThreadSafeQueue<std::optional<size_t>> queue;
|
||||
|
||||
auto thread = std::thread([&]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
std::optional<size_t> dw;
|
||||
queue.WaitAndPop(dw);
|
||||
if (!dw.has_value())
|
||||
return;
|
||||
|
||||
ASSERT_LESS_OR_EQUAL(*dw, kSize, ());
|
||||
}
|
||||
});
|
||||
|
||||
base::DelayedThreadPool pool(4, base::DelayedThreadPool::Exit::ExecPending);
|
||||
for (size_t i = 0; i < kSize; ++i)
|
||||
pool.Push([&, i]() { queue.Push(i); });
|
||||
pool.Push([&]() { queue.Push({}); });
|
||||
|
||||
thread.join();
|
||||
}
|
||||
77
libs/base/base_tests/threaded_list_test.cpp
Normal file
77
libs/base/base_tests/threaded_list_test.cpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
#include "base/thread.hpp"
|
||||
#include "base/threaded_list.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
struct ThreadedListProcessor : public threads::IRoutine
|
||||
{
|
||||
ThreadedList<int> & m_p;
|
||||
std::mutex & m_resMutex;
|
||||
std::list<int> & m_res;
|
||||
int m_id;
|
||||
|
||||
ThreadedListProcessor(ThreadedList<int> & p, std::mutex & resMutex, std::list<int> & res, int id)
|
||||
: m_p(p)
|
||||
, m_resMutex(resMutex)
|
||||
, m_res(res)
|
||||
, m_id(id)
|
||||
{}
|
||||
|
||||
virtual void Do()
|
||||
{
|
||||
while (!m_p.IsCancelled())
|
||||
{
|
||||
int res = m_p.Front(true /* doPop */);
|
||||
{
|
||||
std::lock_guard<std::mutex> resGuard(m_resMutex);
|
||||
m_res.push_back(res);
|
||||
}
|
||||
LOG(LINFO, (m_id, " thread got ", res));
|
||||
threads::Sleep(10);
|
||||
}
|
||||
LOG(LINFO, (m_id, " thread is cancelled"));
|
||||
}
|
||||
};
|
||||
|
||||
UNIT_TEST(ThreadedList)
|
||||
{
|
||||
std::mutex resMutex;
|
||||
std::list<int> res;
|
||||
|
||||
ThreadedList<int> p;
|
||||
|
||||
threads::Thread t0;
|
||||
t0.Create(std::make_unique<ThreadedListProcessor>(p, resMutex, res, 0));
|
||||
|
||||
threads::Thread t1;
|
||||
t1.Create(std::make_unique<ThreadedListProcessor>(p, resMutex, res, 1));
|
||||
|
||||
threads::Thread t2;
|
||||
t2.Create(std::make_unique<ThreadedListProcessor>(p, resMutex, res, 2));
|
||||
|
||||
p.PushBack(0);
|
||||
threads::Sleep(200);
|
||||
|
||||
p.PushBack(1);
|
||||
threads::Sleep(200);
|
||||
|
||||
p.PushBack(2);
|
||||
threads::Sleep(200);
|
||||
|
||||
p.Cancel();
|
||||
|
||||
t0.Join();
|
||||
t1.Join();
|
||||
t2.Join();
|
||||
|
||||
TEST_EQUAL(res.front(), 0, ());
|
||||
res.pop_front();
|
||||
TEST_EQUAL(res.front(), 1, ());
|
||||
res.pop_front();
|
||||
TEST_EQUAL(res.front(), 2, ());
|
||||
res.pop_front();
|
||||
}
|
||||
116
libs/base/base_tests/threads_test.cpp
Normal file
116
libs/base/base_tests/threads_test.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/thread.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
using Vector = std::vector<int>;
|
||||
|
||||
static size_t summ = 0;
|
||||
static size_t checkSumm = 0;
|
||||
static size_t const MAX_COUNT = 1000000;
|
||||
|
||||
struct GeneratorThread : public threads::IRoutine
|
||||
{
|
||||
explicit GeneratorThread(Vector & vec) : m_vec(vec) {}
|
||||
|
||||
virtual void Do()
|
||||
{
|
||||
for (size_t i = 0; i < MAX_COUNT; ++i)
|
||||
{
|
||||
m_vec.push_back(static_cast<int>(i));
|
||||
summ += i;
|
||||
}
|
||||
}
|
||||
Vector & m_vec;
|
||||
};
|
||||
|
||||
struct ReaderThread : public threads::IRoutine
|
||||
{
|
||||
explicit ReaderThread(Vector & vec) : m_vec(vec) {}
|
||||
|
||||
virtual void Do()
|
||||
{
|
||||
for (size_t i = 0; i < m_vec.size(); ++i)
|
||||
checkSumm += m_vec[i];
|
||||
}
|
||||
Vector & m_vec;
|
||||
};
|
||||
|
||||
UNIT_TEST(Simple_Threads)
|
||||
{
|
||||
Vector vec;
|
||||
|
||||
threads::Thread reader;
|
||||
bool ok = reader.Create(std::make_unique<GeneratorThread>(vec));
|
||||
TEST(ok, ("Create Generator thread"));
|
||||
|
||||
reader.Join();
|
||||
|
||||
threads::Thread writer;
|
||||
ok = writer.Create(std::make_unique<ReaderThread>(vec));
|
||||
TEST(ok, ("Create Reader thread"));
|
||||
|
||||
writer.Join();
|
||||
|
||||
TEST_EQUAL(vec.size(), MAX_COUNT, ("vector size"));
|
||||
TEST_EQUAL(summ, checkSumm, ("check summ"));
|
||||
}
|
||||
|
||||
class SomeClass
|
||||
{
|
||||
DISALLOW_COPY(SomeClass);
|
||||
|
||||
public:
|
||||
SomeClass() {}
|
||||
void Increment(int * a, int b) { *a = *a + b; }
|
||||
};
|
||||
|
||||
static void Increment(int * a, int b)
|
||||
{
|
||||
*a = *a + b;
|
||||
}
|
||||
|
||||
UNIT_TEST(SimpleThreadTest1)
|
||||
{
|
||||
int a = 0;
|
||||
|
||||
auto fn = [&a]() { a = 1; };
|
||||
|
||||
threads::SimpleThread t(fn);
|
||||
t.join();
|
||||
|
||||
TEST_EQUAL(a, 1, ("test a"));
|
||||
}
|
||||
|
||||
UNIT_TEST(SimpleThreadTest2)
|
||||
{
|
||||
int a = 0;
|
||||
|
||||
threads::SimpleThread t([&a]() { a = 1; });
|
||||
t.join();
|
||||
|
||||
TEST_EQUAL(a, 1, ("test a"));
|
||||
}
|
||||
|
||||
UNIT_TEST(SimpleThreadTest3)
|
||||
{
|
||||
int a = 0;
|
||||
|
||||
SomeClass instance;
|
||||
threads::SimpleThread t(&SomeClass::Increment, &instance, &a, 1);
|
||||
t.join();
|
||||
|
||||
TEST_EQUAL(a, 1, ("test a"));
|
||||
}
|
||||
|
||||
UNIT_TEST(SimpleThreadTest4)
|
||||
{
|
||||
int a = 0;
|
||||
|
||||
threads::SimpleThread t(&Increment, &a, 1);
|
||||
t.join();
|
||||
|
||||
TEST_EQUAL(a, 1, ("test a"));
|
||||
}
|
||||
31
libs/base/base_tests/timegm_test.cpp
Normal file
31
libs/base/base_tests/timegm_test.cpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
#include "base/timegm.hpp"
|
||||
|
||||
UNIT_TEST(TimegmTest)
|
||||
{
|
||||
std::tm tm1{};
|
||||
std::tm tm2{};
|
||||
|
||||
TEST(strptime("2016-05-17 07:10", "%Y-%m-%d %H:%M", &tm1), ());
|
||||
TEST(strptime("2016-05-17 07:10", "%Y-%m-%d %H:%M", &tm2), ());
|
||||
TEST_EQUAL(timegm(&tm1), base::TimeGM(tm2), ());
|
||||
TEST_EQUAL(timegm(&tm1), base::TimeGM(2016, 5, 17, 7, 10, 0), ());
|
||||
|
||||
TEST(strptime("2016-03-12 11:10", "%Y-%m-%d %H:%M", &tm1), ());
|
||||
TEST(strptime("2016-03-12 11:10", "%Y-%m-%d %H:%M", &tm2), ());
|
||||
TEST_EQUAL(timegm(&tm1), base::TimeGM(tm2), ());
|
||||
TEST_EQUAL(timegm(&tm1), base::TimeGM(2016, 3, 12, 11, 10, 0), ());
|
||||
|
||||
TEST(strptime("1970-01-01 00:00", "%Y-%m-%d %H:%M", &tm1), ());
|
||||
TEST(strptime("1970-01-01 00:00", "%Y-%m-%d %H:%M", &tm2), ());
|
||||
TEST_EQUAL(timegm(&tm1), base::TimeGM(tm2), ());
|
||||
TEST_EQUAL(timegm(&tm1), base::TimeGM(1970, 1, 1, 0, 0, 0), ());
|
||||
|
||||
TEST(strptime("2012-12-02 21:08:34", "%Y-%m-%d %H:%M:%S", &tm1), ());
|
||||
TEST(strptime("2012-12-02 21:08:34", "%Y-%m-%d %H:%M:%S", &tm2), ());
|
||||
TEST_EQUAL(timegm(&tm1), base::TimeGM(tm2), ());
|
||||
TEST_EQUAL(timegm(&tm1), base::TimeGM(2012, 12, 2, 21, 8, 34), ());
|
||||
}
|
||||
75
libs/base/base_tests/timer_test.cpp
Normal file
75
libs/base/base_tests/timer_test.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
#include "base/timer.hpp"
|
||||
|
||||
UNIT_TEST(Timer_Seconds)
|
||||
{
|
||||
base::Timer timer;
|
||||
|
||||
double t1 = timer.ElapsedSeconds();
|
||||
double s = 0.0;
|
||||
for (int i = 0; i < 10000000; ++i)
|
||||
s += i * 0.01;
|
||||
double t2 = timer.ElapsedSeconds();
|
||||
|
||||
TEST_NOT_EQUAL(s, 0.0, ("Fictive, to prevent loop optimization"));
|
||||
TEST_NOT_EQUAL(t1, t2, ("Timer values should not be equal"));
|
||||
}
|
||||
|
||||
UNIT_TEST(Timer_CurrentStringTime)
|
||||
{
|
||||
LOG(LINFO, (base::FormatCurrentTime()));
|
||||
}
|
||||
|
||||
UNIT_TEST(Timer_TimestampConversion)
|
||||
{
|
||||
using namespace base;
|
||||
|
||||
TEST_EQUAL(TimestampToString(0), "1970-01-01T00:00:00Z", ());
|
||||
TEST_EQUAL(TimestampToString(1354482514), "2012-12-02T21:08:34Z", ());
|
||||
|
||||
TEST_EQUAL(StringToTimestamp("1970-01-01T00:00:00Z"), 0, ());
|
||||
TEST_EQUAL(StringToTimestamp("1970-01-01T00:00:00.1Z"), 0, ());
|
||||
TEST_EQUAL(StringToTimestamp("1970-01-01T00:00:00.12Z"), 0, ());
|
||||
TEST_EQUAL(StringToTimestamp("1970-01-01T00:00:00.123Z"), 0, ());
|
||||
TEST_EQUAL(StringToTimestamp("1970-01-01T00:00:00.000009Z"), 0, ());
|
||||
TEST_EQUAL(StringToTimestamp("2012-12-02T21:08:34"), 1354482514, ());
|
||||
TEST_EQUAL(StringToTimestamp("2012-12-02T21:08:34Z"), 1354482514, ());
|
||||
TEST_EQUAL(StringToTimestamp("2012-12-03T00:38:34+03:30"), 1354482514, ());
|
||||
TEST_EQUAL(StringToTimestamp("2012-12-02T11:08:34-10:00"), 1354482514, ());
|
||||
TEST_EQUAL(StringToTimestamp("2014-09-30T23:59:59+23:59"), 1412035259, ());
|
||||
|
||||
time_t const now = time(0);
|
||||
TEST_EQUAL(now, StringToTimestamp(TimestampToString(now)), ());
|
||||
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("asd23423adsfbhj657"), ());
|
||||
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2012-aa-02T21:08:34Z"), ());
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2012-12-0ZT21:08:34Z"), ());
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2012:12:02T21-08-34Z"), ());
|
||||
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2012-"), ());
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2012-12-02"), ());
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("1970-01-01T"), ());
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("1970-01-01T21:"), ());
|
||||
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2000-00-02T11:08:34-10:00"), ());
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2000-13-02T11:08:34-10:00"), ());
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2000-01-00T11:08:34-10:00"), ());
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2000-01-32T11:08:34-10:00"), ());
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2100-01--1T11:08:34-10:00"), ());
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2100--1-02T11:08:34-10:00"), ());
|
||||
TEST_EQUAL(INVALID_TIME_STAMP, StringToTimestamp("2012-12-02T11:08:34-25:88"), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(Timer_GenerateYYMMDD)
|
||||
{
|
||||
TEST_EQUAL(base::GenerateYYMMDD(116, 0, 26), 160126, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(Timer_TimeTConversion)
|
||||
{
|
||||
auto const now = ::time(nullptr);
|
||||
TEST_EQUAL(base::SecondsSinceEpochToTimeT(base::TimeTToSecondsSinceEpoch(now)), now, ());
|
||||
}
|
||||
90
libs/base/base_tests/uni_string_dfa_test.cpp
Normal file
90
libs/base/base_tests/uni_string_dfa_test.cpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/dfa_helpers.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
#include "base/uni_string_dfa.hpp"
|
||||
|
||||
using namespace strings;
|
||||
|
||||
namespace
|
||||
{
|
||||
UNIT_TEST(UniStringDFA_Smoke)
|
||||
{
|
||||
{
|
||||
UniStringDFA dfa("");
|
||||
|
||||
auto it = dfa.Begin();
|
||||
TEST(it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
|
||||
DFAMove(it, "a");
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(it.Rejects(), ());
|
||||
}
|
||||
|
||||
{
|
||||
UniStringDFA dfa("абв");
|
||||
|
||||
auto it = dfa.Begin();
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
|
||||
DFAMove(it, "а");
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
|
||||
DFAMove(it, "б");
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
|
||||
DFAMove(it, "в");
|
||||
TEST(it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
|
||||
DFAMove(it, "г");
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(it.Rejects(), ());
|
||||
}
|
||||
|
||||
{
|
||||
UniStringDFA dfa("абв");
|
||||
|
||||
auto it = dfa.Begin();
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
|
||||
DFAMove(it, "а");
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
|
||||
DFAMove(it, "г");
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(it.Rejects(), ());
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(UniStringDFA_Prefix)
|
||||
{
|
||||
{
|
||||
PrefixDFAModifier<UniStringDFA> dfa(UniStringDFA("abc"));
|
||||
|
||||
auto it = dfa.Begin();
|
||||
DFAMove(it, "ab");
|
||||
|
||||
TEST(!it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
|
||||
DFAMove(it, "c");
|
||||
TEST(it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
|
||||
DFAMove(it, "d");
|
||||
TEST(it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
|
||||
DFAMove(it, "efghijk");
|
||||
TEST(it.Accepts(), ());
|
||||
TEST(!it.Rejects(), ());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
45
libs/base/base_tests/visitor_tests.cpp
Normal file
45
libs/base/base_tests/visitor_tests.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "base/visitor.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Foo
|
||||
{
|
||||
DECLARE_VISITOR()
|
||||
DECLARE_DEBUG_PRINT(Foo)
|
||||
};
|
||||
|
||||
struct Bar
|
||||
{
|
||||
Bar() = default;
|
||||
Bar(int d, string const & s) : m_d(d), m_s(s) {}
|
||||
|
||||
DECLARE_VISITOR(visitor(m_d), visitor(m_s, "string"))
|
||||
DECLARE_DEBUG_PRINT(Bar)
|
||||
|
||||
int m_d = 0;
|
||||
string m_s;
|
||||
};
|
||||
|
||||
UNIT_TEST(DebugPrintVisitor_Smoke)
|
||||
{
|
||||
{
|
||||
Foo foo;
|
||||
TEST_EQUAL(DebugPrint(foo), "Foo []", ());
|
||||
}
|
||||
|
||||
{
|
||||
Bar bar;
|
||||
TEST_EQUAL(DebugPrint(bar), "Bar [0, string: ]", ());
|
||||
|
||||
bar.m_d = 7;
|
||||
bar.m_s = "Hello, World!";
|
||||
TEST_EQUAL(DebugPrint(bar), "Bar [7, string: Hello, World!]", ());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
152
libs/base/beam.hpp
Normal file
152
libs/base/beam.hpp
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/geo_object_id.hpp"
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace base
|
||||
{
|
||||
// A data structure to perform the beam search with.
|
||||
// Maintains a list of (Key, Value) pairs sorted in the decreasing
|
||||
// order of Values.
|
||||
template <typename TKey, typename TValue>
|
||||
class Beam
|
||||
{
|
||||
public:
|
||||
using Key = TKey;
|
||||
using Value = TValue;
|
||||
|
||||
struct Entry
|
||||
{
|
||||
Key m_key;
|
||||
Value m_value;
|
||||
|
||||
Entry(Key const & key, Value const & value) : m_key(key), m_value(value) {}
|
||||
|
||||
bool operator<(Entry const & rhs) const { return m_value > rhs.m_value; }
|
||||
};
|
||||
|
||||
explicit Beam(size_t capacity) : m_capacity(capacity) { m_entries.reserve(m_capacity); }
|
||||
|
||||
// Tries to add a key-value pair to the beam. Evicts the entry with the lowest
|
||||
// value if the insertion would result in an overspill.
|
||||
//
|
||||
// Complexity: O(log(m_capacity)) in the best case (a typical application has a considerable
|
||||
// fraction of calls that do not result in an eviction).
|
||||
// O(m_capacity) in the worst case.
|
||||
void Add(Key const & key, Value const & value)
|
||||
{
|
||||
if (PREDICT_FALSE(m_capacity == 0))
|
||||
return;
|
||||
|
||||
auto const cmp = [&](Entry const & e, Value const & v) { return e.m_value > v; };
|
||||
|
||||
auto it = std::lower_bound(m_entries.begin(), m_entries.end(), value, cmp);
|
||||
|
||||
if (it == m_entries.end())
|
||||
{
|
||||
if (m_entries.size() < m_capacity)
|
||||
m_entries.emplace_back(key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_entries.size() == m_capacity)
|
||||
{
|
||||
if (it == std::prev(m_entries.end()))
|
||||
{
|
||||
m_entries.back() = Entry(key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
m_entries.pop_back();
|
||||
}
|
||||
|
||||
m_entries.insert(it, Entry(key, value));
|
||||
}
|
||||
|
||||
// Calls |fn| for all entries currently held in the beam.
|
||||
// The order of calls is not specified.
|
||||
template <typename Fn>
|
||||
void ForEachEntry(Fn && fn) const
|
||||
{
|
||||
for (Entry const & e : m_entries)
|
||||
fn(e.m_key, e.m_value);
|
||||
}
|
||||
|
||||
// Returns all entries currently held in the beam.
|
||||
// The order of returned entries is not specified.
|
||||
std::vector<Entry> const & GetEntries() const { return m_entries; }
|
||||
|
||||
private:
|
||||
size_t m_capacity;
|
||||
std::vector<Entry> m_entries;
|
||||
};
|
||||
|
||||
// A data structure to perform the beam search with.
|
||||
// Maintains a binary heap of (Key, Value) pairs.
|
||||
template <typename TKey, typename TValue>
|
||||
class HeapBeam
|
||||
{
|
||||
public:
|
||||
using Key = TKey;
|
||||
using Value = TValue;
|
||||
|
||||
struct Entry
|
||||
{
|
||||
Key m_key;
|
||||
Value m_value;
|
||||
|
||||
Entry(Key const & key, Value const & value) : m_key(key), m_value(value) {}
|
||||
|
||||
bool operator<(Entry const & rhs) const { return m_value > rhs.m_value; }
|
||||
};
|
||||
|
||||
explicit HeapBeam(size_t capacity) : m_capacity(capacity), m_size(0) { m_entries.reserve(m_capacity); }
|
||||
|
||||
// Tries to add a key-value pair to the beam. Evicts the entry with the lowest
|
||||
// value if the insertion would result in an overspill.
|
||||
//
|
||||
// Complexity: O(log(m_capacity)).
|
||||
void Add(Key const & key, Value const & value)
|
||||
{
|
||||
if (PREDICT_FALSE(m_capacity == 0))
|
||||
return;
|
||||
|
||||
if (PREDICT_FALSE(m_size < m_capacity))
|
||||
{
|
||||
m_entries.emplace_back(key, value);
|
||||
++m_size;
|
||||
std::push_heap(m_entries.begin(), m_entries.begin() + m_size);
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_GREATER(m_size, 0, ());
|
||||
if (value < m_entries.front().m_value)
|
||||
return;
|
||||
|
||||
std::pop_heap(m_entries.begin(), m_entries.begin() + m_size);
|
||||
m_entries[m_size - 1] = Entry(key, value);
|
||||
std::push_heap(m_entries.begin(), m_entries.begin() + m_size);
|
||||
}
|
||||
|
||||
// Calls |fn| for all entries currently held in the beam.
|
||||
// The order of calls is not specified.
|
||||
template <typename Fn>
|
||||
void ForEachEntry(Fn && fn) const
|
||||
{
|
||||
for (Entry const & e : m_entries)
|
||||
fn(e.m_key, e.m_value);
|
||||
}
|
||||
|
||||
// Returns all entries currently held in the beam.
|
||||
// The order of returned entries is not specified.
|
||||
std::vector<Entry> const & GetEntries() const { return m_entries; }
|
||||
|
||||
private:
|
||||
size_t m_capacity;
|
||||
size_t m_size;
|
||||
std::vector<Entry> m_entries;
|
||||
};
|
||||
} // namespace base
|
||||
99
libs/base/bidirectional_map.hpp
Normal file
99
libs/base/bidirectional_map.hpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace base
|
||||
{
|
||||
// A bidirectional map to store a one-to-one correspondence between
|
||||
// keys and values.
|
||||
template <typename K, typename V, template <typename...> typename KToVMap = std::unordered_map,
|
||||
typename KToVHashOrComparator = std::hash<K>, template <typename...> typename VToKMap = std::unordered_map,
|
||||
typename VToKHashOrComparator = std::hash<V>>
|
||||
class BidirectionalMap
|
||||
{
|
||||
public:
|
||||
BidirectionalMap() = default;
|
||||
|
||||
size_t Size() const { return m_kToV.size(); }
|
||||
|
||||
bool IsEmpty() const { return m_kToV.empty(); }
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_kToV.clear();
|
||||
m_vToK.clear();
|
||||
}
|
||||
|
||||
bool Add(K const & k, V const & v)
|
||||
{
|
||||
if (m_kToV.find(k) != m_kToV.cend() || m_vToK.find(v) != m_vToK.cend())
|
||||
return false;
|
||||
|
||||
m_kToV.emplace(k, v);
|
||||
m_vToK.emplace(v, k);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoveKey(K const & k) { return Remove(k, m_kToV, m_vToK); }
|
||||
|
||||
bool RemoveValue(V const & v) { return Remove(v, m_vToK, m_kToV); }
|
||||
|
||||
void Swap(BidirectionalMap & other)
|
||||
{
|
||||
m_kToV.swap(other.m_kToV);
|
||||
m_vToK.swap(other.m_vToK);
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void ForEachEntry(Fn && fn) const
|
||||
{
|
||||
for (auto const & e : m_kToV)
|
||||
fn(e.first, e.second);
|
||||
}
|
||||
|
||||
bool GetValue(K const & key, V & value) const
|
||||
{
|
||||
auto const it = m_kToV.find(key);
|
||||
if (it == m_kToV.cend())
|
||||
return false;
|
||||
value = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetKey(V const & value, K & key) const
|
||||
{
|
||||
auto const it = m_vToK.find(value);
|
||||
if (it == m_vToK.cend())
|
||||
return false;
|
||||
key = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
using Key = K;
|
||||
using Value = V;
|
||||
using KeysToValues = KToVMap<K, V, KToVHashOrComparator>;
|
||||
using ValuesToKeys = VToKMap<V, K, VToKHashOrComparator>;
|
||||
|
||||
KeysToValues const & GetKeysToValues() const { return m_kToV; }
|
||||
|
||||
ValuesToKeys const & GetValuesToKeys() const { return m_vToK; }
|
||||
|
||||
private:
|
||||
template <typename T, typename FirstMap, typename SecondMap>
|
||||
bool Remove(T const & t, FirstMap & firstMap, SecondMap & secondMap)
|
||||
{
|
||||
auto const firstIt = firstMap.find(t);
|
||||
if (firstIt == firstMap.cend())
|
||||
return false;
|
||||
|
||||
secondMap.erase(firstIt->second);
|
||||
firstMap.erase(firstIt);
|
||||
return true;
|
||||
}
|
||||
|
||||
KeysToValues m_kToV;
|
||||
ValuesToKeys m_vToK;
|
||||
};
|
||||
} // namespace base
|
||||
156
libs/base/bits.hpp
Normal file
156
libs/base/bits.hpp
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
namespace bits
|
||||
{
|
||||
static constexpr int SELECT1_ERROR = -1;
|
||||
|
||||
template <typename T>
|
||||
unsigned int select1(T x, unsigned int i)
|
||||
{
|
||||
// TODO: Fast implementation of select1.
|
||||
ASSERT(i > 0 && i <= sizeof(T) * 8, (i));
|
||||
for (unsigned int j = 0; j < sizeof(T) * 8; x >>= 1, ++j)
|
||||
if (x & 1)
|
||||
if (--i == 0)
|
||||
return j;
|
||||
return static_cast<unsigned int>(SELECT1_ERROR);
|
||||
}
|
||||
|
||||
constexpr uint8_t FloorLog(uint64_t x) noexcept
|
||||
{
|
||||
return x == 0 ? 0 : std::bit_width(x) - 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::make_unsigned_t<T> ZigZagEncode(T x)
|
||||
{
|
||||
static_assert(std::is_signed<T>::value, "Type should be signed");
|
||||
return (x << 1) ^ (x >> (sizeof(x) * 8 - 1));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::make_signed_t<T> ZigZagDecode(T x)
|
||||
{
|
||||
static_assert(std::is_unsigned<T>::value, "Type should be unsigned.");
|
||||
return (x >> 1) ^ -static_cast<std::make_signed_t<T>>(x & 1);
|
||||
}
|
||||
|
||||
// Perform a perfect shuffle of the bits of 64-bit integer, shuffling bits from
|
||||
// 'abcd efgh ijkl mnop ABCD EFGH IJKL MNOP' to 'aAbB cCdD eEfF gGhH iIjJ kKlL mMnN oOpP'.
|
||||
// See http://www.icodeguru.com/Embedded/Hacker's-Delight/047.htm
|
||||
constexpr uint64_t PerfectShuffle(uint64_t x)
|
||||
{
|
||||
x = ((x & 0x00000000FFFF0000ULL) << 16) | ((x >> 16) & 0x00000000FFFF0000ULL) | (x & 0xFFFF00000000FFFFULL);
|
||||
x = ((x & 0x0000FF000000FF00ULL) << 8) | ((x >> 8) & 0x0000FF000000FF00ULL) | (x & 0xFF0000FFFF0000FFULL);
|
||||
x = ((x & 0x00F000F000F000F0ULL) << 4) | ((x >> 4) & 0x00F000F000F000F0ULL) | (x & 0xF00FF00FF00FF00FULL);
|
||||
x = ((x & 0x0C0C0C0C0C0C0C0CULL) << 2) | ((x >> 2) & 0x0C0C0C0C0C0C0C0CULL) | (x & 0xC3C3C3C3C3C3C3C3ULL);
|
||||
x = ((x & 0x2222222222222222ULL) << 1) | ((x >> 1) & 0x2222222222222222ULL) | (x & 0x9999999999999999ULL);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Reverses the perfect shuffle of bits in a 64-bit integer
|
||||
constexpr uint64_t PerfectUnshuffle(uint64_t x)
|
||||
{
|
||||
x = ((x & 0x2222222222222222ULL) << 1) | ((x >> 1) & 0x2222222222222222ULL) | (x & 0x9999999999999999ULL);
|
||||
x = ((x & 0x0C0C0C0C0C0C0C0CULL) << 2) | ((x >> 2) & 0x0C0C0C0C0C0C0C0CULL) | (x & 0xC3C3C3C3C3C3C3C3ULL);
|
||||
x = ((x & 0x00F000F000F000F0ULL) << 4) | ((x >> 4) & 0x00F000F000F000F0ULL) | (x & 0xF00FF00FF00FF00FULL);
|
||||
x = ((x & 0x0000FF000000FF00ULL) << 8) | ((x >> 8) & 0x0000FF000000FF00ULL) | (x & 0xFF0000FFFF0000FFULL);
|
||||
x = ((x & 0x00000000FFFF0000ULL) << 16) | ((x >> 16) & 0x00000000FFFF0000ULL) | (x & 0xFFFF00000000FFFFULL);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Returns the integer that has the bits of |x| at even-numbered positions
|
||||
// and the bits of |y| at odd-numbered positions without changing the
|
||||
// relative order of bits coming from |x| and |y|.
|
||||
// That is, if the bits of |x| are {x31, x30, ..., x0},
|
||||
// and the bits of |y| are {y31, y30, ..., y0},
|
||||
// then the bits of the result are {y31, x31, y30, x30, ..., y0, x0}.
|
||||
constexpr uint64_t BitwiseMerge(uint32_t x, uint32_t y)
|
||||
{
|
||||
return PerfectShuffle((static_cast<uint64_t>(y) << 32) | x);
|
||||
}
|
||||
|
||||
constexpr void BitwiseSplit(uint64_t v, uint32_t & x, uint32_t & y)
|
||||
{
|
||||
uint64_t unshuffle = PerfectUnshuffle(v);
|
||||
x = static_cast<uint32_t>(unshuffle);
|
||||
y = static_cast<uint32_t>(unshuffle >> 32);
|
||||
}
|
||||
|
||||
// Returns 1 if bit is set and 0 otherwise.
|
||||
inline uint8_t GetBit(void const * p, uint32_t offset)
|
||||
{
|
||||
uint8_t const * pData = static_cast<uint8_t const *>(p);
|
||||
return (pData[offset >> 3] >> (offset & 7)) & 1;
|
||||
}
|
||||
|
||||
inline void SetBitTo0(void * p, uint32_t offset)
|
||||
{
|
||||
uint8_t * pData = static_cast<uint8_t *>(p);
|
||||
pData[offset >> 3] &= ~(1 << (offset & 7));
|
||||
}
|
||||
|
||||
inline void SetBitTo1(void * p, uint32_t offset)
|
||||
{
|
||||
uint8_t * pData = static_cast<uint8_t *>(p);
|
||||
pData[offset >> 3] |= (1 << (offset & 7));
|
||||
}
|
||||
|
||||
// Compute number of zero bits from the most significant bits side.
|
||||
constexpr uint32_t NumHiZeroBits32(uint32_t n)
|
||||
{
|
||||
if (n == 0)
|
||||
return 32;
|
||||
uint32_t result = 0;
|
||||
while ((n & (uint32_t{1} << 31)) == 0)
|
||||
{
|
||||
++result;
|
||||
n <<= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr uint32_t NumHiZeroBits64(uint64_t n)
|
||||
{
|
||||
if (n == 0)
|
||||
return 64;
|
||||
uint32_t result = 0;
|
||||
while ((n & (uint64_t{1} << 63)) == 0)
|
||||
{
|
||||
++result;
|
||||
n <<= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Computes number of bits needed to store the number, it is not equal to number of ones.
|
||||
// E.g. if we have a number (in bit representation) 00001000b then NumUsedBits is 4.
|
||||
constexpr uint32_t NumUsedBits(uint64_t n)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
while (n != 0)
|
||||
{
|
||||
++result;
|
||||
n >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr uint64_t GetFullMask(uint8_t numBits)
|
||||
{
|
||||
ASSERT_LESS_OR_EQUAL(numBits, 64, ());
|
||||
return numBits == 64 ? std::numeric_limits<uint64_t>::max() : (static_cast<uint64_t>(1) << numBits) - 1;
|
||||
}
|
||||
|
||||
constexpr bool IsPow2Minus1(uint64_t n)
|
||||
{
|
||||
return (n & (n + 1)) == 0;
|
||||
}
|
||||
} // namespace bits
|
||||
491
libs/base/buffer_vector.hpp
Normal file
491
libs/base/buffer_vector.hpp
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/checked_cast.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
// Calls swap() function using argument dependant lookup.
|
||||
// // Do NOT override this function, but override swap() function instead!
|
||||
template <typename T>
|
||||
void Swap(T & a, T & b)
|
||||
{
|
||||
using std::swap;
|
||||
swap(a, b);
|
||||
}
|
||||
|
||||
template <class T, size_t N>
|
||||
class buffer_vector
|
||||
{
|
||||
private:
|
||||
enum
|
||||
{
|
||||
USE_DYNAMIC = N + 1
|
||||
};
|
||||
// TODO (@gmoryes) consider std::aligned_storage
|
||||
T m_static[N];
|
||||
size_t m_size;
|
||||
std::vector<T> m_dynamic;
|
||||
|
||||
bool IsDynamic() const { return m_size == USE_DYNAMIC; }
|
||||
|
||||
void MoveStatic(buffer_vector & rhs) noexcept
|
||||
{
|
||||
static_assert(std::is_nothrow_move_assignable<T>::value);
|
||||
|
||||
std::move(rhs.m_static, rhs.m_static + rhs.m_size, m_static);
|
||||
}
|
||||
|
||||
void SetStaticSize(size_t newSize)
|
||||
{
|
||||
if constexpr (std::is_destructible<T>::value)
|
||||
{
|
||||
// Call destructors for old elements.
|
||||
for (size_t i = newSize; i < m_size; ++i)
|
||||
m_static[i] = T();
|
||||
}
|
||||
m_size = newSize;
|
||||
}
|
||||
|
||||
static constexpr size_t SwitchCapacity() { return 3 * N / 2 + 1; }
|
||||
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef T const & const_reference;
|
||||
typedef T & reference;
|
||||
typedef size_t size_type;
|
||||
typedef T const * const_iterator;
|
||||
typedef T * iterator;
|
||||
|
||||
buffer_vector() : m_size(0) {}
|
||||
explicit buffer_vector(size_t n) : m_size(0) { resize(n); }
|
||||
|
||||
buffer_vector(std::initializer_list<T> init) : m_size(0)
|
||||
{
|
||||
assign(std::make_move_iterator(init.begin()), std::make_move_iterator(init.end()));
|
||||
}
|
||||
|
||||
template <typename TIt>
|
||||
buffer_vector(TIt beg, TIt end) : m_size(0)
|
||||
{
|
||||
assign(beg, end);
|
||||
}
|
||||
|
||||
buffer_vector(buffer_vector const &) = default;
|
||||
|
||||
buffer_vector(buffer_vector && rhs) noexcept : m_size(rhs.m_size), m_dynamic(std::move(rhs.m_dynamic))
|
||||
{
|
||||
if (!IsDynamic())
|
||||
MoveStatic(rhs);
|
||||
|
||||
rhs.m_size = 0;
|
||||
}
|
||||
|
||||
buffer_vector & operator=(buffer_vector const & rhs) = default;
|
||||
|
||||
buffer_vector & operator=(buffer_vector && rhs) noexcept
|
||||
{
|
||||
if (this != &rhs)
|
||||
{
|
||||
m_size = rhs.m_size;
|
||||
m_dynamic = std::move(rhs.m_dynamic);
|
||||
|
||||
if (!IsDynamic())
|
||||
MoveStatic(rhs);
|
||||
|
||||
rhs.m_size = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <size_t M>
|
||||
void append(buffer_vector<value_type, M> const & v)
|
||||
{
|
||||
append(v.begin(), v.end());
|
||||
}
|
||||
|
||||
template <typename TIt>
|
||||
void append(TIt beg, TIt end)
|
||||
{
|
||||
if (!IsDynamic())
|
||||
{
|
||||
size_t const newSize = std::distance(beg, end) + m_size;
|
||||
if (newSize <= N)
|
||||
{
|
||||
std::copy(beg, end, &m_static[m_size]);
|
||||
m_size = newSize;
|
||||
return;
|
||||
}
|
||||
else
|
||||
SwitchToDynamic(newSize);
|
||||
}
|
||||
|
||||
m_dynamic.insert(m_dynamic.end(), beg, end);
|
||||
}
|
||||
|
||||
void append(size_t count, T const & c)
|
||||
{
|
||||
if (!IsDynamic())
|
||||
{
|
||||
size_t const newSize = count + m_size;
|
||||
if (newSize <= N)
|
||||
{
|
||||
std::fill_n(&m_static[m_size], count, c);
|
||||
m_size = newSize;
|
||||
return;
|
||||
}
|
||||
else
|
||||
SwitchToDynamic(newSize);
|
||||
}
|
||||
|
||||
m_dynamic.insert(m_dynamic.end(), count, c);
|
||||
}
|
||||
|
||||
template <typename TIt>
|
||||
void assign(TIt beg, TIt end)
|
||||
{
|
||||
if (IsDynamic())
|
||||
{
|
||||
m_dynamic.assign(beg, end);
|
||||
return;
|
||||
}
|
||||
|
||||
m_size = 0;
|
||||
append(beg, end);
|
||||
}
|
||||
|
||||
void reserve(size_t n)
|
||||
{
|
||||
if (IsDynamic() || n > N)
|
||||
m_dynamic.reserve(n);
|
||||
}
|
||||
|
||||
void resize(size_t n)
|
||||
{
|
||||
if (IsDynamic())
|
||||
{
|
||||
m_dynamic.resize(n);
|
||||
return;
|
||||
}
|
||||
|
||||
if (n <= N)
|
||||
{
|
||||
SetStaticSize(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
SwitchToDynamic(n);
|
||||
m_dynamic.resize(n);
|
||||
ASSERT_EQUAL(m_dynamic.size(), n, ());
|
||||
}
|
||||
}
|
||||
|
||||
void resize(size_t n, T c)
|
||||
{
|
||||
if (IsDynamic())
|
||||
{
|
||||
m_dynamic.resize(n, c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (n <= N)
|
||||
{
|
||||
if (n > m_size)
|
||||
std::fill_n(&m_static[m_size], n - m_size, c);
|
||||
|
||||
SetStaticSize(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t const oldSize = m_size;
|
||||
SwitchToDynamic(n);
|
||||
m_dynamic.insert(m_dynamic.end(), n - oldSize, c);
|
||||
ASSERT_EQUAL(m_dynamic.size(), n, ());
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (IsDynamic())
|
||||
{
|
||||
m_dynamic.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
SetStaticSize(0);
|
||||
}
|
||||
|
||||
/// @todo Here is some inconsistencies:
|
||||
/// - "data" method should return 0 if vector is empty;\n
|
||||
/// - potential memory overrun if m_dynamic is empty;\n
|
||||
/// The best way to fix this is to reset m_size from USE_DYNAMIC to 0 when vector becomes empty.
|
||||
/// But now I will just add some assertions to test memory overrun.
|
||||
//@{
|
||||
T const * data() const
|
||||
{
|
||||
if (IsDynamic())
|
||||
return m_dynamic.data();
|
||||
|
||||
return &m_static[0];
|
||||
}
|
||||
|
||||
T * data()
|
||||
{
|
||||
if (IsDynamic())
|
||||
return m_dynamic.data();
|
||||
|
||||
return &m_static[0];
|
||||
}
|
||||
//@}
|
||||
|
||||
T const * begin() const { return data(); }
|
||||
T const * cbegin() const { return data(); }
|
||||
T * begin() { return data(); }
|
||||
T const * end() const { return data() + size(); }
|
||||
T const * cend() const { return data() + size(); }
|
||||
T * end() { return data() + size(); }
|
||||
//@}
|
||||
|
||||
bool empty() const { return (IsDynamic() ? m_dynamic.empty() : m_size == 0); }
|
||||
size_t size() const { return (IsDynamic() ? m_dynamic.size() : m_size); }
|
||||
|
||||
T const & front() const
|
||||
{
|
||||
ASSERT(!empty(), ());
|
||||
return *begin();
|
||||
}
|
||||
|
||||
T & front()
|
||||
{
|
||||
ASSERT(!empty(), ());
|
||||
return *begin();
|
||||
}
|
||||
|
||||
T const & back() const
|
||||
{
|
||||
ASSERT(!empty(), ());
|
||||
return *(end() - 1);
|
||||
}
|
||||
|
||||
T & back()
|
||||
{
|
||||
ASSERT(!empty(), ());
|
||||
return *(end() - 1);
|
||||
}
|
||||
|
||||
T const & operator[](size_t i) const
|
||||
{
|
||||
ASSERT_LESS(i, size(), ());
|
||||
return *(begin() + i);
|
||||
}
|
||||
|
||||
T & operator[](size_t i)
|
||||
{
|
||||
ASSERT_LESS(i, size(), ());
|
||||
return *(begin() + i);
|
||||
}
|
||||
|
||||
void swap(buffer_vector & rhs)
|
||||
{
|
||||
m_dynamic.swap(rhs.m_dynamic);
|
||||
Swap(m_size, rhs.m_size);
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
Swap(m_static[i], rhs.m_static[i]);
|
||||
}
|
||||
|
||||
/// By value to be consistent with m_vec.push_back(m_vec[0]).
|
||||
void push_back(T t)
|
||||
{
|
||||
if (!IsDynamic())
|
||||
{
|
||||
if (m_size < N)
|
||||
{
|
||||
m_static[m_size] = std::move(t);
|
||||
++m_size; // keep basic exception safety here
|
||||
return;
|
||||
}
|
||||
else
|
||||
SwitchToDynamic(SwitchCapacity());
|
||||
}
|
||||
|
||||
m_dynamic.push_back(std::move(t));
|
||||
}
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
if (IsDynamic())
|
||||
{
|
||||
m_dynamic.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_GREATER(m_size, 0, ());
|
||||
--m_size;
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void emplace_back(Args &&... args)
|
||||
{
|
||||
if (IsDynamic())
|
||||
{
|
||||
m_dynamic.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
else if (m_size < N)
|
||||
{
|
||||
m_static[m_size] = value_type(std::forward<Args>(args)...);
|
||||
++m_size; // keep basic exception safety here
|
||||
}
|
||||
else
|
||||
{
|
||||
// Construct value first in case of reallocation and m_vec.emplace_back(m_vec[0]).
|
||||
value_type value(std::forward<Args>(args)...);
|
||||
SwitchToDynamic(SwitchCapacity());
|
||||
m_dynamic.push_back(std::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TIt>
|
||||
void insert(const_iterator where, TIt beg, TIt end)
|
||||
{
|
||||
size_t const pos = base::asserted_cast<size_t>(where - data());
|
||||
ASSERT_LESS_OR_EQUAL(pos, size(), ());
|
||||
|
||||
if (IsDynamic())
|
||||
{
|
||||
m_dynamic.insert(m_dynamic.begin() + pos, beg, end);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t const n = end - beg;
|
||||
if (m_size + n <= N)
|
||||
{
|
||||
if (pos != m_size)
|
||||
for (size_t i = m_size - 1; i >= pos && i < m_size; --i)
|
||||
Swap(m_static[i], m_static[i + n]);
|
||||
|
||||
m_size += n;
|
||||
T * writableWhere = &m_static[0] + pos;
|
||||
ASSERT(where == writableWhere, ());
|
||||
while (beg != end)
|
||||
*(writableWhere++) = *(beg++);
|
||||
}
|
||||
else
|
||||
{
|
||||
SwitchToDynamic(m_size + n);
|
||||
m_dynamic.insert(m_dynamic.begin() + pos, beg, end);
|
||||
}
|
||||
}
|
||||
|
||||
void insert(const_iterator where, value_type const & value) { insert(where, &value, &value + 1); }
|
||||
|
||||
template <class Fn>
|
||||
void erase_if(Fn && fn)
|
||||
{
|
||||
iterator b = begin();
|
||||
iterator e = end();
|
||||
iterator i = std::remove_if(b, e, std::forward<Fn>(fn));
|
||||
if (i != e)
|
||||
resize(std::distance(b, i));
|
||||
}
|
||||
|
||||
void erase(iterator first, iterator last)
|
||||
{
|
||||
if (first == last)
|
||||
return;
|
||||
|
||||
auto const numToErase = std::distance(first, last);
|
||||
for (; first != end() - numToErase; ++first)
|
||||
Swap(*first, *(first + numToErase));
|
||||
resize(std::distance(begin(), first));
|
||||
}
|
||||
void erase(iterator it) { erase(it, it + 1); }
|
||||
|
||||
private:
|
||||
void SwitchToDynamic(size_t toReserve)
|
||||
{
|
||||
ASSERT_NOT_EQUAL(m_size, static_cast<size_t>(USE_DYNAMIC), ());
|
||||
ASSERT_EQUAL(m_dynamic.size(), 0, ());
|
||||
|
||||
m_dynamic.reserve(toReserve);
|
||||
m_dynamic.resize(m_size);
|
||||
std::move(m_static, m_static + m_size, m_dynamic.begin());
|
||||
|
||||
m_size = USE_DYNAMIC;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, size_t N>
|
||||
void swap(buffer_vector<T, N> & r1, buffer_vector<T, N> & r2)
|
||||
{
|
||||
r1.swap(r2);
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
std::string DebugPrint(buffer_vector<T, N> const & v)
|
||||
{
|
||||
return DebugPrintSequence(v.data(), v.data() + v.size());
|
||||
}
|
||||
|
||||
template <typename T, size_t N1, size_t N2>
|
||||
bool operator==(buffer_vector<T, N1> const & v1, buffer_vector<T, N2> const & v2)
|
||||
{
|
||||
return (v1.size() == v2.size() && std::equal(v1.begin(), v1.end(), v2.begin()));
|
||||
}
|
||||
|
||||
template <typename T, size_t N1, size_t N2>
|
||||
bool operator!=(buffer_vector<T, N1> const & v1, buffer_vector<T, N2> const & v2)
|
||||
{
|
||||
return !(v1 == v2);
|
||||
}
|
||||
|
||||
template <typename T, size_t N1, size_t N2>
|
||||
bool operator<(buffer_vector<T, N1> const & v1, buffer_vector<T, N2> const & v2)
|
||||
{
|
||||
return std::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end());
|
||||
}
|
||||
|
||||
template <typename T, size_t N1, size_t N2>
|
||||
bool operator>(buffer_vector<T, N1> const & v1, buffer_vector<T, N2> const & v2)
|
||||
{
|
||||
return v2 < v1;
|
||||
}
|
||||
// TODO(AB): Use <=> operator.
|
||||
// Used in:
|
||||
// TrieChar const * const keyData = entry.GetKeyData();
|
||||
// TTrieString key(keyData, keyData + entry.GetKeySize());
|
||||
// using namespace std::rel_ops; // ">=" for keys.
|
||||
// CHECK_GREATER_OR_EQUAL(key, prevKey, (key, prevKey));
|
||||
template <typename T, size_t N1, size_t N2>
|
||||
bool operator>=(buffer_vector<T, N1> const & v1, buffer_vector<T, N2> const & v2)
|
||||
{
|
||||
return !(v1 < v2);
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <typename T, size_t N>
|
||||
typename buffer_vector<T, N>::iterator begin(buffer_vector<T, N> & v)
|
||||
{
|
||||
return v.begin();
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
typename buffer_vector<T, N>::const_iterator begin(buffer_vector<T, N> const & v)
|
||||
{
|
||||
return v.begin();
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
typename buffer_vector<T, N>::const_iterator end(buffer_vector<T, N> const & v)
|
||||
{
|
||||
return v.end();
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
template <class Dest, class Src>
|
||||
void assign_range(Dest & dest, Src const & src)
|
||||
{
|
||||
dest.assign(std::begin(src), std::end(src));
|
||||
}
|
||||
157
libs/base/cache.hpp
Normal file
157
libs/base/cache.hpp
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/base.hpp"
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace base
|
||||
{
|
||||
// Simple cache that stores list of values.
|
||||
template <typename KeyT, typename ValueT>
|
||||
class Cache
|
||||
{
|
||||
DISALLOW_COPY(Cache);
|
||||
|
||||
public:
|
||||
Cache() = default;
|
||||
Cache(Cache && r) = default;
|
||||
|
||||
/// @param[in] logCacheSize is pow of two for number of elements in cache.
|
||||
explicit Cache(uint32_t logCacheSize) { Init(logCacheSize); }
|
||||
|
||||
/// @param[in] logCacheSize is pow of two for number of elements in cache.
|
||||
void Init(uint32_t logCacheSize)
|
||||
{
|
||||
ASSERT(logCacheSize > 0 && logCacheSize < 32, (logCacheSize));
|
||||
static_assert((std::is_same<KeyT, uint32_t>::value || std::is_same<KeyT, uint64_t>::value), "");
|
||||
|
||||
m_cache.reset(new Data[1 << logCacheSize]);
|
||||
m_hashMask = (1 << logCacheSize) - 1;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
uint32_t GetCacheSize() const { return m_hashMask + 1; }
|
||||
|
||||
// Find value by @key. If @key is found, returns reference to its value.
|
||||
// If @key is not found, some other key is removed and it's value is reused without
|
||||
// re-initialization. It's up to caller to re-initialize the new value if @found == false.
|
||||
// TODO: Return pair<ValueT *, bool> instead?
|
||||
ValueT & Find(KeyT const & key, bool & found)
|
||||
{
|
||||
Data & data = m_cache[Index(key)];
|
||||
if (data.m_Key == key)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
found = false;
|
||||
data.m_Key = key;
|
||||
}
|
||||
return data.m_Value;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void ForEachValue(F && f)
|
||||
{
|
||||
for (uint32_t i = 0; i <= m_hashMask; ++i)
|
||||
f(m_cache[i].m_Value);
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
// Initialize m_Cache such, that Index(m_Cache[i].m_Key) != i.
|
||||
for (uint32_t i = 0; i <= m_hashMask; ++i)
|
||||
{
|
||||
KeyT & key = m_cache[i].m_Key;
|
||||
for (key = 0; Index(key) == i; ++key)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t Index(KeyT const & key) const { return static_cast<size_t>(Hash(key) & m_hashMask); }
|
||||
|
||||
static uint32_t Hash(uint32_t x)
|
||||
{
|
||||
x = (x ^ 61) ^ (x >> 16);
|
||||
x = x + (x << 3);
|
||||
x = x ^ (x >> 4);
|
||||
x = x * 0x27d4eb2d;
|
||||
x = x ^ (x >> 15);
|
||||
return x;
|
||||
}
|
||||
|
||||
static uint32_t Hash(uint64_t x) { return Hash(uint32_t(x) ^ uint32_t(x >> 32)); }
|
||||
|
||||
// TODO: Consider using separate arrays for keys and values, to save on padding.
|
||||
struct Data
|
||||
{
|
||||
Data() : m_Key(), m_Value() {}
|
||||
KeyT m_Key;
|
||||
ValueT m_Value;
|
||||
};
|
||||
|
||||
std::unique_ptr<Data[]> m_cache;
|
||||
uint32_t m_hashMask;
|
||||
};
|
||||
|
||||
// Simple cache that stores list of values and provides cache missing statistics.
|
||||
// CacheWithStat class has the same interface as Cache class
|
||||
template <typename TKey, typename TValue>
|
||||
class CacheWithStat
|
||||
{
|
||||
public:
|
||||
CacheWithStat() = default;
|
||||
|
||||
explicit CacheWithStat(uint32_t logCacheSize) : m_cache(logCacheSize), m_miss(0), m_access(0) {}
|
||||
|
||||
/// @param[in] logCacheSize is pow of two for number of elements in cache.
|
||||
void Init(uint32_t logCacheSize)
|
||||
{
|
||||
m_cache.Init(logCacheSize);
|
||||
m_miss = m_access = 0;
|
||||
}
|
||||
|
||||
double GetCacheMiss() const
|
||||
{
|
||||
if (m_access == 0)
|
||||
return 0.0;
|
||||
return static_cast<double>(m_miss) / static_cast<double>(m_access);
|
||||
}
|
||||
|
||||
uint32_t GetCacheSize() const { return m_cache.GetCacheSize(); }
|
||||
|
||||
TValue & Find(TKey const & key, bool & found)
|
||||
{
|
||||
++m_access;
|
||||
TValue & res = m_cache.Find(key, found);
|
||||
if (!found)
|
||||
++m_miss;
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void ForEachValue(F && f)
|
||||
{
|
||||
m_cache.ForEachValue(std::forward<F>(f));
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
m_cache.Reset();
|
||||
m_access = 0;
|
||||
m_miss = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Cache<TKey, TValue> m_cache;
|
||||
uint64_t m_miss;
|
||||
uint64_t m_access;
|
||||
};
|
||||
} // namespace base
|
||||
59
libs/base/cancellable.cpp
Normal file
59
libs/base/cancellable.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#include "base/cancellable.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
namespace base
|
||||
{
|
||||
void Cancellable::Reset()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
m_status = Status::Active;
|
||||
m_deadline = {};
|
||||
}
|
||||
|
||||
void Cancellable::Cancel()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
m_status = Status::CancelCalled;
|
||||
}
|
||||
|
||||
void Cancellable::SetDeadline(std::chrono::steady_clock::time_point const & deadline)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
m_deadline = deadline;
|
||||
CheckDeadline();
|
||||
}
|
||||
|
||||
bool Cancellable::IsCancelled() const
|
||||
{
|
||||
return CancellationStatus() != Status::Active;
|
||||
}
|
||||
|
||||
Cancellable::Status Cancellable::CancellationStatus() const
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
CheckDeadline();
|
||||
return m_status;
|
||||
}
|
||||
|
||||
void Cancellable::CheckDeadline() const
|
||||
{
|
||||
if (m_status == Status::Active && m_deadline && *m_deadline < std::chrono::steady_clock::now())
|
||||
m_status = Status::DeadlineExceeded;
|
||||
}
|
||||
|
||||
std::string DebugPrint(Cancellable::Status status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case Cancellable::Status::Active: return "Active";
|
||||
case Cancellable::Status::CancelCalled: return "CancelCalled";
|
||||
case Cancellable::Status::DeadlineExceeded: return "DeadlineExceeded";
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
} // namespace base
|
||||
56
libs/base/cancellable.hpp
Normal file
56
libs/base/cancellable.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace base
|
||||
{
|
||||
// This is a helper thread-safe class which can be mixed in
|
||||
// classes which represent some cancellable activities.
|
||||
class Cancellable
|
||||
{
|
||||
public:
|
||||
enum class Status
|
||||
{
|
||||
Active,
|
||||
CancelCalled,
|
||||
DeadlineExceeded,
|
||||
};
|
||||
|
||||
Cancellable() = default;
|
||||
|
||||
virtual ~Cancellable() {}
|
||||
|
||||
// Marks current activity as not cancelled.
|
||||
// Resets the deadline if it was present.
|
||||
virtual void Reset();
|
||||
|
||||
// Marks current activity as cancelled.
|
||||
virtual void Cancel();
|
||||
|
||||
// Sets a deadline after which the activity is cancelled.
|
||||
virtual void SetDeadline(std::chrono::steady_clock::time_point const & t);
|
||||
|
||||
// Updates the status.
|
||||
// Returns true iff current activity has been cancelled (either by the call
|
||||
// to Cancel or by timeout).
|
||||
virtual bool IsCancelled() const;
|
||||
|
||||
// Updates the status of current activity and returns its value.
|
||||
virtual Status CancellationStatus() const;
|
||||
|
||||
private:
|
||||
// Checks whether |m_deadline| has exceeded. Must be called with |m_mutex| locked.
|
||||
void CheckDeadline() const;
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
|
||||
mutable Status m_status = Status::Active;
|
||||
|
||||
std::optional<std::chrono::steady_clock::time_point> m_deadline;
|
||||
};
|
||||
|
||||
std::string DebugPrint(Cancellable::Status status);
|
||||
} // namespace base
|
||||
43
libs/base/checked_cast.hpp
Normal file
43
libs/base/checked_cast.hpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
namespace base
|
||||
{
|
||||
template <typename ReturnType, typename ParameterType>
|
||||
ReturnType checked_cast(ParameterType v)
|
||||
{
|
||||
static_assert(std::is_integral_v<ParameterType>, "ParameterType should be integral");
|
||||
static_assert(std::is_integral_v<ReturnType>, "ReturnType should be integral");
|
||||
|
||||
auto const result = static_cast<ReturnType>(v);
|
||||
CHECK_EQUAL(static_cast<ParameterType>(result), v, ());
|
||||
CHECK((result > 0) == (v > 0), ("checked_cast failed, value =", v, ", result =", result));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ReturnType, typename ParameterType>
|
||||
ReturnType asserted_cast(ParameterType v)
|
||||
{
|
||||
static_assert(std::is_integral_v<ParameterType>, "ParameterType should be integral");
|
||||
static_assert(std::is_integral_v<ReturnType>, "ReturnType should be integral");
|
||||
|
||||
auto const result = static_cast<ReturnType>(v);
|
||||
ASSERT_EQUAL(static_cast<ParameterType>(result), v, ());
|
||||
ASSERT((result > 0) == (v > 0), ("asserted_cast failed, value =", v, ", result =", result));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ResultType, typename ParameterType>
|
||||
bool IsCastValid(ParameterType v)
|
||||
{
|
||||
static_assert(std::is_integral_v<ParameterType>, "ParameterType should be integral");
|
||||
static_assert(std::is_integral_v<ResultType>, "ReturnType should be integral");
|
||||
|
||||
auto const result = static_cast<ResultType>(v);
|
||||
return static_cast<ParameterType>(result) == v && ((result > 0) == (v > 0));
|
||||
}
|
||||
} // namespace base
|
||||
160
libs/base/clustering_map.hpp
Normal file
160
libs/base/clustering_map.hpp
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace base
|
||||
{
|
||||
// Maps keys to lists of values, but allows to combine keys into
|
||||
// clusters and to get all values from a cluster.
|
||||
//
|
||||
// NOTE: this class is NOT thread-safe.
|
||||
template <typename Key, typename Value, typename Hash = std::hash<Key>>
|
||||
class ClusteringMap
|
||||
{
|
||||
public:
|
||||
// In complexity specifications below:
|
||||
// * n is the total number of keys in the map
|
||||
// * m is the total number of values in the map
|
||||
// * α() is the inverse Ackermann function
|
||||
// * F is the complexity of find() in unordered_map
|
||||
//
|
||||
// Also, it's assumed that complexity to compare two keys is O(1).
|
||||
|
||||
// Appends |value| to the list of values in the cluster
|
||||
// corresponding to |key|.
|
||||
//
|
||||
// Amortized complexity: O(α(n) * F).
|
||||
template <typename V>
|
||||
void Append(Key const & key, V && value)
|
||||
{
|
||||
auto & entry = GetRoot(key);
|
||||
entry.m_values.push_back(std::forward<V>(value));
|
||||
}
|
||||
|
||||
void Delete(Key const & key) { m_table.erase(key); }
|
||||
|
||||
// Unions clusters corresponding to |u| and |v|.
|
||||
//
|
||||
// Amortized complexity: O(α(n) * F + log(m)).
|
||||
void Union(Key const & u, Key const & v)
|
||||
{
|
||||
auto & ru = GetRoot(u);
|
||||
auto & rv = GetRoot(v);
|
||||
if (ru.m_root == rv.m_root)
|
||||
return;
|
||||
|
||||
if (ru.m_rank < rv.m_rank)
|
||||
Attach(rv /* root */, ru /* child */);
|
||||
else
|
||||
Attach(ru /* root */, rv /* child */);
|
||||
}
|
||||
|
||||
// Returns all values from the cluster corresponding to |key|.
|
||||
//
|
||||
// Amortized complexity: O(α(n) * F).
|
||||
std::vector<Value> const & Get(Key const & key)
|
||||
{
|
||||
auto const & entry = GetRoot(key);
|
||||
return entry.m_values;
|
||||
}
|
||||
|
||||
// Complexity: O(n * log(n)).
|
||||
template <typename Fn>
|
||||
void ForEachCluster(Fn && fn)
|
||||
{
|
||||
struct EntryWithKey
|
||||
{
|
||||
EntryWithKey(Entry const * entry, Key const & key) : m_entry(entry), m_key(key) {}
|
||||
|
||||
bool operator<(EntryWithKey const & rhs) const { return m_entry->m_root < rhs.m_entry->m_root; }
|
||||
|
||||
Entry const * m_entry;
|
||||
Key m_key;
|
||||
};
|
||||
|
||||
std::vector<EntryWithKey> eks;
|
||||
for (auto const & kv : m_table)
|
||||
{
|
||||
auto const & key = kv.first;
|
||||
auto const & entry = GetRoot(key);
|
||||
eks.emplace_back(&entry, key);
|
||||
}
|
||||
std::sort(eks.begin(), eks.end());
|
||||
|
||||
size_t i = 0;
|
||||
while (i < eks.size())
|
||||
{
|
||||
std::vector<Key> keys;
|
||||
keys.push_back(eks[i].m_key);
|
||||
|
||||
size_t j = i + 1;
|
||||
while (j < eks.size() && eks[i].m_entry->m_root == eks[j].m_entry->m_root)
|
||||
{
|
||||
keys.push_back(eks[j].m_key);
|
||||
++j;
|
||||
}
|
||||
|
||||
fn(keys, eks[i].m_entry->m_values);
|
||||
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() { m_table.clear(); }
|
||||
|
||||
private:
|
||||
struct Entry
|
||||
{
|
||||
Key m_root;
|
||||
size_t m_rank = 0;
|
||||
std::vector<Value> m_values;
|
||||
};
|
||||
|
||||
Entry & GetRoot(Key const & key)
|
||||
{
|
||||
auto & entry = GetEntry(key);
|
||||
if (entry.m_root == key)
|
||||
return entry;
|
||||
|
||||
auto & root = GetRoot(entry.m_root);
|
||||
entry.m_root = root.m_root;
|
||||
return root;
|
||||
}
|
||||
|
||||
Entry & GetEntry(Key const & key)
|
||||
{
|
||||
auto it = m_table.find(key);
|
||||
if (it != m_table.end())
|
||||
return it->second;
|
||||
|
||||
auto & entry = m_table[key];
|
||||
entry.m_root = key;
|
||||
return entry;
|
||||
}
|
||||
|
||||
void Attach(Entry & parent, Entry & child)
|
||||
{
|
||||
ASSERT_LESS_OR_EQUAL(child.m_rank, parent.m_rank, ());
|
||||
|
||||
child.m_root = parent.m_root;
|
||||
if (child.m_rank == parent.m_rank)
|
||||
++parent.m_rank;
|
||||
|
||||
auto & pv = parent.m_values;
|
||||
auto & cv = child.m_values;
|
||||
if (pv.size() < cv.size())
|
||||
pv.swap(cv);
|
||||
std::move(cv.begin(), cv.end(), std::back_inserter(pv));
|
||||
cv.clear();
|
||||
cv.shrink_to_fit();
|
||||
}
|
||||
|
||||
std::unordered_map<Key, Entry, Hash> m_table;
|
||||
};
|
||||
} // namespace base
|
||||
29
libs/base/collection_cast.hpp
Normal file
29
libs/base/collection_cast.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace base
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
template <typename T>
|
||||
struct ValueType
|
||||
{
|
||||
using TType = typename std::remove_reference_t<T>::value_type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using TValueType = typename ValueType<T>::TType;
|
||||
} // namespace details
|
||||
|
||||
// Use this function to cast one collection to annother.
|
||||
// I.E. list<int> const myList = collection_cast<list>(vector<int>{1, 2, 4, 5});
|
||||
// More examples:
|
||||
// auto const mySet = collection_cast<set>("aaabcccd");
|
||||
// auto const myMap = collection_cast<map>(vector<pair<int, int>>{{1, 2}, {3, 4}});
|
||||
template <template <typename... TArgs> class TTo, typename TFrom>
|
||||
auto collection_cast(TFrom && from) -> TTo<details::TValueType<TFrom>>
|
||||
{
|
||||
return TTo<details::TValueType<TFrom>>(begin(from), end(from));
|
||||
}
|
||||
} // namespace base
|
||||
42
libs/base/control_flow.hpp
Normal file
42
libs/base/control_flow.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace base
|
||||
{
|
||||
// This enum is used to control the flow of ForEach invocations.
|
||||
enum class ControlFlow
|
||||
{
|
||||
Break,
|
||||
Continue
|
||||
};
|
||||
|
||||
// A wrapper that calls |fn| with arguments |args|.
|
||||
// To avoid excessive calls, |fn| may signal the end of execution via its return value,
|
||||
// which should then be checked by the wrapper's user.
|
||||
template <typename Fn>
|
||||
class ControlFlowWrapper
|
||||
{
|
||||
public:
|
||||
template <typename Gn>
|
||||
explicit ControlFlowWrapper(Gn && gn) : m_fn(std::forward<Gn>(gn))
|
||||
{}
|
||||
|
||||
template <typename... Args>
|
||||
std::enable_if_t<std::is_same_v<std::invoke_result_t<Fn, Args...>, ControlFlow>, ControlFlow> operator()(
|
||||
Args &&... args)
|
||||
{
|
||||
return m_fn(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::enable_if_t<std::is_same_v<std::invoke_result_t<Fn, Args...>, void>, ControlFlow> operator()(Args &&... args)
|
||||
{
|
||||
m_fn(std::forward<Args>(args)...);
|
||||
return ControlFlow::Continue;
|
||||
}
|
||||
|
||||
private:
|
||||
Fn m_fn;
|
||||
};
|
||||
} // namespace base
|
||||
49
libs/base/deferred_task.cpp
Normal file
49
libs/base/deferred_task.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#include "base/deferred_task.hpp"
|
||||
|
||||
namespace base
|
||||
{
|
||||
DeferredTask::DeferredTask(Duration const & duration) : m_duration(duration)
|
||||
{
|
||||
m_thread = threads::SimpleThread([this]
|
||||
{
|
||||
std::unique_lock l(m_mutex);
|
||||
while (!m_terminate)
|
||||
{
|
||||
if (!m_fn)
|
||||
{
|
||||
m_cv.wait(l);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_cv.wait_for(l, m_duration) != std::cv_status::timeout || !m_fn)
|
||||
continue;
|
||||
|
||||
auto fn = std::move(m_fn);
|
||||
m_fn = nullptr;
|
||||
|
||||
l.unlock();
|
||||
fn();
|
||||
l.lock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
DeferredTask::~DeferredTask()
|
||||
{
|
||||
{
|
||||
std::unique_lock l(m_mutex);
|
||||
m_terminate = true;
|
||||
}
|
||||
m_cv.notify_one();
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
void DeferredTask::Drop()
|
||||
{
|
||||
{
|
||||
std::unique_lock l(m_mutex);
|
||||
m_fn = nullptr;
|
||||
}
|
||||
m_cv.notify_one();
|
||||
}
|
||||
} // namespace base
|
||||
40
libs/base/deferred_task.hpp
Normal file
40
libs/base/deferred_task.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/thread.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
namespace base
|
||||
{
|
||||
class DeferredTask
|
||||
{
|
||||
public:
|
||||
using Duration = std::chrono::duration<double>;
|
||||
|
||||
explicit DeferredTask(Duration const & duration);
|
||||
~DeferredTask();
|
||||
|
||||
void Drop();
|
||||
|
||||
template <typename Fn>
|
||||
void RestartWith(Fn const && fn)
|
||||
{
|
||||
{
|
||||
std::unique_lock l(m_mutex);
|
||||
m_fn = fn;
|
||||
}
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
threads::SimpleThread m_thread;
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
std::function<void()> m_fn;
|
||||
Duration m_duration;
|
||||
bool m_terminate = false;
|
||||
};
|
||||
} // namespace base
|
||||
102
libs/base/dfa_helpers.hpp
Normal file
102
libs/base/dfa_helpers.hpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace strings
|
||||
{
|
||||
template <typename DFA>
|
||||
class PrefixDFAModifier
|
||||
{
|
||||
public:
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
Iterator & Move(UniChar c)
|
||||
{
|
||||
if (Rejects())
|
||||
return *this;
|
||||
|
||||
if (Accepts())
|
||||
{
|
||||
auto currentIt = m_it;
|
||||
currentIt.Move(c);
|
||||
|
||||
// While moving m_it, errors number decreases while matching unmatched symbols:
|
||||
// source: a b c d e f
|
||||
// query: a b c d e f
|
||||
// errors: 5 4 3 2 1 0
|
||||
//
|
||||
// After a misprinted symbol errors number remains the same:
|
||||
// source: a b c d e f
|
||||
// query: a b z d e f
|
||||
// errors: 5 4 3 3 2 1
|
||||
//
|
||||
// source: a b c d e f
|
||||
// query: a b d c e f
|
||||
// errors: 5 4 3 3 2 1
|
||||
//
|
||||
// source: a b c d e f
|
||||
// query: a b d e f
|
||||
// errors: 5 4 3 3 2
|
||||
//
|
||||
// source: a b c d e f
|
||||
// query: a b c z d e f
|
||||
// errors: 5 4 3 3 3 2 1
|
||||
//
|
||||
// Errors number cannot decrease after it has increased once.
|
||||
|
||||
if (currentIt.ErrorsMade() > ErrorsMade())
|
||||
return *this;
|
||||
}
|
||||
|
||||
m_it.Move(c);
|
||||
if (m_it.Accepts())
|
||||
m_accepts = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Accepts() const { return m_accepts; }
|
||||
bool Rejects() const { return !Accepts() && m_it.Rejects(); }
|
||||
size_t ErrorsMade() const { return m_it.ErrorsMade(); }
|
||||
size_t PrefixErrorsMade() const { return m_it.PrefixErrorsMade(); }
|
||||
|
||||
private:
|
||||
friend class PrefixDFAModifier;
|
||||
|
||||
Iterator(typename DFA::Iterator it) : m_it(it), m_accepts(m_it.Accepts()) {}
|
||||
|
||||
typename DFA::Iterator m_it;
|
||||
bool m_accepts;
|
||||
};
|
||||
|
||||
explicit PrefixDFAModifier(DFA && dfa) : m_dfa(std::move(dfa)) {}
|
||||
|
||||
Iterator Begin() const { return Iterator(m_dfa.Begin()); }
|
||||
|
||||
private:
|
||||
DFA m_dfa;
|
||||
};
|
||||
|
||||
template <typename DFAIt, typename It>
|
||||
void DFAMove(DFAIt & it, It begin, It end)
|
||||
{
|
||||
for (; begin != end; ++begin)
|
||||
it.Move(*begin);
|
||||
}
|
||||
|
||||
template <typename DFAIt>
|
||||
void DFAMove(DFAIt & it, UniString const & s)
|
||||
{
|
||||
DFAMove(it, s.begin(), s.end());
|
||||
}
|
||||
|
||||
template <typename DFAIt>
|
||||
void DFAMove(DFAIt & it, std::string const & s)
|
||||
{
|
||||
DFAMove(it, MakeUniString(s));
|
||||
}
|
||||
} // namespace strings
|
||||
12
libs/base/exception.cpp
Normal file
12
libs/base/exception.cpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#include "base/exception.hpp"
|
||||
|
||||
RootException::RootException(char const * what, std::string const & msg) : m_msg(msg)
|
||||
{
|
||||
std::string asciiMsg(m_msg.size(), '?');
|
||||
|
||||
for (size_t i = 0; i < m_msg.size(); ++i)
|
||||
if (static_cast<unsigned char>(m_msg[i]) < 128)
|
||||
asciiMsg[i] = static_cast<char>(m_msg[i]);
|
||||
|
||||
m_whatWithAscii = std::string(what) + ", \"" + asciiMsg + "\"";
|
||||
}
|
||||
70
libs/base/exception.hpp
Normal file
70
libs/base/exception.hpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/internal/message.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
class RootException : public std::exception
|
||||
{
|
||||
public:
|
||||
RootException(char const * what, std::string const & msg);
|
||||
|
||||
virtual ~RootException() noexcept = default;
|
||||
|
||||
std::string const & Msg() const { return m_msg; }
|
||||
|
||||
// std::exception overrides:
|
||||
char const * what() const noexcept override { return m_whatWithAscii.c_str(); }
|
||||
|
||||
private:
|
||||
std::string m_whatWithAscii;
|
||||
std::string m_msg;
|
||||
};
|
||||
|
||||
template <typename Fn, typename... Args>
|
||||
std::invoke_result_t<Fn &&, Args &&...> ExceptionCatcher(std::string const & comment, bool & exceptionWasThrown,
|
||||
Fn && fn, Args &&... args) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
exceptionWasThrown = false;
|
||||
return std::forward<Fn>(fn)(std::forward<Args>(args)...);
|
||||
}
|
||||
catch (RootException const & ex)
|
||||
{
|
||||
LOG(LWARNING, ("RootException.", comment, ex.Msg(), ex.what()));
|
||||
}
|
||||
catch (std::exception const & ex)
|
||||
{
|
||||
LOG(LWARNING, ("std::exception.", comment, ex.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG(LWARNING, ("Unknown exception.", comment));
|
||||
}
|
||||
|
||||
exceptionWasThrown = true;
|
||||
using ReturnType = std::decay_t<std::invoke_result_t<Fn &&, Args &&...>>;
|
||||
if constexpr (!std::is_same_v<void, ReturnType>)
|
||||
{
|
||||
static ReturnType const defaultResult = {};
|
||||
return defaultResult;
|
||||
}
|
||||
}
|
||||
|
||||
#define DECLARE_EXCEPTION(exception_name, base_exception) \
|
||||
class exception_name : public base_exception \
|
||||
{ \
|
||||
public: \
|
||||
exception_name(char const * what, std::string const & msg) : base_exception(what, msg) {} \
|
||||
}
|
||||
|
||||
// TODO: Use SRC_LOGGING macro.
|
||||
#define MYTHROW(exception_name, msg) \
|
||||
throw exception_name(#exception_name " " __FILE__ ":" TO_STRING(__LINE__), ::base::Message msg)
|
||||
|
||||
#define MYTHROW1(exception_name, param1, msg) \
|
||||
throw exception_name(param1, #exception_name " " __FILE__ ":" TO_STRING(__LINE__), ::base::Message msg)
|
||||
19
libs/base/fast_math.cpp
Normal file
19
libs/base/fast_math.cpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Separate cpp for different compiler flags.
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace math
|
||||
{
|
||||
double Nan()
|
||||
{
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
double Infinity()
|
||||
{
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
bool is_finite(double t)
|
||||
{
|
||||
return std::isfinite(t);
|
||||
}
|
||||
} // namespace math
|
||||
59
libs/base/fifo_cache.hpp
Normal file
59
libs/base/fifo_cache.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-copy"
|
||||
#endif
|
||||
#include <boost/circular_buffer.hpp>
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
template <class Key, class Value, class HashContainer = std::unordered_map<Key, Value>>
|
||||
class FifoCache
|
||||
{
|
||||
template <typename K, typename V>
|
||||
friend class FifoCacheTest;
|
||||
|
||||
public:
|
||||
using Loader = std::function<void(Key const & key, Value & value)>;
|
||||
|
||||
/// \param capacity maximum size of the cache in number of items.
|
||||
/// \param loader Function which is called if it's necessary to load a new item for the cache.
|
||||
FifoCache(size_t capacity, Loader const & loader) : m_fifo(capacity), m_capacity(capacity), m_loader(loader) {}
|
||||
|
||||
/// \brief Loads value, if it's necessary, by |key| with |m_loader|, puts it to cache and
|
||||
/// returns the reference to the value to |m_map|.
|
||||
Value const & GetValue(Key const & key)
|
||||
{
|
||||
auto const it = m_map.find(key);
|
||||
if (it != m_map.cend())
|
||||
return it->second;
|
||||
|
||||
if (Size() >= m_capacity)
|
||||
{
|
||||
CHECK(!m_fifo.empty(), ());
|
||||
m_map.erase(m_fifo.back());
|
||||
}
|
||||
|
||||
m_fifo.push_front(key);
|
||||
|
||||
auto & v = m_map[key];
|
||||
m_loader(key, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t Size() const { return m_map.size(); }
|
||||
|
||||
HashContainer m_map;
|
||||
boost::circular_buffer<Key> m_fifo;
|
||||
size_t m_capacity;
|
||||
Loader m_loader;
|
||||
};
|
||||
76
libs/base/file_name_utils.cpp
Normal file
76
libs/base/file_name_utils.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#include "base/file_name_utils.hpp"
|
||||
|
||||
namespace base
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
void GetNameWithoutExt(string & name)
|
||||
{
|
||||
string::size_type const i = name.rfind('.');
|
||||
if (i != string::npos)
|
||||
name.erase(i);
|
||||
}
|
||||
|
||||
string FilenameWithoutExt(string name)
|
||||
{
|
||||
GetNameWithoutExt(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
string GetFileExtension(string const & name)
|
||||
{
|
||||
size_t const pos = name.find_last_of("./\\");
|
||||
return ((pos != string::npos && name[pos] == '.') ? name.substr(pos) : string());
|
||||
}
|
||||
|
||||
void GetNameFromFullPath(string & name)
|
||||
{
|
||||
string::size_type const i = name.find_last_of("/\\");
|
||||
if (i != string::npos)
|
||||
name = name.substr(i + 1);
|
||||
}
|
||||
|
||||
std::string FileNameFromFullPath(std::string path)
|
||||
{
|
||||
GetNameFromFullPath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
string GetNameFromFullPathWithoutExt(string path)
|
||||
{
|
||||
GetNameFromFullPath(path);
|
||||
GetNameWithoutExt(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
string GetDirectory(string const & name)
|
||||
{
|
||||
auto const sep = GetNativeSeparator();
|
||||
size_t const sepSize = sizeof(sep);
|
||||
|
||||
string::size_type i = name.rfind(sep);
|
||||
if (i == string::npos)
|
||||
return ".";
|
||||
while (i > sepSize && (name[i - sepSize] == sep))
|
||||
i -= sepSize;
|
||||
return name.substr(0, i ? i : sepSize);
|
||||
}
|
||||
|
||||
string::value_type GetNativeSeparator()
|
||||
{
|
||||
#ifdef OMIM_OS_WINDOWS
|
||||
return '\\';
|
||||
#else
|
||||
return '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
string AddSlashIfNeeded(string const & path)
|
||||
{
|
||||
auto const sep = GetNativeSeparator();
|
||||
string::size_type const pos = path.rfind(sep);
|
||||
if (pos != string::npos && pos + sizeof(sep) == path.size())
|
||||
return path;
|
||||
return path + sep;
|
||||
}
|
||||
} // namespace base
|
||||
60
libs/base/file_name_utils.hpp
Normal file
60
libs/base/file_name_utils.hpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
namespace base
|
||||
{
|
||||
/// Remove extension from file name.
|
||||
void GetNameWithoutExt(std::string & name);
|
||||
std::string FilenameWithoutExt(std::string name);
|
||||
/// @return File extension with the dot or empty std::string if no extension found.
|
||||
std::string GetFileExtension(std::string const & name);
|
||||
|
||||
/// Get file name from full path.
|
||||
void GetNameFromFullPath(std::string & name);
|
||||
|
||||
std::string FileNameFromFullPath(std::string path);
|
||||
|
||||
/// Get file name from full path without extension.
|
||||
std::string GetNameFromFullPathWithoutExt(std::string path);
|
||||
|
||||
/// Returns all but last components of the path. After dropping the last
|
||||
/// component, all trailing slashes are removed, unless the result is a
|
||||
/// root directory. If the argument is a single component, returns ".".
|
||||
std::string GetDirectory(std::string const & path);
|
||||
|
||||
/// Get native folder separator for the platform.
|
||||
std::string::value_type GetNativeSeparator();
|
||||
|
||||
/// Add the terminating slash to the folder path std::string if it's not already there.
|
||||
std::string AddSlashIfNeeded(std::string const & path);
|
||||
|
||||
namespace impl
|
||||
{
|
||||
inline std::string JoinPath(std::string const & file)
|
||||
{
|
||||
return file;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string JoinPath(std::string const & folder, Args &&... args)
|
||||
{
|
||||
if (folder.empty())
|
||||
return {};
|
||||
|
||||
return AddSlashIfNeeded(folder) + impl::JoinPath(std::forward<Args>(args)...);
|
||||
}
|
||||
} // namespace impl
|
||||
|
||||
/// Create full path from some folder using native folders separator.
|
||||
template <typename... Args>
|
||||
std::string JoinPath(std::string const & dir, std::string const & fileOrDir, Args &&... args)
|
||||
{
|
||||
ASSERT(!dir.empty(), ("JoinPath dir is empty"));
|
||||
ASSERT(!fileOrDir.empty(), ("JoinPath fileOrDir is empty"));
|
||||
return impl::JoinPath(dir, fileOrDir, std::forward<Args>(args)...);
|
||||
}
|
||||
} // namespace base
|
||||
110
libs/base/geo_object_id.cpp
Normal file
110
libs/base/geo_object_id.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
#include "base/geo_object_id.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace
|
||||
{
|
||||
// todo(@m) Uncomment when the transition from osm::Id to base::GeoObjectId is complete
|
||||
// and add assertions about the highest bit.
|
||||
// The old scheme used the highest bit and the new one does not.
|
||||
// uint64_t const kTypeMask = 0x7F00000000000000;
|
||||
uint64_t constexpr kTypeMask = 0xFF00000000000000;
|
||||
uint64_t constexpr kReservedMask = 0x00FF000000000000;
|
||||
uint64_t constexpr kSerialMask = 0x0000FFFFFFFFFFFF;
|
||||
} // namespace
|
||||
|
||||
namespace base
|
||||
{
|
||||
GeoObjectId::GeoObjectId(uint64_t encodedId) : m_encodedId(encodedId) {}
|
||||
|
||||
GeoObjectId::GeoObjectId(Type type, uint64_t id) : m_encodedId((static_cast<uint64_t>(type) << 56) | id) {}
|
||||
|
||||
uint64_t GeoObjectId::GetSerialId() const
|
||||
{
|
||||
ASSERT_NOT_EQUAL(m_encodedId & kTypeMask, 0, ());
|
||||
ASSERT_EQUAL(m_encodedId & kReservedMask, 0, ());
|
||||
return m_encodedId & kSerialMask;
|
||||
}
|
||||
|
||||
uint64_t GeoObjectId::GetEncodedId() const
|
||||
{
|
||||
return m_encodedId;
|
||||
}
|
||||
|
||||
GeoObjectId::Type GeoObjectId::GetType() const
|
||||
{
|
||||
ASSERT_EQUAL(m_encodedId & kReservedMask, 0, ());
|
||||
uint64_t const typeBits = (m_encodedId & kTypeMask) >> 56;
|
||||
switch (typeBits)
|
||||
{
|
||||
case 0x00: return Type::Invalid;
|
||||
case 0x01: return Type::OsmNode;
|
||||
case 0x02: return Type::OsmWay;
|
||||
case 0x03: return Type::OsmRelation;
|
||||
case 0x04: return Type::BookingComNode;
|
||||
case 0x05: return Type::OsmSurrogate;
|
||||
case 0x06: return Type::Fias;
|
||||
case 0x40: return Type::ObsoleteOsmNode;
|
||||
case 0x80: return Type::ObsoleteOsmWay;
|
||||
case 0xC0: return Type::ObsoleteOsmRelation;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
GeoObjectId MakeOsmNode(uint64_t id)
|
||||
{
|
||||
return GeoObjectId(GeoObjectId::Type::ObsoleteOsmNode, id);
|
||||
}
|
||||
|
||||
GeoObjectId MakeOsmWay(uint64_t id)
|
||||
{
|
||||
return GeoObjectId(GeoObjectId::Type::ObsoleteOsmWay, id);
|
||||
}
|
||||
|
||||
GeoObjectId MakeOsmRelation(uint64_t id)
|
||||
{
|
||||
return GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, id);
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, GeoObjectId const & geoObjectId)
|
||||
{
|
||||
os << geoObjectId.GetEncodedId();
|
||||
return os;
|
||||
}
|
||||
|
||||
std::istream & operator>>(std::istream & os, GeoObjectId & geoObjectId)
|
||||
{
|
||||
uint64_t encodedId;
|
||||
os >> encodedId;
|
||||
geoObjectId = GeoObjectId(encodedId);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string DebugPrint(GeoObjectId::Type const & t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case GeoObjectId::Type::Invalid: return "Invalid";
|
||||
case GeoObjectId::Type::OsmNode: return "Osm Node";
|
||||
case GeoObjectId::Type::OsmWay: return "Osm Way";
|
||||
case GeoObjectId::Type::OsmRelation: return "Osm Relation";
|
||||
case GeoObjectId::Type::BookingComNode: return "Booking.com";
|
||||
case GeoObjectId::Type::OsmSurrogate: return "Osm Surrogate";
|
||||
case GeoObjectId::Type::Fias: return "FIAS";
|
||||
case GeoObjectId::Type::ObsoleteOsmNode: return "Osm Node";
|
||||
case GeoObjectId::Type::ObsoleteOsmWay: return "Osm Way";
|
||||
case GeoObjectId::Type::ObsoleteOsmRelation: return "Osm Relation";
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::string DebugPrint(GeoObjectId const & id)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
// GetSerialId() does not work for invalid ids but we may still want to print them.
|
||||
oss << DebugPrint(id.GetType()) << " " << (id.GetEncodedId() & kSerialMask);
|
||||
return oss.str();
|
||||
}
|
||||
} // namespace base
|
||||
107
libs/base/geo_object_id.hpp
Normal file
107
libs/base/geo_object_id.hpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
namespace base
|
||||
{
|
||||
// GeoObjectId is used to pack the source of a geographical object together with its
|
||||
// id within this source (that is, its serial number) into a single 64-bit number.
|
||||
//
|
||||
// The bit encoding is as follows:
|
||||
// 0sss ssss RRRR RRRR xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
|
||||
// R - reserved bits
|
||||
// s - bits for object source
|
||||
// x - bits for serial number (object id within its source)
|
||||
//
|
||||
// The highest byte encodes one of (2^7 - 1) = 127 possible object sources.
|
||||
// Another byte is reserved and the last 6 bytes leave us with 2^48 possible values that can be
|
||||
// used for ids within a source.
|
||||
// Typically, the reserved byte will be zero but it may be used in future if the format changes.
|
||||
// At the time of writing (August 2018), OSM has approximately 2^32 different nodes with ids
|
||||
// starting from one (https://wiki.openstreetmap.org/wiki/Stats) and this is by far the largest
|
||||
// serial numbers that we use.
|
||||
// The highest bit is zero so that the resulting number is positive if read as a signed 64-bit
|
||||
// integer in two's complement notation. This is important for readability in some database systems
|
||||
// that do not support unsigned integers. An older scheme we used to store OsmIds tried to keep as
|
||||
// much bits for the serial number as possible and utilized the highest bit which resulted in a lot
|
||||
// of "negative" numbers.
|
||||
//
|
||||
// When all bits are set to zero the GeoObjectId is defined to be invalid.
|
||||
//
|
||||
// Another way would be to use separate headers for source and for categories within the source,
|
||||
// as in OSM->Way->Id instead of OSMWay->Id that we have now but we do not have this many sources
|
||||
// and the difference does not seem important. Also this would probably touch the highest bit.
|
||||
class GeoObjectId
|
||||
{
|
||||
public:
|
||||
// Sources of the objects.
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
Invalid = 0x00,
|
||||
OsmNode = 0x01,
|
||||
OsmWay = 0x02,
|
||||
OsmRelation = 0x03,
|
||||
BookingComNode = 0x04,
|
||||
|
||||
// Artificial objects that substitute objects not presented in OSM for some reason.
|
||||
// E.g., streets that only exist in addr:street fields of houses but not as separate OSM ways.
|
||||
OsmSurrogate = 0x05,
|
||||
|
||||
// Federal informational address system. http://www.ifias.ru/
|
||||
Fias = 0x06,
|
||||
|
||||
ObsoleteOsmNode = 0x40,
|
||||
ObsoleteOsmWay = 0x80,
|
||||
ObsoleteOsmRelation = 0xC0,
|
||||
};
|
||||
|
||||
static constexpr uint64_t kInvalid = 0ULL;
|
||||
|
||||
explicit GeoObjectId(uint64_t encodedId = kInvalid);
|
||||
explicit GeoObjectId(Type type, uint64_t id);
|
||||
|
||||
// Returns the id that the object has within its source.
|
||||
uint64_t GetSerialId() const;
|
||||
|
||||
// Returns the encoded value that contains both
|
||||
// the source type and the serial number.
|
||||
uint64_t GetEncodedId() const;
|
||||
|
||||
// Returns the source type of the object.
|
||||
Type GetType() const;
|
||||
|
||||
bool operator<(GeoObjectId const & other) const { return m_encodedId < other.m_encodedId; }
|
||||
bool operator==(GeoObjectId const & other) const { return m_encodedId == other.m_encodedId; }
|
||||
bool operator!=(GeoObjectId const & other) const { return !(*this == other); }
|
||||
|
||||
private:
|
||||
uint64_t m_encodedId;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, GeoObjectId const & geoObjectId);
|
||||
std::istream & operator>>(std::istream & os, GeoObjectId & geoObjectId);
|
||||
|
||||
// Helper functions for readability.
|
||||
GeoObjectId MakeOsmNode(uint64_t id);
|
||||
GeoObjectId MakeOsmWay(uint64_t id);
|
||||
GeoObjectId MakeOsmRelation(uint64_t id);
|
||||
|
||||
std::string DebugPrint(GeoObjectId::Type const & t);
|
||||
std::string DebugPrint(GeoObjectId const & id);
|
||||
} // namespace base
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<base::GeoObjectId>
|
||||
{
|
||||
std::size_t operator()(base::GeoObjectId const & k) const
|
||||
{
|
||||
auto const hash = std::hash<uint64_t>();
|
||||
return hash(k.GetEncodedId());
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
17
libs/base/gmtime.cpp
Normal file
17
libs/base/gmtime.cpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#include "base/gmtime.hpp"
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
namespace base
|
||||
{
|
||||
std::tm GmTime(time_t time)
|
||||
{
|
||||
std::tm result{};
|
||||
#ifndef OMIM_OS_WINDOWS
|
||||
gmtime_r(&time, &result);
|
||||
#else
|
||||
gmtime_s(&result, &time);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
} // namespace base
|
||||
9
libs/base/gmtime.hpp
Normal file
9
libs/base/gmtime.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <ctime>
|
||||
|
||||
namespace base
|
||||
{
|
||||
/// A cross-platform replacenemt of gmtime_r
|
||||
std::tm GmTime(time_t time);
|
||||
} // namespace base
|
||||
20
libs/base/internal/message.cpp
Normal file
20
libs/base/internal/message.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#include "message.hpp"
|
||||
|
||||
#include <utf8/unchecked.h>
|
||||
|
||||
namespace internal
|
||||
{
|
||||
std::string ToUtf8(std::u16string_view utf16)
|
||||
{
|
||||
std::string utf8;
|
||||
utf8::unchecked::utf16to8(utf16.begin(), utf16.end(), std::back_inserter(utf8));
|
||||
return utf8;
|
||||
}
|
||||
|
||||
std::string ToUtf8(std::u32string_view utf32)
|
||||
{
|
||||
std::string utf8;
|
||||
utf8::unchecked::utf32to8(utf32.begin(), utf32.end(), std::back_inserter(utf8));
|
||||
return utf8;
|
||||
}
|
||||
} // namespace internal
|
||||
279
libs/base/internal/message.hpp
Normal file
279
libs/base/internal/message.hpp
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
/// @name Declarations.
|
||||
//@{
|
||||
template <typename T>
|
||||
inline std::string DebugPrint(T const & t);
|
||||
|
||||
inline std::string DebugPrint(std::string s)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
inline std::string DebugPrint(char const * t);
|
||||
inline std::string DebugPrint(char * t)
|
||||
{
|
||||
return DebugPrint(static_cast<char const *>(t));
|
||||
}
|
||||
inline std::string DebugPrint(char t);
|
||||
inline std::string DebugPrint(char32_t t);
|
||||
|
||||
/// @name We are going step-by-step to C++20. Use UTF8 string literals instead.
|
||||
/// @{
|
||||
std::string DebugPrint(char16_t const * t) = delete;
|
||||
std::string DebugPrint(char16_t * t) = delete;
|
||||
std::string DebugPrint(char32_t const * t) = delete;
|
||||
std::string DebugPrint(char32_t * t) = delete;
|
||||
/// @}
|
||||
|
||||
template <typename U, typename V>
|
||||
inline std::string DebugPrint(std::pair<U, V> const & p);
|
||||
template <typename U, typename V, typename W>
|
||||
inline std::string DebugPrint(std::tuple<U, V, W> const & p);
|
||||
template <typename T>
|
||||
inline std::string DebugPrint(std::list<T> const & v);
|
||||
template <typename T>
|
||||
inline std::string DebugPrint(std::vector<T> const & v);
|
||||
template <typename T, typename C = std::less<T>>
|
||||
inline std::string DebugPrint(std::set<T, C> const & v);
|
||||
template <typename T, typename C = std::less<T>>
|
||||
inline std::string DebugPrint(std::multiset<T, C> const & v);
|
||||
template <typename U, typename V, typename C = std::less<U>>
|
||||
inline std::string DebugPrint(std::map<U, V, C> const & v);
|
||||
template <typename T>
|
||||
inline std::string DebugPrint(std::initializer_list<T> const & v);
|
||||
template <typename T>
|
||||
inline std::string DebugPrint(std::unique_ptr<T> const & v);
|
||||
|
||||
template <class Key, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
|
||||
inline std::string DebugPrint(std::unordered_set<Key, Hash, Pred> const & v);
|
||||
template <class Key, class T, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>>
|
||||
inline std::string DebugPrint(std::unordered_map<Key, T, Hash, Pred> const & v);
|
||||
//@}
|
||||
|
||||
template <typename T>
|
||||
inline std::string DebugPrint(T const & t)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << t;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
inline std::string DebugPrint(char const * t)
|
||||
{
|
||||
if (t)
|
||||
return {t};
|
||||
return {"NULL string pointer"};
|
||||
}
|
||||
|
||||
inline std::string DebugPrint(char t)
|
||||
{
|
||||
// return {1, t} wrongly constructs "\0x1t" string.
|
||||
return std::string(1, t);
|
||||
}
|
||||
|
||||
namespace internal
|
||||
{
|
||||
std::string ToUtf8(std::u16string_view utf16);
|
||||
std::string ToUtf8(std::u32string_view utf32);
|
||||
} // namespace internal
|
||||
|
||||
inline std::string DebugPrint(std::u16string const & utf16)
|
||||
{
|
||||
return internal::ToUtf8(utf16);
|
||||
}
|
||||
|
||||
inline std::string DebugPrint(std::u16string_view utf16)
|
||||
{
|
||||
return internal::ToUtf8(utf16);
|
||||
}
|
||||
|
||||
inline std::string DebugPrint(std::u32string const & utf32)
|
||||
{
|
||||
return internal::ToUtf8(utf32);
|
||||
}
|
||||
|
||||
inline std::string DebugPrint(std::u32string_view utf32)
|
||||
{
|
||||
return internal::ToUtf8(utf32);
|
||||
}
|
||||
|
||||
inline std::string DebugPrint(char32_t t)
|
||||
{
|
||||
return internal::ToUtf8(std::u32string_view{&t, 1});
|
||||
}
|
||||
|
||||
inline std::string DebugPrint(std::chrono::time_point<std::chrono::system_clock> const & ts)
|
||||
{
|
||||
auto t = std::chrono::system_clock::to_time_t(ts);
|
||||
std::string str = std::ctime(&t);
|
||||
str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename U, typename V>
|
||||
std::string DebugPrint(std::pair<U, V> const & p)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "(" << DebugPrint(p.first) << ", " << DebugPrint(p.second) << ")";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
template <typename U, typename V, typename W>
|
||||
std::string DebugPrint(std::tuple<U, V, W> const & p)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "(" << DebugPrint(get<0>(p)) << ", " << DebugPrint(get<1>(p)) << ", " << DebugPrint(get<2>(p)) << ")";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
template <typename IterT>
|
||||
std::string DebugPrintSequence(IterT beg, IterT end)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "[" << std::distance(beg, end) << ":";
|
||||
for (; beg != end; ++beg)
|
||||
out << " " << DebugPrint(*beg);
|
||||
out << " ]";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string DebugPrint(std::optional<T> const & p)
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "optional(" << DebugPrint(p.value()) << ")";
|
||||
return out.str();
|
||||
}
|
||||
else
|
||||
return "nullopt";
|
||||
}
|
||||
|
||||
std::string inline DebugPrint(std::nullopt_t const & p)
|
||||
{
|
||||
return "nullopt";
|
||||
}
|
||||
|
||||
// Avoid calling it for string literals.
|
||||
template <typename T, size_t N,
|
||||
typename = std::enable_if_t<!std::is_same<typename std::remove_cv<T>::type, char>::value &&
|
||||
!std::is_same<typename std::remove_cv<T>::type, char16_t>::value &&
|
||||
!std::is_same<typename std::remove_cv<T>::type, char32_t>::value>>
|
||||
inline std::string DebugPrint(T (&arr)[N])
|
||||
{
|
||||
return DebugPrintSequence(arr, arr + N);
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
inline std::string DebugPrint(std::array<T, N> const & v)
|
||||
{
|
||||
return DebugPrintSequence(v.begin(), v.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::string DebugPrint(std::vector<T> const & v)
|
||||
{
|
||||
return DebugPrintSequence(v.begin(), v.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::string DebugPrint(std::deque<T> const & d)
|
||||
{
|
||||
return DebugPrintSequence(d.begin(), d.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::string DebugPrint(std::list<T> const & v)
|
||||
{
|
||||
return DebugPrintSequence(v.begin(), v.end());
|
||||
}
|
||||
|
||||
template <typename T, typename C>
|
||||
inline std::string DebugPrint(std::set<T, C> const & v)
|
||||
{
|
||||
return DebugPrintSequence(v.begin(), v.end());
|
||||
}
|
||||
|
||||
template <typename T, typename C>
|
||||
inline std::string DebugPrint(std::multiset<T, C> const & v)
|
||||
{
|
||||
return DebugPrintSequence(v.begin(), v.end());
|
||||
}
|
||||
|
||||
template <typename U, typename V, typename C>
|
||||
inline std::string DebugPrint(std::map<U, V, C> const & v)
|
||||
{
|
||||
return DebugPrintSequence(v.begin(), v.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::string DebugPrint(std::initializer_list<T> const & v)
|
||||
{
|
||||
return DebugPrintSequence(v.begin(), v.end());
|
||||
}
|
||||
|
||||
template <class Key, class Hash, class Pred>
|
||||
inline std::string DebugPrint(std::unordered_set<Key, Hash, Pred> const & v)
|
||||
{
|
||||
return DebugPrintSequence(v.begin(), v.end());
|
||||
}
|
||||
|
||||
template <class Key, class T, class Hash, class Pred>
|
||||
inline std::string DebugPrint(std::unordered_map<Key, T, Hash, Pred> const & v)
|
||||
{
|
||||
return DebugPrintSequence(v.begin(), v.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::string DebugPrint(std::unique_ptr<T> const & v)
|
||||
{
|
||||
std::ostringstream out;
|
||||
if (v.get() != nullptr)
|
||||
out << DebugPrint(*v);
|
||||
else
|
||||
out << DebugPrint("null");
|
||||
return out.str();
|
||||
}
|
||||
|
||||
namespace base
|
||||
{
|
||||
inline std::string Message()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string Message(T const & t)
|
||||
{
|
||||
using ::DebugPrint;
|
||||
return DebugPrint(t);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::string Message(T const & t, Args const &... others)
|
||||
{
|
||||
using ::DebugPrint;
|
||||
return DebugPrint(t) + " " + Message(others...);
|
||||
}
|
||||
} // namespace base
|
||||
381
libs/base/levenshtein_dfa.cpp
Normal file
381
libs/base/levenshtein_dfa.cpp
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
#include "base/levenshtein_dfa.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace strings
|
||||
{
|
||||
namespace
|
||||
{
|
||||
size_t AbsDiff(size_t a, size_t b)
|
||||
{
|
||||
return a > b ? a - b : b - a;
|
||||
}
|
||||
|
||||
class TransitionTable
|
||||
{
|
||||
public:
|
||||
TransitionTable(UniString const & s, std::array<UniString, 11> const & prefixMisprints, size_t prefixSize)
|
||||
: m_s(s)
|
||||
, m_size(s.size())
|
||||
, m_prefixMisprints(prefixMisprints)
|
||||
, m_prefixSize(prefixSize)
|
||||
{}
|
||||
|
||||
void Move(LevenshteinDFA::State const & s, UniChar c, LevenshteinDFA::State & t)
|
||||
{
|
||||
t.Clear();
|
||||
for (auto const & p : s.m_positions)
|
||||
GetMoves(p, c, t);
|
||||
t.Normalize();
|
||||
}
|
||||
|
||||
private:
|
||||
void GetMoves(LevenshteinDFA::Position const & p, UniChar c, LevenshteinDFA::State & t)
|
||||
{
|
||||
auto & ps = t.m_positions;
|
||||
|
||||
if (p.IsTransposed())
|
||||
{
|
||||
if (p.m_offset + 2 <= m_size && m_s[p.m_offset] == c)
|
||||
ps.emplace_back(p.m_offset + 2, p.m_errorsLeft, false /* transposed */);
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(p.IsStandard(), ());
|
||||
|
||||
if (p.m_offset < m_size && m_s[p.m_offset] == c)
|
||||
{
|
||||
ps.emplace_back(p.m_offset + 1, p.m_errorsLeft, false /* transposed */);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p.m_errorsLeft == 0)
|
||||
return;
|
||||
|
||||
ps.emplace_back(p.m_offset, p.m_errorsLeft - 1, false /* transposed */);
|
||||
|
||||
if (p.m_offset < m_prefixSize)
|
||||
{
|
||||
// Allow only prefixMisprints for prefix.
|
||||
if (IsAllowedPrefixMisprint(c, p.m_offset))
|
||||
ps.emplace_back(p.m_offset + 1, p.m_errorsLeft - 1, false /* transposed */);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p.m_offset == m_size)
|
||||
return;
|
||||
|
||||
ps.emplace_back(p.m_offset + 1, p.m_errorsLeft - 1, false /* transposed */);
|
||||
|
||||
size_t i;
|
||||
if (FindRelevant(p, c, i))
|
||||
{
|
||||
ASSERT_GREATER(i, 0, (i));
|
||||
ASSERT_LESS_OR_EQUAL(p.m_offset + i + 1, m_size, ());
|
||||
ps.emplace_back(p.m_offset + i + 1, p.m_errorsLeft - i, false /* transposed */);
|
||||
|
||||
if (i == 1)
|
||||
ps.emplace_back(p.m_offset, p.m_errorsLeft - 1, true /* transposed */);
|
||||
}
|
||||
}
|
||||
|
||||
bool FindRelevant(LevenshteinDFA::Position const & p, UniChar c, size_t & i) const
|
||||
{
|
||||
size_t const limit = std::min(m_size - p.m_offset, p.m_errorsLeft + 1);
|
||||
|
||||
for (i = 0; i < limit; ++i)
|
||||
if (m_s[p.m_offset + i] == c)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAllowedPrefixMisprint(UniChar c, size_t position) const
|
||||
{
|
||||
CHECK_LESS(position, m_prefixSize, ());
|
||||
|
||||
for (auto const & misprints : m_prefixMisprints)
|
||||
if (base::IsExist(misprints, c) && base::IsExist(misprints, m_s[position]))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
UniString const & m_s;
|
||||
size_t const m_size;
|
||||
std::array<UniString, 11> const m_prefixMisprints;
|
||||
size_t const m_prefixSize;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// LevenshteinDFA ----------------------------------------------------------------------------------
|
||||
// static
|
||||
size_t const LevenshteinDFA::kStartingState = 0;
|
||||
size_t const LevenshteinDFA::kRejectingState = 1;
|
||||
|
||||
// LevenshteinDFA::Position ------------------------------------------------------------------------
|
||||
LevenshteinDFA::Position::Position(size_t offset, size_t errorsLeft, bool transposed)
|
||||
: m_offset(offset)
|
||||
, m_errorsLeft(errorsLeft)
|
||||
, m_transposed(transposed)
|
||||
{}
|
||||
|
||||
bool LevenshteinDFA::Position::SubsumedBy(Position const & rhs) const
|
||||
{
|
||||
if (m_errorsLeft >= rhs.m_errorsLeft)
|
||||
return false;
|
||||
|
||||
auto const errorsAvail = rhs.m_errorsLeft - m_errorsLeft;
|
||||
|
||||
if (IsStandard() && rhs.IsStandard())
|
||||
return AbsDiff(m_offset, rhs.m_offset) <= errorsAvail;
|
||||
|
||||
if (IsStandard() && rhs.IsTransposed())
|
||||
return m_offset == rhs.m_offset && m_errorsLeft == 0;
|
||||
|
||||
if (IsTransposed() && rhs.IsStandard())
|
||||
return AbsDiff(m_offset + 1, rhs.m_offset) <= errorsAvail;
|
||||
|
||||
ASSERT(IsTransposed(), ());
|
||||
ASSERT(rhs.IsTransposed(), ());
|
||||
return m_offset == rhs.m_offset;
|
||||
}
|
||||
|
||||
bool LevenshteinDFA::Position::operator<(Position const & rhs) const
|
||||
{
|
||||
if (m_offset != rhs.m_offset)
|
||||
return m_offset < rhs.m_offset;
|
||||
if (m_errorsLeft != rhs.m_errorsLeft)
|
||||
return m_errorsLeft < rhs.m_errorsLeft;
|
||||
return m_transposed < rhs.m_transposed;
|
||||
}
|
||||
|
||||
bool LevenshteinDFA::Position::operator==(Position const & rhs) const
|
||||
{
|
||||
return m_offset == rhs.m_offset && m_errorsLeft == rhs.m_errorsLeft && m_transposed == rhs.m_transposed;
|
||||
}
|
||||
|
||||
// LevenshteinDFA::State ---------------------------------------------------------------------------
|
||||
void LevenshteinDFA::State::Normalize()
|
||||
{
|
||||
size_t i = 0;
|
||||
size_t j = m_positions.size();
|
||||
|
||||
while (i < j)
|
||||
{
|
||||
auto const & cur = m_positions[i];
|
||||
|
||||
auto it = find_if(m_positions.begin(), m_positions.begin() + j,
|
||||
[&](Position const & rhs) { return cur.SubsumedBy(rhs); });
|
||||
if (it != m_positions.begin() + j)
|
||||
{
|
||||
ASSERT_GREATER(j, 0, ());
|
||||
--j;
|
||||
std::swap(m_positions[i], m_positions[j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
m_positions.erase(m_positions.begin() + j, m_positions.end());
|
||||
base::SortUnique(m_positions);
|
||||
}
|
||||
|
||||
// LevenshteinDFA ----------------------------------------------------------------------------------
|
||||
// static
|
||||
LevenshteinDFA::LevenshteinDFA(UniString const & s, size_t prefixSize, std::array<UniString, 11> const & prefixMisprints,
|
||||
size_t maxErrors)
|
||||
: m_size(s.size())
|
||||
, m_maxErrors(maxErrors)
|
||||
{
|
||||
m_alphabet.assign(s.begin(), s.end());
|
||||
CHECK_LESS_OR_EQUAL(prefixSize, s.size(), ());
|
||||
|
||||
auto const pSize = static_cast<std::iterator_traits<UniString::iterator>::difference_type>(prefixSize);
|
||||
for (auto it = s.begin(); std::distance(s.begin(), it) < pSize; ++it)
|
||||
{
|
||||
for (auto const & misprints : prefixMisprints)
|
||||
if (base::IsExist(misprints, *it))
|
||||
m_alphabet.insert(m_alphabet.end(), misprints.begin(), misprints.end());
|
||||
}
|
||||
base::SortUnique(m_alphabet);
|
||||
|
||||
UniChar missed = 0;
|
||||
for (size_t i = 0; i < m_alphabet.size() && missed >= m_alphabet[i]; ++i)
|
||||
if (missed == m_alphabet[i])
|
||||
++missed;
|
||||
m_alphabet.push_back(missed);
|
||||
|
||||
std::queue<State> states;
|
||||
std::map<State, size_t> visited;
|
||||
|
||||
auto pushState = [&states, &visited, this](State const & state, size_t id)
|
||||
{
|
||||
ASSERT_EQUAL(id, m_transitions.size(), ());
|
||||
ASSERT_EQUAL(visited.count(state), 0, (state, id));
|
||||
|
||||
ASSERT_EQUAL(m_transitions.size(), m_accepting.size(), ());
|
||||
ASSERT_EQUAL(m_transitions.size(), m_errorsMade.size(), ());
|
||||
|
||||
states.emplace(state);
|
||||
visited[state] = id;
|
||||
m_transitions.emplace_back(m_alphabet.size());
|
||||
m_accepting.push_back(false);
|
||||
m_errorsMade.push_back(ErrorsMade(state));
|
||||
m_prefixErrorsMade.push_back(PrefixErrorsMade(state));
|
||||
};
|
||||
|
||||
pushState(MakeStart(), kStartingState);
|
||||
pushState(MakeRejecting(), kRejectingState);
|
||||
|
||||
TransitionTable table(s, prefixMisprints, prefixSize);
|
||||
|
||||
while (!states.empty())
|
||||
{
|
||||
auto const curr = states.front();
|
||||
states.pop();
|
||||
ASSERT(IsValid(curr), (curr));
|
||||
|
||||
ASSERT_GREATER(visited.count(curr), 0, (curr));
|
||||
auto const id = visited[curr];
|
||||
ASSERT_LESS(id, m_transitions.size(), ());
|
||||
|
||||
if (IsAccepting(curr))
|
||||
m_accepting[id] = true;
|
||||
|
||||
for (size_t i = 0; i < m_alphabet.size(); ++i)
|
||||
{
|
||||
State next;
|
||||
table.Move(curr, m_alphabet[i], next);
|
||||
|
||||
size_t nid;
|
||||
|
||||
auto const it = visited.find(next);
|
||||
if (it == visited.end())
|
||||
{
|
||||
nid = visited.size();
|
||||
pushState(next, nid);
|
||||
}
|
||||
else
|
||||
{
|
||||
nid = it->second;
|
||||
}
|
||||
|
||||
m_transitions[id][i] = nid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LevenshteinDFA::LevenshteinDFA(std::string const & s, size_t prefixSize, size_t maxErrors)
|
||||
: LevenshteinDFA(MakeUniString(s), prefixSize, {} /* prefixMisprints */, maxErrors)
|
||||
{}
|
||||
|
||||
LevenshteinDFA::LevenshteinDFA(UniString const & s, size_t maxErrors)
|
||||
: LevenshteinDFA(s, 0 /* prefixSize */, {} /* prefixMisprints */, maxErrors)
|
||||
{}
|
||||
|
||||
LevenshteinDFA::LevenshteinDFA(std::string const & s, size_t maxErrors)
|
||||
: LevenshteinDFA(MakeUniString(s), 0 /* prefixSize */, {} /* prefixMisprints */, maxErrors)
|
||||
{}
|
||||
|
||||
LevenshteinDFA::State LevenshteinDFA::MakeStart()
|
||||
{
|
||||
State state;
|
||||
state.m_positions.emplace_back(0 /* offset */, m_maxErrors /* errorsLeft */, false /* transposed */);
|
||||
return state;
|
||||
}
|
||||
|
||||
LevenshteinDFA::State LevenshteinDFA::MakeRejecting()
|
||||
{
|
||||
return State();
|
||||
}
|
||||
|
||||
bool LevenshteinDFA::IsValid(Position const & p) const
|
||||
{
|
||||
return p.m_offset <= m_size && p.m_errorsLeft <= m_maxErrors;
|
||||
}
|
||||
|
||||
bool LevenshteinDFA::IsValid(State const & s) const
|
||||
{
|
||||
for (auto const & p : s.m_positions)
|
||||
if (!IsValid(p))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LevenshteinDFA::IsAccepting(Position const & p) const
|
||||
{
|
||||
return p.IsStandard() && m_size - p.m_offset <= p.m_errorsLeft;
|
||||
}
|
||||
|
||||
bool LevenshteinDFA::IsAccepting(State const & s) const
|
||||
{
|
||||
for (auto const & p : s.m_positions)
|
||||
if (IsAccepting(p))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t LevenshteinDFA::ErrorsMade(State const & s) const
|
||||
{
|
||||
size_t errorsMade = m_maxErrors;
|
||||
for (auto const & p : s.m_positions)
|
||||
{
|
||||
if (!IsAccepting(p))
|
||||
continue;
|
||||
auto const errorsLeft = p.m_errorsLeft - (m_size - p.m_offset);
|
||||
errorsMade = std::min(errorsMade, m_maxErrors - errorsLeft);
|
||||
}
|
||||
return errorsMade;
|
||||
}
|
||||
|
||||
size_t LevenshteinDFA::PrefixErrorsMade(State const & s) const
|
||||
{
|
||||
size_t errorsMade = m_maxErrors;
|
||||
for (auto const & p : s.m_positions)
|
||||
errorsMade = std::min(errorsMade, m_maxErrors - p.m_errorsLeft);
|
||||
return errorsMade;
|
||||
}
|
||||
|
||||
size_t LevenshteinDFA::Move(size_t s, UniChar c) const
|
||||
{
|
||||
ASSERT_GREATER(m_alphabet.size(), 0, ());
|
||||
ASSERT(is_sorted(m_alphabet.begin(), m_alphabet.end() - 1), ());
|
||||
|
||||
size_t i;
|
||||
auto const it = lower_bound(m_alphabet.begin(), m_alphabet.end() - 1, c);
|
||||
if (it == m_alphabet.end() - 1 || *it != c)
|
||||
i = m_alphabet.size() - 1;
|
||||
else
|
||||
i = distance(m_alphabet.begin(), it);
|
||||
|
||||
return m_transitions[s][i];
|
||||
}
|
||||
|
||||
std::string DebugPrint(LevenshteinDFA::Position const & p)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Position [" << p.m_offset << ", " << p.m_errorsLeft << ", " << p.m_transposed << "]";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string DebugPrint(LevenshteinDFA::State const & s)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "State [";
|
||||
for (size_t i = 0; i < s.m_positions.size(); ++i)
|
||||
{
|
||||
os << DebugPrint(s.m_positions[i]);
|
||||
if (i + 1 != s.m_positions.size())
|
||||
os << ", ";
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
} // namespace strings
|
||||
153
libs/base/levenshtein_dfa.hpp
Normal file
153
libs/base/levenshtein_dfa.hpp
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace strings
|
||||
{
|
||||
// This class represents a DFA recognizing a language consisting of
|
||||
// all words that are close enough to a given string, in terms of
|
||||
// Levenshtein distance. Levenshtein distance treats deletions,
|
||||
// insertions and replacements as errors. Transpositions are handled
|
||||
// in a quite special way - its assumed that all transformations are
|
||||
// applied in parallel, i.e. the Levenshtein distance between "ab" and
|
||||
// "bca" is three, not two. The code is based on the work "Fast
|
||||
// String Correction with Levenshtein-Automata" by Klaus U. Schulz and
|
||||
// Stoyan Mihov. For a fixed number of allowed errors and fixed
|
||||
// alphabet the construction time and size of automata is
|
||||
// O(length of the pattern), but the size grows exponentially with the
|
||||
// number of errors, so be reasonable and don't use this class when
|
||||
// the number of errors is too high.
|
||||
//
|
||||
// *NOTE* The class *IS* thread-safe.
|
||||
//
|
||||
// TODO (@y): consider to implement a factory of automata, that will
|
||||
// be able to construct them quickly for a fixed number of errors.
|
||||
class LevenshteinDFA
|
||||
{
|
||||
public:
|
||||
static size_t const kStartingState;
|
||||
static size_t const kRejectingState;
|
||||
|
||||
struct Position
|
||||
{
|
||||
Position() = default;
|
||||
Position(size_t offset, size_t errorsLeft, bool transposed);
|
||||
|
||||
// SubsumedBy is a relation on two positions, which allows to
|
||||
// efficiently remove unnecessary positions in a state. When the
|
||||
// function returns true, it means that |rhs| is more powerful
|
||||
// than the current position and it is safe to remove the current
|
||||
// position from the state, if the state contains |rhs|.
|
||||
bool SubsumedBy(Position const & rhs) const;
|
||||
|
||||
bool operator<(Position const & rhs) const;
|
||||
bool operator==(Position const & rhs) const;
|
||||
|
||||
bool IsStandard() const { return !m_transposed; }
|
||||
bool IsTransposed() const { return m_transposed; }
|
||||
|
||||
size_t m_offset = 0;
|
||||
size_t m_errorsLeft = 0;
|
||||
bool m_transposed = false;
|
||||
};
|
||||
|
||||
struct State
|
||||
{
|
||||
void Normalize();
|
||||
void Clear() { m_positions.clear(); }
|
||||
|
||||
bool operator<(State const & rhs) const { return m_positions < rhs.m_positions; }
|
||||
|
||||
std::vector<Position> m_positions;
|
||||
};
|
||||
|
||||
// An iterator to the current state in the DFA.
|
||||
//
|
||||
// *NOTE* The class *IS NOT* thread safe. Moreover, it should not be
|
||||
// used after destruction of the corresponding DFA.
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
Iterator & Move(UniChar c)
|
||||
{
|
||||
m_s = m_dfa.Move(m_s, c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Accepts() const { return m_dfa.IsAccepting(m_s); }
|
||||
bool Rejects() const { return m_dfa.IsRejecting(m_s); }
|
||||
|
||||
size_t ErrorsMade() const { return m_dfa.ErrorsMade(m_s); }
|
||||
size_t PrefixErrorsMade() const { return m_dfa.PrefixErrorsMade(m_s); }
|
||||
|
||||
private:
|
||||
friend class LevenshteinDFA;
|
||||
|
||||
explicit Iterator(LevenshteinDFA const & dfa) : m_s(kStartingState), m_dfa(dfa) {}
|
||||
|
||||
size_t m_s;
|
||||
LevenshteinDFA const & m_dfa;
|
||||
};
|
||||
|
||||
LevenshteinDFA() = default;
|
||||
LevenshteinDFA(LevenshteinDFA const &) = delete;
|
||||
LevenshteinDFA(LevenshteinDFA &&) = default;
|
||||
LevenshteinDFA & operator=(LevenshteinDFA &&) = default;
|
||||
|
||||
LevenshteinDFA(UniString const & s, size_t prefixSize, std::array<UniString, 11> const & prefixMisprints,
|
||||
size_t maxErrors);
|
||||
LevenshteinDFA(std::string const & s, size_t prefixSize, size_t maxErrors);
|
||||
LevenshteinDFA(UniString const & s, size_t maxErrors);
|
||||
LevenshteinDFA(std::string const & s, size_t maxErrors);
|
||||
|
||||
bool IsEmpty() const { return m_alphabet.empty(); }
|
||||
|
||||
Iterator Begin() const { return Iterator(*this); }
|
||||
|
||||
size_t GetNumStates() const { return m_transitions.size(); }
|
||||
size_t GetAlphabetSize() const { return m_alphabet.size(); }
|
||||
|
||||
private:
|
||||
friend class Iterator;
|
||||
|
||||
State MakeStart();
|
||||
State MakeRejecting();
|
||||
|
||||
bool IsValid(Position const & p) const;
|
||||
bool IsValid(State const & s) const;
|
||||
|
||||
bool IsAccepting(Position const & p) const;
|
||||
bool IsAccepting(State const & s) const;
|
||||
bool IsAccepting(size_t s) const { return m_accepting[s]; }
|
||||
|
||||
bool IsRejecting(State const & s) const { return s.m_positions.empty(); }
|
||||
bool IsRejecting(size_t s) const { return s == kRejectingState; }
|
||||
|
||||
// Returns minimum number of made errors among accepting positions in |s|.
|
||||
size_t ErrorsMade(State const & s) const;
|
||||
size_t ErrorsMade(size_t s) const { return m_errorsMade[s]; }
|
||||
|
||||
// Returns minimum number of errors already made. This number cannot decrease.
|
||||
size_t PrefixErrorsMade(State const & s) const;
|
||||
size_t PrefixErrorsMade(size_t s) const { return m_prefixErrorsMade[s]; }
|
||||
|
||||
size_t Move(size_t s, UniChar c) const;
|
||||
|
||||
size_t m_size;
|
||||
size_t m_maxErrors;
|
||||
|
||||
std::vector<UniChar> m_alphabet;
|
||||
|
||||
std::vector<std::vector<size_t>> m_transitions;
|
||||
std::vector<bool> m_accepting;
|
||||
std::vector<size_t> m_errorsMade;
|
||||
std::vector<size_t> m_prefixErrorsMade;
|
||||
};
|
||||
|
||||
std::string DebugPrint(LevenshteinDFA::Position const & p);
|
||||
std::string DebugPrint(LevenshteinDFA::State const & s);
|
||||
} // namespace strings
|
||||
92
libs/base/limited_priority_queue.hpp
Normal file
92
libs/base/limited_priority_queue.hpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace base
|
||||
{
|
||||
// Priority queue that stores only N smallest elements.
|
||||
template <typename T, typename CompareT = std::less<T>>
|
||||
class limited_priority_queue
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef typename std::vector<T>::const_iterator const_iterator;
|
||||
|
||||
explicit limited_priority_queue(size_t maxSize = 1, CompareT compare = CompareT())
|
||||
: m_maxSize(maxSize == 0 ? 1 : maxSize)
|
||||
, m_compare(compare)
|
||||
{}
|
||||
|
||||
void push(T t)
|
||||
{
|
||||
if (m_queue.size() < m_maxSize)
|
||||
{
|
||||
m_queue.push_back(std::move(t));
|
||||
push_heap(m_queue.begin(), m_queue.end(), m_compare);
|
||||
}
|
||||
else if (m_compare(t, top()))
|
||||
{
|
||||
// This can be optimized by writing decrease_head_heap().
|
||||
pop_heap(m_queue.begin(), m_queue.end(), m_compare);
|
||||
m_queue.back() = std::move(t);
|
||||
push_heap(m_queue.begin(), m_queue.end(), m_compare);
|
||||
}
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
ASSERT(!empty(), ());
|
||||
pop_heap(m_queue.begin(), m_queue.end(), m_compare);
|
||||
m_queue.pop_back();
|
||||
}
|
||||
|
||||
void set_max_size(size_t maxSize)
|
||||
{
|
||||
// This can be optimized by writing pop_n_heap().
|
||||
m_maxSize = (maxSize == 0 ? 1 : maxSize);
|
||||
while (size() > m_maxSize)
|
||||
pop();
|
||||
}
|
||||
|
||||
size_t max_size() const { return m_maxSize; }
|
||||
bool empty() const { return m_queue.empty(); }
|
||||
size_t size() const { return m_queue.size(); }
|
||||
T const & top() const
|
||||
{
|
||||
ASSERT(!empty(), ());
|
||||
return m_queue.front();
|
||||
}
|
||||
|
||||
const_iterator begin() const { return m_queue.begin(); }
|
||||
const_iterator end() const { return m_queue.end(); }
|
||||
|
||||
void clear() { m_queue.clear(); }
|
||||
|
||||
void swap(limited_priority_queue & queue) noexcept
|
||||
{
|
||||
m_queue.swap(queue.m_queue);
|
||||
using std::swap;
|
||||
swap(m_maxSize, queue.m_maxSize);
|
||||
swap(m_compare, queue.m_compare);
|
||||
}
|
||||
|
||||
void reserve(size_t n) { m_queue.reserve(n); }
|
||||
|
||||
std::vector<T> move() { return std::move(m_queue); }
|
||||
|
||||
private:
|
||||
std::vector<T> m_queue;
|
||||
size_t m_maxSize;
|
||||
CompareT m_compare;
|
||||
};
|
||||
|
||||
template <typename T, typename CompareT>
|
||||
void swap(limited_priority_queue<T, CompareT> & q1, limited_priority_queue<T, CompareT> & q2) noexcept
|
||||
{
|
||||
q1.swap(q2);
|
||||
}
|
||||
} // namespace base
|
||||
76
libs/base/linked_map.hpp
Normal file
76
libs/base/linked_map.hpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace base
|
||||
{
|
||||
template <typename Key, typename Value, template <typename...> typename Map = std::unordered_map>
|
||||
class LinkedMap
|
||||
{
|
||||
public:
|
||||
using KeyType = Key;
|
||||
using ValueType = Value;
|
||||
using ListType = std::list<std::pair<KeyType, ValueType>>;
|
||||
using MapType = Map<Key, typename ListType::iterator>;
|
||||
|
||||
template <typename T>
|
||||
bool Emplace(KeyType const & key, T && value)
|
||||
{
|
||||
if (m_map.find(key) != m_map.cend())
|
||||
return false;
|
||||
|
||||
m_list.emplace_back(key, std::forward<T>(value));
|
||||
m_map.emplace(key, std::prev(m_list.end()));
|
||||
return true;
|
||||
}
|
||||
|
||||
void PopFront()
|
||||
{
|
||||
CHECK(!m_map.empty(), ());
|
||||
m_map.erase(m_list.front().first);
|
||||
m_list.pop_front();
|
||||
}
|
||||
|
||||
bool Erase(KeyType const & key)
|
||||
{
|
||||
auto const it = m_map.find(key);
|
||||
if (it == m_map.cend())
|
||||
return false;
|
||||
|
||||
m_list.erase(it->second);
|
||||
m_map.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Contains(KeyType const & key) const { return m_map.find(key) != m_map.cend(); }
|
||||
|
||||
ValueType const & Get(KeyType const & key) const
|
||||
{
|
||||
auto const it = m_map.find(key);
|
||||
CHECK(it != m_map.cend(), ());
|
||||
|
||||
return it->second->second;
|
||||
}
|
||||
|
||||
ValueType & Front() { return m_list.front().second; }
|
||||
|
||||
ValueType const & Front() const { return m_list.front().second; }
|
||||
|
||||
void Swap(LinkedMap & linkedMap)
|
||||
{
|
||||
m_map.swap(linkedMap.m_map);
|
||||
m_list.swap(linkedMap.m_list);
|
||||
}
|
||||
|
||||
size_t Size() const { return m_list.size(); }
|
||||
|
||||
bool IsEmpty() const { return m_list.empty(); }
|
||||
|
||||
private:
|
||||
ListType m_list;
|
||||
MapType m_map;
|
||||
};
|
||||
} // namespace base
|
||||
124
libs/base/logging.cpp
Normal file
124
libs/base/logging.cpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#include "base/logging.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/thread.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
|
||||
namespace base
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::mutex g_logMutex;
|
||||
} // namespace
|
||||
|
||||
std::string ToString(LogLevel level)
|
||||
{
|
||||
auto const & names = GetLogLevelNames();
|
||||
CHECK_LESS(level, names.size(), ());
|
||||
return ::DebugPrint(names[level]);
|
||||
}
|
||||
|
||||
std::optional<LogLevel> FromString(std::string const & s)
|
||||
{
|
||||
ASSERT(!s.empty(), ("Log level should not be empty"));
|
||||
|
||||
auto const & names = GetLogLevelNames();
|
||||
auto const it = std::find(names.begin(), names.end(), std::toupper(s[0]));
|
||||
if (it == names.end())
|
||||
return {};
|
||||
return static_cast<LogLevel>(std::distance(names.begin(), it));
|
||||
}
|
||||
|
||||
std::array<char, NUM_LOG_LEVELS> const & GetLogLevelNames()
|
||||
{
|
||||
static std::array<char, NUM_LOG_LEVELS> constexpr kLogLevelNames{'D', 'I', 'W', 'E', 'C'};
|
||||
return kLogLevelNames;
|
||||
}
|
||||
|
||||
// static
|
||||
LogHelper & LogHelper::Instance()
|
||||
{
|
||||
static LogHelper instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
int LogHelper::GetThreadID()
|
||||
{
|
||||
int & id = m_threadID[threads::GetCurrentThreadID()];
|
||||
if (id == 0)
|
||||
id = ++m_threadsCount;
|
||||
return id;
|
||||
}
|
||||
|
||||
void LogHelper::WriteProlog(std::ostream & s, LogLevel level)
|
||||
{
|
||||
double const sec = m_timer.ElapsedSeconds();
|
||||
s << GetLogLevelNames()[level] << '(' << GetThreadID() << ") " << std::fixed << std::setprecision(5) << sec << ' ';
|
||||
}
|
||||
|
||||
void LogHelper::WriteLog(std::ostream & s, SrcPoint const & srcPoint, std::string const & msg)
|
||||
{
|
||||
s << DebugPrint(srcPoint) << msg << std::endl;
|
||||
}
|
||||
|
||||
void LogMessageDefault(LogLevel level, SrcPoint const & srcPoint, std::string const & msg)
|
||||
{
|
||||
auto & logger = LogHelper::Instance();
|
||||
std::ostringstream out;
|
||||
|
||||
std::lock_guard lock(g_logMutex);
|
||||
logger.WriteProlog(out, level);
|
||||
logger.WriteLog(out, srcPoint, msg);
|
||||
|
||||
std::cerr << out.str();
|
||||
|
||||
CHECK_LESS(level, g_LogAbortLevel, ("Abort. Log level is too serious", level));
|
||||
}
|
||||
|
||||
void LogMessageTests(LogLevel level, SrcPoint const &, std::string const & msg)
|
||||
{
|
||||
{
|
||||
std::lock_guard lock(g_logMutex);
|
||||
std::cerr << msg << std::endl;
|
||||
}
|
||||
|
||||
CHECK_LESS(level, g_LogAbortLevel, ("Abort. Log level is too serious", level));
|
||||
}
|
||||
|
||||
LogMessageFn LogMessage = &LogMessageDefault;
|
||||
|
||||
LogMessageFn SetLogMessageFn(LogMessageFn fn)
|
||||
{
|
||||
std::swap(LogMessage, fn);
|
||||
return fn;
|
||||
}
|
||||
|
||||
LogLevel GetDefaultLogLevel()
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
return LDEBUG;
|
||||
#else
|
||||
return LINFO;
|
||||
#endif // defined(DEBUG)
|
||||
}
|
||||
|
||||
LogLevel GetDefaultLogAbortLevel()
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
return LERROR;
|
||||
#else
|
||||
return LCRITICAL;
|
||||
#endif // defined(DEBUG)
|
||||
}
|
||||
|
||||
AtomicLogLevel g_LogLevel = {GetDefaultLogLevel()};
|
||||
AtomicLogLevel g_LogAbortLevel = {GetDefaultLogAbortLevel()};
|
||||
} // namespace base
|
||||
135
libs/base/logging.hpp
Normal file
135
libs/base/logging.hpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/internal/message.hpp"
|
||||
#include "base/src_point.hpp"
|
||||
#include "base/thread.hpp"
|
||||
#include "base/timer.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace base
|
||||
{
|
||||
enum LogLevel
|
||||
{
|
||||
LDEBUG,
|
||||
LINFO,
|
||||
LWARNING,
|
||||
LERROR,
|
||||
LCRITICAL,
|
||||
|
||||
NUM_LOG_LEVELS
|
||||
};
|
||||
|
||||
class LogHelper
|
||||
{
|
||||
public:
|
||||
static LogHelper & Instance();
|
||||
|
||||
int GetThreadID();
|
||||
void WriteProlog(std::ostream & s, LogLevel level);
|
||||
static void WriteLog(std::ostream & s, SrcPoint const & srcPoint, std::string const & msg);
|
||||
|
||||
private:
|
||||
int m_threadsCount{0};
|
||||
std::map<threads::ThreadID, int> m_threadID;
|
||||
|
||||
Timer m_timer;
|
||||
};
|
||||
|
||||
std::string ToString(LogLevel level);
|
||||
std::optional<LogLevel> FromString(std::string const & s);
|
||||
std::array<char, NUM_LOG_LEVELS> const & GetLogLevelNames();
|
||||
|
||||
using AtomicLogLevel = std::atomic<LogLevel>;
|
||||
using LogMessageFn = void (*)(LogLevel level, SrcPoint const &, std::string const &);
|
||||
|
||||
LogLevel GetDefaultLogLevel();
|
||||
LogLevel GetDefaultLogAbortLevel();
|
||||
|
||||
extern LogMessageFn LogMessage;
|
||||
extern AtomicLogLevel g_LogLevel;
|
||||
extern AtomicLogLevel g_LogAbortLevel;
|
||||
|
||||
/// @return Pointer to previous message function.
|
||||
LogMessageFn SetLogMessageFn(LogMessageFn fn);
|
||||
|
||||
void LogMessageDefault(LogLevel level, SrcPoint const & srcPoint, std::string const & msg);
|
||||
void LogMessageTests(LogLevel level, SrcPoint const & srcPoint, std::string const & msg);
|
||||
|
||||
// Scope guard to temporarily suppress a specific log level and all lower ones.
|
||||
//
|
||||
// For example, in unit tests:
|
||||
// {
|
||||
// // Only LERROR and LCRITICAL log messages will be printed.
|
||||
// ScopedLogLevelChanger defaultChanger;
|
||||
// // Call a function that has LDEBUG/LINFO/LWARNING logs that you want to suppress.
|
||||
// TEST(func(), ());
|
||||
// }
|
||||
struct ScopedLogLevelChanger
|
||||
{
|
||||
explicit ScopedLogLevelChanger(LogLevel temporaryLogLevel = LERROR) { g_LogLevel = temporaryLogLevel; }
|
||||
|
||||
~ScopedLogLevelChanger() { g_LogLevel = m_old; }
|
||||
|
||||
LogLevel m_old = g_LogLevel;
|
||||
};
|
||||
|
||||
struct ScopedLogAbortLevelChanger
|
||||
{
|
||||
explicit ScopedLogAbortLevelChanger(LogLevel temporaryLogAbortLevel = LCRITICAL)
|
||||
{
|
||||
g_LogAbortLevel = temporaryLogAbortLevel;
|
||||
}
|
||||
|
||||
~ScopedLogAbortLevelChanger() { g_LogAbortLevel = m_old; }
|
||||
|
||||
LogLevel m_old = g_LogAbortLevel;
|
||||
};
|
||||
} // namespace base
|
||||
|
||||
using base::LCRITICAL;
|
||||
using base::LDEBUG;
|
||||
using base::LERROR;
|
||||
using base::LINFO;
|
||||
using base::LWARNING;
|
||||
using base::NUM_LOG_LEVELS;
|
||||
|
||||
// Logging macro.
|
||||
// Example usage: LOG(LINFO, (Calc(), m_Var, "Some string constant"));
|
||||
#define LOG(level, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((level) >= ::base::g_LogLevel) \
|
||||
::base::LogMessage(level, SRC(), ::base::Message msg); \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
// Logging macro with short info (without entry point)
|
||||
#define LOG_SHORT(level, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((level) >= ::base::g_LogLevel) \
|
||||
::base::LogMessage(level, base::SrcPoint(), ::base::Message msg); \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
// Provides logging despite of |g_LogLevel|.
|
||||
#define LOG_FORCE(level, msg) \
|
||||
do \
|
||||
{ \
|
||||
::base::LogMessage(level, SRC(), ::base::Message msg); \
|
||||
} \
|
||||
while (false)
|
||||
|
||||
// Conditional log. Logs @msg with level @level in case when @X returns false.
|
||||
#define CLOG(level, X, msg) \
|
||||
do \
|
||||
{ \
|
||||
if (!(X)) \
|
||||
LOG(level, (SRC(), "CLOG(" #X ")", ::base::Message msg)); \
|
||||
} \
|
||||
while (false)
|
||||
820
libs/base/lower_case.cpp
Normal file
820
libs/base/lower_case.cpp
Normal file
|
|
@ -0,0 +1,820 @@
|
|||
/// Performs full case folding for string to make it search-compatible according
|
||||
/// to rules in ftp://ftp.unicode.org/Public/UNIDATA/CaseFolding.txt
|
||||
/// This beautiful code has been inspired by Zurich area in 2011 (Alexander Borsuk).
|
||||
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
namespace strings
|
||||
{
|
||||
|
||||
static uint16_t const small00[] = {
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12,
|
||||
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
|
||||
0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
|
||||
0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e,
|
||||
0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71,
|
||||
0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84,
|
||||
0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
||||
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
|
||||
0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x3bc, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd,
|
||||
0xbe, 0xbf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x0, 0xe0, 0xe1, 0xe2, 0xe3,
|
||||
0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
|
||||
0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
|
||||
static uint16_t const small01[] = {
|
||||
0x101, 0x101, 0x103, 0x103, 0x105, 0x105, 0x107, 0x107, 0x109, 0x109, 0x10b, 0x10b, 0x10d, 0x10d, 0x10f, 0x10f,
|
||||
0x111, 0x111, 0x113, 0x113, 0x115, 0x115, 0x117, 0x117, 0x119, 0x119, 0x11b, 0x11b, 0x11d, 0x11d, 0x11f, 0x11f,
|
||||
0x121, 0x121, 0x123, 0x123, 0x125, 0x125, 0x127, 0x127, 0x129, 0x129, 0x12b, 0x12b, 0x12d, 0x12d, 0x12f, 0x12f,
|
||||
0x0, 0x131, 0x133, 0x133, 0x135, 0x135, 0x137, 0x137, 0x138, 0x13a, 0x13a, 0x13c, 0x13c, 0x13e, 0x13e, 0x140,
|
||||
0x140, 0x142, 0x142, 0x144, 0x144, 0x146, 0x146, 0x148, 0x148, 0x0, 0x14b, 0x14b, 0x14d, 0x14d, 0x14f, 0x14f,
|
||||
0x151, 0x151, 0x153, 0x153, 0x155, 0x155, 0x157, 0x157, 0x159, 0x159, 0x15b, 0x15b, 0x15d, 0x15d, 0x15f, 0x15f,
|
||||
0x161, 0x161, 0x163, 0x163, 0x165, 0x165, 0x167, 0x167, 0x169, 0x169, 0x16b, 0x16b, 0x16d, 0x16d, 0x16f, 0x16f,
|
||||
0x171, 0x171, 0x173, 0x173, 0x175, 0x175, 0x177, 0x177, 0xff, 0x17a, 0x17a, 0x17c, 0x17c, 0x17e, 0x17e, 0x73,
|
||||
0x180, 0x253, 0x183, 0x183, 0x185, 0x185, 0x254, 0x188, 0x188, 0x256, 0x257, 0x18c, 0x18c, 0x18d, 0x1dd, 0x259,
|
||||
0x25b, 0x192, 0x192, 0x260, 0x263, 0x195, 0x269, 0x268, 0x199, 0x199, 0x19a, 0x19b, 0x26f, 0x272, 0x19e, 0x275,
|
||||
0x1a1, 0x1a1, 0x1a3, 0x1a3, 0x1a5, 0x1a5, 0x280, 0x1a8, 0x1a8, 0x283, 0x1aa, 0x1ab, 0x1ad, 0x1ad, 0x288, 0x1b0,
|
||||
0x1b0, 0x28a, 0x28b, 0x1b4, 0x1b4, 0x1b6, 0x1b6, 0x292, 0x1b9, 0x1b9, 0x1ba, 0x1bb, 0x1bd, 0x1bd, 0x1be, 0x1bf,
|
||||
0x1c0, 0x1c1, 0x1c2, 0x1c3, 0x1c6, 0x1c6, 0x1c6, 0x1c9, 0x1c9, 0x1c9, 0x1cc, 0x1cc, 0x1cc, 0x1ce, 0x1ce, 0x1d0,
|
||||
0x1d0, 0x1d2, 0x1d2, 0x1d4, 0x1d4, 0x1d6, 0x1d6, 0x1d8, 0x1d8, 0x1da, 0x1da, 0x1dc, 0x1dc, 0x1dd, 0x1df, 0x1df,
|
||||
0x1e1, 0x1e1, 0x1e3, 0x1e3, 0x1e5, 0x1e5, 0x1e7, 0x1e7, 0x1e9, 0x1e9, 0x1eb, 0x1eb, 0x1ed, 0x1ed, 0x1ef, 0x1ef,
|
||||
0x0, 0x1f3, 0x1f3, 0x1f3, 0x1f5, 0x1f5, 0x195, 0x1bf, 0x1f9, 0x1f9, 0x1fb, 0x1fb, 0x1fd, 0x1fd, 0x1ff, 0x1ff};
|
||||
static uint16_t const small02[] = {
|
||||
0x201, 0x201, 0x203, 0x203, 0x205, 0x205, 0x207, 0x207, 0x209, 0x209, 0x20b, 0x20b, 0x20d, 0x20d, 0x20f, 0x20f,
|
||||
0x211, 0x211, 0x213, 0x213, 0x215, 0x215, 0x217, 0x217, 0x219, 0x219, 0x21b, 0x21b, 0x21d, 0x21d, 0x21f, 0x21f,
|
||||
0x19e, 0x221, 0x223, 0x223, 0x225, 0x225, 0x227, 0x227, 0x229, 0x229, 0x22b, 0x22b, 0x22d, 0x22d, 0x22f, 0x22f,
|
||||
0x231, 0x231, 0x233, 0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x2c65, 0x23c, 0x23c, 0x19a, 0x2c66, 0x23f,
|
||||
0x240, 0x242, 0x242, 0x180, 0x289, 0x28c, 0x247, 0x247, 0x249, 0x249, 0x24b, 0x24b, 0x24d, 0x24d, 0x24f, 0x24f,
|
||||
0x250, 0x251, 0x252, 0x253, 0x254, 0x255, 0x256, 0x257, 0x258, 0x259, 0x25a, 0x25b, 0x25c, 0x25d, 0x25e, 0x25f,
|
||||
0x260, 0x261, 0x262, 0x263, 0x264, 0x265, 0x266, 0x267, 0x268, 0x269, 0x26a, 0x26b, 0x26c, 0x26d, 0x26e, 0x26f,
|
||||
0x270, 0x271, 0x272, 0x273, 0x274, 0x275, 0x276, 0x277, 0x278, 0x279, 0x27a, 0x27b, 0x27c, 0x27d, 0x27e, 0x27f,
|
||||
0x280, 0x281, 0x282, 0x283, 0x284, 0x285, 0x286, 0x287, 0x288, 0x289, 0x28a, 0x28b, 0x28c, 0x28d, 0x28e, 0x28f,
|
||||
0x290, 0x291, 0x292, 0x293, 0x294, 0x295, 0x296, 0x297, 0x298, 0x299, 0x29a, 0x29b, 0x29c, 0x29d, 0x29e, 0x29f,
|
||||
0x2a0, 0x2a1, 0x2a2, 0x2a3, 0x2a4, 0x2a5, 0x2a6, 0x2a7, 0x2a8, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ad, 0x2ae, 0x2af,
|
||||
0x2b0, 0x2b1, 0x2b2, 0x2b3, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b8, 0x2b9, 0x2ba, 0x2bb, 0x2bc, 0x2bd, 0x2be, 0x2bf,
|
||||
0x2c0, 0x2c1, 0x2c2, 0x2c3, 0x2c4, 0x2c5, 0x2c6, 0x2c7, 0x2c8, 0x2c9, 0x2ca, 0x2cb, 0x2cc, 0x2cd, 0x2ce, 0x2cf,
|
||||
0x2d0, 0x2d1, 0x2d2, 0x2d3, 0x2d4, 0x2d5, 0x2d6, 0x2d7, 0x2d8, 0x2d9, 0x2da, 0x2db, 0x2dc, 0x2dd, 0x2de, 0x2df,
|
||||
0x2e0, 0x2e1, 0x2e2, 0x2e3, 0x2e4, 0x2e5, 0x2e6, 0x2e7, 0x2e8, 0x2e9, 0x2ea, 0x2eb, 0x2ec, 0x2ed, 0x2ee, 0x2ef,
|
||||
0x2f0, 0x2f1, 0x2f2, 0x2f3, 0x2f4, 0x2f5, 0x2f6, 0x2f7, 0x2f8, 0x2f9, 0x2fa, 0x2fb, 0x2fc, 0x2fd, 0x2fe, 0x2ff};
|
||||
static uint16_t const small03[] = {
|
||||
0x300, 0x301, 0x302, 0x303, 0x304, 0x305, 0x306, 0x307, 0x308, 0x309, 0x30a, 0x30b, 0x30c, 0x30d, 0x30e, 0x30f,
|
||||
0x310, 0x311, 0x312, 0x313, 0x314, 0x315, 0x316, 0x317, 0x318, 0x319, 0x31a, 0x31b, 0x31c, 0x31d, 0x31e, 0x31f,
|
||||
0x320, 0x321, 0x322, 0x323, 0x324, 0x325, 0x326, 0x327, 0x328, 0x329, 0x32a, 0x32b, 0x32c, 0x32d, 0x32e, 0x32f,
|
||||
0x330, 0x331, 0x332, 0x333, 0x334, 0x335, 0x336, 0x337, 0x338, 0x339, 0x33a, 0x33b, 0x33c, 0x33d, 0x33e, 0x33f,
|
||||
0x340, 0x341, 0x342, 0x343, 0x344, 0x3b9, 0x346, 0x347, 0x348, 0x349, 0x34a, 0x34b, 0x34c, 0x34d, 0x34e, 0x34f,
|
||||
0x350, 0x351, 0x352, 0x353, 0x354, 0x355, 0x356, 0x357, 0x358, 0x359, 0x35a, 0x35b, 0x35c, 0x35d, 0x35e, 0x35f,
|
||||
0x360, 0x361, 0x362, 0x363, 0x364, 0x365, 0x366, 0x367, 0x368, 0x369, 0x36a, 0x36b, 0x36c, 0x36d, 0x36e, 0x36f,
|
||||
0x371, 0x371, 0x373, 0x373, 0x374, 0x375, 0x377, 0x377, 0x378, 0x379, 0x37a, 0x37b, 0x37c, 0x37d, 0x37e, 0x37f,
|
||||
0x380, 0x381, 0x382, 0x383, 0x384, 0x385, 0x3ac, 0x387, 0x3ad, 0x3ae, 0x3af, 0x38b, 0x3cc, 0x38d, 0x3cd, 0x3ce,
|
||||
0x0, 0x3b1, 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b7, 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bc, 0x3bd, 0x3be, 0x3bf,
|
||||
0x3c0, 0x3c1, 0x3a2, 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7, 0x3c8, 0x3c9, 0x3ca, 0x3cb, 0x3ac, 0x3ad, 0x3ae, 0x3af,
|
||||
0x0, 0x3b1, 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b7, 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bc, 0x3bd, 0x3be, 0x3bf,
|
||||
0x3c0, 0x3c1, 0x3c3, 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7, 0x3c8, 0x3c9, 0x3ca, 0x3cb, 0x3cc, 0x3cd, 0x3ce, 0x3d7,
|
||||
0x3b2, 0x3b8, 0x3d2, 0x3d3, 0x3d4, 0x3c6, 0x3c0, 0x3d7, 0x3d9, 0x3d9, 0x3db, 0x3db, 0x3dd, 0x3dd, 0x3df, 0x3df,
|
||||
0x3e1, 0x3e1, 0x3e3, 0x3e3, 0x3e5, 0x3e5, 0x3e7, 0x3e7, 0x3e9, 0x3e9, 0x3eb, 0x3eb, 0x3ed, 0x3ed, 0x3ef, 0x3ef,
|
||||
0x3ba, 0x3c1, 0x3f2, 0x3f3, 0x3b8, 0x3b5, 0x3f6, 0x3f8, 0x3f8, 0x3f2, 0x3fb, 0x3fb, 0x3fc, 0x37b, 0x37c, 0x37d};
|
||||
static uint16_t const small04[] = {
|
||||
0x450, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45a, 0x45b, 0x45c, 0x45d, 0x45e, 0x45f,
|
||||
0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43a, 0x43b, 0x43c, 0x43d, 0x43e, 0x43f,
|
||||
0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44a, 0x44b, 0x44c, 0x44d, 0x44e, 0x44f,
|
||||
0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43a, 0x43b, 0x43c, 0x43d, 0x43e, 0x43f,
|
||||
0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44a, 0x44b, 0x44c, 0x44d, 0x44e, 0x44f,
|
||||
0x450, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45a, 0x45b, 0x45c, 0x45d, 0x45e, 0x45f,
|
||||
0x461, 0x461, 0x463, 0x463, 0x465, 0x465, 0x467, 0x467, 0x469, 0x469, 0x46b, 0x46b, 0x46d, 0x46d, 0x46f, 0x46f,
|
||||
0x471, 0x471, 0x473, 0x473, 0x475, 0x475, 0x477, 0x477, 0x479, 0x479, 0x47b, 0x47b, 0x47d, 0x47d, 0x47f, 0x47f,
|
||||
0x481, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, 0x488, 0x489, 0x48b, 0x48b, 0x48d, 0x48d, 0x48f, 0x48f,
|
||||
0x491, 0x491, 0x493, 0x493, 0x495, 0x495, 0x497, 0x497, 0x499, 0x499, 0x49b, 0x49b, 0x49d, 0x49d, 0x49f, 0x49f,
|
||||
0x4a1, 0x4a1, 0x4a3, 0x4a3, 0x4a5, 0x4a5, 0x4a7, 0x4a7, 0x4a9, 0x4a9, 0x4ab, 0x4ab, 0x4ad, 0x4ad, 0x4af, 0x4af,
|
||||
0x4b1, 0x4b1, 0x4b3, 0x4b3, 0x4b5, 0x4b5, 0x4b7, 0x4b7, 0x4b9, 0x4b9, 0x4bb, 0x4bb, 0x4bd, 0x4bd, 0x4bf, 0x4bf,
|
||||
0x4cf, 0x4c2, 0x4c2, 0x4c4, 0x4c4, 0x4c6, 0x4c6, 0x4c8, 0x4c8, 0x4ca, 0x4ca, 0x4cc, 0x4cc, 0x4ce, 0x4ce, 0x4cf,
|
||||
0x4d1, 0x4d1, 0x4d3, 0x4d3, 0x4d5, 0x4d5, 0x4d7, 0x4d7, 0x4d9, 0x4d9, 0x4db, 0x4db, 0x4dd, 0x4dd, 0x4df, 0x4df,
|
||||
0x4e1, 0x4e1, 0x4e3, 0x4e3, 0x4e5, 0x4e5, 0x4e7, 0x4e7, 0x4e9, 0x4e9, 0x4eb, 0x4eb, 0x4ed, 0x4ed, 0x4ef, 0x4ef,
|
||||
0x4f1, 0x4f1, 0x4f3, 0x4f3, 0x4f5, 0x4f5, 0x4f7, 0x4f7, 0x4f9, 0x4f9, 0x4fb, 0x4fb, 0x4fd, 0x4fd, 0x4ff, 0x4ff};
|
||||
static uint16_t const small05[] = {
|
||||
0x501, 0x501, 0x503, 0x503, 0x505, 0x505, 0x507, 0x507, 0x509, 0x509, 0x50b, 0x50b, 0x50d, 0x50d, 0x50f, 0x50f,
|
||||
0x511, 0x511, 0x513, 0x513, 0x515, 0x515, 0x517, 0x517, 0x519, 0x519, 0x51b, 0x51b, 0x51d, 0x51d, 0x51f, 0x51f,
|
||||
0x521, 0x521, 0x523, 0x523, 0x525, 0x525, 0x527, 0x527, 0x528, 0x529, 0x52a, 0x52b, 0x52c, 0x52d, 0x52e, 0x52f,
|
||||
0x530, 0x561, 0x562, 0x563, 0x564, 0x565, 0x566, 0x567, 0x568, 0x569, 0x56a, 0x56b, 0x56c, 0x56d, 0x56e, 0x56f,
|
||||
0x570, 0x571, 0x572, 0x573, 0x574, 0x575, 0x576, 0x577, 0x578, 0x579, 0x57a, 0x57b, 0x57c, 0x57d, 0x57e, 0x57f,
|
||||
0x580, 0x581, 0x582, 0x583, 0x584, 0x585, 0x586, 0x557, 0x558, 0x559, 0x55a, 0x55b, 0x55c, 0x55d, 0x55e, 0x55f,
|
||||
0x560, 0x561, 0x562, 0x563, 0x564, 0x565, 0x566, 0x567, 0x568, 0x569, 0x56a, 0x56b, 0x56c, 0x56d, 0x56e, 0x56f,
|
||||
0x570, 0x571, 0x572, 0x573, 0x574, 0x575, 0x576, 0x577, 0x578, 0x579, 0x57a, 0x57b, 0x57c, 0x57d, 0x57e, 0x57f,
|
||||
0x580, 0x581, 0x582, 0x583, 0x584, 0x585, 0x586, 0x0, 0x588, 0x589, 0x58a, 0x58b, 0x58c, 0x58d, 0x58e, 0x58f,
|
||||
0x590, 0x591, 0x592, 0x593, 0x594, 0x595, 0x596, 0x597, 0x598, 0x599, 0x59a, 0x59b, 0x59c, 0x59d, 0x59e, 0x59f,
|
||||
0x5a0, 0x5a1, 0x5a2, 0x5a3, 0x5a4, 0x5a5, 0x5a6, 0x5a7, 0x5a8, 0x5a9, 0x5aa, 0x5ab, 0x5ac, 0x5ad, 0x5ae, 0x5af,
|
||||
0x5b0, 0x5b1, 0x5b2, 0x5b3, 0x5b4, 0x5b5, 0x5b6, 0x5b7, 0x5b8, 0x5b9, 0x5ba, 0x5bb, 0x5bc, 0x5bd, 0x5be, 0x5bf,
|
||||
0x5c0, 0x5c1, 0x5c2, 0x5c3, 0x5c4, 0x5c5, 0x5c6, 0x5c7, 0x5c8, 0x5c9, 0x5ca, 0x5cb, 0x5cc, 0x5cd, 0x5ce, 0x5cf,
|
||||
0x5d0, 0x5d1, 0x5d2, 0x5d3, 0x5d4, 0x5d5, 0x5d6, 0x5d7, 0x5d8, 0x5d9, 0x5da, 0x5db, 0x5dc, 0x5dd, 0x5de, 0x5df,
|
||||
0x5e0, 0x5e1, 0x5e2, 0x5e3, 0x5e4, 0x5e5, 0x5e6, 0x5e7, 0x5e8, 0x5e9, 0x5ea, 0x5eb, 0x5ec, 0x5ed, 0x5ee, 0x5ef,
|
||||
0x5f0, 0x5f1, 0x5f2, 0x5f3, 0x5f4, 0x5f5, 0x5f6, 0x5f7, 0x5f8, 0x5f9, 0x5fa, 0x5fb, 0x5fc, 0x5fd, 0x5fe, 0x5ff};
|
||||
static uint16_t const small10[] = {
|
||||
0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1008, 0x1009, 0x100a, 0x100b, 0x100c, 0x100d,
|
||||
0x100e, 0x100f, 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017, 0x1018, 0x1019, 0x101a, 0x101b,
|
||||
0x101c, 0x101d, 0x101e, 0x101f, 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, 0x1028, 0x1029,
|
||||
0x102a, 0x102b, 0x102c, 0x102d, 0x102e, 0x102f, 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037,
|
||||
0x1038, 0x1039, 0x103a, 0x103b, 0x103c, 0x103d, 0x103e, 0x103f, 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045,
|
||||
0x1046, 0x1047, 0x1048, 0x1049, 0x104a, 0x104b, 0x104c, 0x104d, 0x104e, 0x104f, 0x1050, 0x1051, 0x1052, 0x1053,
|
||||
0x1054, 0x1055, 0x1056, 0x1057, 0x1058, 0x1059, 0x105a, 0x105b, 0x105c, 0x105d, 0x105e, 0x105f, 0x1060, 0x1061,
|
||||
0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067, 0x1068, 0x1069, 0x106a, 0x106b, 0x106c, 0x106d, 0x106e, 0x106f,
|
||||
0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077, 0x1078, 0x1079, 0x107a, 0x107b, 0x107c, 0x107d,
|
||||
0x107e, 0x107f, 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087, 0x1088, 0x1089, 0x108a, 0x108b,
|
||||
0x108c, 0x108d, 0x108e, 0x108f, 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097, 0x1098, 0x1099,
|
||||
0x109a, 0x109b, 0x109c, 0x109d, 0x109e, 0x109f, 0x2d00, 0x2d01, 0x2d02, 0x2d03, 0x2d04, 0x2d05, 0x2d06, 0x2d07,
|
||||
0x2d08, 0x2d09, 0x2d0a, 0x2d0b, 0x2d0c, 0x2d0d, 0x2d0e, 0x2d0f, 0x2d10, 0x2d11, 0x2d12, 0x2d13, 0x2d14, 0x2d15,
|
||||
0x2d16, 0x2d17, 0x2d18, 0x2d19, 0x2d1a, 0x2d1b, 0x2d1c, 0x2d1d, 0x2d1e, 0x2d1f, 0x2d20, 0x2d21, 0x2d22, 0x2d23,
|
||||
0x2d24, 0x2d25, 0x10c6, 0x10c7, 0x10c8, 0x10c9, 0x10ca, 0x10cb, 0x10cc, 0x10cd, 0x10ce, 0x10cf, 0x10d0, 0x10d1,
|
||||
0x10d2, 0x10d3, 0x10d4, 0x10d5, 0x10d6, 0x10d7, 0x10d8, 0x10d9, 0x10da, 0x10db, 0x10dc, 0x10dd, 0x10de, 0x10df,
|
||||
0x10e0, 0x10e1, 0x10e2, 0x10e3, 0x10e4, 0x10e5, 0x10e6, 0x10e7, 0x10e8, 0x10e9, 0x10ea, 0x10eb, 0x10ec, 0x10ed,
|
||||
0x10ee, 0x10ef, 0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x10f8, 0x10f9, 0x10fa, 0x10fb,
|
||||
0x10fc, 0x10fd, 0x10fe, 0x10ff};
|
||||
static uint16_t const small1e[] = {
|
||||
0x1e01, 0x1e01, 0x1e03, 0x1e03, 0x1e05, 0x1e05, 0x1e07, 0x1e07, 0x1e09, 0x1e09, 0x1e0b, 0x1e0b, 0x1e0d, 0x1e0d,
|
||||
0x1e0f, 0x1e0f, 0x1e11, 0x1e11, 0x1e13, 0x1e13, 0x1e15, 0x1e15, 0x1e17, 0x1e17, 0x1e19, 0x1e19, 0x1e1b, 0x1e1b,
|
||||
0x1e1d, 0x1e1d, 0x1e1f, 0x1e1f, 0x1e21, 0x1e21, 0x1e23, 0x1e23, 0x1e25, 0x1e25, 0x1e27, 0x1e27, 0x1e29, 0x1e29,
|
||||
0x1e2b, 0x1e2b, 0x1e2d, 0x1e2d, 0x1e2f, 0x1e2f, 0x1e31, 0x1e31, 0x1e33, 0x1e33, 0x1e35, 0x1e35, 0x1e37, 0x1e37,
|
||||
0x1e39, 0x1e39, 0x1e3b, 0x1e3b, 0x1e3d, 0x1e3d, 0x1e3f, 0x1e3f, 0x1e41, 0x1e41, 0x1e43, 0x1e43, 0x1e45, 0x1e45,
|
||||
0x1e47, 0x1e47, 0x1e49, 0x1e49, 0x1e4b, 0x1e4b, 0x1e4d, 0x1e4d, 0x1e4f, 0x1e4f, 0x1e51, 0x1e51, 0x1e53, 0x1e53,
|
||||
0x1e55, 0x1e55, 0x1e57, 0x1e57, 0x1e59, 0x1e59, 0x1e5b, 0x1e5b, 0x1e5d, 0x1e5d, 0x1e5f, 0x1e5f, 0x1e61, 0x1e61,
|
||||
0x1e63, 0x1e63, 0x1e65, 0x1e65, 0x1e67, 0x1e67, 0x1e69, 0x1e69, 0x1e6b, 0x1e6b, 0x1e6d, 0x1e6d, 0x1e6f, 0x1e6f,
|
||||
0x1e71, 0x1e71, 0x1e73, 0x1e73, 0x1e75, 0x1e75, 0x1e77, 0x1e77, 0x1e79, 0x1e79, 0x1e7b, 0x1e7b, 0x1e7d, 0x1e7d,
|
||||
0x1e7f, 0x1e7f, 0x1e81, 0x1e81, 0x1e83, 0x1e83, 0x1e85, 0x1e85, 0x1e87, 0x1e87, 0x1e89, 0x1e89, 0x1e8b, 0x1e8b,
|
||||
0x1e8d, 0x1e8d, 0x1e8f, 0x1e8f, 0x1e91, 0x1e91, 0x1e93, 0x1e93, 0x1e95, 0x1e95, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x1e61, 0x1e9c, 0x1e9d, 0x0, 0x1e9f, 0x1ea1, 0x1ea1, 0x1ea3, 0x1ea3, 0x1ea5, 0x1ea5, 0x1ea7, 0x1ea7,
|
||||
0x1ea9, 0x1ea9, 0x1eab, 0x1eab, 0x1ead, 0x1ead, 0x1eaf, 0x1eaf, 0x1eb1, 0x1eb1, 0x1eb3, 0x1eb3, 0x1eb5, 0x1eb5,
|
||||
0x1eb7, 0x1eb7, 0x1eb9, 0x1eb9, 0x1ebb, 0x1ebb, 0x1ebd, 0x1ebd, 0x1ebf, 0x1ebf, 0x1ec1, 0x1ec1, 0x1ec3, 0x1ec3,
|
||||
0x1ec5, 0x1ec5, 0x1ec7, 0x1ec7, 0x1ec9, 0x1ec9, 0x1ecb, 0x1ecb, 0x1ecd, 0x1ecd, 0x1ecf, 0x1ecf, 0x1ed1, 0x1ed1,
|
||||
0x1ed3, 0x1ed3, 0x1ed5, 0x1ed5, 0x1ed7, 0x1ed7, 0x1ed9, 0x1ed9, 0x1edb, 0x1edb, 0x1edd, 0x1edd, 0x1edf, 0x1edf,
|
||||
0x1ee1, 0x1ee1, 0x1ee3, 0x1ee3, 0x1ee5, 0x1ee5, 0x1ee7, 0x1ee7, 0x1ee9, 0x1ee9, 0x1eeb, 0x1eeb, 0x1eed, 0x1eed,
|
||||
0x1eef, 0x1eef, 0x1ef1, 0x1ef1, 0x1ef3, 0x1ef3, 0x1ef5, 0x1ef5, 0x1ef7, 0x1ef7, 0x1ef9, 0x1ef9, 0x1efb, 0x1efb,
|
||||
0x1efd, 0x1efd, 0x1eff, 0x1eff};
|
||||
static uint16_t const small1f[] = {
|
||||
0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05, 0x1f06, 0x1f07, 0x1f00, 0x1f01, 0x1f02, 0x1f03, 0x1f04, 0x1f05,
|
||||
0x1f06, 0x1f07, 0x1f10, 0x1f11, 0x1f12, 0x1f13, 0x1f14, 0x1f15, 0x1f16, 0x1f17, 0x1f10, 0x1f11, 0x1f12, 0x1f13,
|
||||
0x1f14, 0x1f15, 0x1f1e, 0x1f1f, 0x1f20, 0x1f21, 0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27, 0x1f20, 0x1f21,
|
||||
0x1f22, 0x1f23, 0x1f24, 0x1f25, 0x1f26, 0x1f27, 0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37,
|
||||
0x1f30, 0x1f31, 0x1f32, 0x1f33, 0x1f34, 0x1f35, 0x1f36, 0x1f37, 0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45,
|
||||
0x1f46, 0x1f47, 0x1f40, 0x1f41, 0x1f42, 0x1f43, 0x1f44, 0x1f45, 0x1f4e, 0x1f4f, 0x0, 0x1f51, 0x0, 0x1f53,
|
||||
0x0, 0x1f55, 0x0, 0x1f57, 0x1f58, 0x1f51, 0x1f5a, 0x1f53, 0x1f5c, 0x1f55, 0x1f5e, 0x1f57, 0x1f60, 0x1f61,
|
||||
0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67, 0x1f60, 0x1f61, 0x1f62, 0x1f63, 0x1f64, 0x1f65, 0x1f66, 0x1f67,
|
||||
0x1f70, 0x1f71, 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x1f76, 0x1f77, 0x1f78, 0x1f79, 0x1f7a, 0x1f7b, 0x1f7c, 0x1f7d,
|
||||
0x1f7e, 0x1f7f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1fb0, 0x1fb1, 0x0, 0x0, 0x0, 0x1fb5,
|
||||
0x0, 0x0, 0x1fb0, 0x1fb1, 0x1f70, 0x1f71, 0x0, 0x1fbd, 0x3b9, 0x1fbf, 0x1fc0, 0x1fc1, 0x0, 0x0,
|
||||
0x0, 0x1fc5, 0x0, 0x0, 0x1f72, 0x1f73, 0x1f74, 0x1f75, 0x0, 0x1fcd, 0x1fce, 0x1fcf, 0x1fd0, 0x1fd1,
|
||||
0x0, 0x0, 0x1fd4, 0x1fd5, 0x0, 0x0, 0x1fd0, 0x1fd1, 0x1f76, 0x1f77, 0x1fdc, 0x1fdd, 0x1fde, 0x1fdf,
|
||||
0x1fe0, 0x1fe1, 0x0, 0x0, 0x0, 0x1fe5, 0x0, 0x0, 0x1fe0, 0x1fe1, 0x1f7a, 0x1f7b, 0x1fe5, 0x1fed,
|
||||
0x1fee, 0x1fef, 0x1ff0, 0x1ff1, 0x0, 0x0, 0x0, 0x1ff5, 0x0, 0x0, 0x1f78, 0x1f79, 0x1f7c, 0x1f7d,
|
||||
0x0, 0x1ffd, 0x1ffe, 0x1fff};
|
||||
static uint16_t const small21[] = {
|
||||
0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2109, 0x210a, 0x210b, 0x210c, 0x210d,
|
||||
0x210e, 0x210f, 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117, 0x2118, 0x2119, 0x211a, 0x211b,
|
||||
0x211c, 0x211d, 0x211e, 0x211f, 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x3c9, 0x2127, 0x2128, 0x2129,
|
||||
0x6b, 0xe5, 0x212c, 0x212d, 0x212e, 0x212f, 0x2130, 0x2131, 0x214e, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137,
|
||||
0x2138, 0x2139, 0x213a, 0x213b, 0x213c, 0x213d, 0x213e, 0x213f, 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145,
|
||||
0x2146, 0x2147, 0x2148, 0x2149, 0x214a, 0x214b, 0x214c, 0x214d, 0x214e, 0x214f, 0x2150, 0x2151, 0x2152, 0x2153,
|
||||
0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, 0x215b, 0x215c, 0x215d, 0x215e, 0x215f, 0x2170, 0x2171,
|
||||
0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d, 0x217e, 0x217f,
|
||||
0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217a, 0x217b, 0x217c, 0x217d,
|
||||
0x217e, 0x217f, 0x2180, 0x2181, 0x2182, 0x2184, 0x2184, 0x2185, 0x2186, 0x2187, 0x2188, 0x2189, 0x218a, 0x218b,
|
||||
0x218c, 0x218d, 0x218e, 0x218f, 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197, 0x2198, 0x2199,
|
||||
0x219a, 0x219b, 0x219c, 0x219d, 0x219e, 0x219f, 0x21a0, 0x21a1, 0x21a2, 0x21a3, 0x21a4, 0x21a5, 0x21a6, 0x21a7,
|
||||
0x21a8, 0x21a9, 0x21aa, 0x21ab, 0x21ac, 0x21ad, 0x21ae, 0x21af, 0x21b0, 0x21b1, 0x21b2, 0x21b3, 0x21b4, 0x21b5,
|
||||
0x21b6, 0x21b7, 0x21b8, 0x21b9, 0x21ba, 0x21bb, 0x21bc, 0x21bd, 0x21be, 0x21bf, 0x21c0, 0x21c1, 0x21c2, 0x21c3,
|
||||
0x21c4, 0x21c5, 0x21c6, 0x21c7, 0x21c8, 0x21c9, 0x21ca, 0x21cb, 0x21cc, 0x21cd, 0x21ce, 0x21cf, 0x21d0, 0x21d1,
|
||||
0x21d2, 0x21d3, 0x21d4, 0x21d5, 0x21d6, 0x21d7, 0x21d8, 0x21d9, 0x21da, 0x21db, 0x21dc, 0x21dd, 0x21de, 0x21df,
|
||||
0x21e0, 0x21e1, 0x21e2, 0x21e3, 0x21e4, 0x21e5, 0x21e6, 0x21e7, 0x21e8, 0x21e9, 0x21ea, 0x21eb, 0x21ec, 0x21ed,
|
||||
0x21ee, 0x21ef, 0x21f0, 0x21f1, 0x21f2, 0x21f3, 0x21f4, 0x21f5, 0x21f6, 0x21f7, 0x21f8, 0x21f9, 0x21fa, 0x21fb,
|
||||
0x21fc, 0x21fd, 0x21fe, 0x21ff};
|
||||
static uint16_t const small24[] = {
|
||||
0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407, 0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d,
|
||||
0x240e, 0x240f, 0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417, 0x2418, 0x2419, 0x241a, 0x241b,
|
||||
0x241c, 0x241d, 0x241e, 0x241f, 0x2420, 0x2421, 0x2422, 0x2423, 0x2424, 0x2425, 0x2426, 0x2427, 0x2428, 0x2429,
|
||||
0x242a, 0x242b, 0x242c, 0x242d, 0x242e, 0x242f, 0x2430, 0x2431, 0x2432, 0x2433, 0x2434, 0x2435, 0x2436, 0x2437,
|
||||
0x2438, 0x2439, 0x243a, 0x243b, 0x243c, 0x243d, 0x243e, 0x243f, 0x2440, 0x2441, 0x2442, 0x2443, 0x2444, 0x2445,
|
||||
0x2446, 0x2447, 0x2448, 0x2449, 0x244a, 0x244b, 0x244c, 0x244d, 0x244e, 0x244f, 0x2450, 0x2451, 0x2452, 0x2453,
|
||||
0x2454, 0x2455, 0x2456, 0x2457, 0x2458, 0x2459, 0x245a, 0x245b, 0x245c, 0x245d, 0x245e, 0x245f, 0x2460, 0x2461,
|
||||
0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467, 0x2468, 0x2469, 0x246a, 0x246b, 0x246c, 0x246d, 0x246e, 0x246f,
|
||||
0x2470, 0x2471, 0x2472, 0x2473, 0x2474, 0x2475, 0x2476, 0x2477, 0x2478, 0x2479, 0x247a, 0x247b, 0x247c, 0x247d,
|
||||
0x247e, 0x247f, 0x2480, 0x2481, 0x2482, 0x2483, 0x2484, 0x2485, 0x2486, 0x2487, 0x2488, 0x2489, 0x248a, 0x248b,
|
||||
0x248c, 0x248d, 0x248e, 0x248f, 0x2490, 0x2491, 0x2492, 0x2493, 0x2494, 0x2495, 0x2496, 0x2497, 0x2498, 0x2499,
|
||||
0x249a, 0x249b, 0x249c, 0x249d, 0x249e, 0x249f, 0x24a0, 0x24a1, 0x24a2, 0x24a3, 0x24a4, 0x24a5, 0x24a6, 0x24a7,
|
||||
0x24a8, 0x24a9, 0x24aa, 0x24ab, 0x24ac, 0x24ad, 0x24ae, 0x24af, 0x24b0, 0x24b1, 0x24b2, 0x24b3, 0x24b4, 0x24b5,
|
||||
0x24d0, 0x24d1, 0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9, 0x24da, 0x24db, 0x24dc, 0x24dd,
|
||||
0x24de, 0x24df, 0x24e0, 0x24e1, 0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9, 0x24d0, 0x24d1,
|
||||
0x24d2, 0x24d3, 0x24d4, 0x24d5, 0x24d6, 0x24d7, 0x24d8, 0x24d9, 0x24da, 0x24db, 0x24dc, 0x24dd, 0x24de, 0x24df,
|
||||
0x24e0, 0x24e1, 0x24e2, 0x24e3, 0x24e4, 0x24e5, 0x24e6, 0x24e7, 0x24e8, 0x24e9, 0x24ea, 0x24eb, 0x24ec, 0x24ed,
|
||||
0x24ee, 0x24ef, 0x24f0, 0x24f1, 0x24f2, 0x24f3, 0x24f4, 0x24f5, 0x24f6, 0x24f7, 0x24f8, 0x24f9, 0x24fa, 0x24fb,
|
||||
0x24fc, 0x24fd, 0x24fe, 0x24ff};
|
||||
static uint16_t const small2c[] = {
|
||||
0x2c30, 0x2c31, 0x2c32, 0x2c33, 0x2c34, 0x2c35, 0x2c36, 0x2c37, 0x2c38, 0x2c39, 0x2c3a, 0x2c3b, 0x2c3c, 0x2c3d,
|
||||
0x2c3e, 0x2c3f, 0x2c40, 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2c48, 0x2c49, 0x2c4a, 0x2c4b,
|
||||
0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c50, 0x2c51, 0x2c52, 0x2c53, 0x2c54, 0x2c55, 0x2c56, 0x2c57, 0x2c58, 0x2c59,
|
||||
0x2c5a, 0x2c5b, 0x2c5c, 0x2c5d, 0x2c5e, 0x2c2f, 0x2c30, 0x2c31, 0x2c32, 0x2c33, 0x2c34, 0x2c35, 0x2c36, 0x2c37,
|
||||
0x2c38, 0x2c39, 0x2c3a, 0x2c3b, 0x2c3c, 0x2c3d, 0x2c3e, 0x2c3f, 0x2c40, 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45,
|
||||
0x2c46, 0x2c47, 0x2c48, 0x2c49, 0x2c4a, 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c50, 0x2c51, 0x2c52, 0x2c53,
|
||||
0x2c54, 0x2c55, 0x2c56, 0x2c57, 0x2c58, 0x2c59, 0x2c5a, 0x2c5b, 0x2c5c, 0x2c5d, 0x2c5e, 0x2c5f, 0x2c61, 0x2c61,
|
||||
0x26b, 0x1d7d, 0x27d, 0x2c65, 0x2c66, 0x2c68, 0x2c68, 0x2c6a, 0x2c6a, 0x2c6c, 0x2c6c, 0x251, 0x271, 0x250,
|
||||
0x252, 0x2c71, 0x2c73, 0x2c73, 0x2c74, 0x2c76, 0x2c76, 0x2c77, 0x2c78, 0x2c79, 0x2c7a, 0x2c7b, 0x2c7c, 0x2c7d,
|
||||
0x23f, 0x240, 0x2c81, 0x2c81, 0x2c83, 0x2c83, 0x2c85, 0x2c85, 0x2c87, 0x2c87, 0x2c89, 0x2c89, 0x2c8b, 0x2c8b,
|
||||
0x2c8d, 0x2c8d, 0x2c8f, 0x2c8f, 0x2c91, 0x2c91, 0x2c93, 0x2c93, 0x2c95, 0x2c95, 0x2c97, 0x2c97, 0x2c99, 0x2c99,
|
||||
0x2c9b, 0x2c9b, 0x2c9d, 0x2c9d, 0x2c9f, 0x2c9f, 0x2ca1, 0x2ca1, 0x2ca3, 0x2ca3, 0x2ca5, 0x2ca5, 0x2ca7, 0x2ca7,
|
||||
0x2ca9, 0x2ca9, 0x2cab, 0x2cab, 0x2cad, 0x2cad, 0x2caf, 0x2caf, 0x2cb1, 0x2cb1, 0x2cb3, 0x2cb3, 0x2cb5, 0x2cb5,
|
||||
0x2cb7, 0x2cb7, 0x2cb9, 0x2cb9, 0x2cbb, 0x2cbb, 0x2cbd, 0x2cbd, 0x2cbf, 0x2cbf, 0x2cc1, 0x2cc1, 0x2cc3, 0x2cc3,
|
||||
0x2cc5, 0x2cc5, 0x2cc7, 0x2cc7, 0x2cc9, 0x2cc9, 0x2ccb, 0x2ccb, 0x2ccd, 0x2ccd, 0x2ccf, 0x2ccf, 0x2cd1, 0x2cd1,
|
||||
0x2cd3, 0x2cd3, 0x2cd5, 0x2cd5, 0x2cd7, 0x2cd7, 0x2cd9, 0x2cd9, 0x2cdb, 0x2cdb, 0x2cdd, 0x2cdd, 0x2cdf, 0x2cdf,
|
||||
0x2ce1, 0x2ce1, 0x2ce3, 0x2ce3, 0x2ce4, 0x2ce5, 0x2ce6, 0x2ce7, 0x2ce8, 0x2ce9, 0x2cea, 0x2cec, 0x2cec, 0x2cee,
|
||||
0x2cee, 0x2cef, 0x2cf0, 0x2cf1, 0x2cf2, 0x2cf3, 0x2cf4, 0x2cf5, 0x2cf6, 0x2cf7, 0x2cf8, 0x2cf9, 0x2cfa, 0x2cfb,
|
||||
0x2cfc, 0x2cfd, 0x2cfe, 0x2cff};
|
||||
static uint16_t const smalla6[] = {
|
||||
0xa600, 0xa601, 0xa602, 0xa603, 0xa604, 0xa605, 0xa606, 0xa607, 0xa608, 0xa609, 0xa60a, 0xa60b, 0xa60c, 0xa60d,
|
||||
0xa60e, 0xa60f, 0xa610, 0xa611, 0xa612, 0xa613, 0xa614, 0xa615, 0xa616, 0xa617, 0xa618, 0xa619, 0xa61a, 0xa61b,
|
||||
0xa61c, 0xa61d, 0xa61e, 0xa61f, 0xa620, 0xa621, 0xa622, 0xa623, 0xa624, 0xa625, 0xa626, 0xa627, 0xa628, 0xa629,
|
||||
0xa62a, 0xa62b, 0xa62c, 0xa62d, 0xa62e, 0xa62f, 0xa630, 0xa631, 0xa632, 0xa633, 0xa634, 0xa635, 0xa636, 0xa637,
|
||||
0xa638, 0xa639, 0xa63a, 0xa63b, 0xa63c, 0xa63d, 0xa63e, 0xa63f, 0xa641, 0xa641, 0xa643, 0xa643, 0xa645, 0xa645,
|
||||
0xa647, 0xa647, 0xa649, 0xa649, 0xa64b, 0xa64b, 0xa64d, 0xa64d, 0xa64f, 0xa64f, 0xa651, 0xa651, 0xa653, 0xa653,
|
||||
0xa655, 0xa655, 0xa657, 0xa657, 0xa659, 0xa659, 0xa65b, 0xa65b, 0xa65d, 0xa65d, 0xa65f, 0xa65f, 0xa661, 0xa661,
|
||||
0xa663, 0xa663, 0xa665, 0xa665, 0xa667, 0xa667, 0xa669, 0xa669, 0xa66b, 0xa66b, 0xa66d, 0xa66d, 0xa66e, 0xa66f,
|
||||
0xa670, 0xa671, 0xa672, 0xa673, 0xa674, 0xa675, 0xa676, 0xa677, 0xa678, 0xa679, 0xa67a, 0xa67b, 0xa67c, 0xa67d,
|
||||
0xa67e, 0xa67f, 0xa681, 0xa681, 0xa683, 0xa683, 0xa685, 0xa685, 0xa687, 0xa687, 0xa689, 0xa689, 0xa68b, 0xa68b,
|
||||
0xa68d, 0xa68d, 0xa68f, 0xa68f, 0xa691, 0xa691, 0xa693, 0xa693, 0xa695, 0xa695, 0xa697, 0xa697, 0xa698, 0xa699,
|
||||
0xa69a, 0xa69b, 0xa69c, 0xa69d, 0xa69e, 0xa69f, 0xa6a0, 0xa6a1, 0xa6a2, 0xa6a3, 0xa6a4, 0xa6a5, 0xa6a6, 0xa6a7,
|
||||
0xa6a8, 0xa6a9, 0xa6aa, 0xa6ab, 0xa6ac, 0xa6ad, 0xa6ae, 0xa6af, 0xa6b0, 0xa6b1, 0xa6b2, 0xa6b3, 0xa6b4, 0xa6b5,
|
||||
0xa6b6, 0xa6b7, 0xa6b8, 0xa6b9, 0xa6ba, 0xa6bb, 0xa6bc, 0xa6bd, 0xa6be, 0xa6bf, 0xa6c0, 0xa6c1, 0xa6c2, 0xa6c3,
|
||||
0xa6c4, 0xa6c5, 0xa6c6, 0xa6c7, 0xa6c8, 0xa6c9, 0xa6ca, 0xa6cb, 0xa6cc, 0xa6cd, 0xa6ce, 0xa6cf, 0xa6d0, 0xa6d1,
|
||||
0xa6d2, 0xa6d3, 0xa6d4, 0xa6d5, 0xa6d6, 0xa6d7, 0xa6d8, 0xa6d9, 0xa6da, 0xa6db, 0xa6dc, 0xa6dd, 0xa6de, 0xa6df,
|
||||
0xa6e0, 0xa6e1, 0xa6e2, 0xa6e3, 0xa6e4, 0xa6e5, 0xa6e6, 0xa6e7, 0xa6e8, 0xa6e9, 0xa6ea, 0xa6eb, 0xa6ec, 0xa6ed,
|
||||
0xa6ee, 0xa6ef, 0xa6f0, 0xa6f1, 0xa6f2, 0xa6f3, 0xa6f4, 0xa6f5, 0xa6f6, 0xa6f7, 0xa6f8, 0xa6f9, 0xa6fa, 0xa6fb,
|
||||
0xa6fc, 0xa6fd, 0xa6fe, 0xa6ff};
|
||||
static uint16_t const smalla7[] = {
|
||||
0xa700, 0xa701, 0xa702, 0xa703, 0xa704, 0xa705, 0xa706, 0xa707, 0xa708, 0xa709, 0xa70a, 0xa70b, 0xa70c, 0xa70d,
|
||||
0xa70e, 0xa70f, 0xa710, 0xa711, 0xa712, 0xa713, 0xa714, 0xa715, 0xa716, 0xa717, 0xa718, 0xa719, 0xa71a, 0xa71b,
|
||||
0xa71c, 0xa71d, 0xa71e, 0xa71f, 0xa720, 0xa721, 0xa723, 0xa723, 0xa725, 0xa725, 0xa727, 0xa727, 0xa729, 0xa729,
|
||||
0xa72b, 0xa72b, 0xa72d, 0xa72d, 0xa72f, 0xa72f, 0xa730, 0xa731, 0xa733, 0xa733, 0xa735, 0xa735, 0xa737, 0xa737,
|
||||
0xa739, 0xa739, 0xa73b, 0xa73b, 0xa73d, 0xa73d, 0xa73f, 0xa73f, 0xa741, 0xa741, 0xa743, 0xa743, 0xa745, 0xa745,
|
||||
0xa747, 0xa747, 0xa749, 0xa749, 0xa74b, 0xa74b, 0xa74d, 0xa74d, 0xa74f, 0xa74f, 0xa751, 0xa751, 0xa753, 0xa753,
|
||||
0xa755, 0xa755, 0xa757, 0xa757, 0xa759, 0xa759, 0xa75b, 0xa75b, 0xa75d, 0xa75d, 0xa75f, 0xa75f, 0xa761, 0xa761,
|
||||
0xa763, 0xa763, 0xa765, 0xa765, 0xa767, 0xa767, 0xa769, 0xa769, 0xa76b, 0xa76b, 0xa76d, 0xa76d, 0xa76f, 0xa76f,
|
||||
0xa770, 0xa771, 0xa772, 0xa773, 0xa774, 0xa775, 0xa776, 0xa777, 0xa778, 0xa77a, 0xa77a, 0xa77c, 0xa77c, 0x1d79,
|
||||
0xa77f, 0xa77f, 0xa781, 0xa781, 0xa783, 0xa783, 0xa785, 0xa785, 0xa787, 0xa787, 0xa788, 0xa789, 0xa78a, 0xa78c,
|
||||
0xa78c, 0x265, 0xa78e, 0xa78f, 0xa791, 0xa791, 0xa792, 0xa793, 0xa794, 0xa795, 0xa796, 0xa797, 0xa798, 0xa799,
|
||||
0xa79a, 0xa79b, 0xa79c, 0xa79d, 0xa79e, 0xa79f, 0xa7a1, 0xa7a1, 0xa7a3, 0xa7a3, 0xa7a5, 0xa7a5, 0xa7a7, 0xa7a7,
|
||||
0xa7a9, 0xa7a9, 0xa7aa, 0xa7ab, 0xa7ac, 0xa7ad, 0xa7ae, 0xa7af, 0xa7b0, 0xa7b1, 0xa7b2, 0xa7b3, 0xa7b4, 0xa7b5,
|
||||
0xa7b6, 0xa7b7, 0xa7b8, 0xa7b9, 0xa7ba, 0xa7bb, 0xa7bc, 0xa7bd, 0xa7be, 0xa7bf, 0xa7c0, 0xa7c1, 0xa7c2, 0xa7c3,
|
||||
0xa7c4, 0xa7c5, 0xa7c6, 0xa7c7, 0xa7c8, 0xa7c9, 0xa7ca, 0xa7cb, 0xa7cc, 0xa7cd, 0xa7ce, 0xa7cf, 0xa7d0, 0xa7d1,
|
||||
0xa7d2, 0xa7d3, 0xa7d4, 0xa7d5, 0xa7d6, 0xa7d7, 0xa7d8, 0xa7d9, 0xa7da, 0xa7db, 0xa7dc, 0xa7dd, 0xa7de, 0xa7df,
|
||||
0xa7e0, 0xa7e1, 0xa7e2, 0xa7e3, 0xa7e4, 0xa7e5, 0xa7e6, 0xa7e7, 0xa7e8, 0xa7e9, 0xa7ea, 0xa7eb, 0xa7ec, 0xa7ed,
|
||||
0xa7ee, 0xa7ef, 0xa7f0, 0xa7f1, 0xa7f2, 0xa7f3, 0xa7f4, 0xa7f5, 0xa7f6, 0xa7f7, 0xa7f8, 0xa7f9, 0xa7fa, 0xa7fb,
|
||||
0xa7fc, 0xa7fd, 0xa7fe, 0xa7ff};
|
||||
static uint16_t const smallff[] = {
|
||||
0xff00, 0xff01, 0xff02, 0xff03, 0xff04, 0xff05, 0xff06, 0xff07, 0xff08, 0xff09, 0xff0a, 0xff0b, 0xff0c, 0xff0d,
|
||||
0xff0e, 0xff0f, 0xff10, 0xff11, 0xff12, 0xff13, 0xff14, 0xff15, 0xff16, 0xff17, 0xff18, 0xff19, 0xff1a, 0xff1b,
|
||||
0xff1c, 0xff1d, 0xff1e, 0xff1f, 0xff20, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47, 0xff48, 0xff49,
|
||||
0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f, 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57,
|
||||
0xff58, 0xff59, 0xff5a, 0xff3b, 0xff3c, 0xff3d, 0xff3e, 0xff3f, 0xff40, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45,
|
||||
0xff46, 0xff47, 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f, 0xff50, 0xff51, 0xff52, 0xff53,
|
||||
0xff54, 0xff55, 0xff56, 0xff57, 0xff58, 0xff59, 0xff5a, 0xff5b, 0xff5c, 0xff5d, 0xff5e, 0xff5f, 0xff60, 0xff61,
|
||||
0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67, 0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f,
|
||||
0xff70, 0xff71, 0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77, 0xff78, 0xff79, 0xff7a, 0xff7b, 0xff7c, 0xff7d,
|
||||
0xff7e, 0xff7f, 0xff80, 0xff81, 0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87, 0xff88, 0xff89, 0xff8a, 0xff8b,
|
||||
0xff8c, 0xff8d, 0xff8e, 0xff8f, 0xff90, 0xff91, 0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97, 0xff98, 0xff99,
|
||||
0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f, 0xffa0, 0xffa1, 0xffa2, 0xffa3, 0xffa4, 0xffa5, 0xffa6, 0xffa7,
|
||||
0xffa8, 0xffa9, 0xffaa, 0xffab, 0xffac, 0xffad, 0xffae, 0xffaf, 0xffb0, 0xffb1, 0xffb2, 0xffb3, 0xffb4, 0xffb5,
|
||||
0xffb6, 0xffb7, 0xffb8, 0xffb9, 0xffba, 0xffbb, 0xffbc, 0xffbd, 0xffbe, 0xffbf, 0xffc0, 0xffc1, 0xffc2, 0xffc3,
|
||||
0xffc4, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9, 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf, 0xffd0, 0xffd1,
|
||||
0xffd2, 0xffd3, 0xffd4, 0xffd5, 0xffd6, 0xffd7, 0xffd8, 0xffd9, 0xffda, 0xffdb, 0xffdc, 0xffdd, 0xffde, 0xffdf,
|
||||
0xffe0, 0xffe1, 0xffe2, 0xffe3, 0xffe4, 0xffe5, 0xffe6, 0xffe7, 0xffe8, 0xffe9, 0xffea, 0xffeb, 0xffec, 0xffed,
|
||||
0xffee, 0xffef, 0xfff0, 0xfff1, 0xfff2, 0xfff3, 0xfff4, 0xfff5, 0xfff6, 0xfff7, 0xfff8, 0xfff9, 0xfffa, 0xfffb,
|
||||
0xfffc, 0xfffd, 0xfffe, 0xffff};
|
||||
|
||||
/// @return 0 if char should be replaced with 2 or more chars
|
||||
UniChar LowerUniChar(UniChar c)
|
||||
{
|
||||
switch (c & 0xffffff00)
|
||||
{
|
||||
case 0x0000: return small00[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x0100: return small01[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x0200: return small02[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x0300: return small03[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x0400: return small04[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x0500: return small05[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x1000: return small10[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x1e00: return small1e[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x1f00: return small1f[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x2100: return small21[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x2400: return small24[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x2c00: return small2c[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0xa600: return smalla6[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0xa700: return smalla7[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0xfb00:
|
||||
{
|
||||
if (c >= 0xfb00 && c <= 0xfb06)
|
||||
return 0;
|
||||
if (c >= 0xfb13 && c <= 0xfb17)
|
||||
return 0;
|
||||
return c;
|
||||
}
|
||||
case 0xff00: return smallff[static_cast<uint8_t>(c & 0x00ff)];
|
||||
case 0x10400:
|
||||
{
|
||||
if (c >= 0x10400 && c <= 0x10427)
|
||||
return c + 0x28;
|
||||
return c;
|
||||
}
|
||||
default: return c;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t w(UniChar c, UniChar * buf)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 0xdf:
|
||||
*buf++ = 0x73;
|
||||
*buf++ = 0x73;
|
||||
return 2;
|
||||
case 0x130:
|
||||
*buf++ = 0x69;
|
||||
*buf++ = 0x307;
|
||||
return 2;
|
||||
case 0x149:
|
||||
*buf++ = 0x2bc;
|
||||
*buf++ = 0x6e;
|
||||
return 2;
|
||||
case 0x1f0:
|
||||
*buf++ = 0x6a;
|
||||
*buf++ = 0x30c;
|
||||
return 2;
|
||||
case 0x390:
|
||||
*buf++ = 0x3b9;
|
||||
*buf++ = 0x308;
|
||||
*buf++ = 0x301;
|
||||
return 3;
|
||||
case 0x3B0:
|
||||
*buf++ = 0x3c5;
|
||||
*buf++ = 0x308;
|
||||
*buf++ = 0x301;
|
||||
return 3;
|
||||
case 0x587:
|
||||
*buf++ = 0x565;
|
||||
*buf++ = 0x582;
|
||||
return 2;
|
||||
case 0x1e96:
|
||||
*buf++ = 0x68;
|
||||
*buf++ = 0x331;
|
||||
return 2;
|
||||
case 0x1e97:
|
||||
*buf++ = 0x74;
|
||||
*buf++ = 0x308;
|
||||
return 2;
|
||||
case 0x1e98:
|
||||
*buf++ = 0x77;
|
||||
*buf++ = 0x30a;
|
||||
return 2;
|
||||
case 0x1e99:
|
||||
*buf++ = 0x79;
|
||||
*buf++ = 0x30a;
|
||||
return 2;
|
||||
case 0x1e9a:
|
||||
*buf++ = 0x61;
|
||||
*buf++ = 0x2be;
|
||||
return 2;
|
||||
case 0x1e9e:
|
||||
*buf++ = 0x73;
|
||||
*buf++ = 0x73;
|
||||
return 2;
|
||||
case 0x1f50:
|
||||
*buf++ = 0x3c5;
|
||||
*buf++ = 0x313;
|
||||
return 2;
|
||||
case 0x1f52:
|
||||
*buf++ = 0x3c5;
|
||||
*buf++ = 0x313;
|
||||
*buf++ = 0x300;
|
||||
return 3;
|
||||
case 0x1f54:
|
||||
*buf++ = 0x3c5;
|
||||
*buf++ = 0x313;
|
||||
*buf++ = 0x301;
|
||||
return 3;
|
||||
case 0x1f56:
|
||||
*buf++ = 0x3c5;
|
||||
*buf++ = 0x313;
|
||||
*buf++ = 0x342;
|
||||
return 3;
|
||||
case 0x1f80:
|
||||
*buf++ = 0x1f00;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f81:
|
||||
*buf++ = 0x1f01;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f82:
|
||||
*buf++ = 0x1f02;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f83:
|
||||
*buf++ = 0x1f03;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f84:
|
||||
*buf++ = 0x1f04;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f85:
|
||||
*buf++ = 0x1f05;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f86:
|
||||
*buf++ = 0x1f06;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f87:
|
||||
*buf++ = 0x1f07;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f88:
|
||||
*buf++ = 0x1f00;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f89:
|
||||
*buf++ = 0x1f01;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f8a:
|
||||
*buf++ = 0x1f02;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f8b:
|
||||
*buf++ = 0x1f03;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f8c:
|
||||
*buf++ = 0x1f04;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f8d:
|
||||
*buf++ = 0x1f05;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f8e:
|
||||
*buf++ = 0x1f06;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f8f:
|
||||
*buf++ = 0x1f07;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f90:
|
||||
*buf++ = 0x1f20;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f91:
|
||||
*buf++ = 0x1f21;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f92:
|
||||
*buf++ = 0x1f22;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f93:
|
||||
*buf++ = 0x1f23;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f94:
|
||||
*buf++ = 0x1f24;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f95:
|
||||
*buf++ = 0x1f25;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f96:
|
||||
*buf++ = 0x1f26;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f97:
|
||||
*buf++ = 0x1f27;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f98:
|
||||
*buf++ = 0x1f20;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f99:
|
||||
*buf++ = 0x1f21;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f9a:
|
||||
*buf++ = 0x1f22;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f9b:
|
||||
*buf++ = 0x1f23;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f9c:
|
||||
*buf++ = 0x1f24;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f9d:
|
||||
*buf++ = 0x1f25;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f9e:
|
||||
*buf++ = 0x1f26;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1f9f:
|
||||
*buf++ = 0x1f27;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fa0:
|
||||
*buf++ = 0x1f60;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fa1:
|
||||
*buf++ = 0x1f61;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fa2:
|
||||
*buf++ = 0x1f62;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fa3:
|
||||
*buf++ = 0x1f63;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fa4:
|
||||
*buf++ = 0x1f64;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fa5:
|
||||
*buf++ = 0x1f65;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fa6:
|
||||
*buf++ = 0x1f66;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fa7:
|
||||
*buf++ = 0x1f67;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fa8:
|
||||
*buf++ = 0x1f60;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fa9:
|
||||
*buf++ = 0x1f61;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1faa:
|
||||
*buf++ = 0x1f62;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fab:
|
||||
*buf++ = 0x1f63;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fac:
|
||||
*buf++ = 0x1f64;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fad:
|
||||
*buf++ = 0x1f65;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fae:
|
||||
*buf++ = 0x1f66;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1faf:
|
||||
*buf++ = 0x1f67;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fb2:
|
||||
*buf++ = 0x1f70;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fb3:
|
||||
*buf++ = 0x3b1;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fb4:
|
||||
*buf++ = 0x3ac;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fb6:
|
||||
*buf++ = 0x3b1;
|
||||
*buf++ = 0x342;
|
||||
return 2;
|
||||
case 0x1fb7:
|
||||
*buf++ = 0x3b1;
|
||||
*buf++ = 0x342;
|
||||
*buf++ = 0x3b9;
|
||||
return 3;
|
||||
case 0x1fbc:
|
||||
*buf++ = 0x3b1;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fc2:
|
||||
*buf++ = 0x1f74;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fc3:
|
||||
*buf++ = 0x3b7;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fc4:
|
||||
*buf++ = 0x3ae;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fc6:
|
||||
*buf++ = 0x3b7;
|
||||
*buf++ = 0x342;
|
||||
return 2;
|
||||
case 0x1fc7:
|
||||
*buf++ = 0x3b7;
|
||||
*buf++ = 0x342;
|
||||
*buf++ = 0x3b9;
|
||||
return 3;
|
||||
case 0x1fcc:
|
||||
*buf++ = 0x3b7;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1fd2:
|
||||
*buf++ = 0x3b9;
|
||||
*buf++ = 0x308;
|
||||
*buf++ = 0x300;
|
||||
return 3;
|
||||
case 0x1fd3:
|
||||
*buf++ = 0x3b9;
|
||||
*buf++ = 0x308;
|
||||
*buf++ = 0x301;
|
||||
return 3;
|
||||
case 0x1fd6:
|
||||
*buf++ = 0x3b9;
|
||||
*buf++ = 0x342;
|
||||
return 2;
|
||||
case 0x1fd7:
|
||||
*buf++ = 0x3b9;
|
||||
*buf++ = 0x308;
|
||||
*buf++ = 0x342;
|
||||
return 3;
|
||||
case 0x1fe2:
|
||||
*buf++ = 0x3c5;
|
||||
*buf++ = 0x308;
|
||||
*buf++ = 0x300;
|
||||
return 3;
|
||||
case 0x1fe3:
|
||||
*buf++ = 0x3c5;
|
||||
*buf++ = 0x308;
|
||||
*buf++ = 0x301;
|
||||
return 3;
|
||||
case 0x1fe4:
|
||||
*buf++ = 0x3c1;
|
||||
*buf++ = 0x313;
|
||||
return 2;
|
||||
case 0x1fe6:
|
||||
*buf++ = 0x3c5;
|
||||
*buf++ = 0x342;
|
||||
return 2;
|
||||
case 0x1fe7:
|
||||
*buf++ = 0x3c5;
|
||||
*buf++ = 0x308;
|
||||
*buf++ = 0x342;
|
||||
return 3;
|
||||
case 0x1ff2:
|
||||
*buf++ = 0x1f7c;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1ff3:
|
||||
*buf++ = 0x3c9;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1ff4:
|
||||
*buf++ = 0x3ce;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0x1ff6:
|
||||
*buf++ = 0x3c9;
|
||||
*buf++ = 0x342;
|
||||
return 2;
|
||||
case 0x1ff7:
|
||||
*buf++ = 0x3c9;
|
||||
*buf++ = 0x342;
|
||||
*buf++ = 0x3b9;
|
||||
return 3;
|
||||
case 0x1ffc:
|
||||
*buf++ = 0x3c9;
|
||||
*buf++ = 0x3b9;
|
||||
return 2;
|
||||
case 0xfb00:
|
||||
*buf++ = 0x66;
|
||||
*buf++ = 0x66;
|
||||
return 2;
|
||||
case 0xfb01:
|
||||
*buf++ = 0x66;
|
||||
*buf++ = 0x69;
|
||||
return 2;
|
||||
case 0xfb02:
|
||||
*buf++ = 0x66;
|
||||
*buf++ = 0x6c;
|
||||
return 2;
|
||||
case 0xfb03:
|
||||
*buf++ = 0x66;
|
||||
*buf++ = 0x66;
|
||||
*buf++ = 0x69;
|
||||
return 3;
|
||||
case 0xfb04:
|
||||
*buf++ = 0x66;
|
||||
*buf++ = 0x66;
|
||||
*buf++ = 0x6c;
|
||||
return 3;
|
||||
case 0xfb05:
|
||||
*buf++ = 0x73;
|
||||
*buf++ = 0x74;
|
||||
return 2;
|
||||
case 0xfb06:
|
||||
*buf++ = 0x73;
|
||||
*buf++ = 0x74;
|
||||
return 2;
|
||||
case 0xfb13:
|
||||
*buf++ = 0x574;
|
||||
*buf++ = 0x576;
|
||||
return 2;
|
||||
case 0xfb14:
|
||||
*buf++ = 0x574;
|
||||
*buf++ = 0x565;
|
||||
return 2;
|
||||
case 0xfb15:
|
||||
*buf++ = 0x574;
|
||||
*buf++ = 0x56b;
|
||||
return 2;
|
||||
case 0xfb16:
|
||||
*buf++ = 0x57e;
|
||||
*buf++ = 0x576;
|
||||
return 2;
|
||||
case 0xfb17:
|
||||
*buf++ = 0x574;
|
||||
*buf++ = 0x56d;
|
||||
return 2;
|
||||
default: ASSERT(false, ("Invalid UniChar", c));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MakeLowerCaseInplace(UniString & s)
|
||||
{
|
||||
size_t const size = s.size();
|
||||
|
||||
UniString r;
|
||||
r.reserve(size);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
UniChar const c = LowerUniChar(s[i]);
|
||||
if (c != 0)
|
||||
r.push_back(c);
|
||||
else
|
||||
{
|
||||
// special case, replace this char with two or more chars for full case folding
|
||||
UniChar cc[3];
|
||||
size_t const count = w(s[i], &cc[0]);
|
||||
for (size_t j = 0; j < count; ++j)
|
||||
r.push_back(cc[j]);
|
||||
}
|
||||
}
|
||||
|
||||
s.swap(r);
|
||||
}
|
||||
|
||||
size_t CountNormLowerSymbols(UniString const & s, UniString const & lowStr)
|
||||
{
|
||||
size_t const size = s.size();
|
||||
size_t const lowSize = lowStr.size();
|
||||
size_t lowIdx = 0, sIdx = 0;
|
||||
|
||||
while (lowIdx < lowSize)
|
||||
{
|
||||
if (sIdx == size)
|
||||
return 0; // low_s has more length than s
|
||||
|
||||
UniString strCharNorm;
|
||||
strCharNorm.push_back(s[sIdx++]);
|
||||
MakeLowerCaseInplace(strCharNorm);
|
||||
NormalizeInplace(strCharNorm);
|
||||
|
||||
for (size_t i = 0; i < strCharNorm.size(); ++i)
|
||||
if (lowIdx >= lowSize)
|
||||
return sIdx;
|
||||
else if (lowStr[lowIdx++] != strCharNorm[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sIdx;
|
||||
}
|
||||
|
||||
} // namespace strings
|
||||
173
libs/base/lru_cache.hpp
Normal file
173
libs/base/lru_cache.hpp
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
/// \brief Implementation of cache with least recently used replacement policy.
|
||||
template <typename Key, typename Value>
|
||||
class LruCache
|
||||
{
|
||||
template <typename K, typename V>
|
||||
friend class LruCacheTest;
|
||||
template <typename K, typename V>
|
||||
friend class LruCacheKeyAgeTest;
|
||||
|
||||
public:
|
||||
/// \param maxCacheSize Maximum size of the cache in number of items. It should be one or greater.
|
||||
/// \param loader Function which is called if it's necessary to load a new item for the cache.
|
||||
/// For the same |key| should be loaded the same |value|.
|
||||
explicit LruCache(size_t maxCacheSize) : m_maxCacheSize(maxCacheSize) { CHECK_GREATER(maxCacheSize, 0, ()); }
|
||||
|
||||
// Find value by @key. If @key is found, returns reference to its value.
|
||||
Value & Find(Key const & key, bool & found)
|
||||
{
|
||||
auto const it = m_cache.find(key);
|
||||
if (it != m_cache.cend())
|
||||
{
|
||||
m_keyAge.UpdateAge(key);
|
||||
found = true;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
if (m_cache.size() >= m_maxCacheSize)
|
||||
{
|
||||
m_cache.erase(m_keyAge.GetLruKey());
|
||||
m_keyAge.RemoveLru();
|
||||
}
|
||||
|
||||
m_keyAge.InsertKey(key);
|
||||
Value & value = m_cache[key];
|
||||
found = false;
|
||||
return value;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_cache.clear();
|
||||
m_keyAge.Clear();
|
||||
}
|
||||
|
||||
/// \brief Checks for coherence class params.
|
||||
/// \note It's a time consumption method and should be called for tests only.
|
||||
bool IsValidForTesting() const
|
||||
{
|
||||
if (!m_keyAge.IsValidForTesting())
|
||||
return false;
|
||||
|
||||
for (auto const & kv : m_cache)
|
||||
if (!m_keyAge.IsKeyValidForTesting(kv.first))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
/// \brief This class support cross mapping from age to key and for key to age.
|
||||
/// It lets effectively get least recently used key (key with minimum value of age)
|
||||
/// and find key age by its value to update the key age.
|
||||
/// \note Size of |m_ageToKey| and |m_ageToKey| should be the same.
|
||||
/// All keys of |m_ageToKey| should be values of |m_ageToKey| and on the contrary
|
||||
/// all keys of |m_ageToKey| should be values of |m_ageToKey|.
|
||||
/// \note Ages should be unique for all keys.
|
||||
class KeyAge
|
||||
{
|
||||
template <typename K, typename V>
|
||||
friend class LruCacheKeyAgeTest;
|
||||
|
||||
public:
|
||||
void Clear()
|
||||
{
|
||||
m_age = 0;
|
||||
m_ageToKey.clear();
|
||||
m_keyToAge.clear();
|
||||
}
|
||||
|
||||
/// \brief Increments |m_age| and insert key to |m_ageToKey| and |m_keyToAge|.
|
||||
/// \note This method should be used only if there's no |key| in |m_ageToKey| and |m_keyToAge|.
|
||||
void InsertKey(Key const & key)
|
||||
{
|
||||
++m_age;
|
||||
m_ageToKey[m_age] = key;
|
||||
m_keyToAge[key] = m_age;
|
||||
}
|
||||
|
||||
/// \brief Increments |m_age| and updates key age in |m_ageToKey| and |m_keyToAge|.
|
||||
/// \note This method should be used only if there's |key| in |m_ageToKey| and |m_keyToAge|.
|
||||
void UpdateAge(Key const & key)
|
||||
{
|
||||
++m_age;
|
||||
auto keyToAgeIt = m_keyToAge.find(key);
|
||||
CHECK(keyToAgeIt != m_keyToAge.end(), ());
|
||||
// Removing former age.
|
||||
size_t const removed = m_ageToKey.erase(keyToAgeIt->second);
|
||||
CHECK_EQUAL(removed, 1, ());
|
||||
// Putting new age.
|
||||
m_ageToKey[m_age] = key;
|
||||
keyToAgeIt->second = m_age;
|
||||
}
|
||||
|
||||
/// \returns Least recently used key without updating the age.
|
||||
/// \note |m_ageToKey| and |m_keyToAge| shouldn't be empty.
|
||||
Key const & GetLruKey() const
|
||||
{
|
||||
CHECK(!m_ageToKey.empty(), ());
|
||||
// The smaller age the older item.
|
||||
return m_ageToKey.cbegin()->second;
|
||||
}
|
||||
|
||||
void RemoveLru()
|
||||
{
|
||||
Key const & lru = GetLruKey();
|
||||
size_t const removed = m_keyToAge.erase(lru);
|
||||
CHECK_EQUAL(removed, 1, ());
|
||||
m_ageToKey.erase(m_ageToKey.begin());
|
||||
}
|
||||
|
||||
/// \brief Checks for coherence class params.
|
||||
/// \note It's a time consumption method and should be called for tests only.
|
||||
bool IsValidForTesting() const
|
||||
{
|
||||
for (auto const & kv : m_ageToKey)
|
||||
{
|
||||
if (kv.first > m_age)
|
||||
return false;
|
||||
if (m_keyToAge.find(kv.second) == m_keyToAge.cend())
|
||||
return false;
|
||||
}
|
||||
for (auto const & kv : m_keyToAge)
|
||||
{
|
||||
if (kv.second > m_age)
|
||||
return false;
|
||||
if (m_ageToKey.find(kv.second) == m_ageToKey.cend())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \returns true if |key| and its age are contained in |m_keyToAge| and |m_keyToAge|.
|
||||
/// \note It's a time consumption method and should be called for tests only.
|
||||
bool IsKeyValidForTesting(Key const & key) const
|
||||
{
|
||||
auto const keyToAgeId = m_keyToAge.find(key);
|
||||
if (keyToAgeId == m_keyToAge.cend())
|
||||
return false;
|
||||
auto const ageToKeyIt = m_ageToKey.find(keyToAgeId->second);
|
||||
if (ageToKeyIt == m_ageToKey.cend())
|
||||
return false;
|
||||
return key == ageToKeyIt->second;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_age = 0;
|
||||
std::map<size_t, Key> m_ageToKey;
|
||||
std::unordered_map<Key, size_t> m_keyToAge;
|
||||
};
|
||||
|
||||
size_t const m_maxCacheSize;
|
||||
std::unordered_map<Key, Value> m_cache;
|
||||
KeyAge m_keyAge;
|
||||
};
|
||||
79
libs/base/macros.hpp
Normal file
79
libs/base/macros.hpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
namespace base
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
// http://rsdn.ru/Forum/?mid=1025325
|
||||
template <typename T, unsigned int N>
|
||||
char (&ArraySize(T (&)[N]))[N];
|
||||
} // namespace impl
|
||||
} // namespace base
|
||||
|
||||
// Number of elements in array. Compilation error if the type passed is not an array.
|
||||
#define ARRAY_SIZE(X) sizeof(::base::impl::ArraySize(X))
|
||||
|
||||
#define DISALLOW_COPY(className) \
|
||||
className(className const &) = delete; \
|
||||
className & operator=(className const &) = delete
|
||||
|
||||
#define DISALLOW_MOVE(className) \
|
||||
className(className &&) = delete; \
|
||||
className & operator=(className &&) = delete
|
||||
|
||||
#define DISALLOW_COPY_AND_MOVE(className) \
|
||||
DISALLOW_COPY(className); \
|
||||
DISALLOW_MOVE(className)
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
#define TO_STRING_IMPL(x) #x
|
||||
#define TO_STRING(x) TO_STRING_IMPL(x)
|
||||
|
||||
#define UNUSED_VALUE(x) static_cast<void>(x)
|
||||
|
||||
namespace base
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
template <typename T>
|
||||
void ForceUseValue(T const & t)
|
||||
{
|
||||
T volatile dummy = t;
|
||||
UNUSED_VALUE(dummy);
|
||||
}
|
||||
} // namespace impl
|
||||
} // namespace base
|
||||
|
||||
// Prevent compiler optimization.
|
||||
#define FORCE_USE_VALUE(x) ::base::impl::ForceUseValue(x)
|
||||
|
||||
#ifdef __GNUC__
|
||||
// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
|
||||
#define PREDICT(x, prediction) __builtin_expect(static_cast<long>(x), static_cast<long>(prediction))
|
||||
#define PREDICT_TRUE(x) __builtin_expect(x, 1)
|
||||
#define PREDICT_FALSE(x) __builtin_expect(x, 0)
|
||||
#else
|
||||
#define PREDICT(x, prediction) (x)
|
||||
#define PREDICT_TRUE(x) (x)
|
||||
#define PREDICT_FALSE(x) (x)
|
||||
#endif
|
||||
|
||||
#define UINT16_FROM_UINT8(hi, lo) ((static_cast<uint16_t>(hi) << 8) | lo)
|
||||
#define UINT32_FROM_UINT16(hi, lo) ((static_cast<uint32_t>(hi) << 16) | lo)
|
||||
#define UINT64_FROM_UINT32(hi, lo) ((static_cast<uint64_t>(hi) << 32) | lo)
|
||||
|
||||
#define UINT32_FROM_UINT8(u3, u2, u1, u0) UINT32_FROM_UINT16(UINT16_FROM_UINT8(u3, u2), UINT16_FROM_UINT8(u1, u0))
|
||||
#define UINT64_FROM_UINT8(u7, u6, u5, u4, u3, u2, u1, u0) \
|
||||
UINT64_FROM_UINT32(UINT32_FROM_UINT8(u7, u6, u5, u4), UINT32_FROM_UINT8(u3, u2, u1, u0))
|
||||
|
||||
#define UINT16_LO(x) (static_cast<uint8_t>(x & 0xFF))
|
||||
#define UINT16_HI(x) (static_cast<uint8_t>(x >> 8))
|
||||
#define UINT32_LO(x) (static_cast<uint16_t>(x & 0xFFFF))
|
||||
#define UINT32_HI(x) (static_cast<uint16_t>(x >> 16))
|
||||
#define UINT64_LO(x) (static_cast<uint32_t>(x & 0xFFFFFFFF))
|
||||
#define UINT64_HI(x) (static_cast<uint32_t>(x >> 32))
|
||||
|
||||
#define NOTIMPLEMENTED() ASSERT(false, ("Function", __func__, "is not implemented!"))
|
||||
46
libs/base/math.cpp
Normal file
46
libs/base/math.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include "math.hpp"
|
||||
|
||||
#include <boost/integer.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
template <typename Float>
|
||||
bool AlmostEqualULPs(Float x, Float y, uint32_t maxULPs)
|
||||
{
|
||||
static_assert(std::is_floating_point<Float>::value, "");
|
||||
static_assert(std::numeric_limits<Float>::is_iec559, "");
|
||||
|
||||
// Make sure maxUlps is non-negative and small enough that the
|
||||
// default NaN won't compare as equal to anything.
|
||||
ASSERT_LESS(maxULPs, 4 * 1024 * 1024, ());
|
||||
|
||||
int constexpr bits = CHAR_BIT * sizeof(Float);
|
||||
typedef typename boost::int_t<bits>::exact IntType;
|
||||
typedef typename boost::uint_t<bits>::exact UIntType;
|
||||
|
||||
// Same as *reinterpret_cast<IntType const *>(&x), but without warnings.
|
||||
IntType xInt, yInt;
|
||||
static_assert(sizeof(xInt) == sizeof(x), "bit_cast impossible");
|
||||
std::memcpy(&xInt, &x, sizeof(x));
|
||||
std::memcpy(&yInt, &y, sizeof(y));
|
||||
|
||||
// Make xInt and yInt lexicographically ordered as a twos-complement int.
|
||||
IntType const highestBit = IntType(1) << (bits - 1);
|
||||
if (xInt < 0)
|
||||
xInt = highestBit - xInt;
|
||||
if (yInt < 0)
|
||||
yInt = highestBit - yInt;
|
||||
|
||||
// Calculate diff with special case to avoid IntType overflow.
|
||||
UIntType diff;
|
||||
if ((xInt >= 0) == (yInt >= 0))
|
||||
diff = math::Abs(xInt - yInt);
|
||||
else
|
||||
diff = UIntType(math::Abs(xInt)) + UIntType(math::Abs(yInt));
|
||||
|
||||
return diff <= maxULPs;
|
||||
}
|
||||
|
||||
template bool AlmostEqualULPs<float>(float x, float y, uint32_t maxULPs);
|
||||
template bool AlmostEqualULPs<double>(double x, double y, uint32_t maxULPs);
|
||||
178
libs/base/math.hpp
Normal file
178
libs/base/math.hpp
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/checked_cast.hpp"
|
||||
|
||||
#include <algorithm> // std::max
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <functional> // std::hash
|
||||
#include <type_traits>
|
||||
|
||||
namespace math
|
||||
{
|
||||
double constexpr pi = 3.14159265358979323846;
|
||||
double constexpr pi2 = pi / 2.0;
|
||||
double constexpr pi4 = pi / 4.0;
|
||||
|
||||
// Defined in fast_math.cpp
|
||||
double Nan();
|
||||
double Infinity();
|
||||
bool is_finite(double d);
|
||||
|
||||
template <std::floating_point T>
|
||||
int iround(T x)
|
||||
{
|
||||
return base::checked_cast<int>(std::lround(x));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Abs(T x)
|
||||
{
|
||||
static_assert(std::is_signed<T>::value, "");
|
||||
return (x < 0 ? -x : x);
|
||||
}
|
||||
|
||||
template <typename Number, typename EnableIf = typename std::enable_if_t<
|
||||
std::is_integral_v<Number> || std::is_floating_point_v<Number>, void>>
|
||||
int constexpr Sign(Number const number) noexcept
|
||||
{
|
||||
return number == 0 ? 0 : number > 0 ? 1 : -1;
|
||||
}
|
||||
} // namespace math
|
||||
|
||||
// Compare floats or doubles for almost equality.
|
||||
// maxULPs - number of closest floating point values that are considered equal.
|
||||
// Infinity is treated as almost equal to the largest possible floating point values.
|
||||
// NaN produces undefined result.
|
||||
//
|
||||
// This function is deprecated. Use AlmostEqualAbs, AlmostEqualRel or AlmostEqualAbsOrRel instead.
|
||||
// See https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
||||
// for details.
|
||||
// NOTE: Intentionally in the global namespace for ADL (see point2d.hpp)
|
||||
template <typename Float>
|
||||
bool AlmostEqualULPs(Float x, Float y, uint32_t maxULPs = 256);
|
||||
|
||||
// Returns true if x and y are equal up to the absolute difference eps.
|
||||
// Does not produce a sensible result if any of the arguments is NaN or infinity.
|
||||
// The default value for eps is deliberately not provided: the intended usage
|
||||
// is for the client to choose the precision according to the problem domain,
|
||||
// explicitly define the precision constant and call this function.
|
||||
// NOTE: Intentionally in the global namespace for ADL (see point2d.hpp)
|
||||
template <typename Float>
|
||||
bool AlmostEqualAbs(Float x, Float y, Float eps)
|
||||
{
|
||||
return fabs(x - y) < eps;
|
||||
}
|
||||
|
||||
// Returns true if x and y are equal up to the relative difference eps.
|
||||
// Does not produce a sensible result if any of the arguments is NaN, infinity or zero.
|
||||
// The same considerations as in AlmostEqualAbs apply.
|
||||
template <typename Float>
|
||||
bool AlmostEqualRel(Float x, Float y, Float eps)
|
||||
{
|
||||
return fabs(x - y) < eps * std::max(fabs(x), fabs(y));
|
||||
}
|
||||
|
||||
// Returns true if x and y are equal up to the absolute or relative difference eps.
|
||||
template <typename Float>
|
||||
bool AlmostEqualAbsOrRel(Float x, Float y, Float eps)
|
||||
{
|
||||
return AlmostEqualAbs(x, y, eps) || AlmostEqualRel(x, y, eps);
|
||||
}
|
||||
|
||||
namespace math
|
||||
{
|
||||
template <typename Float>
|
||||
Float constexpr DegToRad(Float deg)
|
||||
{
|
||||
return deg * Float(math::pi) / Float(180);
|
||||
}
|
||||
|
||||
template <typename Float>
|
||||
Float constexpr RadToDeg(Float rad)
|
||||
{
|
||||
return rad * Float(180) / Float(math::pi);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T Clamp(T const x, T const xmin, T const xmax)
|
||||
{
|
||||
if (x > xmax)
|
||||
return xmax;
|
||||
if (x < xmin)
|
||||
return xmin;
|
||||
return x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Between(T const a, T const b, T const x)
|
||||
{
|
||||
return a <= x && x <= b;
|
||||
}
|
||||
|
||||
// Computes x^n.
|
||||
template <typename T>
|
||||
T PowUint(T x, uint64_t n)
|
||||
{
|
||||
T res = 1;
|
||||
for (T t = x; n > 0; n >>= 1, t *= t)
|
||||
if (n & 1)
|
||||
res *= t;
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Pow2(T x)
|
||||
{
|
||||
return x * x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T NextModN(T x, T n)
|
||||
{
|
||||
ASSERT_GREATER(n, 0, ());
|
||||
return x + 1 == n ? 0 : x + 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T PrevModN(T x, T n)
|
||||
{
|
||||
ASSERT_GREATER(n, 0, ());
|
||||
return x == 0 ? n - 1 : x - 1;
|
||||
}
|
||||
|
||||
inline uint32_t NextPowOf2(uint32_t v)
|
||||
{
|
||||
v = v - 1;
|
||||
v |= (v >> 1);
|
||||
v |= (v >> 2);
|
||||
v |= (v >> 4);
|
||||
v |= (v >> 8);
|
||||
v |= (v >> 16);
|
||||
|
||||
return v + 1;
|
||||
}
|
||||
|
||||
// Greatest Common Divisor.
|
||||
template <typename Number, typename EnableIf = typename std::enable_if_t<std::is_integral_v<Number>, void>>
|
||||
Number constexpr GCD(Number const a, Number const b)
|
||||
{
|
||||
return b == 0 ? a : GCD(b, a % b);
|
||||
}
|
||||
|
||||
// Least Common Multiple.
|
||||
template <typename Number, typename EnableIf = typename std::enable_if_t<std::is_integral_v<Number>, void>>
|
||||
Number constexpr LCM(Number const a, Number const b)
|
||||
{
|
||||
return a / GCD(a, b) * b;
|
||||
}
|
||||
|
||||
// Calculate hash for the pair of values.
|
||||
template <typename T1, typename T2>
|
||||
size_t Hash(T1 const & t1, T2 const & t2)
|
||||
{
|
||||
/// @todo Probably, we need better hash for 2 integral types.
|
||||
return (std::hash<T1>()(t1) ^ (std::hash<T2>()(t2) << 1));
|
||||
}
|
||||
} // namespace math
|
||||
203
libs/base/matrix.hpp
Normal file
203
libs/base/matrix.hpp
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/math.hpp"
|
||||
|
||||
#include <initializer_list>
|
||||
#include <iomanip>
|
||||
|
||||
namespace math
|
||||
{
|
||||
template <typename T, unsigned Rows, unsigned Cols>
|
||||
struct Matrix
|
||||
{
|
||||
T m_data[Rows * Cols];
|
||||
|
||||
Matrix() {}
|
||||
|
||||
template <typename U>
|
||||
Matrix(Matrix<U, Rows, Cols> const & src)
|
||||
{
|
||||
for (size_t i = 0; i < Rows * Cols; ++i)
|
||||
m_data[i] = src.m_data[i];
|
||||
}
|
||||
|
||||
Matrix(T * data) { copy(data, data + Rows * Cols, m_data); }
|
||||
|
||||
Matrix(std::initializer_list<T> const & initList)
|
||||
{
|
||||
ASSERT(initList.size() == Rows * Cols, ());
|
||||
std::copy(initList.begin(), initList.end(), m_data);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
Matrix & operator=(Matrix<U, Rows, Cols> const & src)
|
||||
{
|
||||
if ((void *)this != (void *)&src)
|
||||
for (size_t i = 0; i < Rows * Cols; ++i)
|
||||
m_data[i] = src.m_data[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
T const & operator()(size_t row, size_t col) const { return m_data[row * Cols + col]; }
|
||||
|
||||
T & operator()(size_t row, size_t col) { return m_data[row * Cols + col]; }
|
||||
|
||||
template <typename U>
|
||||
bool operator==(Matrix<U, Rows, Cols> const & m) const
|
||||
{
|
||||
for (size_t i = 0; i < Rows; ++i)
|
||||
for (size_t j = 0; j < Cols; ++j)
|
||||
if (m_data[i * Cols + j] != m(i, j))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
bool Equal(Matrix<U, Rows, Cols> const & m, T eps = 0.0001) const
|
||||
{
|
||||
for (size_t i = 0; i < Rows; ++i)
|
||||
for (size_t j = 0; j < Cols; ++j)
|
||||
if (Abs(m_data[i * Cols + j] - m(i, j)) > eps)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
bool operator!=(Matrix<U, Rows, Cols> const & m) const
|
||||
{
|
||||
return !(*this == m);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
bool operator<(Matrix<U, Rows, Cols> const & m) const
|
||||
{
|
||||
for (size_t i = 0; i < Rows; ++i)
|
||||
for (size_t j = 0; j < Cols; ++j)
|
||||
{
|
||||
if (m_data[i * Cols + j] > m(i, j))
|
||||
return false;
|
||||
if (m_data[i * Cols + j] < m(i, j))
|
||||
return true;
|
||||
}
|
||||
|
||||
/// All elements are equal
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T Determinant(Matrix<T, 1, 1> const & m)
|
||||
{
|
||||
return m(0, 0);
|
||||
}
|
||||
|
||||
template <typename T, unsigned M>
|
||||
T Determinant(Matrix<T, M, M> const & m)
|
||||
{
|
||||
T sign = 1;
|
||||
T res = 0;
|
||||
for (size_t i = 0; i < M; ++i, sign *= -1)
|
||||
res += sign * m(0, i) * Determinant(Splice(m, 0, i));
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T, unsigned Rows, unsigned Cols>
|
||||
Matrix<T, Rows - 1, Cols - 1> const Splice(Matrix<T, Rows, Cols> const & m, size_t Row, size_t Col)
|
||||
{
|
||||
Matrix<T, Rows - 1, Cols - 1> res;
|
||||
|
||||
for (size_t i = 0; i < Row; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < Col; ++j)
|
||||
res(i, j) = m(i, j);
|
||||
for (size_t j = Col + 1; j < Cols; ++j)
|
||||
res(i, j - 1) = m(i, j);
|
||||
}
|
||||
|
||||
for (size_t i = Row + 1; i < Rows; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < Col; ++j)
|
||||
res(i - 1, j) = m(i, j);
|
||||
for (size_t j = Col + 1; j < Cols; ++j)
|
||||
res(i - 1, j - 1) = m(i, j);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T, unsigned M>
|
||||
Matrix<T, M, M> const Inverse(Matrix<T, M, M> const & m)
|
||||
{
|
||||
T det = Determinant(m);
|
||||
Matrix<T, M, M> res;
|
||||
|
||||
for (size_t i = 0; i < M; ++i)
|
||||
for (size_t j = 0; j < M; ++j)
|
||||
{
|
||||
T sign = 1.0 - 2.0 * ((i + j) % 2);
|
||||
res(j, i) = sign * Determinant(Splice(m, i, j)) / det;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T, unsigned M>
|
||||
Matrix<T, M, M> const Identity()
|
||||
{
|
||||
Matrix<T, M, M> res;
|
||||
|
||||
for (size_t i = 0; i < M; ++i)
|
||||
for (size_t j = 0; j < M; ++j)
|
||||
res(i, j) = 0;
|
||||
|
||||
for (size_t i = 0; i < M; ++i)
|
||||
res(i, i) = 1;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T, unsigned M>
|
||||
Matrix<T, M, M> const Zero()
|
||||
{
|
||||
Matrix<T, M, M> res;
|
||||
|
||||
for (size_t i = 0; i < M; ++i)
|
||||
for (size_t j = 0; j < M; ++j)
|
||||
res(i, j) = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T, unsigned M, unsigned N, unsigned K>
|
||||
Matrix<T, M, K> operator*(Matrix<T, M, N> const & l, Matrix<T, N, K> const & r)
|
||||
{
|
||||
Matrix<T, M, K> res;
|
||||
for (size_t m = 0; m < M; ++m)
|
||||
for (size_t k = 0; k < K; ++k)
|
||||
{
|
||||
T sum = 0;
|
||||
for (size_t n = 0; n < N; ++n)
|
||||
sum += l(m, n) * r(n, k);
|
||||
res(m, k) = sum;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T, unsigned M, unsigned N>
|
||||
std::string DebugPrint(Matrix<T, M, N> const & m)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
|
||||
ss << ":" << std::endl;
|
||||
|
||||
for (unsigned i = 0; i < M; ++i)
|
||||
{
|
||||
for (unsigned j = 0; j < N; ++j)
|
||||
ss << std::setfill(' ') << std::setw(10) << m(i, j) << " ";
|
||||
ss << std::endl;
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
} // namespace math
|
||||
584
libs/base/mem_trie.hpp
Normal file
584
libs/base/mem_trie.hpp
Normal file
|
|
@ -0,0 +1,584 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace base
|
||||
{
|
||||
template <typename Char, typename Subtree>
|
||||
class MapMoves
|
||||
{
|
||||
public:
|
||||
template <typename ToDo>
|
||||
void ForEach(ToDo && toDo) const
|
||||
{
|
||||
for (auto const & subtree : m_subtrees)
|
||||
toDo(subtree.first, *subtree.second);
|
||||
}
|
||||
|
||||
template <typename ToDo>
|
||||
void ForEach(ToDo && toDo)
|
||||
{
|
||||
for (auto const & subtree : m_subtrees)
|
||||
toDo(subtree.first, *subtree.second);
|
||||
}
|
||||
|
||||
Subtree * GetSubtree(Char const & c) const
|
||||
{
|
||||
auto const it = m_subtrees.find(c);
|
||||
if (it == m_subtrees.end())
|
||||
return nullptr;
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
Subtree & GetOrCreateSubtree(Char const & c, bool & created)
|
||||
{
|
||||
auto & node = m_subtrees[c];
|
||||
if (!node)
|
||||
{
|
||||
node = std::make_unique<Subtree>();
|
||||
created = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
created = false;
|
||||
}
|
||||
return *node;
|
||||
}
|
||||
|
||||
void AddSubtree(Char const & c, std::unique_ptr<Subtree> subtree)
|
||||
{
|
||||
ASSERT(!GetSubtree(c), ());
|
||||
m_subtrees.emplace(c, std::move(subtree));
|
||||
}
|
||||
|
||||
void EraseSubtree(Char const & c) { m_subtrees.erase(c); }
|
||||
|
||||
size_t Size() const { return m_subtrees.size(); }
|
||||
bool Empty() const { return Size() == 0; }
|
||||
|
||||
void Clear() { m_subtrees.clear(); }
|
||||
|
||||
void Swap(MapMoves & rhs) { m_subtrees.swap(rhs.m_subtrees); }
|
||||
|
||||
private:
|
||||
std::map<Char, std::unique_ptr<Subtree>> m_subtrees;
|
||||
};
|
||||
|
||||
template <typename Char, typename Subtree>
|
||||
class VectorMoves
|
||||
{
|
||||
public:
|
||||
template <typename ToDo>
|
||||
void ForEach(ToDo && toDo) const
|
||||
{
|
||||
for (auto const & subtree : m_subtrees)
|
||||
toDo(subtree.first, *subtree.second);
|
||||
}
|
||||
|
||||
Subtree * GetSubtree(Char const & c) const
|
||||
{
|
||||
auto const it = Find(c);
|
||||
return it != m_subtrees.cend() ? it->second.get() : nullptr;
|
||||
}
|
||||
|
||||
Subtree & GetOrCreateSubtree(Char const & c, bool & created)
|
||||
{
|
||||
if (auto * const subtree = GetSubtree(c))
|
||||
{
|
||||
created = false;
|
||||
return *subtree;
|
||||
}
|
||||
|
||||
created = true;
|
||||
m_subtrees.emplace_back(c, std::make_unique<Subtree>());
|
||||
return *m_subtrees.back().second;
|
||||
}
|
||||
|
||||
void AddSubtree(Char const & c, std::unique_ptr<Subtree> subtree)
|
||||
{
|
||||
ASSERT(!GetSubtree(c), ());
|
||||
m_subtrees.emplace_back(c, std::move(subtree));
|
||||
}
|
||||
|
||||
void EraseSubtree(Char const & c)
|
||||
{
|
||||
auto const it = Find(c);
|
||||
if (it != m_subtrees.end())
|
||||
m_subtrees.erase(it);
|
||||
}
|
||||
|
||||
size_t Size() const { return m_subtrees.size(); }
|
||||
bool Empty() const { return Size() == 0; }
|
||||
|
||||
void Clear() { m_subtrees.clear(); }
|
||||
|
||||
void Swap(VectorMoves & rhs) { m_subtrees.swap(rhs.m_subtrees); }
|
||||
|
||||
private:
|
||||
using Entry = std::pair<Char, std::unique_ptr<Subtree>>;
|
||||
using Entries = std::vector<Entry>;
|
||||
|
||||
typename Entries::iterator Find(Char const & c)
|
||||
{
|
||||
return std::find_if(m_subtrees.begin(), m_subtrees.end(), [&c](Entry const & entry) { return entry.first == c; });
|
||||
}
|
||||
|
||||
typename Entries::const_iterator Find(Char const & c) const
|
||||
{
|
||||
return std::find_if(m_subtrees.cbegin(), m_subtrees.cend(), [&c](Entry const & entry) { return entry.first == c; });
|
||||
}
|
||||
|
||||
Entries m_subtrees;
|
||||
};
|
||||
|
||||
template <typename V>
|
||||
struct VectorValues
|
||||
{
|
||||
using value_type = V;
|
||||
using Value = V;
|
||||
|
||||
template <typename... Args>
|
||||
void Add(Args &&... args)
|
||||
{
|
||||
m_values.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void Erase(Value const & v)
|
||||
{
|
||||
auto const it = std::find(m_values.begin(), m_values.end(), v);
|
||||
if (it != m_values.end())
|
||||
m_values.erase(it);
|
||||
}
|
||||
|
||||
template <typename ToDo>
|
||||
void ForEach(ToDo && toDo) const
|
||||
{
|
||||
for (auto const & value : m_values)
|
||||
toDo(value);
|
||||
}
|
||||
|
||||
bool Empty() const { return m_values.empty(); }
|
||||
|
||||
void Clear() { m_values.clear(); }
|
||||
|
||||
void Swap(VectorValues & rhs) { m_values.swap(rhs.m_values); }
|
||||
|
||||
std::vector<Value> m_values;
|
||||
};
|
||||
|
||||
// This class is a simple in-memory trie which allows to add
|
||||
// key-value pairs and then traverse them in a sorted order.
|
||||
template <typename String, class ValuesHolder, template <typename...> class Moves = MapMoves>
|
||||
class MemTrie
|
||||
{
|
||||
private:
|
||||
struct Node;
|
||||
|
||||
public:
|
||||
using Char = typename String::value_type;
|
||||
using Value = typename ValuesHolder::value_type;
|
||||
|
||||
MemTrie() = default;
|
||||
MemTrie(MemTrie && rhs) { *this = std::move(rhs); }
|
||||
|
||||
MemTrie & operator=(MemTrie && rhs)
|
||||
{
|
||||
m_root = std::move(rhs.m_root);
|
||||
rhs.Clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// A read-only iterator wrapping a Node. Any modification to the
|
||||
// underlying trie is assumed to invalidate the iterator.
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
using Char = MemTrie::Char;
|
||||
|
||||
Iterator(Node const & node) : m_node(node) {}
|
||||
|
||||
// Iterates over all possible moves from this Iterator's node
|
||||
// and calls |toDo| with two arguments:
|
||||
// (Char of the move, Iterator wrapping the node of the move).
|
||||
template <typename ToDo>
|
||||
void ForEachMove(ToDo && toDo) const
|
||||
{
|
||||
m_node.m_moves.ForEach([&](Char const & c, Node const & node) { toDo(c, Iterator(node)); });
|
||||
}
|
||||
|
||||
// Calls |toDo| for every value in this Iterator's node.
|
||||
template <typename ToDo>
|
||||
void ForEachInNode(ToDo && toDo) const
|
||||
{
|
||||
m_node.m_values.ForEach(toDo);
|
||||
}
|
||||
|
||||
String GetLabel() const { return m_node.m_edge.template As<String>(); }
|
||||
ValuesHolder const & GetValues() const { return m_node.m_values; }
|
||||
|
||||
private:
|
||||
Node const & m_node;
|
||||
};
|
||||
|
||||
// Adds a key-value pair to the trie.
|
||||
template <typename... Args>
|
||||
void Add(String const & key, Args &&... args)
|
||||
{
|
||||
auto * curr = &m_root;
|
||||
|
||||
auto it = key.begin();
|
||||
while (it != key.end())
|
||||
{
|
||||
bool created;
|
||||
curr = &curr->GetOrCreateMove(*it++, created);
|
||||
|
||||
auto & edge = curr->m_edge;
|
||||
|
||||
if (created)
|
||||
{
|
||||
edge.Assign(it, key.end());
|
||||
it = key.end();
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
SkipEqual(edge, key.end(), i, it);
|
||||
|
||||
if (i == edge.Size())
|
||||
{
|
||||
// We may directly add value to the |curr| values, when edge
|
||||
// equals to the rest of the |key|. Otherwise we need to jump
|
||||
// to the next iteration of the loop and continue traversal of
|
||||
// the trie.
|
||||
continue;
|
||||
}
|
||||
|
||||
// We need to split the edge to |curr|.
|
||||
auto node = std::make_unique<Node>();
|
||||
|
||||
ASSERT_LESS(i, edge.Size(), ());
|
||||
node->m_edge = edge.DropFirst(i);
|
||||
|
||||
ASSERT(!edge.Empty(), ());
|
||||
auto const next = edge[0];
|
||||
edge.DropFirst(1);
|
||||
|
||||
node->Swap(*curr);
|
||||
curr->AddChild(next, std::move(node));
|
||||
}
|
||||
|
||||
curr->AddValue(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void Erase(String const & key, Value const & value) { Erase(m_root, key.begin(), key.end(), value); }
|
||||
|
||||
// Traverses all key-value pairs in the trie and calls |toDo| on each of them.
|
||||
template <typename ToDo>
|
||||
void ForEachInTrie(ToDo && toDo) const
|
||||
{
|
||||
String prefix;
|
||||
ForEachInSubtree(m_root, prefix, toDo);
|
||||
}
|
||||
|
||||
// Calls |toDo| for each key-value pair in the node that is reachable
|
||||
// by |prefix| from the trie root. Does nothing if such node does
|
||||
// not exist.
|
||||
template <typename ToDo>
|
||||
void ForEachInNode(String const & prefix, ToDo && toDo) const
|
||||
{
|
||||
MoveTo(prefix, true /* fullMatch */,
|
||||
[&](Node const & node, Edge const & /* edge */, size_t /* offset */) { node.m_values.ForEach(toDo); });
|
||||
}
|
||||
|
||||
template <typename ToDo>
|
||||
void WithValuesHolder(String const & prefix, ToDo && toDo) const
|
||||
{
|
||||
MoveTo(prefix, true /* fullMatch */,
|
||||
[&toDo](Node const & node, Edge const & /* edge */, size_t /* offset */) { toDo(node.m_values); });
|
||||
}
|
||||
|
||||
// Calls |toDo| for each key-value pair in a subtree that is
|
||||
// reachable by |prefix| from the trie root. Does nothing if such
|
||||
// subtree does not exist.
|
||||
template <typename ToDo>
|
||||
void ForEachInSubtree(String const & prefix, ToDo && toDo) const
|
||||
{
|
||||
MoveTo(prefix, false /* fullMatch */, [&](Node const & node, Edge const & edge, size_t offset)
|
||||
{
|
||||
String p = prefix;
|
||||
for (; offset < edge.Size(); ++offset)
|
||||
p.push_back(edge[offset]);
|
||||
ForEachInSubtree(node, p, toDo);
|
||||
});
|
||||
}
|
||||
|
||||
bool HasKey(String const & key) const
|
||||
{
|
||||
bool exists = false;
|
||||
MoveTo(key, true /* fullMatch */,
|
||||
[&](Node const & node, Edge const & /* edge */, size_t /* offset */) { exists = !node.m_values.Empty(); });
|
||||
return exists;
|
||||
}
|
||||
|
||||
bool HasPrefix(String const & prefix) const
|
||||
{
|
||||
bool exists = false;
|
||||
MoveTo(prefix, false /* fullMatch */,
|
||||
[&](Node const & node, Edge const & /* edge */, size_t /* offset */) { exists = !node.Empty(); });
|
||||
return exists;
|
||||
}
|
||||
|
||||
void Clear() { m_root.Clear(); }
|
||||
|
||||
size_t GetNumNodes() const { return m_root.GetNumNodes(); }
|
||||
|
||||
Iterator GetRootIterator() const { return Iterator(m_root); }
|
||||
|
||||
Node const & GetRoot() const { return m_root; }
|
||||
|
||||
private:
|
||||
// Stores tail (all characters except the first one) of an edge from
|
||||
// a parent node to a child node. Characters are stored in reverse
|
||||
// order, because we need to efficiently add and cut prefixes.
|
||||
class Edge
|
||||
{
|
||||
public:
|
||||
Edge() = default;
|
||||
|
||||
template <typename It>
|
||||
Edge(It begin, It end)
|
||||
{
|
||||
Assign(begin, end);
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
void Assign(It begin, It end)
|
||||
{
|
||||
m_label.assign(begin, end);
|
||||
std::reverse(m_label.begin(), m_label.end());
|
||||
}
|
||||
|
||||
// Drops first |n| characters from the edge.
|
||||
//
|
||||
// Complexity: amortized O(n).
|
||||
Edge DropFirst(size_t n)
|
||||
{
|
||||
ASSERT_LESS_OR_EQUAL(n, Size(), ());
|
||||
|
||||
Edge prefix(m_label.rbegin(), m_label.rbegin() + n);
|
||||
m_label.erase(m_label.begin() + Size() - n, m_label.end());
|
||||
return prefix;
|
||||
}
|
||||
|
||||
// Prepends |c| to the edge.
|
||||
//
|
||||
// Complexity: amortized O(1).
|
||||
void Prepend(Char const & c) { m_label.push_back(c); }
|
||||
|
||||
// Prepends |prefix| to the edge.
|
||||
//
|
||||
// Complexity: amortized O(|prefix|)
|
||||
void Prepend(Edge const & prefix) { m_label.insert(m_label.end(), prefix.m_label.cbegin(), prefix.m_label.cend()); }
|
||||
|
||||
Char operator[](size_t i) const
|
||||
{
|
||||
ASSERT_LESS(i, Size(), ());
|
||||
return *(m_label.rbegin() + i);
|
||||
}
|
||||
|
||||
size_t Size() const { return m_label.size(); }
|
||||
|
||||
bool Empty() const { return Size() == 0; }
|
||||
|
||||
void Swap(Edge & rhs) { m_label.swap(rhs.m_label); }
|
||||
|
||||
template <typename Sequence>
|
||||
Sequence As() const
|
||||
{
|
||||
return {m_label.rbegin(), m_label.rend()};
|
||||
}
|
||||
|
||||
friend std::string DebugPrint(Edge const & edge) { return edge.template As<std::string>(); }
|
||||
|
||||
private:
|
||||
std::vector<Char> m_label;
|
||||
};
|
||||
|
||||
struct Node
|
||||
{
|
||||
Node() = default;
|
||||
Node(Node && /* rhs */) = default;
|
||||
|
||||
Node & operator=(Node && /* rhs */) = default;
|
||||
|
||||
Node & GetOrCreateMove(Char const & c, bool & created) { return m_moves.GetOrCreateSubtree(c, created); }
|
||||
|
||||
Node * GetMove(Char const & c) const { return m_moves.GetSubtree(c); }
|
||||
|
||||
void AddChild(Char const & c, std::unique_ptr<Node> node) { m_moves.AddSubtree(c, std::move(node)); }
|
||||
|
||||
void EraseMove(Char const & c) { m_moves.EraseSubtree(c); }
|
||||
|
||||
template <typename... Args>
|
||||
void AddValue(Args &&... args)
|
||||
{
|
||||
m_values.Add(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void EraseValue(Value const & value) { m_values.Erase(value); }
|
||||
|
||||
bool Empty() const { return m_moves.Empty() && m_values.Empty(); }
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_moves.Clear();
|
||||
m_values.Clear();
|
||||
}
|
||||
|
||||
size_t GetNumNodes() const
|
||||
{
|
||||
size_t size = 1;
|
||||
m_moves.ForEach([&size](Char const & /* c */, Node const & child) { size += child.GetNumNodes(); });
|
||||
return size;
|
||||
}
|
||||
|
||||
void Swap(Node & rhs)
|
||||
{
|
||||
m_moves.Swap(rhs.m_moves);
|
||||
m_edge.Swap(rhs.m_edge);
|
||||
m_values.Swap(rhs.m_values);
|
||||
}
|
||||
|
||||
Moves<Char, Node> m_moves;
|
||||
Edge m_edge;
|
||||
ValuesHolder m_values;
|
||||
|
||||
DISALLOW_COPY(Node);
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
void MoveTo(String const & prefix, bool fullMatch, Fn && fn) const
|
||||
{
|
||||
auto const * cur = &m_root;
|
||||
|
||||
auto it = prefix.begin();
|
||||
|
||||
if (it == prefix.end())
|
||||
{
|
||||
fn(*cur, cur->m_edge, 0 /* offset */);
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
ASSERT(it != prefix.end(), ());
|
||||
|
||||
cur = cur->GetMove(*it++);
|
||||
if (!cur)
|
||||
return;
|
||||
|
||||
auto const & edge = cur->m_edge;
|
||||
size_t i = 0;
|
||||
SkipEqual(edge, prefix.end(), i, it);
|
||||
|
||||
if (i < edge.Size())
|
||||
{
|
||||
if (it != prefix.end() || fullMatch)
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(i == edge.Size() || (it == prefix.end() && !fullMatch), ());
|
||||
|
||||
if (it == prefix.end())
|
||||
{
|
||||
fn(*cur, edge, i /* offset */);
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(it != prefix.end() && i == edge.Size(), ());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
void Erase(Node & root, It cur, It end, Value const & value)
|
||||
{
|
||||
if (cur == end)
|
||||
{
|
||||
root.EraseValue(value);
|
||||
if (root.m_values.Empty() && root.m_moves.Size() == 1)
|
||||
{
|
||||
Node child;
|
||||
Char c;
|
||||
root.m_moves.ForEach([&](Char const & mc, Node & mn)
|
||||
{
|
||||
c = mc;
|
||||
child.Swap(mn);
|
||||
});
|
||||
child.m_edge.Prepend(c);
|
||||
child.m_edge.Prepend(root.m_edge);
|
||||
root.Swap(child);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto const symbol = *cur++;
|
||||
|
||||
auto * child = root.GetMove(symbol);
|
||||
if (!child)
|
||||
return;
|
||||
|
||||
auto const & edge = child->m_edge;
|
||||
size_t i = 0;
|
||||
SkipEqual(edge, end, i, cur);
|
||||
|
||||
if (i == edge.Size())
|
||||
{
|
||||
Erase(*child, cur, end, value);
|
||||
if (child->Empty())
|
||||
root.EraseMove(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
// Calls |toDo| for each key-value pair in subtree where |node| is a
|
||||
// root of the subtree. |prefix| is a path from the trie root to the
|
||||
// |node|. Because we need to accumulate labels from the root to the
|
||||
// current |node| somewhere, |prefix| is passed by reference here,
|
||||
// but after the call the contents of the |prefix| will be the same
|
||||
// as before the call.
|
||||
template <typename ToDo>
|
||||
void ForEachInSubtree(Node const & node, String & prefix, ToDo && toDo) const
|
||||
{
|
||||
node.m_values.ForEach([&prefix, &toDo](Value const & value) { toDo(static_cast<String const &>(prefix), value); });
|
||||
|
||||
node.m_moves.ForEach([&](Char const & c, Node const & node)
|
||||
{
|
||||
auto const size = prefix.size();
|
||||
auto const edge = node.m_edge.template As<String>();
|
||||
prefix.push_back(c);
|
||||
prefix.insert(prefix.end(), edge.begin(), edge.end());
|
||||
ForEachInSubtree(node, prefix, toDo);
|
||||
prefix.resize(size);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
void SkipEqual(Edge const & edge, It end, size_t & i, It & cur) const
|
||||
{
|
||||
while (i < edge.Size() && cur != end && edge[i] == *cur)
|
||||
{
|
||||
++i;
|
||||
++cur;
|
||||
}
|
||||
}
|
||||
|
||||
Node m_root;
|
||||
|
||||
DISALLOW_COPY(MemTrie);
|
||||
};
|
||||
} // namespace base
|
||||
169
libs/base/newtype.hpp
Normal file
169
libs/base/newtype.hpp
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace base
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
template <typename From, typename To>
|
||||
using IsConvertibleGuard = std::enable_if_t<std::is_convertible<From, To>::value> *;
|
||||
} // namespace impl
|
||||
|
||||
/// Creates a typesafe alias to a given numeric Type.
|
||||
template <typename Type, typename Tag, typename Hasher = std::hash<Type>>
|
||||
class NewType
|
||||
{
|
||||
static_assert(std::is_integral<Type>::value || std::is_floating_point<Type>::value,
|
||||
"NewType can be used only with integral and floating point type.");
|
||||
|
||||
public:
|
||||
using RepType = Type;
|
||||
|
||||
template <typename V, impl::IsConvertibleGuard<V, Type> = nullptr>
|
||||
constexpr explicit NewType(V const & v) : m_value(v)
|
||||
{}
|
||||
|
||||
constexpr NewType() = default;
|
||||
|
||||
template <typename V, impl::IsConvertibleGuard<V, Type> = nullptr>
|
||||
NewType & Set(V const & v)
|
||||
{
|
||||
m_value = static_cast<Type>(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Type const & Get() const { return m_value; }
|
||||
Type & Get() { return m_value; }
|
||||
|
||||
NewType & operator++()
|
||||
{
|
||||
++m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NewType const operator++(int)
|
||||
{
|
||||
auto const copy = *this;
|
||||
++m_value;
|
||||
return copy;
|
||||
}
|
||||
|
||||
NewType & operator--()
|
||||
{
|
||||
--m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NewType const operator--(int)
|
||||
{
|
||||
auto const copy = *this;
|
||||
--m_value;
|
||||
return copy;
|
||||
}
|
||||
|
||||
NewType & operator+=(NewType const & v)
|
||||
{
|
||||
m_value += v.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NewType & operator-=(NewType const & v)
|
||||
{
|
||||
m_value -= v.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NewType & operator*=(NewType const & v)
|
||||
{
|
||||
m_value *= v.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NewType & operator/=(NewType const & v)
|
||||
{
|
||||
m_value /= v.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NewType & operator%=(NewType const & v)
|
||||
{
|
||||
m_value %= v.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NewType & operator^=(NewType const & v)
|
||||
{
|
||||
m_value ^= v.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NewType & operator|=(NewType const & v)
|
||||
{
|
||||
m_value |= v.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NewType & operator&=(NewType const & v)
|
||||
{
|
||||
m_value &= v.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TODO(mgsergio): Is it meaningful for a newtype to have << operator ?
|
||||
// NewType & operator<<=(NewType<V, VTag> const & v)
|
||||
// NewType & operator>>=(NewType<V, VTag> const & v)
|
||||
|
||||
bool operator==(NewType const & o) const { return m_value == o.m_value; }
|
||||
bool operator!=(NewType const & o) const { return !(m_value == o.m_value); }
|
||||
bool operator<(NewType const & o) const { return m_value < o.m_value; }
|
||||
bool operator>(NewType const & o) const { return m_value > o.m_value; }
|
||||
bool operator<=(NewType const & o) const { return !(m_value > o.m_value); }
|
||||
bool operator>=(NewType const & o) const { return !(m_value < o.m_value); }
|
||||
NewType operator+(NewType const & o) const { return NewType(m_value + o.m_value); }
|
||||
NewType operator-(NewType const & o) const { return NewType(m_value - o.m_value); }
|
||||
NewType operator*(NewType const & o) const { return NewType(m_value * o.m_value); }
|
||||
NewType operator/(NewType const & o) const { return NewType(m_value / o.m_value); }
|
||||
NewType operator%(NewType const & o) const { return NewType(m_value % o.m_value); }
|
||||
NewType operator^(NewType const & o) const { return NewType(m_value ^ o.m_value); }
|
||||
NewType operator|(NewType const & o) const { return NewType(m_value | o.m_value); }
|
||||
NewType operator&(NewType const & o) const { return NewType(m_value & o.m_value); }
|
||||
|
||||
struct Hash
|
||||
{
|
||||
size_t operator()(NewType const & v) const
|
||||
{
|
||||
Hasher h;
|
||||
return h(v.Get());
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
Type m_value;
|
||||
};
|
||||
|
||||
namespace newtype_default_output
|
||||
{
|
||||
template <typename Type, typename Tag>
|
||||
std::string SimpleDebugPrint(NewType<Type, Tag> const & nt)
|
||||
{
|
||||
return ::DebugPrint(nt.Get());
|
||||
}
|
||||
} // namespace newtype_default_output
|
||||
} // namespace base
|
||||
|
||||
#define NEWTYPE(REPR, NAME) \
|
||||
struct NAME##_tag; \
|
||||
using NAME = base::NewType<REPR, NAME##_tag>
|
||||
|
||||
#define NEWTYPE_SIMPLE_OUTPUT(NAME) \
|
||||
inline std::string DebugPrint(NAME const & nt) \
|
||||
{ \
|
||||
return base::newtype_default_output::SimpleDebugPrint(nt); \
|
||||
} \
|
||||
inline std::ostream & operator<<(std::ostream & ost, NAME const & nt) \
|
||||
{ \
|
||||
return ost << base::newtype_default_output::SimpleDebugPrint(nt); \
|
||||
}
|
||||
84
libs/base/non_intersecting_intervals.hpp
Normal file
84
libs/base/non_intersecting_intervals.hpp
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
namespace base
|
||||
{
|
||||
///\brief Data structure that maintains a set of non-intersecting intervals.
|
||||
/// A new interval may only be added to the set if it does not intersect any
|
||||
/// of the present intervals, thus the choice which intervals to keep is made
|
||||
/// in a greedy online fashion with no lookahead.
|
||||
template <typename T>
|
||||
class NonIntersectingIntervals
|
||||
{
|
||||
public:
|
||||
/// \brief Adds new interval to set if it doesn't intersect with any that has been already added.
|
||||
/// \return true if there are no such intervals, that intersect the [left, right] interval.
|
||||
bool AddInterval(T left, T right);
|
||||
bool Intersects(T left, T right) const;
|
||||
|
||||
private:
|
||||
struct Interval
|
||||
{
|
||||
Interval(T left, T right);
|
||||
|
||||
struct LessByLeftEnd
|
||||
{
|
||||
bool operator()(Interval const & lhs, Interval const & rhs) const;
|
||||
};
|
||||
|
||||
bool Intersects(Interval const & rhs) const;
|
||||
|
||||
T m_left{};
|
||||
T m_right{};
|
||||
};
|
||||
|
||||
std::set<Interval, typename Interval::LessByLeftEnd> m_leftEnds;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bool NonIntersectingIntervals<T>::Intersects(T left, T right) const
|
||||
{
|
||||
Interval interval(left, right);
|
||||
auto it = m_leftEnds.lower_bound(interval);
|
||||
if (it != m_leftEnds.end() && interval.Intersects(*it))
|
||||
return true;
|
||||
|
||||
if (it != m_leftEnds.begin() && interval.Intersects(*std::prev(it)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool NonIntersectingIntervals<T>::AddInterval(T left, T right)
|
||||
{
|
||||
if (Intersects(left, right))
|
||||
return false;
|
||||
|
||||
m_leftEnds.emplace(left, right);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
NonIntersectingIntervals<T>::Interval::Interval(T left, T right) : m_left(left)
|
||||
, m_right(right)
|
||||
{
|
||||
CHECK_LESS_OR_EQUAL(left, right, ());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool NonIntersectingIntervals<T>::Interval::LessByLeftEnd::operator()(Interval const & lhs, Interval const & rhs) const
|
||||
{
|
||||
return lhs.m_left < rhs.m_left;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool NonIntersectingIntervals<T>::Interval::Intersects(Interval const & rhs) const
|
||||
{
|
||||
return std::max(m_left, rhs.m_left) <= std::min(m_right, rhs.m_right);
|
||||
}
|
||||
} // namespace base
|
||||
4874
libs/base/normalize_unicode.cpp
Normal file
4874
libs/base/normalize_unicode.cpp
Normal file
File diff suppressed because it is too large
Load diff
72
libs/base/observer_list.hpp
Normal file
72
libs/base/observer_list.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace base
|
||||
{
|
||||
class DummyLockable
|
||||
{
|
||||
public:
|
||||
void lock() {}
|
||||
void unlock() {}
|
||||
};
|
||||
|
||||
template <typename Observer, typename Mutex>
|
||||
class ObserverList;
|
||||
|
||||
/// This alias represents a thread-safe observers list. It allows to
|
||||
/// add/remove observers as well as trigger them all.
|
||||
template <typename Observer>
|
||||
using ObserverListSafe = ObserverList<Observer, std::mutex>;
|
||||
|
||||
template <typename Observer>
|
||||
using ObserverListUnsafe = ObserverList<Observer, DummyLockable>;
|
||||
|
||||
template <typename Observer, typename Mutex>
|
||||
class ObserverList
|
||||
{
|
||||
public:
|
||||
bool Add(Observer & observer)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(m_observersLock);
|
||||
auto const it = std::find(m_observers.begin(), m_observers.end(), &observer);
|
||||
if (it != m_observers.end())
|
||||
{
|
||||
LOG(LWARNING, ("Can't add the same observer twice:", &observer));
|
||||
return false;
|
||||
}
|
||||
m_observers.push_back(&observer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Remove(Observer const & observer)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(m_observersLock);
|
||||
auto const it = std::find(m_observers.begin(), m_observers.end(), &observer);
|
||||
if (it == m_observers.end())
|
||||
{
|
||||
LOG(LWARNING, ("Can't remove non-registered observer:", &observer));
|
||||
return false;
|
||||
}
|
||||
m_observers.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename F, typename... Args>
|
||||
void ForEach(F fn, Args const &... args)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(m_observersLock);
|
||||
for (Observer * observer : m_observers)
|
||||
(observer->*fn)(args...);
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex m_observersLock;
|
||||
std::vector<Observer *> m_observers;
|
||||
};
|
||||
} // namespace base
|
||||
26
libs/base/pprof.cpp
Normal file
26
libs/base/pprof.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#include "base/pprof.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#if defined(USE_PPROF)
|
||||
#include <gperftools/profiler.h>
|
||||
#endif
|
||||
|
||||
namespace base
|
||||
{
|
||||
PProf::PProf(std::string const & path)
|
||||
{
|
||||
#if defined(USE_PPROF)
|
||||
ProfilerStart(path.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
PProf::~PProf()
|
||||
{
|
||||
CHECK(m_checker.CalledOnOriginalThread(), ());
|
||||
|
||||
#if defined(USE_PPROF)
|
||||
ProfilerStop();
|
||||
#endif
|
||||
}
|
||||
} // namespace base
|
||||
29
libs/base/pprof.hpp
Normal file
29
libs/base/pprof.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.hpp"
|
||||
#include "base/thread_checker.hpp"
|
||||
|
||||
namespace base
|
||||
{
|
||||
// This class is a simple RAII wrapper around gperftools profiler. It
|
||||
// is NOT thread-safe, moreover, before using it you must be sure that
|
||||
// there are no interfering instantiations of PProf during the
|
||||
// execution of the code you are interested in.
|
||||
class PProf final
|
||||
{
|
||||
public:
|
||||
// Starts profiling and writes profile info into |path|, discarding
|
||||
// any existing profiling data in that file.
|
||||
PProf(std::string const & path);
|
||||
|
||||
// Stops profiling.
|
||||
~PProf();
|
||||
|
||||
private:
|
||||
ThreadChecker m_checker;
|
||||
|
||||
DISALLOW_COPY_AND_MOVE(PProf);
|
||||
};
|
||||
} // namespace base
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue